WEBKT

GitHub Actions 自动化部署手把手教程:从零构建 CI/CD 工作流并发布至自有服务器

45 0 0 0

在日常开发中,每次提交代码后都要手动登录服务器、拉取最新代码、执行打包编译、重启服务,这一套机械化的操作不仅繁琐,而且极易因遗漏某一步骤导致线上事故。

借助 GitHub 官方提供的 GitHub Actions,我们可以非常轻松地为项目配置一套全自动的 CI/CD(持续集成/持续部署)流水线。只要往指定分支 Push 代码,GitHub 就会自动运行测试、打包,并安全地部署到你自己的云服务器上。

本文将以一个常见的 Node.js/前端项目为例,手把手带你配置一套直达自有 VPS 服务器的自动化部署流程。


准备工作

在配置流水线之前,需要准备好以下要素:

  1. GitHub 仓库:存放你的项目代码。
  2. 一台云服务器:确保可以通过 SSH 登录(拥有公网 IP)。
  3. 基本的 Linux 操作常识:知道如何创建目录及修改文件权限。

第一步:配置安全的 SSH 免密登录

GitHub Actions 运行在 GitHub 提供的托管虚拟环境(Runner)中。要让 Runner 能够把打包好的文件发送到你的服务器并执行重启命令,Runner 必须拥有登录你服务器的权限。

为了安全起见,绝对不能直接将服务器的 root 密码写在脚本里。我们必须使用 SSH Key(密钥对) 来完成鉴权。

1. 在本地或服务器上生成一套专用的密钥对

执行以下命令(不要一路回车,建议给这个专门用于部署的密钥起个名字,比如 id_rsa_deploy):

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

这会生成两个文件:

  • id_rsa_deploy(私钥,留在 GitHub)
  • id_rsa_deploy.pub(公钥,放到服务器)

2. 将公钥部署到目标服务器

登录你的服务器,将 id_rsa_deploy.pub 中的内容追加到目标部署用户家目录下的 .ssh/authorized_keys 文件中:

mkdir -p ~/.ssh
chmod 700 ~/.ssh
cat id_rsa_deploy.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

(注意:如果使用的是非 root 用户登录,确保该用户对部署目录拥有读写权限。)

3. 在 GitHub 仓库配置 Secrets

为了保护隐私,密钥、服务器 IP 等敏感信息不能直接写在代码仓库的 YAML 文件中。GitHub 提供了 Secrets 功能来安全地存储这些变量。

依次点击仓库的:Settings -> Secrets and variables -> Actions -> New repository secret,依次添加以下 4 个变量:

Secret 名称 对应内容 示例
SERVER_HOST 服务器的公网 IP 地址 123.45.67.89
SERVER_USER 登录服务器的用户名 rootdeploy-user
SERVER_SSH_KEY 刚刚生成的 私钥 (id_rsa_deploy) 的完整内容 -----BEGIN RSA PRIVATE KEY----- ...
SERVER_PORT SSH 端口(默认是 22) 22

第二步:编写 GitHub Actions 工作流文件

在项目根目录下,创建 .github/workflows 文件夹,并在其中新建一个名为 deploy.yml 的文件。

这个 YAML 文件定义了流水线在什么时机触发、需要运行哪些步骤。以下是一个标准的、带依赖缓存的前端打包并部署至 VPS 的完整工作流配置:

name: Build and Deploy

# 触发条件:当 main 分支收到 push 请求时触发
on:
  push:
    branches:
      - main

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      # 1. 拉取仓库代码
      - name: Checkout Code
        uses: actions/checkout@v4

      # 2. 设置 Node.js 环境
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      # 3. 配置缓存(优化构建速度,避免每次都完整下载 node_modules)
      - name: Cache Node Modules
        uses: actions/cache@v4
        id: node-cache
        with:
          path: node_modules
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      # 4. 安装依赖并执行打包
      - name: Install Dependencies & Build
        run: |
          npm install
          npm run build

      # 5. 将打包产物同步到服务器 (使用 appleboy/scp-action)
      - name: Copy Files to Server
        uses: appleboy/scp-action@v0.1.7
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          port: ${{ secrets.SERVER_PORT }}
          # source 是你要复制的本地文件/文件夹,dist 是打包生成的静态资源目录
          source: "dist/*"
          # target 是服务器上的目标部署路径
          target: "/var/www/my-app"
          strip_components: 1 # 去除 dist 层级,直接把内容扔进 my-app

      # 6. 在服务器上执行后续命令(如重启 Nginx、PM2,清理旧文件等)
      - name: SSH and Restart Services
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          port: ${{ secrets.SERVER_PORT }}
          script: |
            # 示例:如果是 Node 后端项目,可以在这里重启 PM2
            # cd /var/www/my-app && pm2 restart all
            # 如果是前端静态项目,重载 Nginx 即可
            sudo nginx -s reload

第三步:实战踩坑与优化经验

在实际应用中,鲜有一次性配置成功不报错的。这里整理了几个在搭建 CI/CD 过程中最容易遇到的“深坑”:

1. 权限不足导致 SCP 失败 (Permission Denied)

如果你的 SERVER_USER 不是 root,而是自定义的普通用户(如 deploy),那么当 GitHub Actions 尝试向 /var/www/my-app 写入文件时,极有可能会报权限错误。

解决方案
提前登录服务器,将目标目录的所有者修改为该部署用户,或者赋予其写权限:

sudo chown -R deploy:deploy /var/www/my-app

2. 缓存配置失效,每次构建依然很慢

actions/cache 步骤中,缓存的唯一标识是基于 package-lock.json 的哈希值。如果你的项目根目录下实际使用的是 yarn.lockpnpm-lock.yaml,请务必修改对应的 key 计算规则。例如使用 pnpm 时:

key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}

3. 目标服务器目录积累大量“历史垃圾”

appleboy/scp-action 默认只会覆盖同名文件,并不会自动删除服务器上已经失效的旧文件(比如旧的带 hash 值的 js 文件)。日积月累会导致服务器磁盘被撑爆。

优化方案
在拷贝文件之前,先通过 ssh-action 执行清空历史的操作,或者在打包后直接将整站打包为 tar.gz 压缩包上传,在服务器端解压并覆盖。
例如在 SSH and Restart Services 步骤中加入:

cd /var/www/my-app && rm -rf static/

4. 首次连接报错 "Host key verification failed"

当使用第三方 Action 连接服务器时,有时会因为未知的主机密钥而连接中断。appleboy/ssh-action 默认处理了这个问题,但如果你使用的是原生 ssh 命令,记得在 workflow 脚本中加上 -o StrictHostKeyChecking=no 参数来跳过公钥确认。


检验成果

完成上述配置并把 .github/workflows/deploy.yml 提交并 Push 到 GitHub 仓库的 main 分支后,点击仓库顶部的 Actions 标签页,就能看到正在运行的工作流:

GitHub Actions Flow (此处可查看具体的步骤日志)

每一个步骤左侧都会亮起绿色的对勾,代表该步骤执行成功。如果某一步出错,点击进入即可查看详细的报错日志,方便针对性排查。

至此,一个从零搭建的自动化部署流水线正式运转。未来你只需专注写代码,剩下的打包、传输、部署工作,全部交由 GitHub Actions 自动搞定。

DevOps探路者 CICD自动化部署

评论点评