目录

背景

之前有一篇文章是关于 www.9ong.com 静态站点通过以下方式实现持续集成构建: githubactions 持续集成构建部署 hugo

考虑到,未来几年可能会因为服务器价格问题,频繁更换迁移服务器,所以需要完善下原来的工作流,方便后续迁移服务时,只要调整域名解析、准备服务器相关权限和秘钥对、工作流变量调整,即可完成自动迁移。(如果哪天服务越来越干净简洁时,还可以直接镜像,镜像有个前提是确保系统版本兼容,对于 ubuntu 每 2 年就有个 LTS 版本)

今天这篇文章是备忘如何通过 githubactions 持续集成构建部署 go 动态站点 share.9ong.com

GitHub Actions

GitHub Actions 提供了直接在 GitHub 仓库中创建软件生命周期工作流(workflow)的功能,极大地方便了持续集成、持续部署的实现。

GitHub 做了一个官方市场,可以搜索到他人提交的 actions。另外,还有一个 awesome actions 的仓库,也可以找到不少 action。

GitHub Marketplace · Actions to improve your workflow

复制来源另一份文档。

基本思路

基本思路是在每次 push 到 GitHub 之后触发 Actions 将最新代码 checkout 到 GitHub Actions 虚拟环境中,并开始编译构建,构建成功后通过 rsync 同步部署到 VPS 或云服务器,成功后允许通过 ssh 再执行系统指令,比如重启服务等。

sequenceDiagram;
    Local-->>9ong.git: push
    9ong.git-->>GitHubActions:checkout
    GitHubActions-->>vps:rsync
    GitHubActions-->>vps:command
    

构造工作流

1、准备 systemctl 管理 go 服务

目标:在工作流中编译构建 go 二进制文件后,最后将二进制文件与其他配置、HTML 模板文件同步到目标服务器后,需要重启二进制文件对应的服务,比如我们这里叫 microblog 服务。

所以我们需要对这个服务进行管理,比如 systemctl、supervisorctl 等管理服务状态、启动、重启、重试、日志等。

这里我们选择系统自带的 systemctl ,避免引入其他模块。直接给结果,不详述过程:

  • 1.1 准备 microblog.service 配置文件

    参考:

    tsingchan@iZ7xvds2ldv3fezqxvka5zZ:~$ cat /etc/systemd/system/microblog.service
    

    内容如下,copilot 补充了注释说明:

    [Unit]
    Description=9ong-microblog  # 描述该服务的名称或用途
    ConditionFileIsExecutable=/var/www/html/microblog-new/microblog  # 检查文件是否可执行
    
    After=network-online.target  # 在 network-online.target 启动后运行
    Wants=network-online.target  # 依赖于 network-online.target
    
    [Service]
    StandardOutput=journal+console  # 将标准输出同时记录到系统日志和控制台
    StandardError=journal+console  # 将标准错误输出同时记录到系统日志和控制台
    StartLimitInterval=3600  # 限制启动次数的时间间隔(单位:秒)
    StartLimitBurst=10  # 启动次数的限制阈值
    ExecStart=/var/www/html/microblog-new/microblog  # 指定要运行的命令或程序
    WorkingDirectory=/var/www/html/microblog-new  # 设置工作目录
    
    Restart=on-failure  # 在失败时自动重启服务
    
    RestartSec=120  # 重启间隔(单位:秒)
    [Install]
    WantedBy=multi-user.target  # 指定服务在 multi-user.target 启动时启用
    
    
    

    注:这里特别注意要设置 WorkingDirectory 该配置项用于服务在哪个工作目录启动,确保当前目录就是工作目录,方便读取对应的配置与模板文件。

  • 1.2 reload 生效

    sudo systemctl daemon-reload
    
  • 1.3 检查与测试

    事前,可以先准备一个测试的 go 二进制文件。

    sudo systemctl status microblog
    
    ● microblog.service - 9ong-microblog
     Loaded: loaded (/etc/systemd/system/microblog.service; disabled; vendor preset: enabled)
     Active: active (running) since Sun 2024-04-21 00:15:13 CST; 11h ago
    Main PID: 8602 (microblog)
      Tasks: 5 (limit: 1939)
     Memory: 14.2M
        CPU: 64ms
     CGroup: /system.slice/microblog.service
             └─8602 /var/www/html/microblog-new/microblog
    
    00:15:13 iZ7xvds2ldv3fezqxvka5zZ systemd[1]: Started 9ong-microblog.
    ...
    ...
    
    

    支持:start restart ,更多详见 systemctl 命令选项。

  • 1.4 系统启动时自启动服务

    tsingchan@iZ7xvds2ldv3fezqxvka5zZ:~$ sudo systemctl enable microblog
    Created symlink /etc/systemd/system/multi-user.target.wants/microblog.service → /etc/systemd/system/microblog.service.
    

    这个命令会创建一个符号链接,将服务单元文件指向/etc/systemd/system/multi-user.target.wants/目录,这表明服务应该在达到多用户运行级别时启动。

2、准备github工作流yaml配置

略去过程,直接给结果。在项目代码中的 .github/workflows 目录下新增 go.yml 文件,copilot 加工注释:

This workflow will build a golang project
For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go


name: publish microblog  # 设置工作流的名称

on:
  push:
    branches: [ "master" ]  # 当代码推送到 master 分支时触发工作流
    paths-ignore: # 下列文件的变更不触发部署,可以自行添加
      - '.gitignore'
      - 'README.md'

jobs:

  build:
    runs-on: ubuntu-latest  # 在 Ubuntu 最新版本上运行作业
    steps:
      - uses: actions/checkout@v3  # 使用 actions/checkout 动作以获取代码

      - name: Set up Go  # 设置 Go 环境
        uses: actions/setup-go@v4
        with:
          go-version: '1.16.8'

      - name: Build  # 编译 Go 项目
        run: go build -v

        #- name: Test
        #run: go test -v ./...

      - uses: webfactory/ssh-agent@v0.4.1  # 使用 ssh-agent 动作以设置 SSH 密钥
        with:
          ssh-private-key: |
            ${{ secrets.BLOG_DEPLOY_KEY_166 }}

      - name: Scan public keys  # 扫描公钥
        run: |
          ssh-keyscan ${{ secrets.BLOG_DEPLOY_HOST_166 }} >> ~/.ssh/known_hosts            

      - name: Deploy  # 部署到远程服务器
        run: |
          rsync -a ./ tsingchan@${{ secrets.BLOG_DEPLOY_HOST_166 }}:/var/www/html/microblog-new
          # rsync 不要加--delete 选项,避免将 micro_server.db 给删除了,TODO,更换 db 的路径。

      - name: executing remote ssh commands using password  # 使用密码执行远程 SSH 命令
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.BLOG_DEPLOY_HOST_166 }}
          username: ${{ secrets.BLOG_DEPLOY_USERNAME_166 }}
          password: ${{ secrets.BLOG_DEPLOY_PASSWORD_166 }}
          port: ${{ secrets.BLOG_DEPLOY_PORT_166 }}
          script: echo ${{ secrets.BLOG_DEPLOY_PASSWORD_166 }}|sudo -S systemctl restart microblog

第一个版本就先这样,不做多余说明,该优化的可以在后续工作流使用中进一步优化。

3、工作流环境变量

go.yml 文件

环境变量:其中会使用到环境变量,这些环境变量可以在 github 对应项目中的 setting 中的 secrets and variables - action 中添加。

  • BLOG_DEPLOY_HOST_166

    目标服务器 ip。

  • BLOG_DEPLOY_USERNAME_166 目标服务器可访问用户名。建议将该用户加入 sudo 组。

  • BLOG_DEPLOY_PASSWORD_166 USERNAME 对应的密码。

  • BLOG_DEPLOY_PORT_166 目标服务器 ssh 支持的端口,一般默认是 22。

  • BLOG_DEPLOY_KEY_166 工作流使用 ssh 连接目标服务器,目标服务器需要为工作流提供一个私钥用于访问目标服务器。秘钥对详见 4、SSH秘钥对

    这个环境变量是私钥内容。公钥需要加入目标服务器

4、SSH秘钥对

在 Deploy 部署环节,需要使用 rsync(基于 SSH)连接到 VPS,因此使用 ssh-keygen 命令生成一对密钥(推荐使用 ed25519 算法)。注意此对密钥不能加密码保护(passphrase),以便在工作流中无人值守。此步骤可在任意一台支持 ssh-keygen 命令的设备上完成,但注意密钥不要泄露,因为没有密码保护。

ssh-keygen -t ed25519 -f ~/.ssh/9ong_deploy_key

再将公钥 ~/.ssh/9ong_deploy_key.pub 的内容添加到 VPS 的 ~/.ssh/authorized_keys 中linux 服务器上 authorized_keys 文件用途,将私钥 ~/.ssh/9ong_deploy_key 的内容添加到 GitHub 仓库的 Settings -> Secrets 中并命名为 BLOG_DEPLOY_KEY。

就可以在工作流中以环境变量 ${{secrets.BLOG_DEPLOY_KEY}} 的形式使用私钥,而不需要将私钥内容直接贴在工作流中。

环境变量 ${{secrets.BLOG_DEPLOY_HOST}} 同理。

工作流执行

microblog-github-workflow-go.png

参考