FluxCDのImageUpdateAutomationをPull Requestベースにする & Auto Mergeを設定する

目的

FluxCD には、Manifest 中の Image フィールドを自動的に更新してくれる機能があります。

とても便利な機能なのですが、公式ドキュメントの通りに ImageUpdateAutomationを設定すると、mainブランチに直接コミットされていきます。 そうではなくて、Renovate のように更新ごとに別のブランチを作成し、Pull Request を作成する挙動にしたい!というのが今回の目的です。

また、自動的に Test を実行し、テストに成功したら Merge するという挙動を実現する際に、思ったよりも手間がかかったので、その手順も記録しておきます。

ImageUpdateAutomation のドキュメント通りの設定

あまり詳しくは説明しないので、興味のある方は公式ドキュメントや、id:kakku22 さんの 「Flux v2 で Image Ops を実現する「自動イメージ更新機能」を試した - kakakakakku blog 」などを参照してください。

今回は、アプリごとに以下のような Manifest を適用しました。

apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
  name: hedgedoc
spec:
  git:
    checkout:
      ref:
        branch: main
    commit:
      author:
        email: fluxcdbot@users.noreply.github.com
        name: fluxcdbot
      messageTemplate: "{{range .Updated.Images}}{{println .}}{{end}}"
    push:
      branch: main
  interval: 1m0s
  sourceRef:
    kind: GitRepository
    name: flux-system
    namespace: flux-system
  update:
    path: ./k8s/apps/hedgedoc
    strategy: Setters
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
  name: hedgedoc
spec:
  image: quay.io/hedgedoc/hedgedoc
  interval: 2m0s
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
  name: hedgedoc
spec:
  imageRepositoryRef:
    name: hedgedoc
  policy:
    semver:
      range: "^1.x.x"

ImageUpdateAutomationには、更新方法についての設定、つまり参照する GitRepository や、ブランチ、コミットの Author、更新を監視するパスなどの設定などを記述します。

ImageRepositoryでは image が公開されている repository や取得頻度を設定します。

最後にImagePolicyでは、どのような条件で更新を行うかを設定します。

そして、Deployment などの image の項目に

image: quay.io/hedgedoc/hedgedoc:1.9.9 # {"$imagepolicy": "default:hedgedoc"}

のようにコメントすることで Image の更新が自動的に Push されるようになります。

push 先を別ブランチにする

このままでは、mainブランチに直接コミットされてしまうので、アプリケーション事に別のブランチにコミットされるようにします。

ImageUpdateAutomationpushフィールドを以下のように変更します。

push:
  branch: fluxcd/hedgedoc

こうすると、main から pull し、タグを更新後、fluxcd/hedgedocブランチにコミットされるという挙動になります。

Pull Request を作成する

FluxCD 単体では、自動的に Pull Request を作成する機能は存在しないようでした。そこで、GitHub Actions を利用して、自動的に Pull Request を作成するようにします。

以下のような GitHub Actions を作成しました。

name: flux-auto-PR
on:
  workflow_dispatch:
  push:
    branches:
      - "fluxcd/*"

jobs:
  create-pull-request:
    name: Open PR to main
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        name: checkout
      - name: create pull-request
        run: |
          gh pr create --body ":crown: *An automated PR*" --base main --title "Update image tag"
        env:
          GITHUB_TOKEN: ${{ github.token }}

pushイベントをトリガーに、fluxcd/から始まるブランチに変更があった場合に、自動的に Pull Request を作成します。GitHub CLI を利用しているので、オプションを変更することで、Reviewer の設定や、Label の設定なども行うことができます。

cli.github.com

これで、FluxCD によって作られたブランチに対して、自動的に Pull Request が作成されるようになります。

Auto Merge する

テストが通ったら自動的に Merge するようにしたいですよね。Renovate などでは簡単に実現できますが、Actions 経由だと少し手間がかかります。

まず、Actions の create pull-request ステップに automerge の設定を追加します。

run: |
  gh pr create --body ":crown: *An automated PR*" --base main --title "Update image tag"
  gh pr merge --auto --squash
  gh pr merge --merge --auto

リポジトリの設定でAllow auto-mergeが有効化されており、Branch protection rules でRequire status checks to pass before mergingが適切に設定されていれば、Status Check が通った後に自動的に Merge されるようになります。

しかし、この設定では、Pull Request の作成までは動くのですが、Status Check のための Actions がいつまでたっても実行されないという問題が発生しました。

原因を調べてみると、どうやら Actions で用いられる${{secrets.GITHUB_TOKEN}}の権限では GitHub Actions 内から別の Action の Workflow をトリガーすることができないようです。*1

ということで、記事にある通り、GitHub App を作り、その App に Pull Request を作らせることで、Test の Workflow をトリガーすることにしました。

https://github.com/settings/apps/newに飛び、新しい App を作成します。名前とHomepage URLは適当に、Webhook のActiveを無効化し、Permissionsは、Repository permissions内のContentsPull requestsRead & writeに設定します。

作成した App に対して、Install Appをクリックし、インストールします。その後、App 設定内のApp IDPrivate keysをメモしておきます。

次に、リポジトリの設定に戻り、SecretsAPP_IDPRIVATE_KEYを追加します。

最後に Workflow を以下のように書き換えます。

name: flux-auto-PR
on:
  workflow_dispatch:
  push:
    branches:
      - "fluxcd/*"

jobs:
  create-pull-request:
    name: Open PR to main
    runs-on: ubuntu-latest
    steps:
      - uses: actions/create-github-app-token@v1
        id: generate_token
        with:
          app-id: ${{ secrets.APP_ID }}
          private-key: ${{ secrets.PRIVATE_KEY }}

      - name: Set env
        run: |
          echo "GH_TOKEN=${{ steps.generate_token.outputs.token }}" >> $GITHUB_ENV

      - uses: actions/checkout@v4
        name: checkout

      - name: create pull-request
        run: |
          gh pr create --body ":crown: *An automated PR*" --base main --title "Update image tag"
          gh pr merge --merge --auto

create-github-app-tokenアクションを利用して、GitHub App に対してトークンを発行し、$GH_TOKEN に格納することで、GitHub CLI から新しい Token が自動的に利用されます。

これでようやく、Pull Request が作成され、Test が実行され、Auto Merge されるようになりました!

リポジトリの設定でAutomatically delete head branchesを有効化しておくとmerge後にブランチを消してくれるので便利です。

まとめ

Actions で作成された Pull Request にそのような制約があったことは知りませんでした。

そもそも Actions で頑張るのは面倒すぎるので、FluxCD 側で早く実装してほしいですね~

参考文献

fluxcd.io