AWX/TowerのJob TemplateをPlaybookで追加する方法
こんにちは。株式会社SHIFT、自動化エンジニアの水谷です。
最近AWX(AnsibleのPlaybookやInventoryなどを登録して、Playbookの実行を行ったり、結果を管理するためのWebツール)上に作った数十個のJob Template(実行するPlaybookの指定、および実行時の条件等を指定するオブジェクト)を、別のサーバー上で動いているAWX上に移動する必要がありました。もちろん、1つずつ新しいサーバー上にAWXのUIから手作業でJob Templateを作っていくのは手間も時間もかかるので、(一応自動化エンジニアと名乗っている手前もありw)この作業を自動化したいと思いました。
いろいろ考えた結果、Job Templateをエクスポート⇒インポートするのではなく、PlaybookでJob Templateを作る方法をとったのですが、今回はそのあたりの話をまとめておきたいと思います。
tower-cliとawxkit
まずは調査、ということでネットを検索して調べたところ、"tower-cli"というコマンドラインツールがあって、これでAWX上に登録してあるリソースをエクスポートしたり、インポートできるという情報を見つけました。
やっぱりちゃんとツールが用意されているんだな、と思いながら、さっそくtower-cliのGithubを見てみると、なんと下のように「THIS PROJECT IS NO LONGER MAINTAINED(このプロジェクトは今後メンテナンスされない、要するに開発終了)」の文字が。
https://github.com/ansible/tower-cli
これはつまり、今後AWXに大きな変更が入ったりした際には、tower-cliが動作しなくなる可能性があることも意味しているので、ちょっと躊躇してしまいます(今回1回だけ使うなら大丈夫かもしれませんが・・・)。
ということで、再度調べてみると、今度は"awxkit"というツールがあって、これがtower-cliを置き換える存在との情報を発見。
公式ドキュメント(https://docs.ansible.com/ansible-tower/latest/html/towercli/examples.html)の下のページなどを見てみると、"awx export --jobtemplate > jt.json"と、とても簡単なコマンドで、Job Templateを(Surveyも含めて)全部JSON形式で出力してくれるようです。
そして、そのファイルを新しいサーバー上で"awx import < jt.json"コマンドでインポートしてやればよいようです。簡単ですね。
それでもやっぱりPlaybook化したい
awxkitを使えばJob Templateのサーバー間移動ができることはわかりました。しかし、ふとメンテナンス性について考えると、これでいいのかな? と、ちょっと疑問に思いました。
例えば、今後複数のマシンにJob Templateをインポートして展開していった状態で、Job Templateの一部を変更する必要が生じた場合を考えてみます。すべてのマシンのJob Templateを同じように更新したいのですが、この場合取れる方法は2つあって、1つは一度一部あるいは全部のJob Templateを削除して、修正後の(Job Templateが入った)JSONファイルをインポートする。2つ目の方法は、"awx jobtemplates modify"コマンドで各サーバーにおいてJob Templateを(パッチを当てる感じで)修正する。他にもあるのかもしれませんが、私が思いついたこの2つの方法では、どちらにしても1台1台のサーバーに対して手作業でアップデート作業を行うことになり、結局中途半端な自動化になってしまいます。
そこで、何かうまい方法はないかと考えた結果、Job Templateを作成するPlaybookを作って、それを各マシンで自動実行することで、自動的にJob Templateを追加したり修正したりできるようにしてしまおう、という考えに至りました。
tower_job_templateモジュール
そもそもJob TemplateをPlaybookで作成なんてできるのか?と思われるかと思いますが、実はそのためのモジュール"tower_job_template"がAnsibleには用意されています。
https://docs.ansible.com/ansible/latest/collections/awx/awx/tower_job_template_module.html
Playbookのタスクをこのモジュールで作成して、そこにJob Template名、実行するPlaybook名など各項目を変数として記述していけば、1つのJob Templateを作成するタスクができあがります。下はその例で、ChromeをインストールするPlaybookを実行するためのJob Templateを作るタスクを含むPlaybook(ややこしいw)となります。
---
- name: Add a Job Template
hosts: localhost
tasks:
- name: Add Job Template for Install Chrome Playbook
tower_job_template:
name: Install Chrome
tower_username: "{{ lookup('env', 'TOWER_USERNAME') }}"
tower_password: "{{ lookup('env', 'TOWER_PASSWORD') }}"
tower_host: "{{ lookup('env', 'TOWER_HOST') }}"
job_type: run
inventory: Test Inventory
project: TestPlaybookProject
playbook: windows/windows-install-chrome.yml
credential: Machine Account
state: present
このPlaybookはAnsibleが動作しているマシンで実行するので、"hosts:"は、"localhost"です。また、Job Templateを作成するAWX(あるいはAnsible Tower)の情報は、環境変数から取ってくるようにしています。事前に"TOWER_HOST"、"TOWER_USERNAME"、および"TOWER_PASSWORD"の環境変数を下の例ように設定しておきます。
export TOWER_HOST=http://1.1.1.1/ # 実際のAWXのIPアドレスに変更してください
export TOWER_USERNAME=admin
export TOWER_PASSWORD=password
AWXやTowerを使われている方は、「Survey機能はどうするの?」と、疑問に思われるかと思います。もちろん、Survey(Playbbok実行時に表示される入力フォーム)も合わせて登録できるようになっています。
その方法は、"survey_enabled: true"という行を追加し、さらにSurveyの内容を記述したJSONファイルを用意して、下のように"survey_spec"変数にそのファイル内容を取り込みます。
survey_enabled: true
survey_spec: "{{ lookup('file', 'survey-install-chrome.json') }}"
では、そのJSONファイルの内容はというと、例えば下のようなものです。
{
"name": "",
"description": "",
"spec": [
{
"question_name": "Chocolateyの使用",
"question_description": "Chocolateyを使用してインストール",
"required": true,
"type": "multiplechoice",
"variable": "use_chocolatey",
"min": 0,
"max": 255,
"default": "no",
"choices": "yes\nno",
"new_question": true
}
]
}
このSurveyは、Chromeのインストールにパッケージ管理ソフトのChocolateyを使うかどうかをyes/noで選択するものです。選択結果は文字列として"use_chocolatey"変数に入ることになります。
※ "type"は、"text"(テキスト形式)、"multiplechoice"(複数の選択しから1つを選ぶ。選択肢は"choices"に"\n"区切りで書く)、"multiselect"(複数の選択肢から複数を選ぶ)などが指定できます
ちなみに、なぜAnsibleはYAML形式で書くのにここだけJSONファイルなのかは、わかりません。一部のドキュメントではYAMLでも書けるような記述もありましたが、少し試してみたところ、YAMLでは動きませんでした。
いずれにせよ、このPlaybookを実行すればSurvey付きのJob TemplateがAWX(あるいはTower)に追加されます。あとは、どんどんタスクを増やしていけばOK、ということになります。
Playbook作成も面倒なので自動化
「どんどんタスクを増やしていけばOK」、と書いたものの、何十ものPlaybookについてタスクを手で書いていくのはこれまた手間がかかるし、ミスも起こしそうです。
ということで、これも自動化しましょう。
AnsibleはLinux系OS上でPythonを使って動いているので、なんとなくPythonでPlaybookを生成するスクリプトを書くのが自然かな、と思ったりもしましたが、私はそれほどPythonが流暢には使えないので、慣れているPowershellで下のようなコードを書いてみました(ベタだし、あまりきれいではないですが・・・)。これで、フォルダ内のすべてのPlaybookから必要な情報を拾ってきてJob Template作成タスクを作っていきます。Surveyについては"survey"フォルダにPlaybookと同じファイル名で拡張子が".json"のファイルがあれば"Surveyあり"と判断し"survey_enabled:"の行と"survey_spec:"の行を追加するようにしています。
$ScriptDir = $PSScriptRoot
$PackageRoot = Split-Path $ScriptDir -Parent
$dir = "playbook_directory"
$playbook = Join-Path $PackageRoot "GenerateJobTemplates.yml"
$temp = "---`r`n"
$temp += "- name: Add Job Templates for " + $dir + "`r`n"
$temp += " hosts: localhost`r`n`r`n"
$temp += " tasks:`r`n"
gci (Join-Path $PackageRoot $dir) | ? name -like "*.yml" | foreach {
$pb = gc $_.FullName
# find name
$name = $pb -match '^\s{2}name'
if ($name.length -eq 0) { $name = $pb -match '^-\s{1}name'; $name = " " + $name.substring(1) }
$temp += " -" + $name.Substring(1) + "`r`n"
$temp += " tower_job_template:`r`n"
$temp += " " + $name + "`r`n"
$temp += " tower_username: `"{{ lookup('env', 'TOWER_USERNAME') }}`"`r`n"
$temp += " tower_password: `"{{ lookup('env', 'TOWER_PASSWORD') }}`"`r`n"
$temp += " tower_host: `"{{ lookup('env', 'TOWER_HOST') }}`"`r`n"
$temp += " job_type: run`r`n"
$temp += " inventory: Playbook Package Inventory`r`n"
$temp += " project: PlaybookPackage`r`n"
$temp += " playbook: $dir/$($_.Name)`r`n"
$temp += " credential: $dir-target`r`n"
$temp += " limit: $dir`r`n"
$temp += " state: present`r`n"
# survey
$surveyfile = Join-Path (Join-Path (Join-Path $PackageRoot $dir) "survey") ($_.name).Replace(".yml", ".json")
if (Test-Path $surveyfile) {
$temp += " survey_enabled: true`r`n"
$temp += " survey_spec: `"{{ lookup('file', '{{ playbook_dir | dirname }}/$dir/survey/$($_.Name.Replace(".yml", ".json"))') }}`"`r`n"
}
$temp += "`r`n"
}
while ($temp.LastIndexOf("`r`n") -eq $temp.length - 2) { $temp = $temp.Substring(0, $temp.length -2) }
[System.IO.File]::WriteAllLines($playbook, $temp)
SurveyのJSONファイルを用意するのがちょっと手間ですが、これを実行すればディレクトリ内にあるすべてのPlaybookに対するJob Templateを自動生成するPlaybookができます。
あとは実行するだけ。
実行にはtower-cliが必要!?
しかし、実際にPlaybookを実行して見ると、下のようなエラーが出ました。
エラーの内容は、"tower-cli"がない(インストールされていない)というものです。なんと、もうメンテナンスしないと宣言しているtower-cliがこのモジュール(tower_job_template)では現役で使われているようです。
いずれはモジュールの中で動くtower-cliはawxkitに置き換わるのかなと想像できますが、今はtower-cliをインストールして動かすしかなさそうです。
tower-cliはpip(pip3)でインストールできます。
$ pip3 install ansible-tower-cli
これで、Playbookが実行できます。
Job Templateを追加するPlaybookのJob Template
さて、最後の仕上げ(?)です。このPlaybookをGitにPushしておき、Job Templateを作ってAWX上から定期実行するようにします。こうすれば、例えば1週間に1度最新のJob Template作成PlaybookがGitから取り込まれて実行され、その結果Job Templateが追加されたり、既存のJob Templateも自動的に更新されるようになります。
※tower_job_templateモジュールには冪等性がありますので、同じJob Templateが2つできたりはしません。
ここで1つ注意が必要な点は、tower-cliをAWXでAnsibleが動作するコンテナ内にインストールしておく必要があることです。AWX上でPlaybookを実行する場合、コンテナ内のAnsibleが使われるため、tower-cliそのコンテナ内に存在する必要があるのです。
まずはAWXマシンにSSH接続し、"docker ps"を実行します。ここで一番上に表示されるコンテナがそれです。
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f65af3afdeab ansible/awx:15.0.1 "/usr/bin/tini -- /u…" 10 days ago Up 2 hours 8052/tcp awx_task
f8d806b411af ansible/awx:15.0.1 "/usr/bin/tini -- /b…" 10 days ago Up 2 hours 0.0.0.0:80->8052/tcp awx_web
ec04fc4ad044 redis "docker-entrypoint.s…" 10 days ago Up 2 hours 6379/tcp awx_redis
4f0fc638c4ed postgres:10 "docker-entrypoint.s…" 10 days ago Up 2 hours 5432/tcp awx_postgres
上の例であれば、IDが"f65af3afdeab"なので、以下のコマンドでコンテナに入ります。
$ sudo docker exec -t -i f65af3afdeab /bin/bash
そして、pip3で下のようにtower-cliをインストールします。
$ pip3 install ansible-tower-cli
あとは、環境変数の"TOWER_HOST"、"TOWER_USERNAME"、および"TOWER_PASSWORD"を設定すればOKとなります。なお、tower-cliの動作を確認するには、"tower-cli user list"を実行して、AWX(あるいはTower)に登録したユーザーの情報が表示されるかどうかを見ればよいかと思います。
これで、Playbookを修正したり、追加したりした場合、上のPowershellスクリプトを実行してGitにPushすれば、すべてのマシンにおいて定期実行されるJobでJob Templateが追加されたり修正されることになり、メンテナンスがとても楽になります(Powershellスクリプトの実行をCI化してしまえばなお良し)。
――――――――――――――――――――――――――――――――――
お問合せはお気軽に
https://service.shiftinc.jp/contact/
SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/
SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/
SHIFTの導入事例
https://service.shiftinc.jp/case/
お役立ち資料はこちら
https://service.shiftinc.jp/resources/
SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/