强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Git 服务器搭建完全指南 / 第 14 章 - 故障排除

第 14 章 - 故障排除

本章汇总 Git 服务器运维中的常见问题及其诊断与解决方案。

14.1 SSH 连接问题

14.1.1 连接被拒绝

症状

$ git clone git@server:repo.git
# ssh: connect to host server port 22: Connection refused

诊断步骤

# 1. 检查 SSH 服务是否运行
sudo systemctl status sshd

# 2. 检查端口监听
sudo ss -tlnp | grep :22
sudo netstat -tlnp | grep :22

# 3. 检查防火墙
sudo ufw status
sudo iptables -L -n | grep 22

# 4. 测试 SSH 连接(详细模式)
ssh -vvv git@server

# 5. 检查 SSH 配置
sudo cat /etc/ssh/sshd_config | grep -E "^(Port|ListenAddress|AllowUsers|DenyUsers)"

解决方案

# 启动 SSH 服务
sudo systemctl start sshd
sudo systemctl enable sshd

# 开放防火墙端口
sudo ufw allow 22/tcp
# 或自定义端口
sudo ufw allow 2222/tcp

# 检查并修复 sshd_config
sudo vim /etc/ssh/sshd_config
sudo systemctl restart sshd

14.1.2 认证失败

症状

$ git clone git@server:repo.git
# git@server: Permission denied (publickey).

诊断步骤

# 1. 检查客户端密钥
ls -la ~/.ssh/
cat ~/.ssh/id_ed25519.pub

# 2. 检查 SSH Agent
ssh-add -l
# 如果没有密钥,添加
ssh-add ~/.ssh/id_ed25519

# 3. 检查服务器端 authorized_keys
sudo cat /home/git/.ssh/authorized_keys
# 确认公钥存在且格式正确

# 4. 检查文件权限
ls -la /home/git/.ssh/
# authorized_keys 应该是 600
# .ssh 目录应该是 700
# git 用户的 home 目录应该是 755

# 5. 测试 SSH 认证
ssh -Tvvv git@server 2>&1 | grep -i "auth\|publickey\|offer"

解决方案

# 修复文件权限
sudo chmod 700 /home/git/.ssh
sudo chmod 600 /home/git/.ssh/authorized_keys
sudo chown -R git:git /home/git/.ssh

# 确认 authorized_keys 格式(单行)
# 正确格式:
# ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... comment

# 如果使用 git-shell
sudo usermod -s /usr/bin/git-shell git

# 检查 SELinux(如果启用)
getenforce
sudo restorecon -Rv /home/git/.ssh

14.1.3 SSH 密钥不匹配

症状

$ ssh -T git@server
# debug1: Offering public key: /home/user/.ssh/id_rsa
# debug1: Authentications that can continue: publickey
# debug1: No more authentication methods to try.

诊断

# 查看服务器端接受的密钥
sudo cat /home/git/.ssh/authorized_keys | awk '{print $1, $3}'

# 查看客户端使用的密钥
ssh -vvv git@server 2>&1 | grep "Offering\|accepted"

# 确认公钥匹配
# 客户端
ssh-keygen -lf ~/.ssh/id_ed25519.pub
# 输出: 256 SHA256:xxxxx comment

# 在 authorized_keys 中应该有匹配的公钥
sudo grep "SHA256:xxxxx" /home/git/.ssh/authorized_keys

14.1.4 SSH 连接超时

症状

$ git clone git@server:repo.git
# ssh: connect to host server port 22: Connection timed out

诊断

# 网络连通性测试
ping server
traceroute server
nc -zv server 22

# 检查 DNS 解析
nslookup server
dig server

# 检查防火墙规则
sudo iptables -L -n -v
sudo ufw status verbose

解决方案

# 调整 SSH 超时配置(客户端)
# ~/.ssh/config
Host server
    ServerAliveInterval 60
    ServerAliveCountMax 3
    ConnectTimeout 30

# 服务端 KeepAlive
# /etc/ssh/sshd_config
ClientAliveInterval 60
ClientAliveCountMax 3

14.2 HTTP/HTTPS 访问问题

14.2.1 SSL 证书错误

症状

$ git clone https://git.example.com/repo.git
# fatal: unable to access '...': SSL certificate problem: unable to get local issuer certificate

解决方案

# 临时忽略 SSL 验证(不推荐生产使用)
git -c http.sslVerify=false clone https://git.example.com/repo.git

# 全局关闭 SSL 验证(不推荐)
git config --global http.sslVerify false

# 正确方案:安装正确的 CA 证书
sudo apt install ca-certificates
sudo update-ca-certificates

# 或者将自签名证书添加到信任列表
sudo cp ca-cert.pem /usr/local/share/ca-certificates/
sudo update-ca-certificates

# Git 使用特定 CA 文件
git config --global http.sslCAInfo /path/to/ca-cert.pem

14.2.2 403 Forbidden

症状

$ git push origin main
# remote: Permission to org/repo.git denied to user.
# fatal: unable to access '...': The requested URL returned error: 403

诊断

# 检查 Git 凭据
git config --list | grep credential

# 清除旧凭据
git credential reject << EOF
protocol=https
host=git.example.com
EOF

# 检查 Token 权限
curl -s -H "Authorization: token YOUR_TOKEN" \
    https://git.example.com/api/v1/user | jq '.login'

# 检查仓库权限
curl -s -H "Authorization: token YOUR_TOKEN" \
    https://git.example.com/api/v1/repos/org/repo | jq '.permissions'

14.2.3 Nginx 502 Bad Gateway

症状:访问 Git Web 界面显示 502 错误。

诊断

# 检查 Gitea 是否运行
docker compose ps
sudo systemctl status gitea

# 检查 Gitea 日志
docker logs gitea --tail 100
sudo journalctl -u gitea -f

# 检查 Nginx 错误日志
sudo tail -f /var/log/nginx/error.log

# 检查端口
curl -v http://localhost:3000

解决方案

# 确认 Gitea 正在监听正确端口
docker exec gitea netstat -tlnp 2>/dev/null || \
    docker exec gitea ss -tlnp

# 确认 Nginx upstream 配置正确
# /etc/nginx/sites-available/git.example.com
# proxy_pass http://127.0.0.1:3000;  # 确保端口匹配

# 重启服务
docker compose restart gitea
sudo systemctl restart nginx

14.3 权限问题

14.3.1 推送被拒绝

症状

$ git push origin main
# remote: error: GH006: Protected branch update failed.
# remote: error: Required status check "ci/build" is expected.

诊断

# 检查分支保护规则(Gitea API)
curl -s -H "Authorization: token $TOKEN" \
    "$GITEA_URL/api/v1/repos/owner/repo/branches/main/protection" | jq

# 检查用户权限
curl -s -H "Authorization: token $TOKEN" \
    "$GITEA_URL/api/v1/repos/owner/repo" | jq '.permissions'

解决方案

# 方式一:等待 CI 通过后合并
# 方式二:调整分支保护规则(需要管理员权限)
curl -s -X DELETE -H "Authorization: token $ADMIN_TOKEN" \
    "$GITEA_URL/api/v1/repos/owner/repo/branches/main/protection"

# 方式三:使用正确的合并流程
# 1. 创建 feature 分支
# 2. 提交 PR
# 3. 等待 CI 和 Review 通过
# 4. 由有权限的用户合并

14.3.2 文件权限问题

症状

$ git push
# fatal: Unable to create '/opt/git/repo.git/./objects/pack/tmp_pack_XXXX': Permission denied

解决方案

# 检查仓库目录权限
ls -la /opt/git/
ls -la /opt/git/repo.git/

# 修复权限
sudo chown -R git:git /opt/git/repo.git/
sudo chmod -R 775 /opt/git/repo.git/

# 如果使用 SGID
sudo find /opt/git/repo.git -type d -exec chmod g+s {} \;

# 确认 git 用户的 umask
sudo -u git umask
# 应该是 0002 或 0022

14.3.3 Gitolite 权限问题

症状

$ git push origin main
# FATAL: R any repo alice DENIED by VREF/limit-push-size

诊断

# 检查 Gitolite 配置
sudo -u git cat /home/git/.gitolite/conf/gitolite.conf

# 检查用户权限
sudo -u git /home/git/bin/gitolite info -u alice

# 检查日志
sudo cat /home/git/.gitolite/logs/gitolite-*.log | tail -50

14.4 性能问题

14.4.1 推送/拉取缓慢

诊断

# 测量传输速度
time git clone git@server:large-repo.git

# 检查仓库大小
du -sh /opt/git/large-repo.git

# 检查大文件
git rev-list --objects --all | \
    git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
    sed -n 's/^blob //p' | sort -rnk2 | head -20

# 检查网络带宽
iperf3 -c server -t 10

解决方案

# 1. 清理仓库
cd /opt/git/large-repo.git
git gc --aggressive --prune=now

# 2. 使用 Git LFS 管理大文件
git lfs migrate import --include="*.zip,*.tar.gz,*.psd" --everything

# 3. 启用 Git 协议 v2
git config --global protocol.version 2

# 4. 使用浅克隆
git clone --depth 1 git@server:repo.git

# 5. 配置 Git 压缩
git config --global core.compression 6
git config --global pack.windowMemory 256m

14.4.2 GitLab 内存不足

症状:GitLab 服务不稳定或无法启动。

诊断

# 检查内存使用
free -h
ps aux --sort=-%mem | head -20

# GitLab 组件内存
ps aux | grep -E "(puma|sidekiq|gitaly|postgres)" | awk '{print $4, $11}'

解决方案

# /etc/gitlab/gitlab.rb

# 减少 Puma worker
puma['worker_processes'] = 2

# 减少 Sidekiq 并发
sidekiq['max_concurrency'] = 10

# 禁用不需要的服务
prometheus_monitoring['enable'] = false
alertmanager['enable'] = false
grafana['enable'] = false

# PostgreSQL 优化
postgresql['shared_buffers'] = "256MB"
postgresql['work_mem'] = "8MB"

# Redis 优化
redis['maxmemory'] = "128mb"
sudo gitlab-ctl reconfigure
sudo gitlab-ctl restart

14.4.3 数据库性能

# PostgreSQL 慢查询
sudo -u postgres psql -d gitea -c "
SELECT query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 20;"

# 索引优化
sudo -u postgres psql -d gitea -c "
SELECT schemaname, tablename, indexname
FROM pg_indexes
WHERE schemaname = 'public'
ORDER BY tablename;"

# VACUUM
sudo -u postgres psql -d gitea -c "VACUUM ANALYZE;"

14.5 Docker 相关问题

14.5.1 容器无法启动

诊断

# 查看容器日志
docker logs gitea --tail 100

# 检查容器状态
docker ps -a

# 检查资源限制
docker stats

# 检查磁盘空间
df -h
docker system df

# 检查 Docker 日志
sudo journalctl -u docker -f

常见原因和解决

# 1. 端口被占用
sudo lsof -i :3000
# 停止占用端口的进程或修改端口映射

# 2. 磁盘空间不足
docker system prune -a --volumes  # 谨慎使用
sudo apt autoremove

# 3. 权限问题
# 确保 Docker 卷目录权限正确
ls -la /var/lib/docker/volumes/

# 4. 网络问题
docker network ls
docker network inspect gitea-net

14.5.2 Docker 磁盘空间回收

# 查看 Docker 磁盘使用
docker system df

# 清理未使用的资源
docker system prune          # 清理停止的容器、未使用的网络、悬空镜像
docker system prune -a       # 清理所有未使用的镜像
docker volume prune          # 清理未使用的卷

# 清理构建缓存
docker builder prune

# 清理特定时间前的镜像
docker image prune -a --filter "until=168h"  # 7天前

14.6 Git 操作问题

14.6.1 合并冲突

# 拉取最新代码
git pull origin main

# 如果有冲突,手动解决
# 编辑冲突文件,删除 <<<<<<< ======= >>>>>>> 标记
git add .
git commit -m "Resolve merge conflict"

# 或者使用 rebase
git pull --rebase origin main
# 解决冲突后
git rebase --continue

14.6.2 误操作恢复

# 撤销最后一次提交(保留修改)
git reset --soft HEAD~1

# 撤销最后一次提交(丢弃修改)
git reset --hard HEAD~1

# 恢复误删的分支
git reflog  # 查找分支的最后提交
git checkout -b recovered-branch <commit-hash>

# 恢复误删的文件
git checkout HEAD -- path/to/file

# 恢复到某个提交
git revert <commit-hash>  # 创建新的反向提交(安全)
git reset --hard <commit-hash>  # 直接重置(危险,会影响历史)

14.6.3 大仓库克隆超时

# 浅克隆
git clone --depth 1 --single-branch --branch main git@server:repo.git

# 分步获取历史
git fetch --depth 100
git fetch --depth 1000
git fetch --unshallow  # 获取完整历史

# 使用 Git 部分克隆
git clone --filter=blob:none git@server:repo.git  # 延迟下载 blob
git clone --filter=blob:limit=1m git@server:repo.git  # 只下载小 blob

14.7 服务监控与告警

14.7.1 简单监控脚本

#!/bin/bash
# monitor-git.sh

GITEA_URL="https://git.example.com"
WEBHOOK_URL="https://hooks.example.com/alert"

check() {
    local name="$1"
    local url="$2"
    local expected="${3:-200}"

    code=$(curl -sf -o /dev/null -w "%{http_code}" --connect-timeout 5 "$url" 2>/dev/null || echo "000")

    if [ "$code" != "$expected" ]; then
        echo "❌ $name: HTTP $code"
        curl -sf -X POST "$WEBHOOK_URL" \
            -H "Content-Type: application/json" \
            -d "{\"text\":\"❌ Git 服务告警: $name 返回 HTTP $code\"}" > /dev/null &
        return 1
    else
        echo "✅ $name: HTTP $code"
        return 0
    fi
}

echo "=== Git 服务监控 $(date) ==="
check "Gitea API" "${GITEA_URL}/api/v1/version"
check "Gitea Web" "${GITEA_URL}/"

# 检查 SSH
if ssh -o ConnectTimeout=5 -T git@localhost 2>&1 | grep -q "successfully"; then
    echo "✅ SSH: OK"
else
    echo "❌ SSH: Failed"
fi

# 检查磁盘
disk_usage=$(df /opt/git | tail -1 | awk '{print $5}' | sed 's/%//')
if [ "$disk_usage" -gt 85 ]; then
    echo "⚠️  磁盘使用率: ${disk_usage}%"
fi

# 检查内存
mem_available=$(free -m | awk '/^Mem:/ {print $7}')
if [ "$mem_available" -lt 512 ]; then
    echo "⚠️  可用内存: ${mem_available}MB"
fi

14.7.2 Cron 监控

# 每 5 分钟检查一次
*/5 * * * * /opt/scripts/monitor-git.sh >> /var/log/git-monitor.log 2>&1

14.8 扩展阅读


本章小结

学到了什么关键要点
SSH 问题密钥认证、文件权限、防火墙是三大常见原因
HTTP 问题SSL 证书、Nginx 配置、端口映射需逐一检查
权限问题分支保护、文件权限、用户角色需对照排查
性能问题大仓库用 LFS、GitLab 调内存、数据库优化
Docker 问题日志先行、磁盘空间、端口冲突是最常见原因

下一章:第 15 章 - 最佳实践 — 运维规范、备份策略、安全加固和团队协作。