7629 文字
38 分
AIエージェントに書かせた構築スクリプトが散らかってレビューできない──再現性のためにAnsibleを再評価した
この記事について

Claude(Anthropic)との共同編集により作成されました。

要約
  • AIエージェントに構築作業を任せると、シェルやPythonのスクリプトが増え続け、人間がレビューできない状態になりやすい。構築・運用で避けるべきなのはこれだと思っている
  • 同じ構築を別の環境でも再現したくなったとき、以前とっつきづらくて放置していた Ansible を試した。レビューと可視化の道具がそろっていたのがよかった
  • Ansible は出力の形が「モジュール+パラメータ」に決まり、--check --diff で実行前に差分を見られ、冪等なのでやり直しが安全。AIが書き、人間が差分を承認するという分担に乗せやすい
  • さらに、記法をフレームワークが担保するので、流儀の違うシェルを大量に読まずに済む。コミュニティが発達していて、知見を共有・検索しやすいのも利点になる
  • ただし shell / command に逃げられると利点が消える。ansible-lint(profile: production)と Molecule の idempotence テストで機械的に止めるのが前提になる
  • GPUサーバーのサンプル構築では、ホスト(ドライバ・Docker・Container Toolkit)を Ansible、アプリを Docker に分け、ドライバとCUDAのバージョンの鎖を group_vars で1か所にまとめる
  • レビューを支えるのは可視化。grapher(構造)、ansible-inventory --graph(変数)、--list-*(実行範囲)、--check --diff(変更内容)をCIで出してMRに貼る

同じシステムの構築を、別の環境でもそのまま再現したくなった。

いまなら Claude Code に頼めば、構築用のシェルスクリプトや Python スクリプトはすぐ出てくる。ただ、これを続けると setup.sh、setup_v2.sh、fix_config_final.sh のようなファイルが積み上がっていく。どれを、どの順で、どこまで流したのか。次の環境で同じ結果になるのか。AIが書いたスクリプトが散らかり、人間がレビューできない状態になることが、構築・運用で避けるべきことだと思っている。

そこで思い出したのが Ansible だった。以前はとっつきづらくて放置していたが、改めて試してみると、実行前の差分確認や依存関係の可視化など、レビューのための道具が充実していた。AIエージェントに書かせる前提で見直すと、評価がかなり変わった。

実際の構築例は伏せるが、ローカルの複数の開発環境に同じ変更を入れたいときに使った。この記事では、そのときに調べた内容を整理する。後半の GPU サーバー構築は、調べた内容を具体化するためのサンプル構築例である。

AIにシェル・Pythonを書かせ続けるとスクリプトが散らかり人間が追えなくなる。Ansibleに書かせるとinventory・group_vars・playbookに整理され、--check --diffの差分を人間が承認する形になる


1. AIエージェントと Ansible の相性がよい理由#

シェルスクリプトを書かせる場合と比べると、違いは次のとおり。

観点シェルスクリプトを書かせる場合Ansible を書かせる場合
出力のぶれ書き方が毎回変わる「モジュール+パラメータ」の DSL に制約され、ぶれが小さい
実行前の確認読んで推測するしかない--check --diff で変更内容をプレビューできる1
失敗時のやり直しどこまで実行されたかを人間が追う冪等なモジュールなら再実行で収束する
状態の記録スクリプトは「手順」で、あるべき状態は読み取れないinventory・group_vars・role が「あるべき状態」の記録になる
機械的な検査shellcheck 程度ansible-lint23、Molecule45 が使える
記法の統一書き手ごとに流儀が違い、全部読んで確かめる必要がある記法をフレームワークが決めるので、確認するのは主に「どのモジュールに何を渡したか」
知見の共有・検索書き方が人それぞれで、検索しても自分のケースに当てはまりにくいモジュール名が共通の語彙になり、公式ドキュメント・Galaxy・フォーラムで探しやすい

AIにとっての利点は「書きやすさ」より、検査・承認・やり直しの仕組みに乗せやすいことにある。

1.1 check mode と diff mode#

ansible-playbook site.yml --check --diff を使うと、リモートを変更せずに「何がどう変わるか」を確認できる1。エージェントが作った playbook の差分を MR に貼り、人間が承認してから適用する、というフローにそのまま組み込める。

注意点もある1。

  • check mode は「シミュレーション」で、check mode 非対応のモジュールは何も報告せず、何もしない
  • 前のタスクの register 結果に依存する条件分岐は、check mode 中は意味のある結果にならない
  • diff 出力にはシークレットが含まれうる。該当タスクには diff: false を付ける

1.2 冪等性はモジュール単位で成り立つ#

Ansible が冪等なのは、各モジュールが「現在の状態」と「あるべき状態」を比べて、差があるときだけ変更するように作られているからだ。shell / command はこの比較をしないので、そのまま書くと毎回 changed になり、冪等性が崩れる。

つまり、Ansible を使っていても中身が shell: だらけなら、散らかったシェルスクリプトと変わらない。次の章のガードレールが必要になる理由はここにある。

1.3 記法がフレームワークで担保される#

シェルスクリプトは、同じ処理でも書き方がいくらでもある。パッケージを入れる処理ひとつでも、apt-get install -y か apt install か、dpkg -s で事前に確認するか、set -e を付けるか、エラーをどう扱うかで流儀が分かれる。エージェントに大量に書かせると、流儀の違うシェルが溜まり、その一つひとつを人間が読んで確かめることになる。

Ansible では、この「どう書くか」をフレームワークとモジュールが決める。

- name: Install packages
ansible.builtin.apt:
name: [curl, gnupg2]
state: present

レビューで確認するのは、主に「どのモジュールに、どの値を渡したか」になる。インストール済みかどうかの確認、エラー処理、冪等性はモジュールの側で担保される。記法から外れたものは ansible-lint(2.1節)が機械的に検出する。when の条件やテンプレートは引き続き読む必要があるが、人間が読む量はかなり減る。エージェントが大量に書く時代には、ここが効いてくる。

1.4 コミュニティが発達していて、知見を共有・検索しやすい#

  • モジュール名が共通の語彙になる: 「community.docker.docker_compose_v2 で state: restarted にすると冪等でない」のように、問題をモジュール名とパラメータ名で正確に言い表せる。シェルは書き方が人それぞれなので、検索しても自分のケースに当てはまる情報に行き着きにくい
  • 情報源が整っている: 公式ドキュメントには、モジュールごとにパラメータ・check mode 対応・使用例が同じ形式で載っている6。ansible パッケージに同梱される collection だけでも、amazon・cisco・community・vmware など多数の名前空間がある7。role と collection の共有基盤として Ansible Galaxy があり8、4章で触れる NVIDIA のドライバ role も Galaxy から nvidia.nvidia_driver として入れられる9。質問や議論の場として Ansible Forum もある10
  • エージェントも同じ情報源を使える: ansible-doc community.docker.docker_compose_v2 のように、制御ノード上でモジュールのドキュメントを引ける。エージェントにこれを実行させてから書かせると、記憶に頼ったパラメータの誤りを減らせる
  • 社内でも共有しやすい: 書き方が揃っているので、別チームが書いた role でも読み解ける

1.5 YAML と HCL:記法と抽象化の単位#

1.3節の利点は Terraform にも当てはまる。どちらも記法をツールが担保し、抽象化の単位が決まっている。違うのは、汎用フォーマットに乗るか、専用の設定言語を持つかだ。

観点Ansible(YAML)Terraform(HCL)
記法汎用データ形式の YAMLHashiCorp 製の設定言語 HCL(Terraform の言語はこれに基づく)11
整形・検査yamllint、ansible-lintterraform fmt が正規の書式に整形し、-check で未整形を検出できる12
抽象化の単位role、collectionmodule(一緒に管理するリソースの集合)13
変更の確認--check --diffterraform plan

YAML の良さは、汎用フォーマットであること。yq や Python でそのまま解析できるので、「全 playbook から shell: を使っている箇所を抜き出す」「group_vars のドライバブランチを一覧にする」といった処理を、専用のパーサなしで書ける。インデントとキー・値だけなので、Ansible を書かないレビュー担当でも何をしているかを追える。CI 設定や Compose などで見慣れた形式なので、エージェントも書き慣れている印象がある(定量的な根拠はない)。

HCL の良さは、書式がツールで1つに決まること12。CI で terraform fmt -check を回せば、エージェントが書いたコードの見た目の揺れを機械的にゼロにできる。また、module が抽象化の境界になり、利用側は入力変数だけを渡す13。エージェントには「レビュー済みの module を呼び出すコードだけを書かせる」という制約をかけやすい。

module "gpu_node" {
source = "./modules/gpu_node"
instance_type = "g6.xlarge"
count = 2
}

YAML には落とし穴もある。YAML 1.1 では yes / no / on / off が真偽値として解釈されうる。yamllint の truthy ルールは、既定で true / false 以外を検出する14。ドライバのブランチ番号のように文字列として扱いたい値も、"580" と引用符で囲んでおくほうが安全だ(4章の例でもそうしている)。


2. エージェント運用のガードレール#

2.1 ansible-lint で「shell への逃げ」を止める#

エージェントは適切なモジュールが分からないと shell: で済ませがちだ。ansible-lint の次のルールで検出できる2。

ルール検出内容含まれる最小プロファイル3
command-instead-of-module専用モジュールがあるのに command を使っているbasic
command-instead-of-shellshell が不要な場面で shell を使っているbasic
risky-shell-pipeパイプの失敗を見落とす shell 記述safety
no-changed-whencommand / shell に changed_when がない(冪等性の宣言漏れ)shared

プロファイルは min → basic → moderate → safety → shared → production の順に厳しくなり、上位は下位を包含する3。エージェントが書くコードには production(少なくとも shared)を指定すると、上の4ルールがすべて効く。

.ansible-lint
profile: production

なお、ルールの所属プロファイルはバージョンで変わる可能性がある。導入時に ansible-lint --list-profiles で確認しておく。

2.2 Molecule で冪等性をテストする#

Molecule は role・playbook・collection のテストフレームワークで、コンテナ・VM・ネットワーク機器などをテスト対象にできる4。デフォルトのテストシーケンスは次のとおりで、converge(適用)の直後に idempotence ステップがある5。

dependency → cleanup → destroy → syntax → create → prepare
→ converge → idempotence → side_effect → verify → cleanup → destroy

idempotence ステップは、一般に「もう一度流して changed が出ないこと」を確かめるものとして使われている。これを CI で回せば、エージェントの出力を人間が1行ずつ読む負担が減る。

2.3 変数の置き場所をルール化する#

Ansible の変数の優先順位は 22段階ある15。YAML 自体は読みやすくても、「この値は最終的にどこから来るのか」を追うのは難しくなりやすい。エージェントへの指示(CLAUDE.md や AGENTS.md)に、次のようなルールを入れておくとレビューしやすさを保てる。

  • 環境差分は group_vars/ / host_vars/ に寄せる
  • role のデフォルト値は defaults/main.yml だけに置く
  • -e(extra vars、最優先15)は緊急時以外使わない
  • Jinja2 テンプレートに複雑な条件分岐を書かない

2.4 ドリフト検知#

冪等でも、手作業の変更は Ansible を流さない限り見つからない。対策として、定期的に --check --diff を流し、changed が出たらドリフトとして通知する運用が紹介されている16。さらに進めると、Event-Driven Ansible(ansible-rulebook)17でイベントを受けて修復用の playbook を起動する構成も考えられる。ただ、こちらは公式の専用ガイドが見当たらず、デモやコミュニティ事例が中心だ。

2.5 推奨フロー#

ここまでを1本のフローにすると次のようになる。

エージェントがrole/playbookを生成し、CIでansible-lintとMoleculeを通し、本番相当環境への--check --diffの差分をMRに添付、人間が承認してから適用する。lintやidempotenceで落ちたらエージェントに差し戻す

エージェントの担当は「書く」まで、人間の担当は「差分を承認する」だけ、という分担にできる。

2.6 Claude Code に当てはめると#

普段 Claude Code で作業しているなら、上のガードレールは次の場所に置ける。

ガードレールClaude Code での置き場所
変数の置き場所ルール、shell: を使わない方針CLAUDE.md に書いておく
ansible-lint を毎回通すhooks で、YAML の編集後に ansible-lint を自動実行する
本番への適用は人間が承認する権限設定で ansible-playbook の実行を確認付きにする
差分を見てから適用するエージェントに --check --diff まで流させ、出力そのものを人間が読んで判断する

スクリプトを書かせる場合との違いは、検査の対象が「エージェントが毎回違う形で書いたコード」ではなく「決まった形のYAMLと、その差分」になることだ。


3. クラウドとオンプレでの使い分け#

よく言われる役割分担は、クラウドは Terraform で箱を作り、Ansible で中身を設定するというもの。VPC・IAM・RDS のように「作る・消す」が中心で状態管理が必要な層は Terraform が向く。terraform plan の差分レビューも、--check --diff と同じく「エージェントが書き、人間が差分を承認する」フローに乗る。

オンプレでは「API でリソースを作る」場面が減り、既に存在する物理機器を設定する場面が増える。ここは Ansible の得意領域で、1つのツールで次の層をまとめて扱える。

層主なモジュール・collection用途
ハードウェア(BMC)community.general.redfish_command など18電源操作、BMC ユーザー管理、ログ管理
ネットワーク機器各ベンダーのネットワークリソースモジュール19VLAN・インターフェース設定、設定の取得
仮想化基盤community.proxmox.proxmox_kvm20、VMware 系 collectionVM 作成・設定
OS・ミドルウェアansible.builtin.*、各種 collectionパッケージ、設定ファイル、サービス、ユーザー

エージェントに任せるときに気をつけたい点もある。

  • ネットワークリソースモジュールの state: gathered で現在の設定を構造化データとして取れる19。エージェントにまず現状を読ませてから変更案を作らせる、という使い方ができる
  • state: overridden は、公式ドキュメント自体が「デバイスへのアクセスを失う可能性がある」と警告している19。エージェントには merged / replaced だけを許可するのが安全
  • redfish_command は check mode に対応していない18。電源操作のような破壊的な操作は、エージェントに実行させず、人間の承認を必ず挟む

Terraform もオンプレで使えなくはなく、vSphere 用のプロバイダなどがある21。ただ、物理機器の設定や OS の内部まではカバーしないので、オンプレでは Ansible が主役になりやすい、というのが個人的な見立てだ。


4. サンプル構築例:GPU サーバーにサービスを立てて引き渡す#

ここからは、調べた内容を具体化するためのサンプルである。開発者が素の OS から GPU サーバーを構築し、推論サービスを Docker で動かし、最後にインフラ担当へ引き渡す、という想定で組み立てた。

狙いは「開発環境で通した playbook を、inventory だけ差し替えて本番で流す」こと。インフラ担当は検証済みの手順を実行するだけになり、手順書の読み違いや転記ミスがなくなる。

4.1 Ansible と Docker の責務を分ける#

層担当中身
ホストAnsibleNVIDIA ドライバ、Docker Engine、NVIDIA Container Toolkit、ユーザー・ディレクトリ、ファイアウォール
デプロイAnsiblecompose.yaml・.env の配置、docker compose up
アプリDocker(イメージ)CUDA ランタイム、Python・ライブラリ、推論コード

NVIDIA Container Toolkit はホストにドライバが入っていることを前提にしている22。なのでドライバはホスト側にだけ入れ、コンテナ側は CUDA のユーザー空間ライブラリだけを持つ構成にする。

もう1つの原則は、Dockerfile の中で Ansible を使わないこと。イメージの中身は Dockerfile と CI で作り、Ansible は「どのイメージを、どの設定で起動するか」だけを担当する。こうすると境界がはっきりする。

4.2 ドライバと CUDA は1本の鎖#

コンテナ化しても、ホストのドライバとコンテナ内の CUDA のバージョンだけは連動する。NVIDIA の minor version compatibility で求められる Linux ドライバの最低バージョンは次のとおり23。

コンテナ内の CUDAホストのドライバ
12.x525 以上
13.x580 以上

さらに PyTorch の pip wheel は、CUDA ランタイム・cuDNN・NCCL などを nvidia-* の pip パッケージとして依存に持つ24。CUDA のユーザー空間ライブラリはコンテナ内の Python の依存として入り、ホストに必要なのはドライバだけになる。つまり次の4つは1本の鎖でつながっている。

ホストのドライバ(branch 580)
└─ 対応できる CUDA の範囲(13.x まで)
└─ コンテナ内の torch の CUDA variant と nvidia-* pip パッケージ
└─ 推論速度・使えるカーネル(cuDNN / NCCL のバージョン)

速度やライブラリのバージョンを決めるのは開発者なので、鎖の根元のドライバも開発側で持つのが筋が通っている。そのうえで、ドライバを入れた後に要件を満たしているかを検査するタスクを残しておく。誰かが手作業でドライバを変えた場合にも、デプロイ前に止められる。

roles/gpu_host/defaults/main.yml
gpu_driver_branch: "580"
gpu_min_driver_major: 580 # CUDA 13.x イメージを使う場合
# roles/gpu_host/tasks/check_driver.yml
- name: Get driver version
ansible.builtin.command: nvidia-smi --query-gpu=driver_version --format=csv,noheader
register: gpu_driver
changed_when: false
- name: Assert driver meets CUDA requirement
ansible.builtin.assert:
that:
- (gpu_driver.stdout_lines[0].split('.')[0] | int) >= gpu_min_driver_major
fail_msg: "Driver {{ gpu_driver.stdout_lines[0] }} < {{ gpu_min_driver_major }}"

command を使っているが、changed_when: false で「状態を変えない読み取り」だと宣言しているので、no-changed-when には引っかからない。

4.3 ドライバのブランチを選んで固定する#

NVIDIA のデータセンター向けドライバには3種類のブランチがある25。

ブランチサポート期間用途
New Feature Branch(NFB)なし新機能の評価用。本番には使わない
Production Branch(PB)最大1年本番向け
Long-Term Support Branch(LTSB)3年長期間更新したくない本番向け

本番は PB か LTSB を選び、ブランチ単位で固定する。Ubuntu では、NVIDIA が提供する pinning パッケージ(例: nvidia-driver-pinning-580)をドライバより先に入れるのが推奨されている26。意図しない apt upgrade でブランチが変わるのを防げる。カーネルモジュールは open 版(nvidia-open パッケージ)で入れる27。

# roles/gpu_host/tasks/driver.yml(CUDA の apt リポジトリ追加は省略)
- name: Pin driver branch
ansible.builtin.apt:
name: "nvidia-driver-pinning-{{ gpu_driver_branch }}"
state: present
- name: Install NVIDIA driver (open kernel modules)
ansible.builtin.apt:
name: nvidia-open
state: present
register: driver_install
- name: Reboot if driver changed
ansible.builtin.reboot:
when: driver_install is changed

ただし Ubuntu では、580 以前のブランチはパッケージ名にブランチ番号が入っていて、upgrade / downgrade でのブランチ切り替えがサポートされるのは 590 以降だけ、という制約がある26。別案として NVIDIA 公式の role(nvidia.nvidia_driver)もあり、インストール時にノードを再起動するため、GPU ノードとは別のノードから実行することが推奨されている9。再起動には ansible.builtin.reboot を使えば、ホストの復帰待ちまで含めて扱える28。

4.4 更新はローリングで(設計案)#

ドライバ更新では再起動が必要になり、サービスが止まる。複数台あるなら serial で1台ずつ進める29。以下は公式機能を組み合わせた設計案で、実機で検証したものではない。

driver_update.yml
- name: Rolling driver update
hosts: gpu_servers
serial: 1 # 1台ずつ。[1, "50%"] のように段階的にもできる
tasks:
- name: Stop service
community.docker.docker_compose_v2:
project_src: /opt/inference
state: stopped
- name: Update driver via gpu_host role
ansible.builtin.import_role:
name: gpu_host # pinning 切替 → ドライバ更新 → reboot → assert
- name: Smoke test (GPU visible from container)
ansible.builtin.command: docker run --rm --gpus all ubuntu nvidia-smi
changed_when: false
- name: Start service
community.docker.docker_compose_v2:
project_src: /opt/inference
state: present

1台目でスモークテストが落ちれば、残りの台には進まない。「検証済みの手順で実行するだけ」を更新作業にも広げる仕組みになる。

また、ドライバのカーネルモジュールは実行中のカーネルと組み合わせて動く。一般的な運用知見として、カーネルの自動更新後にモジュールがロードできず GPU が見えなくなるケースが知られているので、カーネル更新もドライバ更新と同じ playbook で扱い、unattended-upgrades に任せきりにしないほうが安全だ(挙動は DKMS かプリビルドかで変わる)。

4.5 コンテナ実行環境とデプロイ#

NVIDIA Container Toolkit の公式手順は、apt リポジトリを追加 → パッケージをインストール → nvidia-ctk runtime configure --runtime=docker → Docker 再起動、という流れになっている22。

nvidia-ctk runtime configure は /etc/docker/daemon.json を書き換えるコマンドだ22。そのまま command で呼ぶと冪等性が崩れ、ansible-lint にも指摘される。代わりに、同じ内容の daemon.json をテンプレートで管理すると diff でレビューでき、冪等にもなる。生成内容はバージョンで変わりうるので、一度実機で nvidia-ctk を実行し、出力をコピーしてテンプレートにするのが確実だ。

- name: Install NVIDIA Container Toolkit (version pinned)
ansible.builtin.apt:
name:
- "nvidia-container-toolkit={{ nvidia_ctk_version }}"
- "nvidia-container-toolkit-base={{ nvidia_ctk_version }}"
- "libnvidia-container-tools={{ nvidia_ctk_version }}"
- "libnvidia-container1={{ nvidia_ctk_version }}"
state: present
notify: Restart docker
- name: Configure Docker daemon (nvidia runtime)
ansible.builtin.template:
src: daemon.json.j2
dest: /etc/docker/daemon.json
mode: "0644"
notify: Restart docker

compose.yaml での GPU 指定は deploy.resources.reservations.devices を使う。capabilities は必須で、count と device_ids は同時に指定できない30。

compose.yaml
services:
inference:
image: registry.example.com/team/inference@sha256:... # digest で固定
deploy:
resources:
reservations:
devices:
- driver: nvidia
device_ids: ["0"]
capabilities: [gpu]

Ansible からは community.docker.docker_compose_v2 で起動する。このモジュールは check mode に対応している。ただし state: restarted と recreate: always は冪等ではないので、通常のデプロイでは使わない6。最後に、公式のサンプルワークロード31相当のスモークテストを changed_when: false で入れておくと、本番で流すたびに「GPU がコンテナから見える」ことを確認できる。

4.6 引き渡し物の構成#

gpu-service-deploy/
├── roles/
│ ├── gpu_host/ # ドライバ導入・固定・検査, Docker, Container Toolkit
│ └── app_deploy/ # compose.yaml・.env 配置と起動
├── inventories/
│ ├── dev/ # 開発者が管理・検証済み
│ └── prod/ # インフラが記入(ホスト名、Vault)
├── group_vars/
│ └── gpu_servers.yml # ドライバブランチ・イメージ digest をまとめて管理
├── site.yml # 素の OS からの初期構築
├── driver_update.yml # ローリングでのドライバ更新
├── molecule/ # OS 層のテスト
└── README.md # 前提条件・変数一覧・実行コマンド
項目開発者インフラ
role・playbook・compose.yaml作成・検証レビュー
ドライバのブランチ選定・更新タイミング決定・dev 環境で検証更新の実施日を調整
inventories/prod・Vault変数の雛形を用意値を記入・管理
本番実行—--check --diff を確認してから実行

引き渡しで効くのはバージョンの固定だ。ドライバのブランチ、Container Toolkit のバージョン、イメージの digest を group_vars に並べ、同じ MR でまとめて変えるルールにしておけば、「開発で通ったものと同じもの」が本番で動くことを保証しやすくなる。

テストには限界もある。Molecule のコンテナには GPU がないので、OS 層は Molecule、GPU 層は開発用 GPU サーバーを実機テスト環境として使う。エージェントに任せやすいのは、role の作成と開発用サーバーでの「流す → 直す」の反復で、本番への実行は人間が差分を見てから行う。


5. 依存関係の可視化#

Ansible を試してよかったのは、この可視化の道具がそろっていることだった。エージェントが書いた playbook をレビューするとき、YAML を1ファイルずつ読むだけでは「どの role がどの順で呼ばれ、どのホストに当たるか」をつかみにくい。次の4つの見方を組み合わせる。

何を見たいかツール出力
playbook → play → role → task の構造ansible-playbook-grapher32SVG / Mermaid / JSON
どのホストがどのグループに属し、どの変数を持つかansible-inventory --graph --vars33テキストのツリー
実行されるタスク・タグ・ホストの一覧ansible-playbook --list-tasks など34テキスト
実際の実行結果の履歴ARA Records Ansible35Web UI / CLI / REST API

5.1 playbook の構造:ansible-playbook-grapher#

playbook を静的に解析して、play・role・task の関係をグラフにするツールだ。インベントリに対しては何も実行しない32。

Terminal window
pip install ansible-playbook-grapher
# role 内のタスクまで含めて SVG で出力(Graphviz が必要)
ansible-playbook-grapher --include-role-tasks --show-handlers site.yml -o docs/site
# Mermaid で出力(Markdown に埋め込める)
ansible-playbook-grapher --include-role-tasks --renderer mermaid-flowchart site.yml -o docs/site
オプション内容
--include-role-tasksrole 内のタスクもグラフに含める
--renderergraphviz(既定、SVG)/ mermaid-flowchart / json
--show-handlershandler も表示する
-t / --skip-tagsタグで絞り込む
--group-roles-by-name同名の role を1つのノードにまとめる

4章のサンプル(site.yml)なら、次のような形のグラフになる。

site.ymlからplay gpu_serversを経てrole gpu_hostとapp_deployに分かれ、各タスクが並ぶ。Container Toolkitの導入とDocker daemon設定のタスクがhandler Restart dockerに通知している

NOTE

この図は出力のイメージを手で描いたもので、grapher の実際の出力とはノードの形や表記が異なる。

レビューで効く使い方は2つある。

  • CI で Mermaid を生成して MR に貼る: エージェントが role を追加したり、タスクの順序を変えたりしたとき、構造の変化が図の差分として見える。「Container Toolkit の導入がドライバの検査より前に来ている」といった順序の誤りに気づきやすい
  • handler の通知先を確認する: --show-handlers を付けると、どのタスクがどの handler を呼ぶかが見える。Restart docker が想定外のタスクから呼ばれていないかを確認できる

5.2 インベントリの構造:ansible-inventory —graph#

Terminal window
ansible-inventory -i inventories/prod --graph --vars

--graph はグループとホストのツリーを出力し、--vars を付けると各ホスト・グループの変数も表示する(--vars は --graph と組み合わせたときだけ有効)33。出力はおおよそ次のような形になる(ホスト名は例)。

@all:
|--@ungrouped:
|--@gpu_servers:
| |--gpu01
| | |--{gpu_driver_branch = 580}
| | |--{gpu_min_driver_major = 580}
| |--gpu02
| | |--{gpu_driver_branch = 580}
| | |--{gpu_min_driver_major = 580}

2.3節の「変数の優先順位が22段階あって追いにくい」問題への対策になる。group_vars と host_vars を合成した最終的な値を、ホストごとに確認できる。インフラ担当が inventories/prod を書いた後にこれを流せば、記入漏れや値の食い違いを一覧で確認できる。

5.3 実行計画の一覧:ansible-playbook —list-*#

Terminal window
ansible-playbook -i inventories/prod driver_update.yml --list-hosts # 対象ホスト
ansible-playbook -i inventories/prod driver_update.yml --list-tasks # 実行されるタスク
ansible-playbook -i inventories/prod driver_update.yml --list-tags # 利用可能なタグ

いずれも何も実行しない34。--check --diff が「何が変わるか」を示すのに対し、こちらは「何が、どのホストに対して走るか」を示す。

5.4 実行結果の履歴:ARA Records Ansible#

ARA は Ansible の callback plugin として動き、ansible-playbook の実行結果(play・task・ホストごとの結果)を記録する。既定ではローカルの SQLite に保存し、Web UI・CLI・REST API で参照できる35。

Terminal window
pip install "ara[server]"
export ANSIBLE_CALLBACK_PLUGINS="$(python3 -m ara.setup.callback_plugins)"
ansible-playbook -i inventories/dev site.yml
ara-manage runserver # http://127.0.0.1:8000

callback の設定方法はバージョンで変わりうるので、導入時に公式ドキュメントで確認しておく。「いつ、どのホストで、何が changed になったか」を後から追いたい作業で役に立つ。エージェントが開発環境で繰り返した実行の履歴を、後から人間が確認する用途にも使える。

5.5 レビューフローへの組み込み#

2.5節のフローに、可視化を足すと次のようになる。

CIでlintとMoleculeを通したあと、grapherで構造の変化、inventory --graph --varsで変数の最終値、--list-hosts/--list-tasksで実行範囲、--check --diffで変更内容を出し、4つをMRに貼って人間が承認、ARAで履歴を記録しながら適用する

構造・値・範囲・変更内容の4つが図とテキストで並ぶので、AIが書いたYAMLを頭の中で実行しなくても判断できる。散らかったスクリプトを読む場合との大きな差はここにある。


まとめ#

環境推奨構成エージェントの関わり方
クラウドTerraform(箱)+ Ansible(中身)plan と --check --diff の差分を人間が承認
オンプレAnsible が主役(BMC・ネットワーク・仮想化・OS)gathered で現状把握 → 変更案 → 差分承認。電源操作など破壊的な操作は人間が実行
GPU サーバー+Docker(サンプル)Ansible(ホスト・デプロイ)+ Docker(アプリ)開発環境で反復 → inventory を差し替えて本番へ引き渡し
共通ansible-lint(production)+ Molecule(idempotence)lint とテストに通らないものはマージしない
可視化grapher(構造)+ inventory —graph(変数)+ —list-*(範囲)+ ARA(履歴)CI で生成して MR に貼り、レビューの材料にする

AIエージェントのおかげで、構築の手順を書くコストはほぼゼロになった。そのぶん、書かれたものを人間がレビューできる形に保つことの価値が上がっている。シェルやPythonのスクリプトを書かせ続けるより、出力の形が決まっていて、差分と構造を見せてくれるフレームワークに寄せたほうが、構築の再現性は上げやすい。Ansible はそのためのフレームワークとして、よい選択肢なのではないかと思う。


参考文献#

  1. Ansible Documentation - Validating tasks: check mode and diff mode https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_checkmode.html
  2. Ansible Lint Documentation - Rules https://docs.ansible.com/projects/lint/rules/
  3. Ansible Lint Documentation - Profiles https://docs.ansible.com/projects/lint/profiles/
  4. Molecule Documentation https://docs.ansible.com/projects/molecule/
  5. Molecule Documentation - Configuration(test_sequence) https://docs.ansible.com/projects/molecule/configuration/
  6. community.docker.docker_compose_v2 module https://docs.ansible.com/ansible/latest/collections/community/docker/docker_compose_v2_module.html
  7. Ansible Documentation - Collection Index https://docs.ansible.com/ansible/latest/collections/index.html
  8. Ansible Galaxy https://galaxy.ansible.com/
  9. NVIDIA/ansible-role-nvidia-driver https://github.com/NVIDIA/ansible-role-nvidia-driver
  10. Ansible Community Forum https://forum.ansible.com/
  11. Terraform Documentation - Configuration Syntax https://developer.hashicorp.com/terraform/language/syntax/configuration
  12. Terraform CLI - terraform fmt https://developer.hashicorp.com/terraform/cli/commands/fmt
  13. Terraform Documentation - Modules https://developer.hashicorp.com/terraform/language/modules
  14. yamllint - Rules(truthy) https://yamllint.readthedocs.io/en/stable/rules.html
  15. Ansible Documentation - Using variables(Variable precedence) https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html
  16. Spacelift - Ansible configuration drift management https://spacelift.io/blog/ansible-configuration-drift-management
  17. Ansible Rulebook Documentation - Getting started https://docs.ansible.com/projects/rulebook/en/v1.2.0/getting_started.html
  18. community.general.redfish_command module https://docs.ansible.com/ansible/latest/collections/community/general/redfish_command_module.html
  19. Ansible Documentation - Network Resource Modules https://docs.ansible.com/ansible/latest/network/user_guide/network_resource_modules.html
  20. community.proxmox collection https://docs.ansible.com/ansible/latest/collections/community/proxmox/index.html
  21. Terraform Provider for VMware vSphere https://github.com/vmware/terraform-provider-vsphere
  22. NVIDIA Container Toolkit - Installation Guide https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html
  23. NVIDIA CUDA Toolkit Release Notes(minor version compatibility) https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html
  24. PyTorch Forums - Where and how do pip installs of Pytorch install CUDA https://discuss.pytorch.org/t/where-and-how-do-pip-installs-of-pytorch-install-cuda/129760
  25. NVIDIA Data Center Drivers - Driver Lifecycle https://docs.nvidia.com/datacenter/tesla/drivers/driver-lifecycle.html
  26. NVIDIA Driver Installation Guide - Version locking https://docs.nvidia.com/datacenter/tesla/driver-installation-guide/version-locking.html
  27. NVIDIA Driver Installation Guide - Kernel Modules https://docs.nvidia.com/datacenter/tesla/driver-installation-guide/kernel-modules.html
  28. ansible.builtin.reboot module https://docs.ansible.com/ansible/latest/collections/ansible/builtin/reboot_module.html
  29. Ansible Documentation - Controlling playbook execution: strategies and more(serial) https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_strategies.html
  30. Docker Docs - Enable GPU support(Compose) https://docs.docker.com/compose/how-tos/gpu-support/
  31. NVIDIA Container Toolkit - Running a Sample Workload https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/sample-workload.html
  32. ansible-playbook-grapher https://github.com/haidaraM/ansible-playbook-grapher
  33. Ansible Documentation - ansible-inventory https://docs.ansible.com/ansible/latest/cli/ansible-inventory.html
  34. Ansible Documentation - ansible-playbook https://docs.ansible.com/ansible/latest/cli/ansible-playbook.html
  35. ARA Records Ansible https://ara.recordsansible.org/
AIエージェントに書かせた構築スクリプトが散らかってレビューできない──再現性のためにAnsibleを再評価した
https://yurudeep.com/posts/aicoding/2026/20261110/
作者
ひらノルム
公開日
2026-11-10
ライセンス
CC BY-NC-SA 4.0