zaki work log

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

[Ansible Builder]パッケージインストールに追加のリポジトリ設定やファイルコピーが必要な場合のイメージビルド

本記事はAnsible Advent Calendar 2021の14日目のエントリです。
珍しくインプットが湧いてきた1ため2日連続です笑

adventar.org

環境その他もろもろは、以前Ansible Builderについて試したこの記事のときのものと同じです。

解決したい課題

7月に一度、新しいAnsibleの実行環境であるExecution Environment(以下EE)をお試し実行してみましたが、この時「OSパッケージインストール周りでリポジトリ追加したい場合なんかは現状不明。」という課題を残したまま放置してました。

zaki-hmkc.hatenablog.com

当時は「イメージ内に追加で必要なパッケージはbindep.txtに記述する」機能から発想が縛られて、「一度ビルドを実行すると生成されるcontext/Containerfileファイルがあるため、内容を書き換えて(as builderの後にリポジトリを追加する処理を差し込む)リビルド」というかなり無理やりなことをしていました。

というのも、EEの定義ファイル(デフォルトexecution-environment.yml)にパッケージインストール関連で記載できるのはbindep.txtにパッケージ名、あとリポジトリの変更を行いたければadditional_build_steps以下に記載はできるけど、肝心の処理順序が「bindepによるパッケージインストール」が先で、「additional_build_stepsprependの処理」が後(※)のため、bindep.txtリポジトリ追加が必要なパッケージを記載しても、そのリポジトリ追加を行うタイミングが無い、というモノでした。(鶏卵 ちょっと違うか…)

※12/14夜追記: 正確には、イメージビルドの内訳として、builderのイメージビルド(FROM $EE_BUILDER_IMAGE as builderの部分)と、runnerのイメージビルド(FROM $EE_BASE_IMAGEの部分)があり、最後のrunnerのイメージビルドはadditional_build_stepsに記述した処理のあとにbindep.txtを参照したパッケージインストールが行われるので期待通りの動きをするが、中間にあるbuilderのイメージビルドはadditional_build_stepsの処理は行わずにbindep.txtを使ったパッケージインストールを行う処理内容になっている。詳細はビルド時に生成されるContainerfileを参照。

回避策

実際のところ、Execution Environment Definitionを見ても、リポジトリ設定の余地は現状ないので、機能的には無さそう。

ansible-builder.readthedocs.io

ただ、そもそも7月の時点でどうして思いつかなかったんだ、というレベルだったりするけど、additional_build_stepsの中で、

  1. リポジトリを追加する処理
  2. 追加したリポジトリからパッケージインストールする処理

をどちらも書いてしまえばいいじゃん、というのを思いついたのでお試し。

お題は前回と同じくKubernetesCLIコマンドのインストール。

execution-environment.yml (抜粋)

additional_build_stepsの部分は以下の通り。

additional_build_steps:
  prepend:
    - COPY kubernetes.repo /etc/yum.repos.d/kubernetes.repo
    - RUN dnf install -y kubectl

Kubernetesのドキュメントではcatとヒアドキュメントでファイル生成してるけど、ちょっとコツがいるので素直に外部ファイル化してCOPY使っている。
(これはこれでコツがいるんだけど…後述)

なお、EEのベースイメージはCentOSベースなので、パッケージもCentOSの場合の手順を実施。

kubernetes.repo (追加ファイル)

これはドキュメントの通りの内容。
インライン記載は後述してるけど、別途ファイル用意した方が(今回の場合は)ミスは少ないと思う。

[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg

ビルド

execution-environment.ymlなどの制御に使用するファイル以外の、イメージ内部に追加したい(今回のkubernetes.repoファイルのような)追加ファイルがある場合は、ビルド時にコンテキストの指定が必要。

$ ansible-builder build --help

  [...]

  -c BUILD_CONTEXT, --context BUILD_CONTEXT
                        The directory to use for the build context (default: context)

これはデフォルドではcontextというディレクトリが指定されるが、このディレクトリがビルド時のルートディレクトリとして扱われるイメージ。もっといえば、ここがchrootされる感じ。
つまり、イメージへ追加したいファイルがある場合は、コンテキストで指定したディレクトリ以下になければ、ビルド時にファイルを(例えフルパスで指定してたとしても、ビルド時のルートはコンテキストで指定したディレクトリになるため)認識できない。

よって、具体的なコマンドラインは、execution-environment.ymlkubernetes.repoのあるディレクトリで以下の通り。

$ ls -F
Containerfile  bindep.txt                 kubernetes.repo   requirements.yml
ansible.cfg    execution-environment.yml  requirements.txt
$ ansible-builder build --tag kube-sample:devel --context .

これで、イメージビルド時のCOPYの対象であるkubernetes.repoを認識してイメージへコピーできるようになる。

ファイル数が多く、execution-environment.ymlなどとは分離したい場合は、「イメージへ組み込みたいファイル用のディレクトリを作成し、--contextでそのディレクトリを指定」すればOK.
その場合でも、COPYの引数に指定するファイルのパスは、--contextで指定するディレクトリ基準の相対パスにすれば良い。

確認

$ ansible-builder build --tag kube-sample:devel --context files
Running command:
  podman build -f files/Containerfile -t kube-sample:devel files
Complete! The build context can be found at: /home/zaki/ee/kube/files
$ podman run -it --rm localhost/kube-sample:devel bash
bash-4.4# cat /etc/yum.repos.d/kubernetes.repo 
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
bash-4.4# kubectl version --short --client
Client Version: v1.23.0

ちゃんとできている。

ヒアドキュメントは…無理かな?

これはダメだった例。

  prepend: |
    COPY <<EOF /etc/yum.repos.d/kubernetes.repo
    [kubernetes]
    name=Kubernetes
    baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
    enabled=1
    gpgcheck=1
    repo_gpgcheck=1
    gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
    EOF

これはワンチャン行けるかなと思ったけど、構文エラーにはならずビルドは動いて、COPYのタイミングで「<<EOFというファイルが無い」というエラーとなった。

STEP 17: COPY <<EOF /etc/yum.repos.d/kubernetes.repo
Error: error building at STEP "COPY <<EOF /etc/yum.repos.d/kubernetes.repo": checking on sources under "/home/zaki/ee/kube/context": copier: stat: "/<<EOF": no such file or directory

インラインでやるならこんな感じ。
echo-eオプションを付加し、\nで改行しつつ、行末の\で次行へ続ける記述でファイル作成を確認。

  prepend: |
    RUN echo -e "[kubernetes]\n\
    name=Kubernetes\n\
    baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64\n\
    enabled=1\n\
    gpgcheck=1\n\
    repo_gpgcheck=1\n\
    gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg" > /etc/yum.repos.d/kubernetes.repo
    RUN dnf install -y kubectl

確認した環境

$ cat /etc/redhat-release 
Fedora release 34 (Thirty Four)
$ ansible-builder --version
1.0.1
$ python --version
Python 3.9.4

おまけというか備忘録
additional_build_steps以下prependへのコマンドの指定は、リストでも複数行テキストでもどっちでもOKでした。


  1. 同じシチュエーションの対応策に困ってる部署のメンバがいて、回避策を偶然思いついた、というものw やはり業務を通して(にゃーん