Automated Git Deploy on VPS: CI/CD Beginner Guide

厌倦了每次更改都通过 FTP 上传文件,或 SSH 进入服务器手动拉取更新?如果您已经有一个 VPS,您可以设置 自动 Git 部署,这样每个 git push 都会立即将最新代码部署到服务器。

本指南从头开始指导您在 Ubuntu VPS 上创建 Git 裸库 + post-receive 钩子。它适用于 HTML、PHP、Node.js 或 Python 项目,并作为现代 CI/CD 工作流程的基础。

前置条件:安装了 Git 的 Ubuntu 20.04/22.04 VPS、SSH 访问权限、运行的 Nginx 或 Apache,以及本地机器上安装的 Git。

Git 部署如何工作?

核心思想是在 VPS 上创建一个裸库来接收来自本地机器的推送,然后使用名为 post-receive 的 Git 钩子每当推送到达时自动将最新代码检出到网络根目录。

步骤描述
1. 裸库在 VPS 上创建 Git 库(无工作树)— 仅接收推送
2. post-receive在每次推送后自动运行的 Shell 脚本 — 将文件检出到网络根目录
3. 远程库在本地机器上将 VPS 添加为 Git 远程
4. git push推送代码到 VPS — 钩子触发 — 网站自动更新

步骤 1 — 准备 VPS 文件夹结构

通过 SSH 连接到您的 VPS 并创建两个目录:裸库和网络根目录:

# Bare repository — receives Git pushes
mkdir -p /home/deploy/repos/mysite.git

# Web root — Nginx/Apache serves files from here
mkdir -p /var/www/mysite

为什么要两个目录?裸库仅存储 Git 对象(无项目文件)。网络根目录保存由网络服务器提供的实际文件。这种分离是最佳做法 — 它将 Git 内部结构与公共网络目录分开。

步骤 2 — 初始化裸库

cd /home/deploy/repos/mysite.git
git init --bare

您应该看到:已在 /home/deploy/repos/mysite.git/ 初始化空 Git 库。该目录将包含 HEAD、config、objects/、refs/ — 无项目文件。

步骤 3 — 创建 post-receive 钩子

钩子是 Git 在事件发生时自动运行的脚本。post-receive 钩子在推送被接受后触发:

nano /home/deploy/repos/mysite.git/hooks/post-receive

粘贴此内容:

#!/bin/bash
GIT_WORK_TREE=/var/www/mysite GIT_DIR=/home/deploy/repos/mysite.git git checkout -f main
echo "✅ 部署成功 → /var/www/mysite"

保存并使其可执行:

chmod +x /home/deploy/repos/mysite.git/hooks/post-receive

重要:如果您的默认分支是 master 而不是 main,请相应地更新 git checkout 行。

步骤 4 — 设置网络根目录权限

确保网络服务器用户对网络根目录具有读取访问权限(通常为 Ubuntu 上 Nginx/Apache 的 www-data):

chown -R www-data:www-data /var/www/mysite
chmod -R 755 /var/www/mysite

如果您以 deploy 用户而不是 root 进行部署,请将该用户添加到 www-data 组:

usermod -aG www-data deploy

步骤 5 — 将 VPS 添加为 Git 远程(本地机器)

在本地机器上(不是 VPS),导航到您的项目文件夹并将 VPS 添加为名为 production 的远程:

# If you don't have a local repo yet
git init
git add .
git commit -m "Initial commit"

# Add VPS as remote (replace YOUR_VPS_IP)
git remote add production ssh://root@YOUR_VPS_IP/home/deploy/repos/mysite.git

使用 SSH 密钥:如果已配置 SSH 密钥身份验证,推送不需要密码。有关设置说明,请参阅我们的通过 SSH 连接到 VPS指南。

步骤 6 — 首次部署

首次将代码推送到 VPS:

git push production main

如果一切配置正确,您将看到如下输出:

Counting objects: 5, done.
Writing objects: 100% (5/5), 512 bytes | 512.00 KiB/s, done.
remote: ✅ Deploy successful → /var/www/mysite
To ssh://root@YOUR_VPS_IP/home/deploy/repos/mysite.git
 * [new branch]      main -> main

验证文件是否在网络根目录中:

ls -la /var/www/mysite

步骤 7 — 配置 Nginx 虚拟主机

如果还未设置虚拟主机,请创建一个指向网络根目录的虚拟主机:

nano /etc/nginx/sites-available/mysite
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    root /var/www/mysite;
    index index.html index.php;

    location / {
        try_files $uri $uri/ =404;
    }
}
# Enable the config
ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

步骤 8 — 日常工作流

设置完成后,从现在开始部署只需三个命令:

# Make changes, commit, and push
git add .
git commit -m "Fix navigation header"
git push production main

钩子立即将最新文件检出到网络根目录 — 无需 FTP,无需手动 SSH。

Git 部署设置的安全加固

配置错误的 Git 部署可能会暴露您的服务器受到未授权访问。在使用任何生产部署前,请应用以下安全措施:

1. 禁用 SSH 密码身份验证

SSH 密钥身份验证在密码学上比密码更强,并且对暴力破解免疫。禁用所有用户的密码登录:

# Edit /etc/ssh/sshd_config
PasswordAuthentication no
PubkeyAuthentication yes
# Restart SSH service to apply changes
systemctl restart sshd

2. 创建专用部署用户

永远不要为自动化部署使用 root。创建权限仅限于裸库和网络根目录的 deploy 用户:

# Create deploy user with no shell access
adduser --disabled-login --gecos "" deploy
# Grant ownership of the bare repo and web root
chown -R deploy:deploy /home/deploy/repos/
chown -R deploy:www-data /var/www/mysite
chmod -R 775 /var/www/mysite

3. 将 SSH 密钥限制为仅 Git 命令

为 authorized_keys 中的部署密钥添加 command= 限制,这样即使密钥被泄露,它也只能运行 Git 操作:

# In /home/deploy/.ssh/authorized_keys
command="git-shell -c \"$SSH_ORIGINAL_COMMAND\"",no-port-forwarding,no-X11-forwarding ssh-rsa AAAA... your-deploy-key

生产前安全检查清单:禁用 PasswordAuthentication,仅使用 SSH 密钥,配置 UFW 防火墙,使用专用部署用户,并定期使用 journalctl -u sshd 查看 SSH 日志。

在钩子中管理环境变量和机密

现代应用程序依赖于环境变量来存储数据库凭据、API 密钥和其他不应提交到版本控制的机密。以下是在服务器上管理机密的两种可靠方法:

方法 1:从网络根目录外的文件源获取机密

在网络服务器无法读取的目录中创建 .env 文件,并在钩子中获取它:

#!/bin/bash
GIT_WORK_TREE=/var/www/mysite GIT_DIR=/home/deploy/repos/mysite.git git checkout -f main
# Load secrets stored outside the web root
source /home/deploy/secrets/mysite.env
# Run database migrations (example for Laravel)
cd /var/www/mysite
php artisan migrate --force
echo "✅ Deployed with secrets loaded"

对机密目录设置严格的权限:

mkdir -p /home/deploy/secrets
chmod 700 /home/deploy/secrets
nano /home/deploy/secrets/mysite.env
# Example .env content
export DB_HOST=localhost
export DB_NAME=mysite_db
export DB_PASS=your_secure_password
export APP_KEY=base64:xxxxxxxxxxxxx

方法 2:检出后符号链接 .env

如果您的框架从项目根目录读取 .env,请在钩子中创建符号链接:

#!/bin/bash
GIT_WORK_TREE=/var/www/mysite GIT_DIR=/home/deploy/repos/mysite.git git checkout -f main
# Symlink persistent .env from outside the repo
ln -sf /home/deploy/secrets/mysite.env /var/www/mysite/.env
echo "✅ Deployed and .env linked"

永远不要将机密提交到 Git。即使在私有库中,一旦机密出现在提交历史中,必须立即轮换 — 无法轻易从所有克隆中删除历史。如果不小心提交了机密,请先撤销它并生成新机密。

将 Git 部署与其他部署方法进行比较

了解 Git 部署在生态系统中的位置可以帮助您为每个项目阶段选择正确的工具:

Method Complexity Best For Limitation
Git Bare + Hook Low–Medium Single VPS, small team No automatic rollback
GitHub Actions Medium Test-gated deployments Requires GitHub, usage limits
FTP Manual Very Low Shared hosting, tiny sites Slow, prone to human error
rsync + SSH Low Static sites, no history needed No version control
Docker + Registry High Microservices, multi-server Requires Docker expertise

对于运行小型到中型项目的单个 VPS 的开发人员,Git 裸库 + 钩子是实现自动化部署的最快方法 — 设置不到 30 分钟,零外部依赖,以后可轻松扩展为完整 CI/CD 管道。

同时推送到 GitHub 和 VPS

要同时推送到 GitHub 和您的 VPS,您可以为单个远程添加多个推送 URL:

# Add GitHub as origin
git remote add origin https://github.com/youruser/mysite.git

# Or configure multiple push URLs for one remote
git remote set-url --add --push origin https://github.com/youruser/mysite.git
git remote set-url --add --push origin ssh://root@YOUR_VPS_IP/home/deploy/repos/mysite.git

# One push goes to both GitHub and VPS
git push origin main

在钩子中添加构建步骤 (Node.js / PHP)

对于需要构建步骤的项目,直接将其添加到钩子中:

Node.js 示例

#!/bin/bash
GIT_WORK_TREE=/var/www/mysite GIT_DIR=/home/deploy/repos/mysite.git git checkout -f main
cd /var/www/mysite
npm install --production
pm2 restart mysite || pm2 start app.js --name mysite
echo "✅ Deployed and PM2 restarted"

PHP Composer 示例

#!/bin/bash
GIT_WORK_TREE=/var/www/mysite GIT_DIR=/home/deploy/repos/mysite.git git checkout -f main
cd /var/www/mysite
composer install --no-dev --optimize-autoloader
echo "✅ 部署完成并安装了 Composer"

常见问题故障排除

推送成功但网络根目录中没有文件

验证本地机器和钩子之间的分支名称是否匹配(main vs master)。检查钩子文件是否正确:

cat /home/deploy/repos/mysite.git/hooks/post-receive

推送时拒绝访问

验证您的 SSH 密钥是否配置正确,以及用户是否有权写入裸库:

ls -la /home/deploy/repos/

部署后网站显示 403

修复网络根目录权限:

chown -R www-data:www-data /var/www/mysite
find /var/www/mysite -type d -exec chmod 755 {} \;
find /var/www/mysite -type f -exec chmod 644 {} \;

后续步骤:熟悉 Git 部署后,升级到使用 GitHub Actions 或 GitLab CI 的完整 CI/CD 管道 — 在每次部署前自动运行测试,以确保只有通过的代码到达生产环境。

需要用于 Git 部署的 VPS?

AsiaGB 在泰国和新加坡提供具有完全 root 访问权限的 VPS,起价 ฿500/月。

查看 VPS 方案 →