跳过正文
  1. 系列/
  2. VPS 实战系列/

任务 10:打通 CI&CD(持续集成与部署),全自动上云实战

其雁过无痕
作者
其雁过无痕
目录

过去写完一篇笔记,需要手动执行编译指令,打开 FTP 软件,再把文件生硬地拖拽到服务器上。现在可以通过 GitHub Actions,实现一套极致的极客工作流:只要在本地执行 git push 把代码推送到 GitHub 的主分支,云端就会自动完成编译,并通过 SSH 悄无声息地将最新博客更新到 VPS 上 。这不仅让我们彻底告别了手动上传的繁琐,更是掌握现代互联网大厂 DevOps 核心工作流的关键一步 。

针对这台小内存 VPS,我们采用了最轻量级的方案:利用 GitHub 免费的高配置服务器进行“重体力”编译,然后直接通过 SCP 协议将纯静态网页传输到 Nginx 目录下,全程不占用 VPS 的一丁点算力。

第一步:将服务器“钥匙”托管给 GitHub
#

为了让 GitHub 机器人能够免密登录 VPS 传文件,必须将安全凭证存入仓库的密码箱(Secrets)中。

在私有仓库的 Settings -> Secrets and variables -> Actions 中,依次添加独立变量:

  • SERVER_IP:VPS 的公网 IP。
  • SERVER_USERNAME:登录用户名(通常为 root)。
  • SERVER_PORT:修改过后的高位 SSH 端口。
  • SERVER_SSH_KEY:本地电脑的 SSH 私钥(包含头尾的完整多行文本)。
  • SERVER_SSH_PASSPHRASE:如果生成密钥时设置了保护密码,必须填入此项,否则机器人握手会被无情拒绝。

当然,可以为机器人专门生成一个“无密码”密钥,这样也更符合 DevOps 规范,同时下方的 deploy.yml 要将 passphrase: ${{ secrets.SERVER_SSH_PASSPHRASE }} 这一行删掉

第二步:编写云端流水线剧本 (GitHub Actions)
#

在项目根目录新建 .github/workflows/deploy.yml,为 GitHub 的临时服务器下达指令集:

name: Deploy Hugo Blog to VPS

on:
  push:
    branches:
      - main # 监听 main 分支的代码推送

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

    steps:
      # 1. 拉取最新代码 (必须开启 submodules 以拉取主题)
      - name: Checkout Code
        uses: actions/checkout@v4
        with:
          submodules: true
          fetch-depth: 0

      # 2. 配置编译环境
      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v3
        with:
          hugo-version: "latest"
          extended: true

      # 3. 执行编译并压缩静态资源
      - name: Build Site
        run: hugo --minify

      # 4. SCP 传输到 VPS
      - name: Deploy to VPS
        uses: appleboy/scp-action@master
        with:
          host: ${{ secrets.SERVER_IP }}
          username: ${{ secrets.SERVER_USERNAME }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          passphrase: ${{ secrets.SERVER_SSH_PASSPHRASE }} # 密钥保护密码
          port: ${{ secrets.SERVER_PORT }}
          source: "public/" # 编译产物目录
          target: "/opt/blog/public" # VPS 上的目标存放路径
          strip_components: 1 # 剥离 public 外壳,直接倾倒内部文件

第三步:Nginx 容器的最终接应
#

文件顺利抵达 VPS 后,还需要让前端的 Nginx 准确找到它们。

在部署 Nginx 的 docker-compose.yml 中,将宿主机的接收目录挂载到容器内部:

volumes:
  # 将 GitHub Actions 传过来的静态文件,挂载到 Nginx 默认读取目录
  - /opt/blog/public:/usr/share/nginx/html

并在映射的 nginx.conf 中,将根目录精确指向容器内的挂载点:

    location / {
        root /usr/share/nginx/html;  # 必须是容器内的绝对路径
        index index.html index.htm;
    }

执行 docker compose downdocker compose up -d 重启容器,大功告成!

🛠️ 避坑指南 (Troubleshooting)
#

在打通这条流水线的过程中,我踩过了几个极其经典的工程化“大坑”,特此记录:

  1. 私钥的密码保护门槛:报错 ssh: this private key is passphrase protected。就是生成密钥时设置了 Passphrase,机器人将无法输入。必须在流水线的 scp 插件参数中显式传递 passphrase 字段。
  2. 连字符问题:在 VPS 终端执行重启时报错 Command 'docker-compose' not found。现代 Docker 环境(V2+)已将 compose 集成为内置插件,必须去掉中间的连字符,改用 docker compose
  3. Nginx 强拦截指令的遮挡:部署成功后依旧显示旧版测试页面。原因在于 nginx.conf 中遗留了 return 200 '...'; 拦截指令。Nginx 识别到该指令会直接返回文本,不再读取本地 HTML,必须将其替换为标准的 root 指令。

至此,整套自动化流程已彻底打通。把重复的劳动外包给机器,把精力留给创作!

补充记录:为 GitHub Actions 申请专属密钥(DevOps规范)
#

为了安全隔离风险(防止在GitHub仓库密钥泄露时,波及个人主机),不应该直接将私人电脑的私钥交给机器人,而是为它单独配备一把专用的“无密码”钥匙,出问题就直接删除这个bot的专属密钥就可以了:

  1. 生成无密码密钥: 在本地执行 ssh-keygen -t ed25519 -C "github-actions-bot"
    • 提示输入保存路径时,切记输入新名字(例如 C:\Users\guo05\.ssh\id_ed25519_github_bot)以防覆盖自己原有的主密钥!
    • 提示设置密码(Passphrase)时,直接连按两次回车跳过,生成无密码私钥以满足自动化无人值守的需求。
  2. 给 VPS 放行: 将生成的公钥.pub 结尾的文件)内容复制,追加粘贴到 VPS 的 /root/.ssh/authorized_keys 文件的最下方新起一行。 (建议在公钥的乱码末尾空一格,加上如 github-actions-bot 的备注,方便辨认。)
  3. 把私钥托付给 GitHub: 将私钥(无后缀的文件)的完整内容复制。去到 GitHub 仓库的 Settings -> Secrets and variables -> Actions 中,新建或覆盖掉原本的 SERVER_SSH_KEY 机密变量。
  4. 清理冗余配置: 因为这是一把无锁的钥匙,所以不用再传解密密码:
    • .github/workflows/deploy.yml 脚本中 passphrase: ${{ secrets.SERVER_SSH_PASSPHRASE }} 这一行整行删除,防止解密报错。
    • 顺手在 GitHub 网页上删掉不再使用的 SERVER_SSH_PASSPHRASE Secret,保持环境配置纯净。

相关文章

番外:3X-UI 节点订阅合并与优化

在使用 3X-UI 搭建代理节点时,很多朋友可能会遇到这样一个痛点: 因为 3X-UI 的 Vision 流控(xtls-rprx-vision)是需要在具体客户端上设置的,这就导致开启了 Vision 和未开启 Vision 的节点,被强制拆分成了不同的订阅链接。 当把它们导入到代理软件(比如 V2rayN)时,软件里会显示出多个不同的订阅分组,不仅观感杂乱,后期维护起来也极其不便。

任务 7:部署限制内存的数据库

在服务器部署的进阶之路上,部署限制内存的数据库是一个必经的关卡 。对于这种小内存的 VPS(比如 RackNerd),如果直接裸跑默认配置的 MySQL 8.0,它会一口气吞掉 400MB 甚至更多的内存,极易导致服务器 OOM(内存溢出)死机。

番外:1G 小内存 VPS 部署 Java JSP 项目实战:Docker 本地构建 + 远程运行完美方案

在拥有了一台属于自己的 VPS(如 1核 1G内存,配置了 2G Swap)后,很多新手在尝试部署 Java 项目时,往往会选择直接在服务器上安装 Maven 或运行 docker build。但现实很残酷:Java 编译极其消耗内存,1G 的内存在构建瞬间就会被挤爆,导致系统卡死或触发 OOM (Out Of Memory) 杀掉进程。

任务 3:熟练使用终端复用工具

核心目标 # 操作:安装并学习使用 tmux。 收获:掌握在服务器上跑长耗时任务的能力。即使本地 SSH 突然断开,任务依然会在后台运行,不会前功尽弃。 核心工具:tmux # tmux (Terminal Multiplexer) 是现代服务端开发的必备工具,解决了远程连接中途掉线导致任务中断的痛点。