zaki work log

作業ログやら生活ログやらなんやら

GitHub ActionsとAnsible BuilderでExecution Environmentコンテナイメージの自動ビルド

周りでGitLab Runnerを使ったExecution Environmentの自動ビルドが急に流行りだしたので、GitHub Actionsを使った自動ビルドを試してみた。
Ansibleの実行環境がコンテナ化し、その定義ファイルとしてexecution-environment.ymlに内容を記述できるようになった時点で試してみたかったけど、業務でさわる機会が全然なく後回しにしてたので、いい機会でした。

実物は以下

EEについては以下も参照

zaki-hmkc.hatenablog.com

execution-environment.yml

サンプルなので特記事項無し。
ファイルは以下。

github.com

---
version: 1

build_arg_defaults:
  EE_BASE_IMAGE: 'registry.redhat.io/ansible-automation-platform-21/ee-minimal-rhel8:latest'
  ## require authentication

ansible_config: 'ansible.cfg'

dependencies:
  galaxy: requirements.yml
  python: requirements.txt
  system: bindep.txt

additional_build_steps:

特記事項無しといいつつ、ファイルの内容については以前書いた[Ansible / Podman] もうこのぉ、venvを使った実行環境は終わりだ (Ansible RunnerとAnsible Builderお試し) - zaki work logも参照。

補足 蛇足

追記:「ver1.2だとVersion 3 Formatが使える」は誤り。ドキュメントの記述は

Version 3: Supported by ansible-builder versions after 1.2.

なので1.2 より新しい バージョンで対応する、ということっぽい。

追記ここまで。

Ansible Builderのドキュメントによると ansible-builder ver1.2だとVersion 3 Formatが使える (↑前述) ようだけど、ドキュメントの通りにimagesadditional_build_filesを記述するとunknown yaml keyのエラーが出るので古い書式(Version 1 Format)を使ってる。(2023.04.16時点)

(builder) (main %=) [zaki@cloud-dev2 builder-example]$ ansible-builder --version
1.2.0
(builder) (main %=) [zaki@cloud-dev2 builder-example]$ ansible-builder build 
ansible_builder.exceptions.DefinitionError: 
Error: Unknown yaml key(s), {'images', 'additional_build_files'}, found in the definition file.

Allowed options are:
{'dependencies', 'build_arg_defaults', 'additional_build_steps', 'ansible_config', 'version'}

ansible-builder.readthedocs.io

GitHub Actions

Actionsを起動するための定義ファイルは.github/workflows/main.ymlに記述。
Gitのtagを作成したらそのタイミングで処理を起動し、tag名でイメージビルド、GitHub Container Registryにpushするようにしている。

name: build ee

on:
  push:
    branches-ignore:
      - '**'
    tags:
      - 'v*'
    # https://docs.docker.com/build/ci/github-actions/manage-tags-labels/

jobs:
  build:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
      - name: setup ansible builder
        run: python3 -m pip install ansible-builder
      - name: create Containerfile
        run: ansible-builder create
      - id: meta
        # https://docs.docker.com/build/ci/github-actions/manage-tags-labels/
        uses: docker/metadata-action@v4
        with:
          images: ghcr.io/zaki-lknr/ee-example
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{major}}
      - name: login github container registry
        uses: docker/login-action@v2
        with:
          username: zaki-lknr
          password: ${{ secrets.PERSONAL_TOKEN }}
          registry: ghcr.io
      - name: login redhat registry
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.RH_USERNAME }}
          password: ${{ secrets.RH_PASSWORD }}
          registry: registry.redhat.io
      - name: build & push
        uses: docker/build-push-action@v4
        with:
          context: context
          file: context/Containerfile
          push: true
          tags: ${{ steps.meta.outputs.tags }}

処理の流れとしては以下の通りで、tagが作成されたらジョブを起動する。

  • ビルド環境はUbuntu 22.04
  • リポジトリのチェックアウト
  • PythonインタプリタとAnsible Builderをセットアップ
  • ansible-builder buildで直接ビルドせず、createContainerfileを作成
  • リポジトリのtagをイメージのtagに使用するためのdocker/metadataアクション実行
  • docker/loginのアクションでリポジトリへの認証情報作成
  • docker/build-pushのアクションでイメージのbuild & pushを実行

細かい内容はGitHub Actionsのドキュメント参照

docs.github.com

tagが作成されたら起動

以下で実現

on:
  push:
    branches-ignore:
      - '**'
    tags:
      - 'v*'

on: pushだけだと、pushに反応してジョブが走るため、今回はpushでなくtagに反応するように記述。そのためにbranches-ignoreでブランチへのpushを無視するようにしている。

GitHub Actions のワークフロー構文 | on.push.<branches|tags|branches-ignore|tags-ignore>

リポジトリのチェックアウトとビルド環境構築

      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
      - name: setup ansible builder
        run: python3 -m pip install ansible-builder

actions/checkout@v3リポジトリのチェックアウト、actions/setup-python@v4Pythonの環境へのインストールを実施。バージョンは特に指定してないのでその時点の最新安定版(のはず。。)

Ansible Builderのインストールはrunでコマンドを実行する。

Containerfileの作成

      - name: create Containerfile
        run: ansible-builder create

これもrunを使ってコマンドを実行。
ansible-builder buildでビルドをせずにContainerfileを生成しているのは、後続の処理で、用意されているGitHub Actions用のDockerアクションでlogin/build/pushを行うため。
もちろん残りの処理もrunで実装は可能。

ちなみにansible-builder buildの実行で、内部的にはpodman buildでのビルドもできる。

tag名の取り出し

      - id: meta
        # https://docs.docker.com/build/ci/github-actions/manage-tags-labels/
        uses: docker/metadata-action@v4
        with:
          images: ghcr.io/zaki-lknr/ee-example
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{major}}

docker/metadata-action@v4アクションを使用。
このアクションはv1.2.3形式のtag作成時にlatest1.2.31.21のイメージタグを自動でセットできるようにするための準備。build & push時tagを参照するのは最後の「build & push」の以下の箇所。

tags: ${{ steps.meta.outputs.tags }}

詳細はドキュメントを参照。

バージョン表記についてはセマンティック バージョニング 2.0.0 | Semantic Versioningを参照

コンテナレジストリへのログイン

ビルドしたイメージのpush先のコンテナレジストリと、ビルド時のベースイメージであるregistry.redhat.io/ansible-automation-platform-21/ee-minimal-rhel8:latestをpullするためのRed Hatコンテナレジストリの2箇所へログインしている(Red Hatのコンテナレジストリの認証情報は無料のDeveloper Subscriptionのものを使用)。
ベースイメージをquay.io/ansible/ansible-runner:stable-2.12-devel等のように認証不要のリポジトリからpullする場合はpush用のみでOK

今回はGitHub Container Registry(ghcr.io)を使用。もちろんDocker HubでもOK

      - name: login github container registry
        uses: docker/login-action@v2
        with:
          username: zaki-lknr
          password: ${{ secrets.PERSONAL_TOKEN }}
          registry: ghcr.io
      - name: login redhat registry
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.RH_USERNAME }}
          password: ${{ secrets.RH_PASSWORD }}
          registry: registry.redhat.io

パスワードなどのSecretデータは、リポジトリの[Settings]->[Secrets and variables]->[Actions]のRepository secretsに登録しておけば、${{ secrets.<シークレット名> }}で参照できる。
Ansible的にはAAP/AWXの「Credentials (認証情報)」と同様の機能。

複数レジストリへのログインをまとめられたらキレイな気がするけど、公式サイトの記載もこの通りなので必要な回数分実行する必要があると思われ。

github.com

GitHub Container Registryの認証情報

GitHub Container Registryの認証情報は、Personal Access Tokensを作成し、それをパスワードとして使用する。
アカウントの[Settings]->[Developer settings]にあるPersonal Access Tokens (Classic)で[Generate new token (classic)]を押下。

Expirationを無期限、scopeはwrite:packagesにチェックを入れて[Generate token]押下で生成する。
(write:packagesを有効にすると、repoは連動して全て有効になる)

GitHub Container Registryは、サーバーがghcr.io、ユーザー名にGitHubアカウント名、パスワードに前述のパーソナルトークンを使えばログインできる。

build and push

いよいよbuildとpush・・・といってもdocker/build-push-actionがあるので、ビルドのコンテキスト(パス)とContainerfileをパラメタとして指定すればOK
tagには前処理で行ったリポジトリのtagから生成した値を参照するように記述する。

      - name: build & push
        uses: docker/build-push-action@v4
        with:
          context: context
          file: context/Containerfile
          push: true
          tags: ${{ steps.meta.outputs.tags }}

これを使用するために前段ではansible-builder buildでなくansible-builder createContainerfileを生成するだけにしている。

参考

あとブログ記事化してないけれど、GitHub PagesへのMkDocsを使ったサイト生成をGitHub Actionsで自動化したのが以下。

.github/workflows/main.yml

生成しているサイト zaki-lknr.github.io

リポジトリ github.com

MkDocsの紹介 zaki-hmkc.hatenablog.com


マルチプラットフォームのビルドもできるみたい。arm64ビルドも欲しいな。。
Multi-platform image with GitHub Actions | Docker Documentation