🐙

k8s上のコンテナのイメージタグをGitHub ActionsとArgoCDで自動で書き換える

2023/04/23に公開

前提

  • Kubernetesクラスタ上でwebアプリを動かしている
  • ArgoCDがreleaseブランチと同期している
  • mainブランチに変更があったらマニフェストを書き換えてreleaseにpushする、という運用
  • appディレクトリにアプリケーションのコードがある
  • manifestsディレクトリにKubernetesマニフェストがある

イメージの差し替えについてはArgo CD Image Updaterというプラグインでも達成できるが今回は使わない。

ワークフロー例

name: Build-and-Push
on:
  push:
    branches:
      - main
    paths:
      - app/**

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

    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
        with:
          token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
          fetch-depth: 0

      - name: Fetch Git tags
        run: |
          git fetch --depth=1 origin +refs/tags/*:refs/tags/*

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1

      - name: Login to Docker Registry
        uses: docker/login-action@v2
        with:
          registry: your.container.registry
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Get current version
        id: get_version
        run: |
          CURRENT_VERSION=$(git tag --sort=-version:refname | head -n 1)
          echo "Current version: $CURRENT_VERSION"
          echo "::set-output name=CURRENT_VERSION::$CURRENT_VERSION"

      - name: Bump patch version
        id: bump_version
        run: |
          CURRENT_VERSION="${{ steps.get_version.outputs.CURRENT_VERSION }}"
          PATCH_VERSION=$(echo $CURRENT_VERSION | awk -F. '{print $3}')
          NEW_PATCH_VERSION=$((PATCH_VERSION + 1))
          NEW_VERSION=$(echo $CURRENT_VERSION | sed "s/\.[0-9]*$/.${NEW_PATCH_VERSION}/")
          echo "New version: $NEW_VERSION"
          echo "::set-output name=NEW_VERSION::$NEW_VERSION"

      - name: Build and push Docker image
        uses: docker/build-push-action@v2
        with:
          context: ./app
          push: true
          tags: |
            your.container.registry/your-app:latest
            your.container.registr/your-app:${{ steps.bump_version.outputs.NEW_VERSION }}

      - name: Update Deployment and Application manifests
        run: |
          NEW_VERSION="${{ steps.bump_version.outputs.NEW_VERSION }}"
          sed -i "s/your-app:v[0-9]*\.[0-9]*\.[0-9]*/your-app:$NEW_VERSION/g" manifests/your-app/deploy.yaml

      - name: Commit and push changes
        run: |
          git config user.name "GitHub Actions Bot"
          git config user.email "actions@github.com"
          git -A
          git status
          git commit -m "Update image tag to ${{ steps.bump_version.outputs.NEW_VERSION }}"
          git tag ${{ steps.bump_version.outputs.NEW_VERSION }}
          git push origin --tags
          git switch -c release
          git push origin release --force

解説

最初はリポジトリにvX.X.Xというタグが存在している必要がある。あとは自動でタグがpushされる。
最新のタグを取得し、そこからパッチバージョンを1だけ上げる。
dockerイメージは、ビルドして最新+1のタグをつけた上で任意のレジストリにpushされる。
メジャーバージョン、マイナーバージョンを上げたければ手動でタグをpushすればいい。
イメージタグが書き換えられたマニフェストがreleaseブランチにpushされる。

token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}

リポジトリにpushするためにrepo権限が必要。

fetch-depth: 0

タグ情報を得るために必要。

CURRENT_VERSION=$(git tag --sort=-version:refname | head -n 1)

fetchしたタグの中から最新のものを抽出している。やり方が冗長な気もするがほかに思いつかなかった。

your.container.registry/your-app:latest

なくてもいい。なんとなくlatestタグをつけておきたくなる。

git push origin release --force

コンフリクトで自動デプロイ失敗すると萎えるのでforceつけたい。リスクは高いので多分forceつけないほうがいい。

注意

set-outputはdeprecatedらしい。代わりに環境変数を使ったほうがいい。

Discussion