TT Lab
はじめる
学ぶ 学習パス コース

Ansible実戦

コレクション — 名前に出所が書いてある理由

TT Labで続きを見る

一言でいうと

コレクションは、ロール・モジュール・プラグインを1つの塊にまとめ、バージョン(version)を付けて配布する単位です。ansible.builtin.copyのような長い名前は、その塊の出典を名前の中に書いたものです。

なぜ必要なのか

2019年まで、Ansibleは数千のモジュールを本体に同梱して配布していました。mysql_user1つを直すにもAnsible全体が新しいバージョンを出す必要があり、逆にAnsibleを上げると、使ってもいない数千のモジュールがまとめて変わりました。コミュニティのモジュールのメンテナーとAnsibleコアのメンテナーが同じリポジトリで衝突し、リリースはだんだん重くなりました。

名前の衝突も実際の事故でした。社内で作ったdeployロールがあるのに、誰かが同じ名前のロールをroles/にもう1つ置くと、実行されたのがどちらなのか、ログを見るだけではわかりませんでした。検索パスの順序が結果を決め、その順序は人ではなく環境が決めていました。

Ansible 2.9で導入されたコレクションは、この2つの問題を一度に扱います。内容物を<네임스페이스>.<이름>(プレースホルダーはネームスペースと名前です)でまとめ、そのバンドルにSemVerのバージョンを付け、タスクから呼ぶときは<네임스페이스>.<이름>.<모듈>(プレースホルダーはネームスペース、名前、モジュールです)という完全な名前(FQCN)で呼びます。名前を見れば出典がわかり、同じ短い名前が2つあっても、FQCNは重なりません。

どう動くのか

コレクションのソースは、決まったディレクトリ構成を持ちます。ansible-galaxy collection init <네임스페이스>.<이름>(プレースホルダーはネームスペースと名前です)が、そのスケルトンを作ってくれます。

場所 入れるもの
galaxy.yml ネームスペース・名前・バージョン・作者・ライセンス・タグ。ビルドがこのファイルを読む
roles/ ロール。ロールの中の規約は従来と同じ
plugins/ モジュール・フィルター・ルックアップ・コールバックなどのプラグイン。種類ごとにサブディレクトリが決まっている
playbooks/ コレクションが一緒に配布するプレイブック
meta/runtime.yml 最小のansibleバージョン(requires_ansible)、名前の変更(plugin_routing)、アクショングループ
docs/ ドキュメント

galaxy.ymlのversionは、SemVerでなければなりません。1.2のように2つの数字だけで書くと、ビルドが拒否します。このルールがあるからこそ、>=1.2.0,<2.0.0のような範囲表記が意味を持ちます。

配布は3つの段階で行います。

ansible-galaxy collection init acme.platform --init-path src   # 뼈대
ansible-galaxy collection build --output-path dist             # 묶음 만들기
ansible-galaxy collection install dist/acme-platform-1.2.0.tar.gz -p collections

buildが作るのは、ただのtar.gzではありません。中にMANIFEST.json(メタデータ)とFILES.json(ファイルごとのチェックサム)が一緒に入ります。インストールのときにこの一覧で完全性を確認するので、社内のファイルサーバーにバンドルだけを置いておいても、「私たちが作ったそのバージョンで間違いないか」を、機械が判定できます。

インストールするバージョンは、コマンドラインではなくファイルに書きます。

# requirements.yml
collections:
  - name: acme.platform
    version: "1.2.0"           # Galaxy 나 사내 저장소에서 받을 때
  - name: /srv/artifacts/acme-platform-1.2.0.tar.gz
    type: file                 # 로컬 묶음 파일을 그대로 설치할 때

ansible-galaxy collection install -r requirements.yml -p <경로>(プレースホルダーはパスです)でインストールします。固定ファイルがあれば、ソースリポジトリが1.3.0へ先に進んでも、インストールされるのは1.2.0です。この1行が、コレクションを導入する最大の理由です。リポジトリの最新の状態と、配布されるバージョンが分離されます。

探索の順序も、知っておく価値があります。ansibleは、ANSIBLE_COLLECTIONS_PATH(またはansible.cfgのcollections_path)に書かれたパスと、プレイブックの隣にあるcollections/ディレクトリを見ます。プレイにcollections:キーワードを書くと、そのコレクションを短い名前で呼べますが、公式ドキュメントはFQCNを推奨しています。短い名前は検索順序に頼る方式で、その順序は環境が決めるからです。

現場での姿

1つ目は、「インストールしたのに見つからない」です。ほとんどの場合、パスの問題です。-pでインストールしたパスが、実行時の検索パスに入っていないと、ansibleはそのコレクションを知りません。ansible-galaxy collection list -p <경로>(プレースホルダーはパスです)で「そこにあるか」を、ansible-doc -t role <FQCN>で「ここから見えるか」を、別々に確認する習慣が、時間を節約します。

2つ目は、ロールの引数仕様です。meta/argument_specs.ymlに、引数の型・デフォルト値・許可する値を書いておくと、ロールが最初のタスクを動かす前に引数を検証し、間違っていればその場で止まります。他人が使うロールを配布するなら、これがドキュメントであり防衛線です。仕様がないと、誤った値は5つ目のタスクあたりで、見当違いのエラーとして噴き出します。

3つ目は、社内での配布です。Galaxyに載せられないコードがほとんどです。実務では、CIがbuildしたバンドルを社内のファイルサーバーやArtifactoryに置き、各チームのrequirements.ymlがそのアドレスとバージョンを指します。パイプラインは、実行の直前にinstall -r requirements.ymlを1回実行します。この構造では、「昨日動いたものが今日動かない」ことが減ります。バージョンがファイルに書かれているからです。

4つ目は、依存関係はタダではないということです。galaxy.ymlのdependencies:にほかのコレクションを書くと、インストールのときに一緒に引き込みますが、これは引き込める保存先があるときの話です。インターネットが遮断されたネットワークでは、依存するコレクションのバンドルまで、手作業で一緒に持ち込む必要があります。このラボ環境もそうしたネットワークなので、依存関係の自動解決は扱いません。

次のラボですること

acme.platformコレクションをスケルトンから作ってgalaxy.ymlを埋め、その中にロールを1つ入れ、meta/argument_specs.ymlで引数を強制し、バンドルをビルドして内容物とMANIFESTを確認し、ローカルのパスにインストールしてansible-docで調べ、FQCNで呼んで実行します。最後に、ソースのバージョンを1.3.0に上げたあと、requirements.ymlが1.2.0をそのまま守っていることを、目で確かめます。

参考ドキュメント