使用GitHub Actions实现CI/CD流水线

GitHub Actions 是 GitHub 提供的一个强大而灵活的自动化工具,能够帮助开发者实现持续集成(CI)和持续交付/部署(CD)流水线。本文讨论了GitHub Actions如何与Azure Kubernetes和Azure Container Registry结合实现服务的自动化构建。
使用GitHub Actions实现CI/CD流水线

On this page

关于GitHub Actions和Terraform

GitHub Actions 是 GitHub 提供的一个强大而灵活的自动化工具,能够帮助开发者实现持续集成(CI)和持续交付/部署(CD)流水线。它能够让开发者在代码库的不同阶段(如代码提交、拉取请求、发布等)自动触发构建、测试、部署等任务。借助 GitHub Actions,开发者可以通过自定义的 YAML 文件定义工作流,实现从代码提交到应用发布的全自动化过程。
Terraform是目前最热门的基础设施即代码工具,它能够以脚本的形式定义基础设施配置,这极大地增强了基础设施的可复用性,并且可以更好的同CI/CD流水线集成。

GitHub Actions的优势

深度集成:GitHub Actions 与 GitHub 仓库无缝集成,可以根据事件自动触发工作流,适用于各类开发项目。
灵活性高:支持高度自定义的工作流,开发者可以根据不同的项目需求,灵活地配置不同的任务、条件和依赖
生态系统丰富:GitHub Marketplace 提供了数千个现成的 Actions(如容器部署、测试工具、CI/CD 任务等),开发者可以方便地复用已有组件,减少重复工作。
并行化任务执行:GitHub Actions 可以让多个任务并行运行,提升 CI/CD 的效率,适应大型项目的复杂需求。
可扩展性强: GitHub actions可以与主流云计算平台相结合,实现无缝的云部署。
实践:通过GitHub Actions实现自动化构建docker镜像并部署到Azure Kubernetes上

项目链接:

GitHub - chunyiw8080/sit722-part5
Contribute to chunyiw8080/sit722-part5 development by creating an account on GitHub.

使用Terraform创建Azure Kubernetes集群和Container Registry镜像存储库

Terraform脚本可以参照之前的这篇文章,需要将variables中设置的默认值清空

使用Terraform部署Azure Kubernetes和Container Regsitry
基础设施即代码(Infrastructure as Code,简称 IaC)是一种通过代码管理和自动化部署基础设施的方法。随着云计算的发展,手动配置和管理资源的复杂性与日俱增,IaC 通过将基础设施的配置与应用代码一样进行版本控制和自动化部署,大大提高了部署效率、可靠性和可重复性。 Terraform是目前最流行的 IaC 工具之一,它能通过定义“计划文件”来描述云资源,用户可以用它来管理各种基础设施,包括虚拟机、网络、存储、容器服务等。它的优势在于对多种云平台的支持,无论是 AWS、Azure、Google Cloud,还是本地的私有云,Terraform 都能够帮助用户自动化部署复杂的云环境。 本文将讨论如何使用 Terraform 在 Azure 平台上自动化部署 Kubernetes 集群和镜像存储库(如 Azure Container Registry)。通过将 Terraform 与 Azure 云平台结合,可以轻松实现

编写Shell脚本

GitHub Actions会使用Linux环境执行Workflow中的操作,因此将所有涉及到的命令拆分为多个Shell脚本效率更高,也更便于修改。

构建镜像脚本

set -u
: $CONTAINER_REGISTRY
: $VERSION

docker build -t $CONTAINER_REGISTRY/book-catalog:$VERSION --file ./book_catalog/Dockerfile .
docker build -t $CONTAINER_REGISTRY/inventory:$VERSION --file ./inventory_management/Dockerfile .

推送镜像脚本

set -u
: "$CONTAINER_REGISTRY"
: "$VERSION"
: "$REGISTRY_UN"
: "$REGISTRY_PW"

echo $REGISTRY_PW | docker login $CONTAINER_REGISTRY --username $REGISTRY_UN --password-stdin
docker push $CONTAINER_REGISTRY/book-catalog:$VERSION
docker push $CONTAINER_REGISTRY/inventory:$VERSION

部署脚本

set -u
: "$CONTAINER_REGISTRY"
: "$VERSION"
: "$DB_URL"

envsubst < ./scripts/kubernetes/configmap.yaml | kubectl apply -f -
envsubst < ./scripts/kubernetes/service.yaml | kubectl apply -f -
envsubst < ./scripts/kubernetes/deployment.yaml | kubectl apply -f -

其中envsubst命令能将后面文件中的环境变量占位符替换为当前环境中的实际值。

删除脚本

set -u
: "$CONTAINER_REGISTRY"
: "$VERSION"
: "$DB_URL"

envsubst < ./scripts/kubernetes/deployment.yaml | kubectl delete -f -
envsubst < ./scripts/kubernetes/service.yaml | kubectl delete -f -
envsubst < ./scripts/kubernetes/configmap.yaml | kubectl delete -f -

编写GitHub workflow配置

Apply terraform yaml

在编写配置之前,需要做一些准备工作

  • 创建Azure服务主体
    Azure服务主体包含了一些数据,如CLIENT_ID,CLIENT_SECRET和TENANT_ID,这些数据可以用来免交互登录Azure。使用如下命令:
az ad sp create-for-rbac --name <服务主体名称> --role Owner --scopes /subscriptions/<你的subscriptions ID> --sdk-auth

该命令执行成功后会生成一个Json文件,保存好该文件

  • 为GitHub账户创建Personal access tokens
    该字符可以让GitHub Actions执行器访问GitHub仓库中的secrets并设置。
Build software better, together
GitHub is where people build software. More than 100 million people use GitHub to discover, fork, and contribute to over 420 million projects.
  • 编写Workflow配置
name: Terraform Apply
# 仅限手动构建,如果使用branches关键字,可以指定当代码被推送到对应分支是触发自动构建
on:
  workflow_dispatch:

jobs:
  terraform:
    name: Terraform apply
    runs-on: ubuntu-latest

    steps:
      - name: Checkout Code
        uses: actions/checkout@v3

      - name: Set up Terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.5.6
        # 使用之前生成的服务主体数据
      - name: Azure Login
        run: az login --service-principal -u ${{ secrets.CLIENT_ID }} -p ${{ secrets.CLIENT_SECRET }} --tenant ${{ secrets.TENANT_ID }}

      - name: Terraform Init
        working-directory: ./scripts/terraform
        run: terraform init
        # ${{ vars.AZURE_RESOURCE_NAME }}等变量保存在GitHub仓库中的Repository variables列表下,由于这些内容并非机密数据,因此不必设置为secrets
        # 这些变量指定的Terraform资源所使用的资源组名,位置,K8s集群和容器注册表名称以及K8s版本
      - name: Terraform Apply
        working-directory: ./scripts/terraform
        run: terraform apply -auto-approve -var "resource_group_name=${{ vars.AZURE_RESOURCE_NAME }}" -var "location=${{ vars.AZURE_RESOURCE_LOCATION }}" -var "app_name=${{ vars.APP_NAME }}" -var "kubernetes_version=${{ vars.AZURE_AKS_VERSION }}"
        continue-on-error: true

        # 安装后续操作所需的依赖,其中gh为GitHub-Cli,jq用来过滤Json数据
      - name: Install dependencies
        run: |
          sudo apt install gh -y
          sudo apt install jq -y

        # 这里的Token是先前创建的Personal access tokens
      - name: GitHub-Cli login
        run: |
          echo "${{ secrets.TOKEN }}" | gh auth login --with-token

        # 刷新Terraform状态
      - name: Refresh terraform state
        run: terraform refresh

        # 通过gh命令设置Secrets
      - name: Set Secrets
        run: |
          acr_name=$(az acr list | jq -r '.[0].loginServer')
          gh secret set CONTAINER_REGISTRY --body "$acr_name"

          az acr credential show --name "$acr_name" --query "{username:username, passwords:passwords[0].value}" --output json > credentials.json
          gh secret set REGISTRY_UN --body "$(cat credentials.json | jq -r '.username')"
          gh secret set REGISTRY_PW --body "$(cat credentials.json | jq -r '.passwords')"

          az aks get-credentials --resource-group ${{ vars.AZURE_RESOURCE_NAME }} --name ${{ vars.APP_NAME }}  
          gh secret set KUBE_CONFIG --body "$(cat ~/.kube/config | base64 -w 0)"

Deploy Microservice yaml

name: Deploy microservice
on:
  workflow_dispatch:

# 将要运行的任务
jobs:
  deploy:
    runs-on: ubuntu-latest
    # 环境变量,这些变量在仓库的secrets中声明和配置
    env:
      VERSION: ${{ github.sha }}
      CONTAINER_REGISTRY: ${{ secrets.CONTAINER_REGISTRY }}
      REGISTRY_UN: ${{ secrets.REGISTRY_UN }}
      REGISTRY_PW: ${{ secrets.REGISTRY_PW  }}
      DB_URL: ${{ secrets.DB_URL }}
    # 具体的任务步骤
    steps:
        # actions/checkout:GitHub 官方提供的一个标准 Action,用来将代码库的当前版本拉取到虚拟机或容器中。
      - uses: actions/checkout@v3
      - name: Build images
        run: bash ./scripts/build-image.sh
      - name: Push image
        run: bash ./scripts/push-image.sh
        # tale/kubectl-action:一个第三方 Action,用于在 GitHub Actions 工作流中执行 kubectl 命令,管理 Kubernetes 集群
      - uses: tale/kubectl-action@v1
        with:
          # 经过 base64 编码的 Kubernetes 配置文件
          base64-kube-config: ${{ secrets.KUBE_CONFIG }}
          # 指定 kubectl 的版本号
          kubectl-version: v1.30.3
      - name: Deploy
        run: bash ./scripts/deploy.sh

Delete Microservice yaml

和Deploy差不多,只不过在关键的地方使用delete.sh脚本触发kubectl delete操作。

name: Delete microservice

on:
  workflow_dispatch: 

jobs:

  deploy:
    runs-on: ubuntu-latest
    
    env:
      VERSION: ${{ github.sha }}
      CONTAINER_REGISTRY: ${{ secrets.CONTAINER_REGISTRY }}
      REGISTRY_UN: ${{ secrets.REGISTRY_UN }}
      REGISTRY_PW: ${{ secrets.REGISTRY_PW  }}
      DB_URL: ${{ secrets.DB_URL }}

    steps:
      - uses: actions/checkout@v3
      - uses: tale/kubectl-action@v1
        with:
          base64-kube-config: ${{ secrets.KUBE_CONFIG }}
          kubectl-version: v1.30.3
      - name: delete
        run: ./scripts/delete.sh

在仓库的secrets中声明和配置变量

需要将azure登录凭据和gh登录凭据存储到secrets中,其他的secrets会通过gh命令在执行Actions自动设置。

触发构建

在GitHub仓库中点击Actions,在workflow中找到Deploy microservice,然后点击Run workflow就可以触发构建。

当右上角出现对号图标,意味着构建成功。
值得注意的是,GitHub Actions构建成功不一定代表部署成功,比如如果Kubernetes配置文件中存在错误,除非是会导致exit code 1的语法错误,否则是不会提示错误的。

同理,要想删除部署,只需要点击Delete microservice并运行构建。