zaki work log

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

Debian11(Bullseye)でRootless Dockerインストールしてコンテナをデプロイ

Debian10では依存パッケージのバージョンが古くてインストールできなかったルートレスDocker、先々週リリースされたDebian11ではパッケージバージョンが新しくなって全て要件を満たしていたので試してみた。

docs.docker.com

Deiban10だとうまくいかないパターンは以下

zaki-hmkc.hatenablog.com

環境

root@debian11:~# cat /etc/debian_version 
11.0

ルートレスDockerのインストール自体は公式ドキュメントの通りセットアップすれば基本的に動く。
以下は最小構成でOSインストールした直後の状態(通常のDockerが未インストール)の場合。

ちなみに、sudoまだ入ってないので、rootにsuして実行してる。

準備

uidmap

root@debian11:~# apt-get install uidmap
zaki@debian11:~$ id -u
1000
zaki@debian11:~$ whoami 
zaki
zaki@debian11:~$ grep ^$(whoami): /etc/subuid
zaki:100000:65536
zaki@debian11:~$ grep ^$(whoami): /etc/subgid
zaki:100000:65536

dbus-user-session

root@debian11:~# apt-get install dbus-user-session

このあと一度ログインしなおす。

overlay2

一旦省略

slirp4netns

root@debian11:~# apt search slirp4netns 
ソート中... 完了
全文検索... 完了  
slirp4netns/stable 1.0.1-2 amd64
  User-mode networking for unprivileged network namespaces

うむ、バージョン要件を満たしている。

root@debian11:~# apt-get install slirp4netns
:
:
root@debian11:~# slirp4netns --version
slirp4netns version 1.0.1
commit: 6a7b16babc95b6a3056b33fb45b74a6f62262dd4
libslirp: 4.4.0

install

docs.docker.com

install docker

まずは普通に

root@debian11:~# apt-get update
root@debian11:~# apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
root@debian11:~# curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
root@debian11:~# echo \
  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
  $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

インストールする。

root@debian11:~# apt-get update
root@debian11:~# apt-get install docker-ce docker-ce-cli containerd.io

この通り。

root@debian11:~# docker --version
Docker version 20.10.8, build 3967b7d

インストール完了するとサービスが有効になってるので止める。

root@debian11:~# systemctl disable --now docker.service docker.socket
Synchronizing state of docker.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install disable docker
Removed /etc/systemd/system/sockets.target.wants/docker.socket.
Removed /etc/systemd/system/multi-user.target.wants/docker.service.

rootless

一般ユーザーで以下実行。

zaki@debian11:~$ dockerd-rootless-setuptool.sh install
[INFO] Creating /home/zaki/.config/systemd/user/docker.service
[INFO] starting systemd service docker.service
+ systemctl --user start docker.service
+ sleep 3
+ systemctl --user --no-pager --full status docker.service
● docker.service - Docker Application Container Engine (Rootless)
     Loaded: loaded (/home/zaki/.config/systemd/user/docker.service; disabled; vendor preset: enabled)
     Active: active (running) since Sat 2021-08-28 18:03:56 JST; 3s ago
       Docs: https://docs.docker.com/go/rootless/
   Main PID: 2277 (rootlesskit)
      Tasks: 33
     Memory: 72.7M
        CPU: 255ms
     CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/docker.service
             ├─2277 rootlesskit --net=slirp4netns --mtu=65520 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run --propagation=rslave /usr/bin/dockerd-rootless.sh
             ├─2287 /proc/self/exe --net=slirp4netns --mtu=65520 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run --propagation=rslave /usr/bin/dockerd-rootless.sh
             ├─2305 slirp4netns --mtu 65520 -r 3 --disable-host-loopback --enable-sandbox --enable-seccomp 2287 tap0
             ├─2312 dockerd
             └─2328 containerd --config /run/user/1000/docker/containerd/containerd.toml --log-level info

 8月 28 18:03:56 debian11 dockerd-rootless.sh[2312]: time="2021-08-28T18:03:56.970908740+09:00" level=error msg="failed to mount overlay: operation not permitted" storage-driver=overlay
 8月 28 18:03:56 debian11 dockerd-rootless.sh[2312]: time="2021-08-28T18:03:56.983809483+09:00" level=warning msg="Unable to find cpu controller"
 8月 28 18:03:56 debian11 dockerd-rootless.sh[2312]: time="2021-08-28T18:03:56.983830087+09:00" level=warning msg="Unable to find io controller"
 8月 28 18:03:56 debian11 dockerd-rootless.sh[2312]: time="2021-08-28T18:03:56.983835839+09:00" level=warning msg="Unable to find cpuset controller"
 8月 28 18:03:56 debian11 dockerd-rootless.sh[2312]: time="2021-08-28T18:03:56.983989597+09:00" level=info msg="Loading containers: start."
 8月 28 18:03:57 debian11 dockerd-rootless.sh[2312]: time="2021-08-28T18:03:57.023743296+09:00" level=info msg="Default bridge (docker0) is assigned with an IP address 172.17.0.0/16. Daemon option --bip can be used to set a preferred IP address"
 8月 28 18:03:57 debian11 dockerd-rootless.sh[2312]: time="2021-08-28T18:03:57.060855981+09:00" level=info msg="Loading containers: done."
 8月 28 18:03:57 debian11 dockerd-rootless.sh[2312]: time="2021-08-28T18:03:57.068177847+09:00" level=info msg="Docker daemon" commit=75249d8 graphdriver(s)=vfs version=20.10.8
 8月 28 18:03:57 debian11 dockerd-rootless.sh[2312]: time="2021-08-28T18:03:57.068280606+09:00" level=info msg="Daemon has completed initialization"
 8月 28 18:03:57 debian11 dockerd-rootless.sh[2312]: time="2021-08-28T18:03:57.088288454+09:00" level=info msg="API listen on /run/user/1000/docker.sock"
+ DOCKER_HOST=unix:///run/user/1000/docker.sock /usr/bin/docker version
Client: Docker Engine - Community
 Version:           20.10.8
 API version:       1.41
 Go version:        go1.16.6
 Git commit:        3967b7d
 Built:             Fri Jul 30 19:54:22 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.8
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.6
  Git commit:       75249d8
  Built:            Fri Jul 30 19:52:31 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.9
  GitCommit:        e25210fe30a0a703442421b0f60afac609f950a3
 runc:
  Version:          1.0.1
  GitCommit:        v1.0.1-0-g4144b63
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
+ systemctl --user enable docker.service
Created symlink /home/zaki/.config/systemd/user/default.target.wants/docker.service → /home/zaki/.config/systemd/user/docker.service.
[INFO] Installed docker.service successfully.
[INFO] To control docker.service, run: `systemctl --user (start|stop|restart) docker.service`
[INFO] To run docker.service on system startup, run: `sudo loginctl enable-linger zaki`

[INFO] Creating CLI context "rootless"
Successfully created context "rootless"

[INFO] Make sure the following environment variables are set (or add them to ~/.bashrc):

export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///run/user/1000/docker.sock

環境変数を追加する。PATHはすでにあるのでDOCKER_HOSTのみ。

zaki@debian11:~$ export DOCKER_HOST=unix:///run/user/1000/docker.sock
zaki@debian11:~$ docker image ls
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE
zaki@debian11:~$ docker container ls
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

docker runお試し

zaki@debian11:~$ docker run --rm -d -p 8080:80 nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
e1acddbe380c: Pull complete 
e21006f71c6f: Pull complete 
f3341cc17e58: Pull complete 
2a53fa598ee2: Pull complete 
12455f71a9b5: Pull complete 
b86f2ba62d17: Pull complete 
Digest: sha256:4d4d96ac750af48c6a551d757c1cbfc071692309b491b70b2b8976e102dd3fef
Status: Downloaded newer image for nginx:latest
76b82bf2fcc9d16e42142221bf52581ccb69a077e87b4863bff4913040d69e95
docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: process_linux.go:385: applying cgroup configuration for process caused: error while starting unit "docker-76b82bf2fcc9d16e42142221bf52581ccb69a077e87b4863bff4913040d69e95.scope" with properties [{Name:Description Value:"libcontainer container 76b82bf2fcc9d16e42142221bf52581ccb69a077e87b4863bff4913040d69e95"} {Name:Slice Value:"user.slice"} {Name:PIDs Value:@au [2645]} {Name:Delegate Value:true} {Name:MemoryAccounting Value:true} {Name:CPUAccounting Value:true} {Name:IOAccounting Value:true} {Name:TasksAccounting Value:true} {Name:DefaultDependencies Value:false}]: read unix @->/run/systemd/private: read: connection reset by peer: unknown.

あ、やっぱこのエラーでるな。。。

対応はdocker run errorsを確認。

zaki@debian11:~$ systemctl --user is-active dbus
inactive
zaki@debian11:~$ systemctl --user status dbus
● dbus.service - D-Bus User Message Bus
     Loaded: loaded (/usr/lib/systemd/user/dbus.service; static)
     Active: inactive (dead)
TriggeredBy: ● dbus.socket
       Docs: man:dbus-daemon(1)

dbus、インストールはされてるけど動いてないので有効化する。

zaki@debian11:~$ systemctl --user enable --now dbus
The unit files have no installation config (WantedBy=, RequiredBy=, Also=,
Alias= settings in the [Install] section, and DefaultInstance= for template
units). This means they are not meant to be enabled using systemctl.
 
Possible reasons for having this kind of units are:
• A unit may be statically enabled by being symlinked from another unit's
  .wants/ or .requires/ directory.
• A unit's purpose may be to act as a helper for some other unit which has
  a requirement dependency on it.
• A unit may be started when needed via activation (socket, path, timer,
  D-Bus, udev, scripted systemctl call, ...).
• In case of template units, the unit is meant to be enabled with some
  instance name specified.
zaki@debian11:~$ 
zaki@debian11:~$ 
zaki@debian11:~$ systemctl --user status dbus
● dbus.service - D-Bus User Message Bus
     Loaded: loaded (/usr/lib/systemd/user/dbus.service; static)
     Active: active (running) since Sat 2021-08-28 18:07:30 JST; 37s ago
TriggeredBy: ● dbus.socket
       Docs: man:dbus-daemon(1)
   Main PID: 2674 (dbus-daemon)
      Tasks: 1 (limit: 4675)
     Memory: 496.0K
        CPU: 2ms
     CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/dbus.service
             └─2674 /usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only

 8月 28 18:07:30 debian11 systemd[584]: Started D-Bus User Message Bus.

コンテナを再実行する。

zaki@debian11:~$ docker run --rm -d -p 8080:80 nginx
1872f49375bfd1e86839be7903c0ffb11ac97772926854b50782438c27a8a87f
zaki@debian11:~$ curl localhost:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

[snip]

動いた。


おまけ

Debian11入れて割とすぐくらいのタイミングで試してもうまくいかず、藁にもすがる思いでツイートしたら助けてもらいました!

というのも実は私が試したときはdbus関連のパッケージについて記載がドキュメントに載ってなくて、このやりとりのあとに、ドキュメント修正のPR出してしてくれたらしい…感謝です。

github.com

エラーメッセージもわかりやすくなるかも。

github.com

コミュニティの開発、すごい。。


ちなみにディストリビューションでいうと、「Distribution-specific hint」のところに

Note: We recommend that you use the Ubuntu kernel.

と書いてある笑

[Ansible] roleの雛形作成をカスタムする (ansible-galaxy / --role-skeleton)

roleの雛形の作り方と、雛形のカスタマイズ。
今回は試さなかったけどcollectionにも使えるはず。。

role雛形の作成

Ansibleのroleの雛形は、ansible-galaxyを使って以下のコマンドで作成できます。

$ ansible-galaxy init my-role
$ tree my-role/
my-role/
├── README.md
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

8 directories, 8 files

引数で指定した「my-role」というrole名で各ファイルが生成され、各ファイル内のrole名がセットされます。

role雛形のカスタム

role作成のansible-galaxyのオプションで--role-skeleton=/path/to/role-skeletonを指定することで、雛形のスケルトン(ややこしい)を指定できます。
ケルトンは一旦ansible-galaxy initでrole雛形を作り、出来た雛形に必要なファイルを作成・編集したり、不要ファイル削除したりすれば、それをスケルトンとして使用可能。
例えば「ディレクトリだけのfilestemplatesは要らん(必要があるときに作る)」「defaultsは使わない」「README.mdはプロジェクトのテンプレートを使う」とか。

雛形のスケルトンを作る

$ ansible-galaxy init role-skeleton
- Role role-skeleton was created successfully

そこから(例として)不要なファイルを削除。

$ rm -rf role-skeleton/files/ role-skeleton/handlers/ role-skeleton/templates/ role-skeleton/defaults/
$ tree role-skeleton/role-skeleton/
├── README.md
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

4 directories, 6 files

(例として)READMEを編集。(ちなみにREADME.md.orgは残しておくとこれも雛形に含まれるので削除を忘れずに)

$ diff -u role-skeleton/README.md.org role-skeleton/README.md
--- role-skeleton/README.md.org 2021-08-27 08:39:54.140736835 +0900
+++ role-skeleton/README.md     2021-08-27 08:43:23.359197543 +0900
@@ -1,38 +1,34 @@
 Role Name
 =========
 
-A brief description of the role goes here.
+fixme
 
 Requirements
 ------------
 
(中略)

 License
 -------
 
-BSD
+MIT
 
 Author Information
 ------------------
 
-An optional section for the role authors to include contact information, or a website (HTML is not allowed).
+@zaki_hmkc

ケルトンからroleの雛形を作成

↑で作成したスケルトンを指定してrole雛形を作成します。

$ ansible-galaxy init --role-skeleton=./role-skeleton customize-role
- Role customize-role was created successfully

これでカスタムしたスケルトンで用意したファイルのみのrole雛形になります。

$ tree customize-role/
customize-role/
├── README.md
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

4 directories, 6 files

カスタムしたREADME.mdもこの通り。

$ cat customize-role/README.md 
Role Name
=========

fixme

Requirements

(中略)

License
-------

MIT

Author Information
------------------

@zaki_hmkc

role名の埋め込み

ただし↑の手順だけだと、ファイル内のrole名がセットされる箇所が、スケルトンを作ったときのままになります。

$ cat customize-role/tasks/main.yml 
---
# tasks file for role-skeleton

ここにスケルトンではなく新しく作るrole雛形の名前をセットするにはJinja2テンプレートで変数を指定します。
その際、テンプレートで変数使う場合はファイル名も.j2を追加します。

tasks/main.ymlでテンプレートを参照するのであれば、tasks/main.yml.j2にして、role名に置き換えたい箇所を{{ role_name }}に置き換えます。

$ cat role-skeleton/tasks/main.yml.j2
---
# tasks file for {{ role_name }}

この内容のスケルトンを指定してrole雛形を作成すると、以下の通り。

$ ansible-galaxy init --role-skeleton=./role-skeleton customize-role2
- Role customize-role2 was created successfully
$ cat customize-role2/tasks/main.yml 
---
# tasks file for customize-role2

role名を変換できました。
現状変数指定できるのは{{ role_name }}のみ、とのこと。
また、templatesディレクトリ以下の*.j2ファイルに対しては、テンプレートとしての変数変換は行われません。(なので、スケルトンに用意したファイルはそのまま雛形として出力される)

any .j2 files found outside of a templates folder will be rendered as templates. The only useful variable at the moment is role_name

https://docs.ansible.com/ansible/latest/galaxy/dev_guide.html#using-a-custom-role-skeleton

role名変換とJinja2テンプレートの書式を同時に使いたいとき

ファイル名を.j2にすると、ファイル内のJinja2テンプレートの変数参照は全て雛形作成時に変換処理が行われます(そして{{ role_name }}以外は未定義エラーになる)。

{{ role_name }}は変換しつつ、それ以外はJinja2として解釈せずにそのまま雛形作成するには、Jinja2の{% raw %}を使ってエスケープします。

タスクのスケルトンをこのように記述しておくと、

$ cat role-skeleton/tasks/main.yml.j2
---
# tasks file for {{ role_name }}
- name: print values
  ansible.builtin.debug:
    msg: {% raw %}"{{ role_values }}"{% endraw %}

生成されるrole雛形のタスクファイルはこの通り。
{{ role_name }}はテンプレートとして変換されて、{{ role_values }}はそのまま。

$ cat customize-role3/tasks/main.yml 
---
# tasks file for customize-role3
- name: print values
  ansible.builtin.debug:
    msg: "{{ role_values }}"

↑は変数参照部分だけ'{% raw %}`で囲んでるけど、個数が多いなら上から下まで丸ごと囲んでも良いかも。

参考情報

docs.ansible.com

「Using a custom role skeleton」参照。

環境

ansible-galaxy [core 2.11.2] 
  config file = /home/zaki/.ansible.cfg
  configured module search path = ['/home/zaki/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/zaki/src/ansible-sample/venv/ansible4/lib64/python3.6/site-packages/ansible
  ansible collection location = /home/zaki/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/zaki/src/ansible-sample/venv/ansible4/bin/ansible-galaxy
  python version = 3.6.8 (default, Nov 16 2020, 16:55:22) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
  jinja version = 3.0.1
  libyaml = True

Pythonのバージョンが(ry

[Ansible] playbookと別リポジトリ管理のroleを実行するにはroles/requirements.ymlを使う (CLI / Tower)

playbookのリポジトリとroleのリポジトリを分けた場合に、どうやって別リポジトリのroleをplaybookから実行するかについて。半年以上前に情報として聞いてはいたけれど試してなかったので実際に動かしてみた。

roleのリポジトリ

ansible-galaxy role init <role-name>で作成できるファイル群がリポジトリ直下になるようにrole作成。
この際、meta/main.ymlが必須。(無いとplaybook側で…というかansible-galaxy installコマンドでイントールできない)
リポジトリのファイル構成例としてはこんな感じ。

$ tree
.
├── README.md
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
└── vars
    └── main.yml

実装例は以下。

github.com

サンプルroleの中身はこんな感じ。
(curryって変数はvars/main.ymlで定義)

---
# tasks file for sample_role
- name: sample role start
  debug:
    msg: "this is sample role"

- name: print value
  debug:
    msg: 'curry: {{ curry }}'

これをリモートリポジトリにpushしておくと、ansible-galaxy installでroleをインストールできるようになる。

playbookのリポジトリ

github.com

roleのインストール

roles/requirements.ymlに使用するroleの情報を記述する。
例としてはこんな感じ。

---
- name: hogehoge
  src: https://github.com/zaki-lknr/ansible-sample-role.git
  version: main

このrequirements.ymlを引数にansible-galaxy install -r roles/requirements.ymlを実行すると、(デフォルトでは)~/.ansible/roles/hogehoge以下へroleがインストールされる。
書式についてはInstalling multiple roles from a fileを参照。
あとscmというパラメタに、デフォルトはgitだけどhgも指定できる模様。そういやTowerもMercurialが選択肢にあったな。

(追記) role(のリポジトリ)が更新されてる場合は、-f も付与してアップデート(強制上書きインストール)が必要。
(少し前にアップデートオプションが追加されたような気がしたけど、あれってコレクションの場合(ansible-galaxy collection)のオプションだったようだ)

$ ansible-galaxy install -fr roles/requirements.yml

playbook

playbookはこの通り、hogehogeというroleを実行するように記述。

---
- hosts: localhost
  gather_facts: false

  roles:
  - hogehoge

事前に~/.ansible/roles/hogehogeへ対象roleをインストール済みであれば、このplaybookをansible-playbookで普通に実行すればhogehoge roleが呼ばれて普通に実行できる。

roleインストール先を変更したい場合

ansible.cfgなどで

[defaults]
roles_path = /var/tmp/ansible/roles

とか書いておけば指定可能。
詳しくはDEFAULT_ROLES_PATH参照。

Towerの場合

前述「playbookのリポジトリ」の内容をGitリモートリポジトリにpushしておき、Towerでは通常通りにこのリポジトリを使うプロジェクトを作成する。
プロジェクトの同期のタイミングでroles/requirements.ymlのroleが同期されるので、あとはこちらも普通に実行できる。
よくよく見ると、collections/requirements.yml もここで参照されてるんだな。

roleのリポジトリで更新を行った場合はプロジェクトの再同期が必要。(と思う。自動設定は…Webhookとかで出来るかな?)
手元の環境ではジョブテンプレートの再実行だけではroleの内容は更新されなかった。
※ (追記) ↑ プロジェクトの設定で「起動時のリビジョン更新」にチェックを入れておけば、ジョブ実行毎にリポジトリの情報が更新されるので、実行時にroleも更新される。

ジョブを実行したときのログはこの通りで、指定のroleがちゃんと実行されている。

これは何ができるの?

複数のリポジトリでそれぞれの目的の処理を行うplaybookを作成してて、でもいくつかのplaybookで似たような処理が出てきたので共通化したい、みたいな場合に処理を集約できる。

環境

AnsibleはCentOS7
Pythonがちょっと古い。

$ ansible --version
ansible [core 2.11.2] 
  config file = /home/zaki/.ansible.cfg
  configured module search path = ['/home/zaki/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/zaki/src/ansible-sample/venv/ansible4/lib64/python3.6/site-packages/ansible
  ansible collection location = /home/zaki/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/zaki/src/ansible-sample/venv/ansible4/bin/ansible
  python version = 3.6.8 (default, Nov 16 2020, 16:55:22) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
  jinja version = 3.0.1
  libyaml = True

Towerは3.8.0 (on RHEL8)

zaki-hmkc.hatenablog.com

参考情報

www.ansible.com

docs.ansible.com

コレクションの場合

zaki-hmkc.hatenablog.com


続報に期待です :)
(いつも貴重な情報ありがとうございます!!)

Ansible Automation Platform 2.0 Early Accessお試し (Automation Controller 4.0でデモジョブ実行まで)

Ansible Automation Platform 2.0 Early Access Homepage - Red Hat Customer Portal

巷で流行っている次期Ansible TowerになるAnsible Automation Platform (2.0 Early Access)を手元の環境へインストールしてみる。開発者ライセンスがあればOKの模様。

access.redhat.com

インストールガイドは日本語版も公開されている。
本エントリは2021.08.04時点の2.0 Early Access版の情報なので、GAになるとまた少し変わるかもしれないので注意。

開発者ライセンスのサブスクリプションで利用できるリポジトリを確認すると、Ansible Automation Platform 2.0 Early Accessもリストにあるので多分大丈夫なはず。。

f:id:zaki-hmkc:20210804203343p:plain

※ 公開後にフォローいただいた内容をいくつか追記

システム要件

目の前にある物理ESXiに、RHEL 8.4のVMを1つ作成。
システム要件は下記。

1.1. Red Hat Ansible Automation Platform のシステム要件

  • CPU: 最小2つ
  • RAM: 最小4GB
  • ストレージ: 40GB + DBで20GB

今回はサービスノードとDBを共用の予定なので60GBで。
ちなみにRAMは、最小4GBとなってるが実際にインストールしようとすると7400MBあるかどうかチェックしてるっぽくてエラーになる。8GBにしておくと無難。
あと、このエントリの手順ではインターネットアクセスも必要。

インストーラーの準備

ダウンロードページから「Ansible Automation Platform 2.0.0 Setup」をダウンロード

f:id:zaki-hmkc:20210804194952p:plain

[zaki@aap-ea ~]$ tar xf ansible-automation-platform-setup-2.0.0-1-early-access.tar.gz 
[zaki@aap-ea ~]$ ls -F
ansible-automation-platform-setup-2.0.0-1-early-access/
ansible-automation-platform-setup-2.0.0-1-early-access.tar.gz

RHELサブスクリプション

OSインストール後に開発者ライセンスを登録済み

[zaki@aap-ea ~]$ sudo subscription-manager list --consumed | grep Ansible
                          Red Hat Ansible Automation Platform
                          Red Hat Ansible Engine

開発者ライセンスを使ったAnsible Towerのインストールについては下記も参照。

zaki-hmkc.hatenablog.com

単一マシンへのインストール

今回は1つのノード(RHEL8ネイティブ)へAnsible Automation Controller 4.0とDBを入れる。
ドキュメントはこちら。

access.redhat.com

インベントリファイル

アーカイブを展開し、インベントリファイルを作成。
最低限変更箇所は以下の通り。

  • admin_password
  • pg_password

もう1点、構築後のRed Hatのコンテナレジストリへの認証情報を追加も行う。各パラメタに、Red Hatアカウントの情報を記入する。
変更点は全体でこんな感じ。

$ diff -u inventory.org inventory
--- inventory.org       2021-07-13 20:24:51.000000000 +0900
+++ inventory   2021-08-04 17:23:17.647128071 +0900
@@ -8,14 +8,18 @@
 [servicescatalog_workers]
 
 [all:vars]
-admin_password=''
+admin_password='adminのパスワード'
+
+registry_username='Red Hatアカウント名'
+registry_password='パスワード'
+registry_url=https://registry.redhat.io
 
 pg_host=''
 pg_port=''
 
 pg_database='awx'
 pg_username='awx'
-pg_password=''
+pg_password='password'
 pg_sslmode='prefer'  # set to 'verify-full' for client-side enforced SSL
 
 # Automation Hub Configuration

レジストリ情報については以下。

なお、レジストリの認証情報は最悪無くてもあとから設定するワークアラウンドがある(後述)

インストール

言語設定

8/3時点でドキュメンテーションされてないっぽいけど、シェルの言語設定が日本語だと途中以下のエラーがでてうまくいかない。

TASK [ansible.automation_platform_installer.packages_el : Uninstall nginx-all-modules] ***
fatal: [localhost]: FAILED! => {"changed": false, "failures": ["nginx-mod* - 一致した引数がありません: nginx-mod*"], "msg": "Failed to install some of the specified packages", "rc": 1, "results": []}

:
:

[error] Oops!  An error occurred while running setup.
[warn] /var/log/tower does not exist. Setup log saved to setup.log.

現状のワークアラウンドとしては、言語パッケージを入れて、LANG環境変数Cにしてインストールすれば回避できる。

$ sudo dnf install glibc-langpack-en

手元で試した限り、このパッケージインストールは無くても良いかも…と思ったけど、DBの作成がうまくいかないかもしれないのであった方が良いかもしれない。

access.redhat.com

RAMサイズ

前述したけど、実は4GBじゃない。
足りてない構成でインストールしようとすると、下記エラーになる。

TASK [ansible.automation_platform_installer.preflight : Preflight check - Fail if this machine lacks sufficient RAM.] ***
fatal: [localhost]: FAILED! => {"changed": false, "msg": "This machine does not have sufficient RAM to run Ansible Automation Platform."}

メッセージで検索すると、下記roleでメモリチェックしてる。

$ less collections/ansible_collections/ansible/automation_platform_installer/roles/preflight/tasks/main.yml 
:
:
- name: Preflight check - Fail if this machine lacks sufficient RAM.
  fail:
    msg: This machine does not have sufficient RAM to run Ansible Automation Platform.
  when:
    - ansible_memtotal_mb < required_ram|int
    - not ignore_preflight_errors| default(false) | bool

最初は4GBちょうどで作ったからかなー、と6GBに増やして再実行…したら同じエラーになった。
もっと確認すると、チェックに使う変数required_ramが4GBじゃなくて以下の通り7400となっている。

$ find . -type f | xargs grep required_ram
./collections/ansible_collections/ansible/automation_platform_installer/roles/preflight/defaults/main.yml:required_ram: 7400

というわけで、ノードのRAMを8GBくらいにするか、-e ignore_preflight_errors=Trueをオプションに追加してインストールする。

表2.2 追加変数

インストール

特権必要なので、sudoつけるかrootユーザーで実行。
LANG環境変数もあるのでrootユーザーで実行するのが確実かな。

手元の環境ではLANG環境変数sudoで渡せたのでsudoで。
あと前述のメモリサイズ(とレジストリの認証情報)に気を付ければ特に問題ないはず。

$ time sudo ./setup.sh -e ignore_preflight_errors=True

手元の環境では10分強で完了。
エラーが無ければ、インストールしたノードへHTTPSアクセスすればAutomation ControllerのUIのログイン画面になる。

f:id:zaki-hmkc:20210804200414p:plain

Ansible Automation Platform

サブスクリプション

初回ログイン時はサブスクリプションの入力が必要。
開発者ライセンスを使っている場合は、「ユーザー名/パスワード」を押下し、Red Hatアカウント情報を入力して「サブスクリプションの取得」押下。

f:id:zaki-hmkc:20210804200743p:plain

サブスクリプションが表示されるので選択。

f:id:zaki-hmkc:20210804200620p:plain

選択済みにサブスクリプション名がでるので確認して「次へ」で進める。

f:id:zaki-hmkc:20210804200659p:plain

ユーザーとInsightsのアナリティクス

任意で。

使用許諾

ライセンスとかちゃんと入っていれば「送信」が押下できるので「送信」
正常に処理完了すればダッシュボード画面が表示される。

f:id:zaki-hmkc:20210804200946p:plain

ジョブ実行

サンプルジョブを実行。
「テンプレート」に「Demo Job Template」があるのでテンプレートを起動。

f:id:zaki-hmkc:20210804201241p:plain

インベントリファイルでレジストリ情報を入力していれば問題なくジョブが実行されるはず。

f:id:zaki-hmkc:20210804201454p:plain

認証情報追加するには

インベントリにレジストリの認証情報を入力していない場合下記エラーになり、プロジェクトの同期に失敗する。

Error: Error initializing source docker://registry.redhat.io/ansible-automation-platform-20-early-access/ee-supported-rhel8:2.0.0: unable to retrieve auth token: invalid username/password: unauthorized: Please login to the Red Hat Registry using your Customer Portal credentials. Further instructions can be found here: https://access.redhat.com/RegistryAuthentication

UIの「認証情報」メニューでコンテナレジストリの情報を作成し、「実行環境」にコンテナレジストリの認証情報を追加することは基本的にできるけど、このプロジェクト更新に必要な「Control Plane Execution Environment」のみ認証情報設定がグレーアウトされ設定できないようになっている。

f:id:zaki-hmkc:20210804202101p:plain

インベントリにRed Hatアカウントを入力していると、この情報が設定済みの状態でセットアップされる。

f:id:zaki-hmkc:20210804201358p:plain

awxユーザーでレジストリの認証情報作成

回避策として、Ansible Automation Controllerをインストールしたホストのawxユーザーでpodman loginで認証情報をホスト上に作成する。

[awx@aap-ea ~]$ podman login registry.redhat.io
Username: zaki-rh
Password: 
Login Succeeded!
[awx@aap-ea ~]$

ログインすると手元の環境では /tmp/podman-run-985/containers/auth.json に認証情報作成される。 この状態になれば、プロジェクトの同期ができるようになるので、ジョブが実行できるようになる。

ちなみにコンテナエンジンは当然だけどPodmanが使用されている。


ひとまずTowerの次期バージョンになるAnsible Automation Controller 4.0を試してみた。
まだ用語を把握できてないんだけど、Ansible Automation ControllerはAnsible Automation Platform 2.0の構成要素の一つってことかな?ほかにAutomation Hubとかがあるって認識。

Execution Environmentsについては以下も参照。

zaki-hmkc.hatenablog.com


参考

access.redhat.com

access.redhat.com

[Ansible / Podman] もうこのぉ、venvを使った実行環境は終わりだ (Ansible RunnerとAnsible Builderお試し)

トキメk…じゃなくてAnsible RunnerやAnsible Builderの話で、Ansibleとコンテナ環境の話題がとても盛り上がってるところに乗り遅れつつある*1ので、マイペースに動作確認してみた作業ログ。
コンテナの中身寄り情報あり。

環境

Fedora 34で確認。

[zaki@fedora-node ~]$ cat /etc/fedora-release 
Fedora release 34 (Thirty Four)

Pythonは3.9.4

(runner) [zaki@fedora-node ~]$ python --version
Python 3.9.4

BuilderとRunnerのバージョン

(runner) [zaki@fedora-node ~]$ ansible-builder --version
1.0.1
(runner) [zaki@fedora-node ~]$ ansible-runner --version
2.0.1

ちなみにDockerとPodmanは両方入ってる環境。
周りがみんなDocker使ってるのでPodmanで確認。

(runner) [zaki@fedora-node ~]$ podman --version
podman version 3.2.2
(runner) [zaki@fedora-node ~]$ docker --version
Docker version 20.10.6, build 370c289

Podmanはdnfでシュッっとインストールできて設定も特に不要。

$ sudo dnf install podman

ちなみに、Ansible RunnerとBuilderはデフォルトではコンテナエンジンにPodmanが使用される。

Ansible Builder

[zaki@fedora-node ~]$ python -m venv python/venv/runner
[zaki@fedora-node ~]$ . python/venv/runner/bin/activate
(runner) [zaki@fedora-node ~]$ 
(runner) [zaki@fedora-node ~]$ pip install --upgrade pip

インストール

ansible-builder.readthedocs.io

pip install ansible-builder
(runner) [zaki@fedora-node ~]$ pip install --upgrade pip
Requirement already satisfied: pip in ./python/venv/runner/lib/python3.9/site-packages (21.0.1)
Collecting pip
  Using cached pip-21.1.3-py3-none-any.whl (1.5 MB)
Installing collected packages: pip
  Attempting uninstall: pip
    Found existing installation: pip 21.0.1
    Uninstalling pip-21.0.1:
      Successfully uninstalled pip-21.0.1
Successfully installed pip-21.1.3
(runner) [zaki@fedora-node ~]$ pip install ansible-builder
Collecting ansible-builder
  Downloading ansible_builder-1.0.1-py3-none-any.whl (16 kB)
Collecting bindep
  Downloading bindep-2.9.0-py2.py3-none-any.whl (32 kB)
Collecting PyYAML
  Using cached PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl (630 kB)
Collecting requirements-parser
  Downloading requirements-parser-0.2.0.tar.gz (6.3 kB)
Collecting Parsley
  Downloading Parsley-1.3-py2.py3-none-any.whl (88 kB)
     |████████████████████████████████| 88 kB 3.6 MB/s 
Collecting distro
  Using cached distro-1.5.0-py2.py3-none-any.whl (18 kB)
Collecting pbr>=2.0.0
  Downloading pbr-5.6.0-py2.py3-none-any.whl (111 kB)
     |████████████████████████████████| 111 kB 10.8 MB/s 
Using legacy 'setup.py install' for requirements-parser, since package 'wheel' is not installed.
Installing collected packages: pbr, Parsley, distro, requirements-parser, PyYAML, bindep, ansible-builder
    Running setup.py install for requirements-parser ... done
Successfully installed Parsley-1.3 PyYAML-5.4.1 ansible-builder-1.0.1 bindep-2.9.0 distro-1.5.0 pbr-5.6.0 requirements-parser-0.2.0
(runner) [zaki@fedora-node ansible-ee]$ ansible-builder --version
1.0.1

execution environment definition

ansible-builder.readthedocs.io

YAMLファイルを作成するようになってるけど、特にファイル名の指定がなさそう?
と思ったけどこっちか。

ansible-builder.readthedocs.io

デフォルトではカレントのexecution-environment.ymlを見るっぽい。

(runner) [zaki@fedora-node ansible-ee]$ ansible-builder build --help
:
:
optional arguments:
  -h, --help            show this help message and exit
:
  -f FILENAME, --file FILENAME
                        The definition of the execution environment (default: execution-environment.yml)

なるほど、ファイル名は任意に指定可能みたい。
とりあえずデフォルトのexecution-environment.ymlでやっていく。

以下の定義はExecution Environment Definitionから。

---
version: 1

build_arg_defaults:
  EE_BASE_IMAGE: 'quay.io/ansible/ansible-runner:stable-2.10-devel'

ansible_config: 'ansible.cfg'

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

additional_build_steps:
  prepend: |
    RUN whoami
    RUN cat /etc/os-release
  append:
    - RUN echo This is a post-install command!
    - RUN ls -la /etc

ベースイメージに指定してるのは、サンプル通りでquay.io/ansible/ansible-runner:stable-2.10-develで、Ansible 2.10のイメージ。 選択できるベースのイメージのタグは、現時点(2021.07.22)でこの辺りかな?

quay.io

  • stable-2.11-devel (latest)
  • stable-2.10-devel
  • stable-2.9-devel

dependenciesに指定するのは、

  • galaxy: ansible-galaxy collection installするコレクションの定義
  • requirements.txt: 追加でpip installするPythonパッケージの定義
  • bindep.txt: おそらく、OS(今回はコンテナだけど)で追加インストールするパッケージ一覧っぽい

docs.opendev.org

requirements.yml

お試しでKubernetesとNetboxのコレクションを指定。

---
collections:
  - name: kubernetes.core
    version: 2.1.1
  - name: netbox.netbox
    version: 3.1.1

requirements.txt

たとえばjson_query使う場合などはこんな感じ。

jmespath

bindep.txt

お試しで以下の通り。

git

コントロールノード上に必要なコマンドを追加しないといけないときはこれに指定すれば良さそう。

ビルド

まずはオプション何も指定せずにやってみる。
(イメージビルドでtagしていないといろいろアレだけど)

(runner) [zaki@fedora-node ansible-ee]$ ansible-builder build 
Running command:
  podman build -f context/Containerfile -t ansible-execution-env:latest context
...showing last 20 lines of output...
--> e34f056f5eb
STEP 3: USER root
--> 9a704c0920c
STEP 4: ADD _build/ansible.cfg ~/.ansible.cfg
--> 17c5ffa6fb5
STEP 5: ADD _build /build
--> 95fe088171b
STEP 6: WORKDIR /build
--> 66c5fcbc890
STEP 7: RUN ansible-galaxy role install -r requirements.yml --roles-path /usr/share/ansible/roles
Skipping install, no requirements found
--> fe014f0f998
STEP 8: RUN ansible-galaxy collection install $ANSIBLE_GALAXY_CLI_COLLECTION_OPTS -r requirements.yml --collections-path /usr/share/ansible/collections
Starting galaxy collection install process
Process install dependency map
ERROR! Invalid collection name 'netbox', name must be in the format <namespace>.<collection>.
Please make sure namespace and collection name contains characters from [a-zA-Z0-9_] only.
STEP 9: FROM quay.io/ansible/ansible-builder:latest AS builder
Trying to pull quay.io/ansible/ansible-builder:latest...
Error: error building at STEP "RUN ansible-galaxy collection install $ANSIBLE_GALAXY_CLI_COLLECTION_OPTS -r requirements.yml --collections-path /usr/share/ansible/collections": error while running runtime: exit status 1

An error occured (rc=125), see output line(s) above for details.
(runner) [zaki@fedora-node ansible-ee]$ 

ん、netboxのcollectionのFQCN指定ミスりました。(netbox.netboxにすべきところをnetboxだけしか書いてなかった)

出力を見た感じ、中身は普通のイメージビルドね。あとdockerでなくpodmanが使われている。

(runner) [zaki@fedora-node ansible-ee]$ ansible-builder build --help
:
:
  --container-runtime {podman,docker}
                        Specifies which container runtime to use (default: podman)
(runner) [zaki@fedora-node ansible-ee]$ ansible-builder build 
File context/_build/requirements.yml had modifications and will be rewritten
Running command:
  podman build -f context/Containerfile -t ansible-execution-env:latest context
Complete! The build context can be found at: /home/zaki/ansible-ee/context

できた。(timeつけておけばよかった)

(runner) [zaki@fedora-node ansible-ee]$ podman image ls
REPOSITORY                       TAG                IMAGE ID      CREATED        SIZE
localhost/ansible-execution-env  latest             79a18fb0244f  5 minutes ago  800 MB
<none>                           <none>             0d9f9ebaef03  6 minutes ago  677 MB
<none>                           <none>             83aa24b33b99  7 minutes ago  729 MB
<none>                           <none>             fe014f0f9986  9 minutes ago  723 MB
quay.io/ansible/ansible-runner   stable-2.10-devel  6354f87b1cb2  7 hours ago    723 MB
quay.io/ansible/ansible-builder  latest             c38a5c4514a4  7 hours ago    630 MB

ベースが723MBでそれなりにあって、ビルド結果は800MBになってる。

イメージの中身をちょっと見てみる

(runner) [zaki@fedora-node ansible-ee]$ podman run -it --rm localhost/ansible-execution-env bash

これでコンテナのbash起動できる。(この辺りはDocker/Podmanの使い方)

ベースのディストリビューション

bash-4.4# cat /etc/redhat-release 
CentOS Linux release 8.4.2105

ディストリビューションは現状ではCentOS 8だった。 ここはたぶんディストリビューションに依存する使い方にはならないと思うので(apt系では無いくらいを覚えておけば)あまり気にしなくていいかな?

bindepの指定

bash-4.4# git --version
git version 2.27.0

入っている。

(runner) [zaki@fedora-node ~]$ podman run -it --rm quay.io/ansible/ansible-runner:stable-2.10-devel bash
bash-4.4# git --version
git version 2.27.0

と思ったけど、ベースイメージの時点で最初から入っていた。(gitをインストールする、という指定は不要かな)

後述したけどgccを入れれば、インストールされることを確認。

Pythonパッケージ

bash-4.4# pip list
Package           Version
----------------- ---------------
ansible-base      2.10.12.post0
ansible-runner    2.0.0.0a4.dev61
asn1crypto        1.2.0
Babel             2.7.0
bcrypt            3.2.0
cachetools        4.2.2
certifi           2021.5.30
cffi              1.13.2
chardet           3.0.4
cryptography      2.8
decorator         5.0.9
docutils          0.17.1
dumb-init         1.2.5
google-auth       1.33.1
gssapi            1.6.14
idna              2.8
Jinja2            2.10.3
jmespath          0.10.0
jsonpatch         1.32
jsonpointer       2.1
kubernetes        17.17.0
lockfile          0.12.2
lxml              4.4.1
MarkupSafe        1.1.1
ncclient          0.6.12
ntlm-auth         1.5.0
oauthlib          3.1.1
packaging         21.0
paramiko          2.7.2
pexpect           4.8.0
pip               21.1.3
ply               3.11
ptyprocess        0.7.0
pyasn1            0.4.8
pyasn1-modules    0.2.8
pycparser         2.19
pykerberos        1.2.1
PyNaCl            1.4.0
pyOpenSSL         19.1.0
pyparsing         2.4.7
pypsrp            0.5.0
PySocks           1.7.1
pyspnego          0.1.6
python-daemon     2.3.0
python-dateutil   2.8.2
pytz              2019.3
pywinrm           0.4.2
PyYAML            5.4.1
requests          2.22.0
requests-credssp  1.2.0
requests-ntlm     1.1.0
requests-oauthlib 1.3.0
rsa               4.7.2
setuptools        41.6.0
six               1.12.0
toml              0.10.2
urllib3           1.25.7
websocket-client  1.1.0
wheel             0.33.6
xmltodict         0.12.0

jmespathが入っている。

今回の場合の、ベースイメージとの差分はこんな感じ。

[zaki@fedora-node ~]$ diff -u <(podman run --rm quay.io/ansible/ansible-runner:stable-2.10-devel pip freeze | sort) <(podman run --rm localhost/ansible-execution-env pip freeze | sort)
--- /dev/fd/63  2021-07-22 16:57:46.000908246 +0900
+++ /dev/fd/62  2021-07-22 16:57:46.000908246 +0900
@@ -8,24 +8,33 @@
 ansible-runner @ file:///output/wheels/ansible_runner-2.0.0.0a4.dev61-py3-none-any.whl
 asn1crypto==1.2.0
 bcrypt==3.2.0
+cachetools==4.2.2
+certifi==2021.5.30
 cffi==1.13.2
 chardet==3.0.4
 cryptography==2.8
 decorator==5.0.9
 docutils==0.17.1
 dumb-init==1.2.5
+google-auth==1.33.1
 gssapi==1.6.14
 idna==2.8
+jmespath==0.10.0
+jsonpatch==1.32
+jsonpointer==2.1
+kubernetes==17.17.0
 lockfile==0.12.2
 lxml==4.4.1
 ncclient==0.6.12
 ntlm-auth==1.5.0
+oauthlib==3.1.1
 packaging==21.0
 paramiko==2.7.2
 pexpect==4.8.0
 ply==3.11
 ptyprocess==0.7.0
 pyOpenSSL==19.1.0
+pyasn1-modules==0.2.8
 pyasn1==0.4.8
 pycparser==2.19
 pykerberos==1.2.1
@@ -33,12 +42,16 @@
 pypsrp==0.5.0
 pyspnego==0.1.6
 python-daemon==2.3.0
+python-dateutil==2.8.2
 pytz==2019.3
 pywinrm==0.4.2
 requests-credssp==1.2.0
 requests-ntlm==1.1.0
+requests-oauthlib==1.3.0
 requests==2.22.0
+rsa==4.7.2
 six==1.12.0
 toml==0.10.2
 urllib3==1.25.7
+websocket-client==1.1.0
 xmltodict==0.12.0

コレクション

bash-4.4# ansible-galaxy collection list

# /usr/share/ansible/collections/ansible_collections
Collection      Version
--------------- -------
kubernetes.core 2.1.1  
netbox.netbox   3.1.1  

Ansibleバージョン

bash-4.4# ansible --version
ansible 2.10.12.post0
  config file = None
  configured module search path = ['/home/runner/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.8/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.8.6 (default, Jan 29 2021, 17:38:16) [GCC 8.4.1 20200928 (Red Hat 8.4.1-1)]

シェル起動直下のファイル

bash-4.4# pwd
/runner
bash-4.4# ls -F
artifacts/  env/  inventory/  project/
bash-4.4# find
.
./artifacts
./env
./inventory
./project

ビルドに使われたファイル

Dockerfile相当のファイルなどは、context/Containerfileに出力される。
イメージに組み込まれたファイル類はcontext/_build/以下。

(runner) [zaki@fedora-node ansible-ee]$ find context/
context/
context/_build
context/_build/requirements.yml
context/_build/requirements.txt
context/_build/bindep.txt
context/_build/ansible.cfg
context/Containerfile

Dockerfileに相当するcontext/Containerfileの中身は以下の通り。
マルチステージビルドで3段階のビルドを行ってる。

ARG EE_BASE_IMAGE=quay.io/ansible/ansible-runner:stable-2.10-devel
ARG EE_BUILDER_IMAGE=quay.io/ansible/ansible-builder:latest

FROM $EE_BASE_IMAGE as galaxy
ARG ANSIBLE_GALAXY_CLI_COLLECTION_OPTS=
USER root

ADD _build/ansible.cfg ~/.ansible.cfg

ADD _build /build
WORKDIR /build

RUN ansible-galaxy role install -r requirements.yml --roles-path /usr/share/ansible/roles
RUN ansible-galaxy collection install $ANSIBLE_GALAXY_CLI_COLLECTION_OPTS -r requirements.yml --collections-path /usr/share/ansible/collections

FROM $EE_BUILDER_IMAGE as builder

COPY --from=galaxy /usr/share/ansible /usr/share/ansible

ADD _build/requirements.txt requirements.txt
ADD _build/bindep.txt bindep.txt
RUN ansible-builder introspect --sanitize --user-pip=requirements.txt --user-bindep=bindep.txt --write-bindep=/tmp/src/bindep.txt --write-pip=/tmp/src/requirements.txt
RUN assemble

FROM $EE_BASE_IMAGE
USER root
RUN whoami
RUN cat /etc/os-release

COPY --from=galaxy /usr/share/ansible /usr/share/ansible

COPY --from=builder /output/ /output/
RUN /output/install-from-bindep && rm -rf /output/wheels
RUN echo This is a post-install command!
RUN ls -la /etc

リビルドする場合 (bindep.txtにgcc追加)

例えばbindep.txtを更新した場合でも、再実行すればbuilderがちゃんと検知してくれる。

git
gcc

gccを追加して再実行。今度はtag指定あり。

(runner) [zaki@fedora-node ansible-ee]$ ansible-builder build -t devel:latest
File context/_build/bindep.txt had modifications and will be rewritten
Running command:
  podman build -f context/Containerfile -t devel:latest context
Complete! The build context can be found at: /home/zaki/ansible-ee/context
(runner) [zaki@fedora-node ansible-ee]$ podman image ls
REPOSITORY                       TAG                IMAGE ID      CREATED         SIZE
localhost/devel                  latest             131d82f1aa27  45 seconds ago  886 MB
<none>                           <none>             50d91977e012  2 minutes ago   783 MB
<none>                           <none>             acf4c57bb6f6  3 minutes ago   729 MB
localhost/ansible-execution-env  latest             79a18fb0244f  16 hours ago    800 MB
<none>                           <none>             0d9f9ebaef03  16 hours ago    677 MB
<none>                           <none>             83aa24b33b99  16 hours ago    729 MB
<none>                           <none>             fe014f0f9986  16 hours ago    723 MB
quay.io/ansible/ansible-runner   stable-2.10-devel  6354f87b1cb2  23 hours ago    723 MB
quay.io/ansible/ansible-builder  latest             c38a5c4514a4  23 hours ago    630 MB
(runner) [zaki@fedora-node ansible-ee]$ podman run -it --rm localhost/devel bash
bash-4.4# gcc --version
gcc (GCC) 8.4.1 20200928 (Red Hat 8.4.1-1)
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

ちゃんとパッケージインストールも確認できた。
これはデフォルトでは入っていない。

(runner) [zaki@fedora-node ansible-ee]$ podman run -it --rm quay.io/ansible/ansible-runner:stable-2.10-devel bash
bash-4.4# gcc
bash: gcc: command not found

Ansible Runner

ansible-runner.readthedocs.io

ん-、こっちはGetting StartedとかQuick Startみたいな節がない。
Introduction to Ansible Runner かな?

install

ansible-runner.readthedocs.io

Runnerはインストール方法がpip installだけでなく、パッケージインストールもできる模様。 builderで作ったイメージの実行方法は環境によっていろいろ合わせられるって感じかな。

とりあえずpipで。

(runner) [zaki@fedora-node ~]$ pip install ansible-runner
Collecting ansible-runner
  Downloading ansible_runner-2.0.1-py3-none-any.whl (77 kB)
     |████████████████████████████████| 77 kB 2.6 MB/s 
Collecting python-daemon
  Downloading python_daemon-2.3.0-py2.py3-none-any.whl (35 kB)
Requirement already satisfied: pyyaml in ./python/venv/runner/lib/python3.9/site-packages (from ansible-runner) (5.4.1)
Collecting pexpect>=4.5
  Downloading pexpect-4.8.0-py2.py3-none-any.whl (59 kB)
     |████████████████████████████████| 59 kB 1.7 MB/s 
Collecting six
  Using cached six-1.16.0-py2.py3-none-any.whl (11 kB)
Collecting ptyprocess>=0.5
  Downloading ptyprocess-0.7.0-py2.py3-none-any.whl (13 kB)
Collecting lockfile>=0.10
  Downloading lockfile-0.12.2-py2.py3-none-any.whl (13 kB)
Requirement already satisfied: setuptools in ./python/venv/runner/lib/python3.9/site-packages (from python-daemon->ansible-runner) (53.0.0)
Collecting docutils
  Downloading docutils-0.17.1-py2.py3-none-any.whl (575 kB)
     |████████████████████████████████| 575 kB 12.8 MB/s 
Installing collected packages: ptyprocess, lockfile, docutils, six, python-daemon, pexpect, ansible-runner
Successfully installed ansible-runner-2.0.1 docutils-0.17.1 lockfile-0.12.2 pexpect-4.8.0 ptyprocess-0.7.0 python-daemon-2.3.0 six-1.16.0
(runner) [zaki@fedora-node ~]$ ansible-runner --version
2.0.1

adhoc (失敗)

ansible-runner.readthedocs.io

Running Ansible adhocの項。

(runner) [zaki@fedora-node ansible-runner]$ ansible-runner adhoc localhost -m ping
usage: ansible-runner [-h] [--version] [--debug] [--logfile LOGFILE] [-b BINARY]
                      [-i IDENT] [--rotate-artifacts ROTATE_ARTIFACTS]
                      [--artifact-dir ARTIFACT_DIR] [--project-dir PROJECT_DIR]
                      [--inventory INVENTORY] [-j] [--omit-event-data]
                      [--only-failed-event-data] [-q] [-v]
                      {run,start,stop,is-alive,transmit,worker,process} ...
ansible-runner: error: argument command: invalid choice: 'adhoc' (choose from 'run', 'start', 'stop', 'is-alive', 'transmit', 'worker', 'process')

サブコマンド無かった。

(runner) [zaki@fedora-node ~]$ ansible-runner --help

[...]

subcommands:
  COMMAND PRIVATE_DATA_DIR [ARGS]

  {run,start,stop,is-alive,transmit,worker,process}
                        Command to invoke
    run                 Run ansible-runner in the foreground
    start               Start an ansible-runner process in the background
    stop                Stop an ansible-runner process that's running in the background
    is-alive            Check if a an ansible-runner process in the background is still running.
    transmit            Send a job to a remote ansible-runner process
    worker              Execute work streamed from a controlling instance
    process             Receive the output of remote ansible-runner work and distribute the results

というか、次の項の「Running Ansible ansible-playbook」のplaybookってサブコマンドも無いような…見てるドキュメントが違うとかバージョンが違うとかかな。。

ansible-runner.readthedocs.io

こっちかな?

とりあえずadhocあたりのサブコマンド周りはスキップ。

ping to localhost

ディレクトリ構造

Runner Input Directory Hierarchyを見ると、実行の基準のディレクトリから以下のディレクトリを使うようになってる模様

  • env
  • inventory
  • project

ssh_keyなんかはenv以下に置くらしい。

設定とplaybook作成

一番何もしてなさそうなplaybookをお試しで。
ディレクトリ構造ベースにファイル作成する。

以下で使ってるrunner-filesというディレクトリ名は任意 (引数で指定するだけ)

(runner) [zaki@fedora-node ~]$ find runner-files/
runner-files/
runner-files/env
runner-files/env/settings
runner-files/inventory
runner-files/project
runner-files/project/playbook.yml

分かりにくかったけど、env/settingsで実行環境としてコンテナ(podmandocker)を使う設定にないと、実行ノードのansible-playbookを探して実行しようとするため、入れていない場合はエラーになる。

(runner) [zaki@fedora-node ~]$ ansible-runner run runner-files/ -p playbook.yml 
The command was not found or was not executable: ansible-playbook.

env/settingsは最低限以下の通り。
process_isolationtrue指定し、process_isolation_executableにコンテナエンジンを指定。
container_imageに実行するAnsible Runnerのイメージ名を指定。(これがいままでのvenvに相当する)

process_isolation: true
process_isolation_executable: podman
container_image: localhost/ansible-execution-env:latest

project/playbook.ymlは以下の通り。

---
- hosts: localhost
  gather_facts: false

  tasks:
  - name: ping
    ping:

実行時のplaybookの指定は-pで、ノードOS上のplaybookのファイルパスではなく、projectディレクトリ内のファイル名を指定する。

(runner) [zaki@fedora-node ~]$ ansible-runner run runner-files/ -p playbook.yml 
[WARNING]: Unable to parse /runner/inventory/hosts as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'

PLAY [localhost] ***************************************************************

TASK [ping] ********************************************************************
ok: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

一度実行すると、artifactsディレクトリができる。

(runner) [zaki@fedora-node ~]$ ls runner-files/artifacts/
0e86e736-127b-4f31-ba87-7582fb150861  5f237ac7-eaa6-41de-9ff2-c56cb9529061
35951222-37e1-42ba-b9cd-7886a4f2e674  ba21e63e-5589-450c-85f4-cada04600d40

中を確認すると、Ansibleの実行結果やログなどが格納される。
実行後にコンテナの残骸は残らないのでここを確認すればよさそう。

(runner) [zaki@fedora-node ~]$ podman container ls -a
CONTAINER ID  IMAGE       COMMAND     CREATED     STATUS      PORTS       NAMES

-v付けてもちゃんと反応する。

(runner) [zaki@fedora-node ~]$ ansible-runner run runner-files/ -p playbook.yml -v
No config file found; using defaults
[WARNING]: Unable to parse /runner/inventory/hosts as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'

PLAY [localhost] ***************************************************************

TASK [ping] ********************************************************************
ok: [localhost] => {"changed": false, "ping": "pong"}

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Ansible Runnerを実行した時のコンテナIDがディレクトリ名に使われてると思うけど、どのIDで実行されてるかが実行時にわからないな。。
ちなみに実行時のlocalhostはノードOSでなくコンテナ内。

  - name: print hostname
    debug:
      msg: "{{ lookup('pipe', 'hostname') }}"

これを実行すると、

TASK [print hostname] **********************************************************
ok: [localhost] => {
    "msg": "949bae49d041"
}

こんな感じ。

コレクション使用 + ssh接続のターゲットノード

せっかくKubernetesコレクションを入れたので、podの状態を拾うplaybookをproject/k8s.ymlに作成。

---
- hosts: k8s_cluster
  gather_facts: false

  tasks:
  - name: k8s get pods
    kubernetes.core.k8s_info:
      namespace: example
      kind: Pod

インベントリはディレクトリ構成通りinventory/hostsに作成

[k8s_cluster]
192.168.0.44

接続ユーザー

ターゲットノードへの接続情報ないのでエラーになるのはわかってるけど、ひとまずこれで実行。

(runner) [zaki@fedora-node ~]$ ansible-runner run -v runner-files/ -p k8s.yml
No config file found; using defaults

PLAY [k8s_cluster] *************************************************************

TASK [k8s get pods] ************************************************************
fatal: [192.168.0.44]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Warning: Permanently added '192.168.0.44' (ECDSA) to the list of known hosts.\r\nroot@192.168.0.44: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).", "unreachable": true}

PLAY RECAP *********************************************************************
192.168.0.44               : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0   

なるほど、rootユーザーで接続しようとしてる。というよりはユーザー名未指定なので、コンテナ内の実行ユーザーが使われてて、それがrootってことだと思われる。

(runner) [zaki@fedora-node ~]$ podman run -it --rm localhost/ansible-execution-env iduid=0(root) gid=0(root) groups=0(root)

ユーザー名を以下のように指定して、

[k8s_cluster]
192.168.0.44 ansible_user=zaki

まだ認証情報の設定してないのでエラーになる想定だけどユーザー名は制御できてるかの確認。

(runner) [zaki@fedora-node ~]$ ansible-runner run -v runner-files/ -p k8s.yml
No config file found; using defaults

PLAY [k8s_cluster] *************************************************************

TASK [k8s get pods] ************************************************************
fatal: [192.168.0.44]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Warning: Permanently added '192.168.0.44' (ECDSA) to the list of known hosts.\r\nzaki@192.168.0.44: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).", "unreachable": true}

PLAY RECAP *********************************************************************
192.168.0.44               : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0   

接続先がzaki@192.168.0.44になってる。大丈夫。

ssh秘密鍵

ベストプラクティスとは思えないけど必要な秘密鍵を配置

(runner) [zaki@fedora-node ~]$ cp .ssh/id_rsa runner-files/env/ssh_key

env/ssh_keyのドキュメントを見る限り、現バージョンではssh秘密鍵は一つしか使用できないようだけど、既に複数ファイルを使用できるような対応が進んでる模様。

実行

(runner) [zaki@fedora-node ~]$ ansible-runner run -v runner-files/ -p k8s.yml
Identity added: /runner/artifacts/d690476a-e0d3-4fcb-98ea-1924e4deeb91/ssh_key_data (/runner/artifacts/d690476a-e0d3-4fcb-98ea-1924e4deeb91/ssh_key_data)
No config file found; using defaults

PLAY [k8s_cluster] *************************************************************

TASK [k8s get pods] ************************************************************
ok: [192.168.0.44] => {"api_found": true, "changed": false, "resources": [{"apiVersion": "v1", "kind": "Pod", "metadata": {"creationTimestamp": "2021-05-16T23:36:35Z", "generateName": "sample-http-6c94f59975-", "labels": {"app": "sample-http", "pod-template-hash": "6c94f59975"}, "managedFields": [{"apiVersion": "v1", "fieldsType": "FieldsV1", "fieldsV1": { ......

動きました。 (Kubernetesモジュールはターゲットにopenshiftパッケージが必要などのモジュール特有の要件は従来通り)

Dockerの場合

指定方法は--helpで確認できる。
実行は未確認。

builder

$ ansible-builder build --container-runtime docker

runner

env/settingファイルのprocess_isolation_executabledocker指定するか、

$ ansible-runner run --process-isolation-executable docker

かな?

ざっくりまとめ

  • これからのAnsible実行環境はvevnでなくコンテナ
  • 実行環境としてのコンテナイメージがAnsible Runner
  • 今までvenvなどで作ってた自分用の実行環境はAnsible Runnerのベースイメージでビルドする
  • ビルドに使用するのがAnsible Builder

そういえばコンテナ実行になると、Docker Connectionプラグインとかちゃんと使えるのかな?dindな構成になるのかな。

あと、OSパッケージインストール周りでリポジトリ追加したい場合なんかは現状不明。

リポジトリ追加したい場合のビルドについて書きました。

zaki-hmkc.hatenablog.com

参考情報

usage-automate.hatenablog.com

ansible-builder.readthedocs.io

ansible-runner.readthedocs.io


さらばvenv
コレクションのインストールとか、venv使ったPythonのランタイム関係ファイルのみを隔離した環境って、Ansible視点だと事故起こり過ぎで好きじゃなかったのでコンテナ利用は嬉しいな。

*1:社内で話題になってる日に限って健康診断でいなかったりリアルタイムでキャッチアップできてなかったので。笑

Ansible Night Online 2021.07で「タグの継承を知らずにやらかした話」というLT登壇した振り返り

Ansible NightでLT登壇しましたので、その振り返りブログです。(1年ぶり2回目)

ansible-users.connpass.com

振り返りといっても、LTでも触れましたが、以前記事にしたものを再構築してLT登壇だったので、詳細は過去記事参照…ということで、主に資料のリンクや感想など。

資料

speakerdeck.com

内容の概要

Ansibleのタグ(に限らないけど)は、呼び出し階層のあるとこで指定すると呼び出し先に継承されるので気を付けよう、という話…というかそれを知らずに以前やらかしたのでそれについてLTしました。

今回のテーマが「やらかしAnsible / ハマったところ」だったので、以前リアルにやらかしてしまってその内容を技術観点で一度下記でまとめてはいて、「いつかこの内容で話したいなー」とは思っていたので参加してみました。

zaki-hmkc.hatenablog.com

こっちのブログはソースコードの中とか結構ディープなとこまで見てるので、ぜひチェックしてみてください。
あと、初公開のときから少しずつ追記したりもしてます。(import / includeなど)

基本はスライド最後のページの内容をチェックしつつ↑のブログを参考にしていただければ。

  • 実行されるタスクやタグを確認できるオプションを活用してください (ただしimportしたタスクが対象)
    • --list-tasks
    • --list-tags
  • 機能が継承されるのもimportしたタスク
    • 機能継承されるのはタグだけではない (when, check_mode, ignore_errorsなど…)
  • includeは機能継承されないけど実行時に動的に読み込まれるので--list-tasksなどでリストアップできない

申し込みとか準備とか

いつか話してみたいなとは思いつつ、でも「いつ話す機会が来てもいいように準備してた」わけでは全くないので、いつも通り申し込んでから準備しました。
が、ここ最近ずっといろいろ忙しくて、(他のIT勉強会にもあまり参加できなかったり、ソシャゲのログインボーナスもらう余裕もなくて)ギリギリまで参加は迷ってたけど、結局開催の3日前に申し込みました。資料作成間に合ってよかったです。。

感想とかいろいろ

自分のLTでなくて、一番最初のセッションの「モジュールがつくりやすくなったよ!Ansible Collections概要紹介 & 対応してみたベンダー体験談」ですが、

f:id:zaki-hmkc:20210722153341p:plain

去年の年末のアドベントカレンダーで書いた記事が紹介されてた…!!
目の前で自分のアウトプットを参考情報として紹介されるのは嬉しいですね。励みになりました。

qiita.com


スライドの表紙の写真、今回はなんとなく好きな風景の写真を使ってみたら、一発で場所を見破られたんだけどなぜだろうね

[YAML] 複数行テキストの行頭にインデントを設定するには (Ansible/Kubernetes)

YAMLで複数行のテキストを記述しつつ、その先頭にインデントを設定するにはどうするか。

YAMLの複数行テキスト

---
data:
  sample1-1.txt: |
    this file is sample file.
    curry tabetai!

YAML| を使って、次行から複数行のテキストを書ける。
この場合、sample1-1.txtというキーに対して、以下の2行のテキストが値になる。

this file is sample file.
curry tabetai!

インデント設定

このとき、各行に一律インデントを入れたい場合、YAMLでインデントをこのように増やしても、、

data:
  sample-indent1.txt: |
          this file is sample file.
          curry tabetai!
  #^^^^^ この部分

増やしたインデントはすべてYAMLの構造として解釈されるので、sample-indent1.txtの値はインデントの量にかかわらず、

this file is sample file.
curry tabetai!

となる。

増やしたインデントをYAMLの構造として解釈せずに、文字列値の中のスペースとして認識させるには、、
多分この「8.2.3. Block Nodes」だと思うんだけど、実は書いてあることがよくわからなかった。
プログラマーのための YAML 入門 (初級編)の「複数行の文字列」の章にも少しかかれてるけど、インデント指定のところは詳しい記述がない。

ということで、試してみた結果。

f:id:zaki-hmkc:20210717215212p:plain

|の後に数字を入力することで、キー文字列のインデント位置から何文字のスペースをYAML構造のインデントとして解釈するかを指定できる。
残りのスペースはYAMLのインデントとしては解釈されず、複数行テキスト内のスペース文字列として認識されるようになる。

この内容でConfigMapを作って、

---
apiVersion: v1
data:
  sample-indent2.txt: |4
            this file is sample file.
            curry tabetai!
kind: ConfigMap
metadata:
  name: yaml-multiline-sample

-o jsonで出力すると、この通り、各行先頭に6文字のスペースが入っていることを確認できる。

    "data": {
        "sample-indent2.txt": "      this file is sample file.\n      curry tabetai!\n",
    }

複数行表記で使用する記号の種類と効果

まずは前フリ

---
apiVersion: v1
data:
  sample1-1.txt: |
    this file is sample file.
    curry tabetai!

  sample1-2.txt: |+
    this file is sample file.
    curry tabetai!

  sample1-3.txt: |-
    this file is sample file.
    curry tabetai!

  sample2-1.txt: >
    this file is sample file.
    curry tabetai!

  sample2-2.txt: >+
    this file is sample file.
    curry tabetai!

  sample2-3.txt: >-
    this file is sample file.
    curry tabetai!

kind: ConfigMap
metadata:
  name: yaml-multiline-sample

この内容で定義したConfigMapをファイルとしてマウントすると、参照できるファイルは以下の通り。

|を使ったファイル

root@configmap-app-68f8ddc68f-n4f2k:/var/tmp/configmap# cat sample1-1.txt 
this file is sample file.
curry tabetai!
root@configmap-app-68f8ddc68f-n4f2k:/var/tmp/configmap# cat sample1-2.txt 
this file is sample file.
curry tabetai!

root@configmap-app-68f8ddc68f-n4f2k:/var/tmp/configmap# cat sample1-3.txt 
this file is sample file.
curry tabetai!root@configmap-app-68f8ddc68f-n4f2k:/var/tmp/configmap# 

>を使ったファイル

root@configmap-app-68f8ddc68f-n4f2k:/var/tmp/configmap# cat sample2-1.txt 
this file is sample file. curry tabetai!
root@configmap-app-68f8ddc68f-n4f2k:/var/tmp/configmap# cat sample2-2.txt 
this file is sample file. curry tabetai!

root@configmap-app-68f8ddc68f-n4f2k:/var/tmp/configmap# cat sample2-3.txt 
this file is sample file. curry tabetai!root@configmap-app-68f8ddc68f-n4f2k:/var/tmp/configmap# 
$ kubectl get cm -n sample yaml-multiline-sample -o json
{
    "apiVersion": "v1",
    "data": {
        "sample1-1.txt": "this file is sample file.\ncurry tabetai!\n",
        "sample1-2.txt": "this file is sample file.\ncurry tabetai!\n\n",
        "sample1-3.txt": "this file is sample file.\ncurry tabetai!",
        "sample2-1.txt": "this file is sample file. curry tabetai!\n",
        "sample2-2.txt": "this file is sample file. curry tabetai!\n\n",
        "sample2-3.txt": "this file is sample file. curry tabetai!"
    },
    ...

+を指定した場合は、末尾に余計な改行が入っている。
対して-を指定した場合は、末尾に改行が無い。(そのためプロンプトが同じ行に表示される)

記号まとめ

  • 書き出し
    • |: 記述そのまま。ただし末尾に改行が複数ある場合は1つにまとめられる
    • >: 各行の末尾は改行でなくスペースに変換される
  • オプション
    • +: デフォルトの「末尾の複数改行を1つにする」機能がオフになる (書いたままになる)
    • -: 末尾の改行が削除される

ここも図にした方が分かりやすいと思うけど力尽きた()

インデント量の指定

---
apiVersion: v1
data:
  sample1-1.txt: |2
      this file is sample file.
      curry tabetai!

  sample1-2.txt: |+2
        this file is sample file.
        curry tabetai!

  sample1-3.txt: |-2
          this file is sample file.
          curry tabetai!

  sample2-1.txt: >2
      this file is sample file.
      curry tabetai!

  sample2-2.txt: >+2
        this file is sample file.
        curry tabetai!

  sample2-3.txt: >-2
          this file is sample file.
          curry tabetai!

kind: ConfigMap
metadata:
  name: yaml-multiline-sample

こうすると

    "data": {
        "sample1-1.txt": "  this file is sample file.\n  curry tabetai!\n",
        "sample1-2.txt": "    this file is sample file.\n    curry tabetai!\n\n",
        "sample1-3.txt": "      this file is sample file.\n      curry tabetai!",
        "sample2-1.txt": "  this file is sample file.\n  curry tabetai!\n",
        "sample2-2.txt": "    this file is sample file.\n    curry tabetai!\n\n",
        "sample2-3.txt": "      this file is sample file.\n      curry tabetai!"
    },

>との併用時は、>の機能である改行をスペースに変換する機能がオフになるっぽい?

Ansibleの場合は

---
- hosts: localhost
  gather_facts: false
  vars:
    data:
      sample1-1.txt: |2
          this file is sample file.
          curry tabetai!

      sample1-2.txt: |+2
            this file is sample file.
            curry tabetai!

      sample1-3.txt: |-2
              this file is sample file.
              curry tabetai!

      sample2-1.txt: >2
          this file is sample file.
          curry tabetai!

      sample2-2.txt: >+2
            this file is sample file.
            curry tabetai!

      sample2-3.txt: >-2
              this file is sample file.
              curry tabetai!

  tasks:
    - name: print data
      ansible.builtin.debug:
        msg: "{{ data }}"

このplaybookをJSON形式で出力すると

$ ANSIBLE_STDOUT_CALLBACK=json ansible-playbook yaml-multiline.yml
:
:
    "msg": {
        "sample1-1.txt": "  this file is sample file.\n  curry tabetai!\n",
        "sample1-2.txt": "    this file is sample file.\n    curry tabetai!\n\n",
        "sample1-3.txt": "      this file is sample file.\n      curry tabetai!",
        "sample2-1.txt": "  this file is sample file.\n  curry tabetai!\n",
        "sample2-2.txt": "    this file is sample file.\n    curry tabetai!\n\n",
        "sample2-3.txt": "      this file is sample file.\n      curry tabetai!"
    }

Kubernetes(のConfigMap)の場合と同じ結果になる。