本エントリは「エーピーコミュニケーションズ Advent Calendar 2023」の24日目のエントリです。クリスマスイブ🎄は一切関係ないネタです。
Ansible Automation PlatformのAutomation Controller(旧Ansible Tower / 以下AAP)やAWXはGUIでAnsibleを実行することができるアプリケーションですが、率直に言ってマウス使って操作…とくにプロジェクトやテンプレート作ったりするのって億劫じゃないでしょうか?私は嫌ですやりたくありません(直球)
この記事ではそんなGUI操作が必要なAAPやAWXの操作をAnsibleで自動化するためのタスクの実装について紹介します。
接続方法
サーバーアドレス
最初に戸惑いがちなところですが、抑えるべきポイントは「タスクが実行されるホスト(hosts
指定のホスト)から見たAAPのアドレスを指定する」です。
指定の方法は2通りあり、環境変数CONTROLLER_HOST
で指定するか、タスクごとに各モジュールのcontroller_host
パラメタで指定です。
基本的には1台のAAPに対して様々なリソースを作成していくパターンが多いので、その場合は環境変数をプレイに指定すれば各タスクの記述が減ってplaybookがスッキリします。本記事では環境変数で指定している前提でタスク実装の紹介していきます。
認証情報
これもサーバーアドレスと同様に、環境変数もしくは、タスクごとに各モジュールのcontroller_username
とcontroller_password
で指定します。パスワード認証でなくトークンを使う場合は環境変数CONTROLLER_OAUTH_TOKEN
かパラメタcontroller_oauthtoken
を使います。
通常はサーバーアドレスとセットで指定することになると思います。
運用時の操作であれば適切なロールを設定したユーザーのトークンを使うのが良いですが、AAP/AWXインストールから初期構築までをすべて自動化するのであればadmin
とそのパスワード認証を指定することになります。
(ゼロの状態からトークンでの認証ってできない…よね?)
実装例
ローカル実行でリモートのAAPへ接続する場合
おそらく一番よくあるパターン。
localコネクションプラグインでタスクはローカル実行しつつ、CONTROLLER_HOST
でAAPサーバーを指定する構成。
---
- hosts: localhost
environment:
CONTROLLER_HOST: 192.168.0.79
CONTROLLER_VERIFY_SSL: false
CONTROLLER_USERNAME: admin
CONTROLLER_PASSWORD: curry_tabetai
tasks:
また、AAPの場合はHTTPS接続でポートも443/TCPなのでアドレスのみ指定すればOKですが、AXWは構成によっては(Kubernetesからの公開方法によっては)HTTPだったりポート番号もデフォルト以外の場合もあります。
その場合は、http
から記述すればOK
(この場合はCONTROLLER_VERIFY_SSL
は無くても動く)
- hosts: localhost
environment:
CONTROLLER_HOST: http://192.168.0.75:8080
CONTROLLER_VERIFY_SSL: false
CONTROLLER_USERNAME: admin
CONTROLLER_PASSWORD: ...
タスク実行ノードを中継サーバーにする場合
Ansibleを実行するノードからAAPへ疎通がない場合などの構成。
hosts
で指定する対象ノードから見たAAPのアドレスをCONTROLLER_HOST
に指定。
---
- hosts: server
environment:
CONTROLLER_HOST: 172.29.1.21
CONTROLLER_VERIFY_SSL: false
CONTROLLER_USERNAME: admin
CONTROLLER_PASSWORD: curry_tabetai
tasks:
AnsibleのマネージドノードとしてAAPのホストに直接接続
aap
に指定しているサーバーにSSH接続し、そこからlocalhost
で接続する、という構成。
複数のAAPサーバーがあり、設定内容が同一であれば、この構成ならまとめて処理できます。
- hosts: aap
environment:
CONTROLLER_HOST: localhost
CONTROLLER_VERIFY_SSL: false
CONTROLLER_USERNAME: admin
CONTROLLER_PASSWORD: curry_tabetai
tasks:
AWXの場合はkubectlコネクションプラグイン使用
SSH接続する構成が取れるのはAAPの場合で、AWXの場合はコンテナ内でSSHは動いていないため、同様の構成をとる場合はkubectl
コネクションプラグインを使う必要があります。
- hosts: awx
environment:
CONTROLLER_HOST: http://localhost:8052
CONTROLLER_USERNAME: admin
CONTROLLER_PASSWORD: ...
※ 以下は2023年12月時点での内容。AWXはたまにコンテナ構成が変わるので、その場合は指定する対象が変化するかも。おそらく「web」に対して接続すればよいと思うけど…
HTTPであることと、接続先のポートが8052なのがポイント(これはServiceリソースのtargetPort
で確認できます)
インベントリファイルは以下のようになります。
この例はAWXはawx
ネームスペースにawx-demo
の名前でデプロイされています。対象はWebのPodでここではawx-demo-web-68b54f6bdf-mx55s
というPod名です。
(いくつかのコンテナで構成されていますがweb
のコンテナに接続します)
[awx]
awx ansible_kubectl_pod=awx-demo-web-68b54f6bdf-mx55s ansible_kubectl_namespace=awx ansible_kubectl_container=awx-demo-web
[all:vars]
ansible_connection=kubectl
kubectl
を実行したときのpodとserviceの状態は以下の通り。
$ kubectl get pod,svc -n awx
NAME READY STATUS RESTARTS AGE
pod/awx-operator-controller-manager-76b545976d-jlcts 2/2 Running 0 16h
pod/awx-demo-postgres-13-0 1/1 Running 0 16h
pod/awx-demo-task-5ff94fb4d9-65zdt 4/4 Running 0 16h
pod/awx-demo-web-68b54f6bdf-mx55s 3/3 Running 0 15h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/awx-operator-controller-manager-metrics-service ClusterIP 10.43.167.210 <none> 8443/TCP 16h
service/awx-demo-postgres-13 ClusterIP None <none> 5432/TCP 16h
service/awx-demo-service LoadBalancer 10.43.194.190 192.168.0.75 8080:30448/TCP 16h
kubectl
コネクションプラグインについては以下参照。
zaki-hmkc.hatenablog.com
パスワードでなくトークンを使用する場合
ここまではCONTROLLER_PASSWORD
を使ってパスワードを指定した例でしたが、トークン(パーソナルアクセストークン)の場合は以下の通り。
environment:
CONTROLLER_HOST: 192.168.0.79
CONTROLLER_VERIFY_SSL: false
CONTROLLER_USERNAME: zaki
CONTROLLER_OAUTH_TOKEN: ....... (ユーザーメニューの「トークン」で作成するトークン文字列)
リソース作成
awx.awx.licenseモジュールを使用します。
これはAWXになくAAPのみ。
実行ノード上にzipのマニフェストファイルを配置しておき、パスを指定します。
- name: set the license
awx.awx.license:
manifest: manifest.zip
マニフェストファイルの作成方法は以下参照。
zaki-hmkc.hatenablog.com
認証情報
awx.awx.credentialモジュールを使用します。
よく使うものを紹介。
Gitリポジトリにアクセスするための認証情報で、主にプロジェクトの更新時に使用。
- name: create git repository credential
awx.awx.credential:
name: git-repository-credential
organization: Default
credential_type: Source Control
inputs:
username: username
password: password
公開鍵認証で秘密鍵を登録したい場合は、password
でなくssh_key_data
を使用して、鍵ファイルの内容を指定します。
inputs:
username: zaki
ssh_key_data: '{{ lookup("file", "~/.ssh/id_rsa") }}'
マシン
Ansible実行時のホストに対するSSH認証情報として使用。
- name: create ssh credential
awx.awx.credential:
name: server
organization: Default
credential_type: Machine
inputs:
username: username
password: password
SSH秘密鍵を登録するのであれば前述ソースコントロールの場合と同様にssh_key_data
を使用します。
認証が必要なコンテナレジストリで使用。
例えばDocker Hubへのログインであればdocker.io
を指定します。
- name: create registry credential
awx.awx.credential:
name: dockerhub credential
organization: Default
credential_type: Container Registry
inputs:
host: docker.io
username: username
password: password
カスタム認証情報タイプ
認証情報タイプの作成
プリセットの認証情報の型で足りない場合にカスタム認証情報タイプを作成する場合、awx.awx.credential_typeモジュールを使用します。
以下は「ホスト名」「ユーザー名」「パスワード」の3値を一つの情報として定義する例。
インジェクターに{{ 変数名 }}
という文字列を設定するため、raw
を使ってJinja2のテンプレート処理を無効にしているのがポイント。
- name: create credential type for host
awx.awx.credential_type:
name: host-auth-info
kind: cloud
inputs:
fields:
- id: username
type: string
label: username
- id: password
type: string
label: password
secret: true
- id: hostname
type: string
label: hostname
injectors:
extra_vars:
auth_username: "{% raw %}{{ username }}{% endraw %}"
auth_password: "{% raw %}{{ password }}{% endraw %}"
auth_hostname: "{% raw %}{{ hostname }}{% endraw %}"
ちなみにkind
は必須項目でドキュメントを見るとcloud
かnet
が指定できると書かれているが、どこに作用しているかは不明。。あまり影響しなさそうな気がしますが。。
Note that only cloud and net can be used for creating credential types.
上記の自作認証情報タイプの認証情報
前述host-auth-info
の認証情報タイプを使った認証情報を作成するには、credential_type
に認証情報タイプ名を指定します。
- name: create my credential
awx.awx.credential:
name: my-credential
organization: Default
credential_type: host-auth-info
inputs:
hostname: nijigasaki.example.org
username: zaki
password: tokimeki
ジョブ関連
インベントリとホスト
awx.awx.inventoryモジュールを使用します。
ホスト作成時にインベントリの指定があるため、まずインベントリを作成してからホストを作成します。
インベントリ作成
- name: create inventory
awx.awx.inventory:
name: my inventory
organization: Default
ホスト作成
awx.awx.hostモジュールを使用します。
inventory
に作成済みインベントリを指定して、そこに属するホストを登録します。
ホスト変数の指定がある場合はvariables
で指定します。
- name: add host to inventory
awx.awx.host:
name: "{{ item.name }}"
inventory: my inventory
variables:
ansible_host: "{{ item.addr }}"
loop:
- name: server1
addr: 192.168.0.71
- name: server2
addr: 192.168.0.72
プロジェクト作成
awx.awx.projectモジュールを使用します。
認証が不要なGitリポジトリとしてhttps://github.com/zaki-lknr/ansible-sample.gitを指定するなら以下。
- name: create project
awx.awx.project:
name: sample project
organization: Default
scm_type: git
scm_url: https://github.com/zaki-lknr/ansible-sample.git
scm_update_on_launch: false
認証が必要なリポジトリの場合はcredential
パラメタに作成済みの認証情報名を指定します。
credential: git-repository-credential
実行環境
awx.awx.execution_environmentモジュールを使用してコンテナレジストリ上のEEコンテナを指定します。
認証が必要な場合は、credential
に「credential_type
=Container Registry」を指定して作ったクレデンシャルリソースを指定します。
pull
にはイメージpullポリシーを指定します。
- name: create ee setting
awx.awx.execution_environment:
name: Network Automation Execution Environment
image: docker.io/zakihmkc/ansible-ee:1.2.3
pull: missing
credential: dockerhub credential
ジョブテンプレート
ジョブテンプレートを作成するには、awx.awx.job_templateモジュールを使用してここまで掲載したプロジェクトやインベントリ、認証情報、EEなどを指定します。
playbook
にはリポジトリに存在するプレイブックのファイル名を指定する必要があります。(存在しないとエラー)
- name: create job template
awx.awx.job_template:
name: sample job template
job_type: run
project: sample project
organization: Default
inventory: my inventory
credentials:
- server1
- my-credential
playbook: path/to/playbook.yml
verbosity: 0
execution_environment: my-image
ワークフローテンプレート…は長いのでまた別途 m__m
awx.awx.workflow_job_template
を使って定義します。
schema
以下に実行したジョブテンプレートを記述して、related
に成功時・失敗時などの次のジョブを指定します。それぞれのジョブテンプレートはidentifier
でタスク定義内で使用するIDを割り振ってそのIDを指定します。
スケジュール
作成済みジョブテンプレートを定期実行させたい場合はawx.awx.scheduleモジュールを使って作成します。
スケジュール定義はrrule
に記述しますが、生で書くのは若干複雑というほどでもないけどハードコーディングせざるを得なくなるため、awx.awx.schedule_rruleset lookupプラグインを使用すると管理しやすいです。
ただしschedule_rruleset
を使用するにはPythonのpytz
パッケージが必要なのでpip
でインストールしておく必要があります。
- name: create schedule
awx.awx.schedule:
name: sample job template schedule
unified_job_template: sample job template
rrule: "{{ query('awx.awx.schedule_rruleset', start_time, rules=rrules, timezone='Asia/Tokyo') }}"
vars:
rrules:
- frequency: 'day'
interval: 1
start_time: "{{ now(false, '%Y-%m-%d') }} 12:00:00"
あとschedule_rrule
もあって、どちらを使っても以下の警告が出て解決できなかった(運用では実際はこっち使ってたりするけど)
メッセージの感じからplaybookの書きっぷりじゃなくてプラグインの内部実装の方に修正が必要そうな感じがしなくもないけど…詳細不明。
[DEPRECATION WARNING]: The lookup plugin 'awx.awx.schedule_rruleset' was expected to return a list,
got '<class 'str'>' instead. The lookup plugin 'awx.awx.schedule_rruleset' needs to be changed to return a list.
This will be an error in Ansible 2.18. This feature will be removed in version 2.18.
Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
…なんかここまで書いてるとlookup plugin使わず生で書いてもよいかもしれない。。
その他
設定
設定についてはawx.awx.settingsモジュールを使用します。
インベントリファイルへの指定でできそうだけどわからなかった「ベースURLの指定」をセットするには以下の通り。
自動化を実行するだけであればこの設定は特にイジらなくても大丈夫だけど、ベースURLは「通知」を行う場合のメッセージ内のURLに使われるので、使い方によってはきちんと設定しておきたい値。
- name: update base url
awx.awx.settings:
name: TOWER_URL_BASE
value: "https://ansible.example.org"
他には、例えばAWX_ISOLATION_SHOW_PATHS
に空リストを設定するには以下。
- name: unset isolation path (workaround)
awx.awx.settings:
name: AWX_ISOLATION_SHOW_PATHS
value: []
access.redhat.com
ジョブの起動
ジョブを起動するにはawx.awx.job_launchモジュールを使用します。
job_template
にジョブテンプレート名を指定すればOK
- name: launch sample job template
awx.awx.job_launch:
job_template: sample job template
通知のテスト
モジュールはなさそうなので、uriを使ってAPIを直接叩きます。
URLに含める必要がある通知ID(以下例でのnotify_id
)は事前に知っておく必要があります。
- name: notification test
ansible.builtin.uri:
method: POST
url: "https://ansible.example.org/api/v2/notification_templates/{{ notify_id }}/test/"
url_username: admin
url_password: "{{ password }}"
validate_certs: false
force_basic_auth: true
status_code: [200, 201, 202]
ほんとは先に「AAP/AWXの結果をSlackに通知する設定」という記事を書いておきたかったのに…いつか書きます。
docs.ansible.com
環境
- Ansible実行元
- 自動化対象
- AAP Controller 4.4.8 (on RHEL9 / AAP 2.4.3)
- AWX 23.4.0 (on K3s 1.28.4)
弊社では各展示会に出展する際に「ネットワーク自動化デモ」を実施しています。
automation.ap-com.co.jp
本記事で紹介したタスクはこのデモ環境を構築するときに使用しており、環境の構築をAnsibleで自動化しています。
(以下懺悔) 1年以上前からこの構築を行っててAnsible使った自動構築の情報としては持っていたので「いつかブログでまとめよう…」と思いつつもずっと保留してる間に、チームメンバーが何人も「AnsibleでAAPリソース作成の自動化」を行う機会があって、でもわかりやすい情報共有ができずに「デモの自動構築のソース参考になるよ」とリポジトリの場所を教えることしかできなかったという機会損失が今年は何度もあり、本当に申し訳ありませんでした。(これが言いたかった)