Github ci/cd 自动化部署服务
在Saas产品迭代过程中,难免需要多次上线新加的功能服务。一开始通过手动操作来上线没啥问题,但是后续不断维护升级,操作次数增加,环境增多,那么消耗的时间会越来越多,手动操作带来的风险也会越来越大。所以最好能实现自动化脚本上线,带来的好处不言而喻:
- 减少多次手动操作的时间
- 降低手动误操作风险
- 快速自动执行,提高开发效率
公司是做开源产品,使用了 Github 托管代码,Github 有个 action 的 ci/cd 自动化组件,所以下面将主要分享 Github 的 action 如何实现自动部署服务。
环境架构
如上图所示,主要涉及4个部分:
- code repo:代码仓库,当有代码更新时会触发个事件
- app image:根据仓库构建的服务镜像
- app chart:helm chart包,整合了服务需要在 k8s 运行的 workload 配置
- k8s:服务运行的环境
github action 主要是将它们串了起来:
监听代码变更事件,执行部署任务,先用 docker 将服务打包成镜像,再用 helm 将 chart 包和镜像部署到 k8s 中去。
Github Action 介绍
-
在项目仓库根路径有个
.github/workflows目录,里面存储了 action 相关 yml 配置文件;当新增 action 工作流时,只要在目录下增加一个对应的 yml 文件即可。 -
每个组织的有 2000 分钟免费额度跑私有仓库的 action ,但公开仓库无限制。
-
可以在仓库中先预定义变量(vars)和密钥值(secrets),在 yml 文件中按语法进行引用,避免一些敏感信息放在代码仓库里。
-
有环境(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 是直接运行自己编写的脚本。
- {build}:自定义的构建任务
定义触发时机
目前我们需要支持手动部署和合并分支进主分支能自动部署 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 上述便是我这期分析的内容,希望对你有帮助,我们有缘再见。