前言

在现代软件开发实践中,持续集成(CI)与持续部署(CD))已成为保障代码质量、提升发布效率的核心机制

常用CI/CD工具对比

工具集成方式适用场景
GitHub Actions原生集成GitHub仓库开源项目、中小型团队
GitLab CI内置在GitLab中企业级私有化部署
Jenkins自托管,插件丰富复杂定制化流程

CI/CD的基本组成
一个典型的CI/CD流水线包含以下关键环节:

  • 代码提交触发:开发者推送代码至版本仓库(如GitHub、GitLab)时自动触发流水线
  • 依赖安装
  • 代码检测
  • 单元测试
  • 构建与部署

本文将基于一个纯HTML的简单项目(my-site)进行标准步骤剖析(步骤不含:代码检查/测试/监控)


前置准备

  1. 触发条件:push 到 main 分支
  2. 动作:通过 SSH 将项目文件(HTML/JS/CSS)同步到服务器指定目录(如 /www/wwwroot/my-site)
  3. 前提:服务器已配置好 Web 服务(如 Nginx),并监听对应域名,服务器已安装好 rsync(检查命令:rsync --version)
  4. 备份!!!

下述为文章配置,实际可根据场景调整

  • 服务器:Debian
  • 代码仓库:GitHub
  • 域名:my-site.com
  • 网站目录:/www/wwwroot/my-site

创建用户

以 root 或 sudo 用户登录服务器

ssh root@server-ip
# 或
ssh admin-user@-server-ip
sudo -i

创建 deployer 用户(仅用于文件同步,禁止交互式登录)
1.创建用户

# 创建用户,指定 home 目录,并设置 shell 为 /bin/bash(必须!否则 rsync 无法执行)
useradd -m -s /bin/bash deployer

2.清理 profile 文件(防止输出污染协议)

# 确保 deployer 用户不会输出任何欢迎信息(避免污染 rsync 协议)
rm -f /home/deployer/.bashrc /home/deployer/.profile /home/deployer/.bash_logout
touch /home/deployer/.hushlogin  # 抑制 motd
chown deployer:deployer /home/deployer/.hushlogin

生成密钥

在本地生成 SSH 密钥对(用于 GitHub)

⚠️ 不要在服务器上生成私钥!私钥只应存在于本地或 GitHub Secrets。

# 生成无密码的专用密钥(名称随意,此处为 deploy_key)
ssh-keygen -t rsa -b 4096 -f ./deploy_key -N ""

这个命令会生成两个文件:
deploy_key → 私钥(给 GitHub Secrets)
-deploy_key.pub → 公钥(放到服务器的 authorized_keys)
-N "" 表示不设 passphrase,这样 GitHub Actions 才能自动使用

生成秘钥.png


配置密钥认证

为 deployer 用户配置 SSH 密钥认证
1.创建 .ssh 目录并设置权限

mkdir -p /home/deployer/.ssh
chmod 700 /home/deployer/.ssh
chown deployer:deployer /home/deployer/.ssh

2.准备公钥,复制公钥(deploy_key.pub)内容

3.将公钥写入 authorized_keys

# 将公钥内容复制到剪贴板,执行以下命令粘贴:
cat > /home/deployer/.ssh/authorized_keys <<EOF
ssh-rsa AAAAB3NzaC1yc2E...(公钥内容)... deploy-github-action
EOF

4.设置正确权限

chmod 600 /home/deployer/.ssh/authorized_keys
chown deployer:deployer /home/deployer/.ssh/authorized_keys

设置网站目录权限

目标:让 deployer 对网站目录有读写权限,同时确保 Web 服务能读取文件

设置目录归属(假设原所有者用户为www)

chown -R deployer:www /www/wwwroot/my-site

设置安全的权限:

  • 所有者(deployer):可读写、目录可进入(rwX)
  • 组(www)和其他人:只读、目录可进入(rX)
chmod -R u=rwX,go=rX /www/wwwroot/my-site

PS: 如果设置目录权限出现类似如下错误(未使用宝塔面板或 PHP,通常不会有 .user.ini,可忽略此节)

chown: changing ownership of ‘.../.user.ini’: Operation not permitted  
chmod: changing permissions of ‘.../.user.ini’: Operation not permitted

这是是因为 .user.ini 文件被 设置了不可变属性(immutable attribute)
命令验证:

lsattr /www/wwwroot/my-site/.user.ini

如果输出包含 ----i---------,说明文件被设为 不可变!

解决方案:
1.临时移除 immutable 属性

chattr -i /www/wwwroot/my-site/.user.ini

2.继续设置目录归属(假设原所有者用户为www)

chown -R deployer:www /www/wwwroot/my-site

设置安全的权限:

  • 所有者(deployer):可读写、目录可进入(rwX)
  • 组(www)和其他人:只读、目录可进入(rX)
chmod -R u=rwX,go=rX /www/wwwroot/my-site

测试连通性

在本地测试服务器rsync能否正常工作
使用Git Bash(Windows 命令行不支持,MAC或Linux可直接使用终端)(切换到deploy_key所在目录)

ssh -i ./deploy_key deployer@my-site.com "id"
#输出类似用户信息:uid=1004(deployer) gid=1004 groups=1004
ssh -i ./deploy_key deployer@my-site.com "rsync --version"
#应该返回版本信息

配置GitHub

将私钥添加到 GitHub Secrets
复制私钥内容

cat ./deploy_key
  1. 进入 GitHub 仓库 → Settings → Secrets and variables → Actions
  2. 点击 New repository secret
  3. 填入Name和Value

Name: DEPLOY_KEY
Value: 粘贴 deploy_key 的全部内容(包括 -----BEGIN RSA PRIVATE KEY----- 和 -----END RSA PRIVATE KEY-----)

私钥配置.png

更新 GitHub Actions YAML

创建.github/workflows/ci-cd.yml文件(若无)

# 工作流的名称,在 GitHub Actions 页面中显示
name: CICD to Server

# 触发条件:定义在什么情况下运行此工作流
on:
  # 当向 `main` 分支推送代码时自动触发
  push:
    branches: [ main ]
  
  # 允许在 GitHub 网页上手动点击“Run workflow”来触发部署(用于紧急发布或测试)
  workflow_dispatch:

# 定义一个名为 `deploy` 的作业(Job)
jobs:
  deploy:
    # 在 GitHub Actions 界面中显示的作业名称
    name: Deploy via rsync
    
    # 指定使用 Ubuntu 最新版本的虚拟机来运行此作业(Linux 环境,自带 rsync/ssh)
    runs-on: ubuntu-latest

    # 定义该作业的具体执行步骤
    steps:
      # 第一步:从 GitHub 拉取当前仓库的源代码到运行环境中
      - name: 📥 Checkout code
        uses: actions/checkout@v4  # 使用官方 checkout 动作获取代码

      # 第二步:配置 SSH 私钥,使后续的 rsync 能通过 SSH 免密登录你的服务器
      - name: 🔑 Setup SSH key for deployment
        uses: webfactory/ssh-agent@v0.9.0  # 使用社区维护的 ssh-agent 动作
        with:
          # 从 GitHub Secrets 中读取名为 DEPLOY_KEY 的私钥内容
          # 注意:这不是文件路径,而是直接传入私钥文本
          ssh-private-key: ${{ secrets.DEPLOY_KEY }}

      # 第三步:使用 rsync 命令将本地所有文件同步到你的生产服务器
       # 执行 rsync 同步命令,参数说明如下:
          # -a: 归档模式(保留权限/时间等);-v: 显示过程;-z: 压缩传输;--delete: 删除服务器上已不存在的文件
          # 通过 SSH 传输,并跳过主机密钥检查(避免首次连接交互)
          # 不同步时间(--no-times)
          # 不同步权限(--no-perms)
          # 不同步用户/组(--no-group)
          # 排除 .git 目录(版本控制文件,无需部署)
          # 排除 GitHub Actions 配置目录
          # 排除宝塔面板生成的 PHP 配置文件(常带不可变属性,避免权限错误)
          # 排除说明文档
          # 排除 Git 忽略规则文件
          # 同步当前目录(即整个项目根目录)下的所有内容
          # 目标地址:用户名@域名:远程路径
      - name: 🚀 Sync files to production server
        run: |
          rsync -avz --delete \
            -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=QUIET" \
            --rsync-path="rsync" \
            --no-times \
            --no-perms \
            --no-group \
            --exclude='.git/' \
            --exclude='.github/' \
            --exclude='.user.ini' \
            --exclude='README.md' \
            --exclude='.gitignore' \
            ./ \
            deployer@my-site.com:/www/wwwroot/my-site/

最终测试

提交代码到GitHub仓库,合并到主分支,然后触发动作(自动/可手动触发),更新网站代码

测试.png

Last modification:February 3, 2026
如果觉得我的文章对你有用,您可以给博主买一杯果汁,谢谢!