Github ci/cd 自动化部署服务

205 阅读4分钟

Github ci/cd 自动化部署服务

在Saas产品迭代过程中,难免需要多次上线新加的功能服务。一开始通过手动操作来上线没啥问题,但是后续不断维护升级,操作次数增加,环境增多,那么消耗的时间会越来越多,手动操作带来的风险也会越来越大。所以最好能实现自动化脚本上线,带来的好处不言而喻:

  • 减少多次手动操作的时间
  • 降低手动误操作风险
  • 快速自动执行,提高开发效率

公司是做开源产品,使用了 Github 托管代码,Github 有个 action 的 ci/cd 自动化组件,所以下面将主要分享 Github 的 action 如何实现自动部署服务。

环境架构

图片.png

如上图所示,主要涉及4个部分:

  • code repo:代码仓库,当有代码更新时会触发个事件
  • app image:根据仓库构建的服务镜像
  • app chart:helm chart包,整合了服务需要在 k8s 运行的 workload 配置
  • k8s:服务运行的环境

github action 主要是将它们串了起来:

监听代码变更事件,执行部署任务,先用 docker 将服务打包成镜像,再用 helm 将 chart 包和镜像部署到 k8s 中去。

Github Action 介绍

  1. 在项目仓库根路径有个 .github/workflows 目录,里面存储了 action 相关 yml 配置文件;当新增 action 工作流时,只要在目录下增加一个对应的 yml 文件即可。

  2. 每个组织的有 2000 分钟免费额度跑私有仓库的 action ,但公开仓库无限制。

  3. 可以在仓库中先预定义变量(vars)和密钥值(secrets),在 yml 文件中按语法进行引用,避免一些敏感信息放在代码仓库里。

  4. 有环境(enviroment)的概念,可以在不同环境中用同个变量名,执行 action 时指定对应的环境即可。

例子

name: Example # action 显示的名称

on:
  workflow_dispatch:
  
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    environment: dev
    steps:
      - uses: actions/checkout@v2

      - name: Echo
        run: |
          echo "hello world"

主要有2个部分:

  • on:执行的时机,上述例子中 workflow_dispatch 表示手动执行,push 表示在代码更新时执行(这里指定了 main 分支)
  • jobs:要执行的任务,每个任务便是其一个子对象
    • {build}:自定义的构建任务
      • runs-on:表示任务在那个机器上运行,github 官方提供了3个环境 ubuntu-latest, windows-latest, macos-latest
      • environment:运行的环境,如上文所指这个是在 github 仓库先配置的,可以自己创建多个以存放特定的环境变量值
      • steps:任务里多个顺序步骤,有2种格式 uses 和 run。uses 是使用了 github marketplane 上别人提供的;run 是直接运行自己编写的脚本。

定义触发时机

目前我们需要支持手动部署和合并分支进主分支能自动部署 2 种方式,可以这样写:

on:
  workflow_dispatch:
    inputs:
      environment:
        type: environment
        default: dev
        required: true
  push:
    branches:
      - main

上面的 workflow_dispatch 表示可以手动触发 ci,这里还添加个 environment 参数,值是自己预先创建的环境列表。

push 表示有代码更新时触发,需要指定对应的分支,这里是 main。

构建镜像

注意需要拉取仓库代码,然后根据代码构建出镜像,默认运行环境是没有当前仓库代码的。

jobs:
  build-image:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
      - uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: 1.21

      - name: Login to Aliyun Container Registry (ACR)
        uses: aliyun/acr-login@v1
        with:
          login-server: xxx-xxxx.cn-shenzhen.cr.aliyuncs.com
          region-id: cn-shenzhen
          username: "${{ secrets.REGISTRY_USERNAME }}"
          password: "${{ secrets.REGISTRY_PASSWORD }}"

      - name: Build and Push Image
        run: |
          IMAGE_TAG=$(echo "${{ github.sha }}" | cut -c 1-7)

          go build -o ./bin/server .

          docker build -t xxx-xxxx.cn-shenzhen.cr.aliyuncs.com/mynamespace/server:$IMAGE_TAG .
          docker push xxx-xxxx.cn-shenzhen.cr.aliyuncs.com/mynamespace/server:$IMAGE_TAG

environment: ${{ inputs.environment }} 动态指定 github 预先设置的环境,通过 ci 输入来控制。

github 官方提供的 ubuntu-latest 运行环境默认带有 docker 服务,这里利用了 actions/checkout@v4 拉取代码, aliyun/acr-login@v1 登陆到我们的阿里云镜像仓库。

当前代码仓库是一个 golang 服务,构建二进制可执行文件需要 go 编译器,通过 actions/setup-go@v5 进行安装。

构建过程直接用 run 方式写个 shell 脚本,利用了 ${{ github.sha }} 提取 7 位镜像版本号。docker build 当然依赖一个 Dockerfile,需要在仓库里写好,拉取代码时会自动同步下来。

部署服务

利用 helm 工具,结合已构建的阿里云镜像,将服务部署上 k8s

jobs:
  deploy:
    name: DeployToK8s
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
      - uses: actions/checkout@v4

      - uses: azure/setup-helm@v3
        with:
          version: v3.12.0

      - name: PreapareKubeconfig
        run: |
          mkdir -p ~/.kube
          echo -e "${{ secrets.KUBECONFIG }}" >> ~/.kube/config

      - name: Deploy service
        run: |
          IMAGE_TAG=$(echo ${{ github.sha }} | cut -c1-7)

          helm upgrade --install \
            -n ${{ vars.namespace }} --create-namespace \
            --set image.tag=$IMAGE_TAG \
            server ./chart

我们需要先把 kubeconfig 放到 github secrets 中,在部署时需要通过它连接到远方的 k8s 集群。

这里利用 azure/setup-helm@v3 安装 helm v3 版本工具,部署时主要通过 --set image.tag=xxx 修改 chart 包里的镜像版本号。

chart 包目前在代码仓库下的 chart 目录,这个也可以单独一个仓库,只需修改 checkout 配置:

- uses: actions/checkout@v4
  with:
    repository: xxxx/server-chart  # 指定仓库地址
    path: chart # 拉取到当前 chart 目录
    token: ${{ secrets.GH_TOKEN }} # 需要使用 Personal Access Token,并设置在 github secrets 中

个人建议镜像构建和服务部署独立成 2 个 job,这样可以多次重新触发部署任务,而无需等待构建镜像。这有什么用? github 触发的 action 会有历史记录,这些历史归档可以当作回滚用,当有需要回滚服务时,只需要重新触发下部署任务即可,不用再构建镜像。

ok 上述便是我这期分析的内容,希望对你有帮助,我们有缘再见。