Contents
AnsibleのPlaybookやRoleのCI/CDをAnsible Tower + GitLab + moleculeを使って出来ないか検証してみたので書いてみようと思います。
やること
ここでは、Apacheの設定やコンテンツを自動的にテストしてプロダクション環境への反映までやってみます。
流れは以下の通りです。
User
ユーザーは、PlaybookやRoleおよびApacheの設定とコンテンツを作成します。
普通、コンテンツはCMSで管理するとは思いますが、ここでは挙動確認のため簡単なコンテンツを作成してプロダクション環境へ反映します。
GitLab
GitLabでは、リポジトリの管理とCIおよびAnsible TowerのWF実行までを実施します。
ユーザーは、作成したPlaybookなどをリポジトリに反映すると自動で以下のテストが実施されます。
- main playbook lintテスト
- roleを呼び出すPlaybookのyamllint
- roleを呼び出すPlaybookのansible-lint
- moleculeでroleのテスト
- yamllint
- ansible-lint
- dockerコンテナをデプロイ
- コンテナ内でroleを実行
- role実行後にWeb接続のテスト
- dockerコンテナを削除
最後にテストで問題が無い場合はAnsible TowerのWorkFlow(WF)を実行します。
※本番ではmasterへ直接pushなんとことはしないと思いますが、今回は検証なので直接pushします
うれしいこと
これをやって嬉しい(メリット)と感じることは以下の通りです。
いつ
だれが
どのファイルに対して
どういった変更を加えたか
が自動で記録されます- マージリクエストを使うことで
背景や理由
目的
切り戻し用のcommit
なども書いて記録できます - テキストファイル(変数ファイルやraw設定ファイルなど)で構成を管理できます
- テストが自動で実行され記録も自動で残ります
- この記録があると無いとじゃ本番への適応の心理的負担が減ると感じています
- また、記録が残ることで振り返りや情報共有もやりやすくなります
- 自動化出来るものは人が記録を残すよりシステム側で機械的にやった方が情報は詳細に残りやすいと思います。
Ansible Tower
Ansible Towerでは、プロダクションサーバにPlaybookを実行します。
WFで定義しておいた以下のフローが実行されます。
- Apacheのインストール
- Apacheの設定とコンテンツのアップデート
- アップデート処理が走った場合は最後にApacheの再起動(graceful or restart)
- (理想は)もし、新しい設定をプロダクションサーバ適応後にエラーが発生した場合は自動切り戻し
- ここでは、簡単な検証しかしないため実装していないですが、本番では自動切り戻しを意識して実装しておくのが良いと思います。
うれしいこと
これをやって嬉しい(メリット)と感じることは以下の通りです。
いつ
だれが
なんのサーバに対して処理をしたか
が自動で記録されます- セキュリティーやロールによるアクセス制御などのガバナンスの強化ができます
- Ansible Towerの詳細な実行記録をログ解析ツール(例えばElasticsearch + Kibana、Splunkなど)へ取り込むことによってログを自在に可視化することができます
- Ansible Towerであれば何かあったらサポートに助けてもらえます
まだ、Ansible Towerでは実装されていませんが、AWX(Ansible Towerのアップストリーム版)で実装された承認機能と連携すれば自動でWFは実行されるが本番適応は別担当者が承認するという運用も可能です。
詳しくは以下を参照ください。
検証環境
検証環境は以下のようになっています。
バージョン
項目 | OS | バージョン | 備考 |
---|---|---|---|
Client | CentOS 7.5 | playbook作成する端末 | |
Ansible Tower | RHEL 7.6 | 3.5.1 | |
GitLab | CentOS 7.7 | ce-12.2.5-ce.0 | |
GitLabRunner | CentOS 7.7 | 12.3.0-1 | |
webserver | CentOS 7.6 | Web設定を適応するサーバ |
検証環境設定
GitLab
パイプラインの最後にdeployを実行します。
deployでは tower-cli
を使ってAnsible TowerのWFを実行します。
WFを実行するにはユーザーとパスワードが必要です。
そのため、パスワードはGitLabのCI/CD Variableに登録してマスクします。
プロジェクトの Settings
-> CI/CD
-> Variables
へ移動して変数を登録します。
ここでは ansible_tower_passwd
という変数名で登録しました。
GitLabRunner
GitLab Runnerの設定は以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[root@GitLabRunner ~]# cat /etc/gitlab-runner/config.toml concurrent = 1 check_interval = 0 [session_server] session_timeout = 1800 [[runners]] name = "GitLabRunner" url = "http://192.168.0.115/" token = "token" executor = "shell" [runners.custom_build_dir] [runners.cache] [runners.cache.s3] [runners.cache.gcs] |
executorを shell
にしている理由については以下を参照ください。
後、dockerのコンテナ操作をするのでgitlab-runnerユーザーには以下のsudo設定をしています。
1 2 3 4 5 6 7 |
[root@GitLabRunner ~]# visudo (snip) Defaults env_keep += "PATH" (snip) gitlab-runner ALL=(ALL) NOPASSWD: ALL (snip) |
sudoを実行してもPATH環境変数を維持するのとNOPASSWDで実行できるようにしています。
後、必要なモジュールは以下の通りインストールしています。
1 2 3 |
(venv)[gitlab-runner@GitLabRunner ~]$ pip install ansible molecule ansible-tower-cli (venv)[gitlab-runner@GitLabRunner ~]$ pip install molecule[docker] |
tower-cliで証明書検証を無効かしています。
1 2 |
(venv)[gitlab-runner@GitLabRunner ~]$ tower-cli config verify_ssl false |
また、CentOS7の標準gitでは以下の不具合が発生します。
そのため、GitLabRunnerではgitをソースからインストールしています。
1 2 3 4 5 6 |
[root@GitLabRunner ~]# yum -y install expat-devel libcurl-devel gcc [root@GitLabRunner ~]# git clone https://github.com/git/git.git [root@GitLabRunner git]# make configure [root@GitLabRunner git]# ./configure --prefix=/usr/local/git --with-curl --with-expat [root@GitLabRunner git]# make && make install |
gitインストール後、ソースでインストールしたgitにパスを通します。
1 2 3 4 |
[gitlab-runner@GitLabRunner ~]$ vi .bashrc (snip) export PATH="/usr/local/git/bin:$PATH" |
Ansible Tower
プロジェクトの設定です。
テンプレートの設定です。
ワークフローの設定です。
ワークフローは以下のようになっています。
ワークフローでは同じテンプレートを使っています。
テンプレートの流れは apache install
-> apache config and web contents update
の順で実行されます。
処理の中では以下のようにタグで実行を分けています。
最初の処理(apache install)のタグです。
次の処理(apache config and web contents update)のタグです。
WFには切り戻し用のテンプレートもあるように見えますが、今回は実装していないのでイメージです。
Playbook、Roleの実装についてはこの後に説明します。
Playbook/Role仕様
今回作成したものは以下にアップしました。
具体的なコードについては以下を参照してください。
階層仕様
今回は以下のように作成しました。
PlaybookとRoleについて
今回はタグのand条件で実行したい処理を分けるようにしています。
例えば、以下はmain.ymlの実装です。
1 2 3 4 5 6 7 8 9 |
--- - name: web server playbook hosts: all gather_facts: false tasks: - include_role: name: apache tags: ['never', 'apache'] |
apache
タグが指定された場合はapacheのroleが実行されるようにしています。
roleのtasksでは install
config_update
delete
が定義されています。
1 2 3 4 5 6 7 8 9 10 |
--- - include_tasks: install.yml tags: install - include_tasks: config_update.yml tags: config_update - include_tasks: delete.yml tags: delete |
例えば apache,install
というタグを指定した場合はapache roleのinstall処理だけが実行されます。
また apache,config_update
というタグを指定した場合はapache roleのconfig_update処理だけが実行されます。
これで、目的に合ったタグを指定することでやりたい処理のみを実行するようにしています。
これはroleで処理をひとまとめに管理したい場合やAnsible Towerで一つのテンプレートでいくつかの処理を分けたい場合に役立つと思っています。
moleculeのテストについて
工夫した点
moleculeのテストでテストインスタンス内でroleを実行した後のWeb接続確認テストをどうしようかと思っていたのですが、moleculeではroleを実行した後に side_effect
と言うタスクが実行されます。
今回はroleを実行した後のWeb接続確認用Playbookを side_effect
で実行しています。
side_effectの実装は以下のようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
--- - name: Side Effect hosts: all gather_facts: true tasks: - name: check example1 site. uri: url: "http://{{ ansible_facts.all_ipv4_addresses.0 }}" headers: Host: www.example1.com return_content: yes register: example1_result delegate_to: localhost tags: - always - assert: that: - example1_result.status == 200 - example1_result.content is search("example1") tags: - always |
テストインスタンスのIP情報を元にuriモジュールで接続して、ステータスコードが200か、取得したコンテンツ内に指定した文字が存在しているか、をassertで確認しています。
例えば、VirtualHostを増やした場合は、このテストを増やすイメージです。
デモ動画
以下はapacheのインストールからwww.example1.comのVirtualHostのテスト・webserverへ反映するまでのデモ動画です。
Ansible CICD demo (apache version) from sky_joker on Vimeo.
以下はVirtualHost(www.example2.com)を追加した時のデモ動画です。
Ansible CICD demo (example add virtualhost to apache) from sky_joker on Vimeo.
こんなイメージで運用が出来るのではないかと思っています。
最後に
Ansible Tower + GitLab + moleculeを組み合わせてCI/CDが出来ることが確認できました 🙂
moleculeでは、roleテストを実行する前にprepareに事前環境を定義することも可能です。
また、delegatedドライバを使うことで標準ドライバが存在しない場合やそれ以上のことをやりたい時に柔軟に対応できます。
基本的にAnsibleの処理はroleで書いてroleごとのテストをmoleculeでやってデプロイをAnsible TowerやAWXを使う感じが今のところいいのかなと個人的に思っています。
後、直デプロイではなくDry RunまでをAnsible Tower側で自動実行しておいて、プロダクション環境への反映は人がAnsible Towerのボタンを押すと言うのもいいと思います。
インフラ自動化も奥が深くて楽しいです 🙂
それでは、みんなでハッピーオートメーション!