はじめに
参考にさせていただいた記事にもありますように、IBM CloudをAnsibleで操作するためのAnsibleモジュール、IBM Cloud Ansible Collectionsがリリースされました。
数多くのモジュールがある反面、サンプルがIaaS関連のごく一部しか存在しないのが現状です。
そこで、IBM Cloudに数多くあるサービス(SaaS)の作成を通して、このツールでIBM Cloudサービスの構成管理ができないか検証してみたので、メモ書きとして残そうと思います。
環境
当方の環境は以下のとおりです。
- VirtualBox (+ vagrant)
- CentOS 7.7
- ansible 2.9.6
- Python 3.6.8 (OS同梱のPython 2.7.5はそのまま) + jinja2
- Terraform v0.12.24
- IBM Cloud Terraform Provider v1.3.0
- IBM Cloud Ansible Collections 1.3.0
Ansible、Python3、jinja2は、これらのコマンドで導入します。
$ sudo yum install python3
$ sudo pip3 install "ansible>=2.9.2"
$ sudo pip3 install jinja2
Terraform と Terraform Provider は、こちらを参考に導入します。
Ansible Collectionsの導入は、このコマンドで導入します。
$ ansible-galaxy collection install ibmcloud.ibmcollection
ansible.cfgの設定
この Closed issue にあるように、ansible.cfg(例:./ansible.cfg
) の [default] セクションに2行定義を追加します。
[defaults]
library = ~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules:~/.ansible/collections/ansible_collectio
ns/ibmcloud/ibmcollection/plugins/modules
module_utils = ~/.ansible/plugins/module_utils:/usr/share/ansible/plugins/module_utils:~/.ansible/collections/ansible
_collections/ibmcloud/ibmcollection/plugins/module_utils
インベントリファイルと変数の準備
インベントリファイル
インベントリファイル(例:inventories/test/hosts
)に以下のように定義し、Ansible Collection実行時はPython3が使われるようにします。(Ansible Collectionの前提がPython3とあるので、おそらくこれでよいかと・・・)
localhost ansible_python_interpreter=/usr/bin/python3
変数:APIキー
IBM CloudのAPIキーを記載する変数ファイルを用意します。例えば group_vars/all/id.yml
などに以下のように記載します。(そして本来はAnsible Vaultを用いて、本ファイルを暗号化しておくことになります)
ic_api_key: xxxxxxxxxxxxxxxxxxxxx
変数:その他
APIキー以外を定義する変数ファイル、例えば group_vars/all/resource.yml
に必要な定義を追加していきます。
まずサービスを配置するリソースグループを定義します。ここでは、resource_group_name
ではなく、resource_group_id
を指定する必要があります。使用するリソースグループのIDを、ibmcloudコマンド ibmcloud resource groups
で確認して、以下のように変数ファイルに定義します。
resource_group_id: xxxxxxxxxxxxx
続いて、同じファイルに Db2 lite オーダーに必要な情報を定義します。詳細はコメントを参照ください。
db2:
instance_name: db2-test # 作成されるサービスの名前
service: dashdb-for-transactions # 作成するサービス
plan: free # サービスのプラン
location: eu-gb # サービスのロケーション
tags: tag1,tag2 # タグ (任意)
state: available # いわゆる state (present は指定不可・・・absentを指定すると既存のインスタンスを削除する。省略時は available)
Playbookの作成
いよいよPlaybookです。参考にさせていただいた記事にもあるように、残念ながらIBM Cloud Ansible Collectionsにはまだ冪等性が一部考慮されていないようです。ibm_resource_instanceモジュールも、idフィールドを指定せずに何度もPlaybookを実行すると、同じ名前のサービスが幾つも幾つも作成されます。(サービス名でユニーク性を保っていないとはいえ、エラーにならないので辛いです)
そのため、ibm_resource_instance_infoモジュールで、まず同名のサービスがあるかどうかチェックし、ibm_resource_instanceモジュールで作成、更新あるいは削除を実施する流れとします。
まずPlaybook冒頭で、必要な定義を行います。詳細はコメントを参照ください。
---
- name: IBM Cloud resource instance
hosts: localhost # Python3を利用するため (インベントリファイルで指定)
connection: local # ローカルで実行
gather_facts: false # 時短
become: false # Playbook実行ユーザーの ~/.local にある jinja2 を利用するためsudoしない
environment:
IC_API_KEY: '{{ ic_api_key }}' # IBM Cloud APIキーを環境変数に設定
collections:
- ibmcloud.ibmcollection # IBM Cloud Ansible Collectionsを利用
続いてタスク定義です。1つ目のタスクで同名のサービスが、指定したリソースグループID及びロケーションに存在するかどうかチェックします。ここでもし存在しないと、このタスクがfailしてしまい、処理が中断します。それでは困るので、結果failでも、指定した名前のサービスが存在しない、というエラーメッセージの時だけは通すよう、failed_whenで指定します。存在する場合は、そのままokで通りますが、その結果を次のタスクで使用するので、registerで保管します。
tasks:
- name: Check resource instance {{ db2.instance_name }}
ibm_resource_instance_info:
name: '{{ db2.instance_name }}'
resource_group_id: '{{ resource_group_id }}'
location: '{{ db2.location }}'
register: check_result
failed_when:
- check_result.failed
- "'Error: No resource instance found with name' not in check_result.stderr"
次のタスクでサービスを作成、更新あるいは削除します。前タスクのチェックでもし存在していた場合は、そのサービスのidをここで指定していますので、既存のサービスを更新または削除することが出来ます。例えば、変数tagsを変更して、Playbookを再実行すると、タグの内容が変更されますし、stateをabsentにすると、サービスが削除されます。
- name: The resource instance {{ db2.instance_name }} is provisioned
ibm_resource_instance:
name: '{{ db2.instance_name }}'
resource_group_id: '{{ resource_group_id }}'
service: '{{ db2.service }}'
plan: '{{ db2.plan }}'
location: '{{ db2.location }}'
# check_result.resource.idが無ければ、このフィールド指定そのものをなかったことにします。tags, stateも同様。
id: '{{ check_result.resource.id | default(omit) }}'
tags: '{{ db2.tags | default(omit) }}'
state: '{{ db2.state | default(omit) }}'
register: provision_result
ちなみに、このPlaybookの例でlocationを変えても、前タスクで重複無しと判断され、idがセットされないので、同じ名前のサービスがもう1つ出来ます。逆に、そのチェックをせずに、例えばus-southにすでにあるサービスのIDを使って、locationをeu-gbに変えてこのタスクを実行すると、なんとまずus-southのサービスを削除してから、eu-gbに改めてサービスを作成する動きをします...(そりゃそうかもしれませんが...ちょっと焦ります)
基本的に以上ですが、Playbook実行の結果何が起きたのか不安になるので、最後にibm_resource_instanceモジュールの実行結果を表示させます。
- name: Show resource instance information
debug:
var: provision_result
Playbook実行
上記を、例えば ibm_resource_instance.yml
に記述し、実行します。
$ ansible-playbook -i inventories/test ibm_resource_instance.yml
PLAY [IBM Cloud resource instance] *************************************************************************************
TASK [Check resource instance db2-test] ********************************************************************************
ok: [localhost]
TASK [The resource instance db2-test is provisioned] *******************************************************************
changed: [localhost]
TASK [Show resource instance information] ******************************************************************************
ok: [localhost] => {
"provision_result": {
"changed": true,
"failed": false,
"rc": 0,
"resource": {
"crn": "略",
"guid": "略",
"id": "略",
"location": "eu-gb",
"name": "db2-test",
"parameters": null,
"plan": "free",
"resource_controller_url": "https://cloud.ibm.com/services/",
"resource_crn": "略",
"resource_group_id": "略",
"resource_group_name": "",
"resource_name": "db2-test",
"resource_status": "active",
"service": "dashdb-for-transactions",
"service_endpoints": null,
"status": "active",
"tags": [
"tag1",
"tag2"
],
"timeouts": null
},
"stderr": "",
"stderr_lines": [],
"stdout": "ibm_resource_instance.db2-test: Creating...\nibm_resource_instance.db2-test: Still creating... [10s elapsed]\nibm_resource_instance.db2-test: Still creating... [20s elapsed]\nibm_resource_instance.db2-test: Creation complete after 23s [id=略]\n\nApply complete! Resources: 1 added, 0 changed, 0 destroyed.\n",
"stdout_lines": [
"ibm_resource_instance.db2-test: Creating...",
"ibm_resource_instance.db2-test: Still creating... [10s elapsed]",
"ibm_resource_instance.db2-test: Still creating... [20s elapsed]",
"ibm_resource_instance.db2-test: Creation complete after 23s [id=略]",
"",
"Apply complete! Resources: 1 added, 0 changed, 0 destroyed."
]
}
}
PLAY RECAP *************************************************************************************************************
localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
その後、IBM Cloudのコンソールでリソース一覧を見ると、作成されていることがわかります。
もう一度同じPlaybookを実行すると、changedは無しですべてokになります。(この瞬間のこの結果を得るためにPlaybookを書いているようなもの。。。)
後始末としては、変数ファイルの state: available
を state: absent
に変更した後、Playbookを実行すると、作成したインスタンスを削除してくれます。
まとめ
サンプルが無い中、かなり試行錯誤しました。また、冪等性を自分で担保しなければいけないので、画面操作なら数分で出来ることに数日悩んでしまいました・・・
ただ、一度仕組みを確率させれば、あとは変数ファイルに必要なリソースをどんどん追加していけば、このPlaybookで使用しているサービス構成を一元管理・・・出来る・・・ようになるには、まだまだ改善の余地はありそうです。
- リソースグループのIDを事前に調べるとかダサい
- リソースグループIDを調べられるモジュールはある
- ただ、defaultとそうでないリソースグループで動きが違ってて、また作り込み発生の匂いが・・・
- リソースグループIDを調べられるモジュールはある
- 有償のサービスで色々試したいが、その度にチャリンチャリン課金されてはたまらない・・・もっとサンプル出して欲しいですね
という、構成管理に向けてまだまだ取っ掛かりの手始めでした。