AWXサーバの外からansibleでジョブを実行する

この記事は MicroAd Advent Calendar 202120日目 と Ansible Advent Calendar 2021 - Adventarの 7日目の記事です(空いててもったいないので埋めました)。

qiita.com

adventar.org

はじめに

以下の記事の補足になります。

yassan.hatenablog.jp
yassan.hatenablog.jp

PRをベースとしたダッシュボード運用をしていると任意のタイミングでリポジトリの状態とダッシュボードを同期したい場合が出てきます。

例えば以下。

  • CIジョブが失敗して再実行したい
  • 本番ダッシュボードを削除・壊して復元できない

上記などの状況からどうしても同期取りたい場合が出てきます。
その場合、空コミットを追加しPR作成することで同期できますが、コミットログも汚れてしまいます。

また、ローカルからansible-playbookコマンドを実行する場合、Vault変数を復号するために復号用パスワードが必要になります。
その場合、Grafana運用者以外のユーザにも復号用パスワードを公開することになり具合が悪いです。さらに、ローカルから実行してしまうと「いつ・誰が・何を」と言った大事な実行履歴も残らないので困りものです。

そこで、AWXのジョブテンプレートを用意して、ユーザ及びCIサーバからの実行をAWX経由で実行するようにしてみます。

環境

前提となる環境は以下の通りです。

  • ansible-core v2.11(v2.9でも動きます)
  • AWX v13.0.0 (多分、v13以降でも大丈夫)
  • Jenkins(CircleCIやGitHub ActionsでもOK)

概要

今回は以下のAnsible Collectionの awx.awxを使います。

galaxy.ansible.com

こちらを利用することで、CIサーバからAWXのジョブを実行出来るようになります。 また、AWXにジョブを作成する事でAWXのWebUIからユーザーが任意のタイミングで実行も可能になります。

前準備

AWXのCollectionを利用できるようにするためのセットアップを行います。

まず、 collections/requirements.yml に以下のようにして追加。

---
collections:
  - name: awx.awx
    version: "13.0.0" # 利用しているAWXに合わせて指定

上記のversionは、 galaxyの awxのページInstall Version にあるドロップダウンリストのVersionを指定してください。 また、指定するVersionは、利用しているAWXのVersionに合わせて近いものを使うのが良いです(互換性の問題を回避する目的で)。

collections/requirements.yml に定義するのは、AWXがデフォルトで参照するCollectionのパスとなっています1

また、ローカルで実行したい場合は以下のコマンドでinstall出来ます2

# ansible-core v2.11以降
ansible-galaxy collection install -r collections/requirements.yml

Playbookの作成

awx_launch_job.yml を以下の様に作成します。

---
- hosts: "{{ grafana_group }}"
  gather_facts: false
  connection: local

  tasks:
    - name: ジョブの起動
      awx.awx.tower_job_launch:  # v19の場合は tower_job_launch→job_launch
        tower_host: "{{ awx_host }}"
        tower_username: "{{ awx_user }}"
        tower_oauthtoken: "{{ vault_awx.awx_oauth_token }}"
        name: "{{ job_name }}"
        extra_vars:
          revision: "{{ revision }}"
          grafana_group: "{{ grafana_group }}"
      register: job

    - name: "Wait for job max {{ job_timeout }}s"
      awx.awx.tower_job_wait:  # v19の場合は tower_job_wait→job_wait
        tower_host: "{{ awx_host }}"
        tower_username: "{{ awx_user }}"
        tower_oauthtoken: "{{ vault_awx.awx_oauth_token }}"
        job_id: "{{ job.id }}"
        timeout: "{{ job_timeout }}"

モジュール awx.awx.tower_job_launch を使うことでAWXのジョブを実行出来ます3

また、 awx.awx.tower_job_wait を使って待っているのは、CIジョブを以下の順番で実行するので、各ジョブが終わるのを待ち合わせするために設定しています4

  1. import-datasources
  2. import-notification-channels
  3. import-dashboards

どちらのモジュールでも利用している tower_oauthtoken に指定する値は、事前にVaultの暗号化変数として{{ vault_awx.awx_oauth_token }}に格納します5

また、実行する際は、Makefileに以下を定義して実行を簡略化します。

SHELL=/bin/bash
REVISION := $(shell git rev-parse --short HEAD)

# Grafanaにダッシュボードを反映する
# AWX job: grafana-import-dashboard を実行する
# 例: make import-dashboards group=prod
.PHONY: import-dashboards
import-dashboards:
    ansible-playbook -i inventories/inventory \
        --vault-id .vault_password \
        awx_launch_job.yml \
            --extra-vars role_name=import_dashboard \
            --extra-vars job_name=grafana-import-dashboard \
            --extra-vars grafana_group=$(group) \
            --extra-vars revision=$(REVISION) \
            --extra-vars job_timeout=600;

※他にも role_namejob_name を変更して他のロール分(import-datasourcesimport-notification-channels)も定義します。

AWX側の設定

以下のようにしてAWXからジョブテンプレートを作成します。
また以下は import_dashboard の分ですが、別途、他のロール分(import-datasourcesimport-notification-channels)も作成します。

f:id:yassan0627:20211220161327p:plain
ジョブテンプレートの設定例

注意点としては、上図の右下隅にある「起動プロンプト」にチェックを入れないと実行出来ないので忘れずにチェックしてください。 また、手動で実行したい場合は、追加変数に grafana_grouprevision を追加して実行してやればOK。

CI側のフローの設定

特に前回から変更は無いです。Makefileダッシュボードやデータソース、通知チャンネルのimportを実行するMakeターゲットをAWXジョブからの実行に変更しているので特にここでは変更点がありません。

詳細は前回の記事を参照して下さい。

yassan.hatenablog.jp

最後に

これでPRを使わずに実行履歴を残しつつダッシュボードの更新が出来るようになりました。
とは言え、PRベースに更新出来ないのでレビューが出来ず、また、更新意図を残せないのであくまで緊急事態用というところです。

全体を通した今後の改善点

現状、Grafanaのダッシュボード・データソース・通知チャンネルを毎回すべて更新してしまいます。 理想的には更新を検知して、更新ファイルだけ反映するような対応があるとより早く同期できたりします。 ただ、git diffで更新差分を使うアイディアもあるのですが差分をキレイに出すのが難しいです6

他にも、今回の仕組みではダッシュボードを削除する仕組みが無いので、リポジトリから削除して後、GrafanaのWebUIから手動で削除する必要があるので若干面倒です。

改善ポイントについて良案があれば是非教えて下さい!


  1. 詳細は 14. Projects — Ansible Tower User Guide v3.8.5 を参照。
    また、 GitHubのAWXのdoc にも説明があるので参考になります。

  2. ansible-core v2.11未満の場合は、 install オプションに -f がないと2回目以降でエラーになります。
    ただし、 ansible/ansible#65699 にて検討されて、ansible/ansible#73336で、v2.11より -u オプションが追加されています。

  3. パラメータの詳細は awx.awx.job_wait を参照下さい。

  4. パラメータの詳細は awx.awx.job_launch を参照下さい。

  5. この辺の説明は前回の記事を参照して下さい。
    yassan.hatenablog.jp

  6. mainブランチとの差分を使うと後から作成したPRを先にマージすると差分を取れなくなる問題があります(rebaseしたら治りますけど)。