LoginSignup
4
2

More than 3 years have passed since last update.

IBM Cloud Collection for Ansible を用いた IBM Cloud の構成管理(まずは手始めに)

Last updated at Posted at 2020-04-09

はじめに

参考にさせていただいた記事にもありますように、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行定義を追加します。

ansible.cfg
[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とあるので、おそらくこれでよいかと・・・)

inventories/test/hosts
localhost ansible_python_interpreter=/usr/bin/python3

変数:APIキー

IBM CloudのAPIキーを記載する変数ファイルを用意します。例えば group_vars/all/id.yml などに以下のように記載します。(そして本来はAnsible Vaultを用いて、本ファイルを暗号化しておくことになります)

group_vars/all/id.yml
ic_api_key: xxxxxxxxxxxxxxxxxxxxx

変数:その他

APIキー以外を定義する変数ファイル、例えば group_vars/all/resource.yml に必要な定義を追加していきます。

まずサービスを配置するリソースグループを定義します。ここでは、resource_group_name ではなく、resource_group_id を指定する必要があります。使用するリソースグループのIDを、ibmcloudコマンド ibmcloud resource groups で確認して、以下のように変数ファイルに定義します。

group_vars/all/resource.yml
resource_group_id: xxxxxxxxxxxxx

続いて、同じファイルに Db2 lite オーダーに必要な情報を定義します。詳細はコメントを参照ください。

group_vars/all/resource.yml
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冒頭で、必要な定義を行います。詳細はコメントを参照ください。

ibm_resource_instance.yml
---
- 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で保管します。

ibm_resource_instance.yml
  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にすると、サービスが削除されます。

ibm_resource_instance.yml
    - 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モジュールの実行結果を表示させます。

ibm_resource_instance.yml
    - 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のコンソールでリソース一覧を見ると、作成されていることがわかります。

image.png

もう一度同じPlaybookを実行すると、changedは無しですべてokになります。(この瞬間のこの結果を得るためにPlaybookを書いているようなもの。。。)

後始末としては、変数ファイルの state: availablestate: absent に変更した後、Playbookを実行すると、作成したインスタンスを削除してくれます。

まとめ

サンプルが無い中、かなり試行錯誤しました。また、冪等性を自分で担保しなければいけないので、画面操作なら数分で出来ることに数日悩んでしまいました・・・

ただ、一度仕組みを確率させれば、あとは変数ファイルに必要なリソースをどんどん追加していけば、このPlaybookで使用しているサービス構成を一元管理・・・出来る・・・ようになるには、まだまだ改善の余地はありそうです。

  • リソースグループのIDを事前に調べるとかダサい
    • リソースグループIDを調べられるモジュールはある
      • ただ、defaultとそうでないリソースグループで動きが違ってて、また作り込み発生の匂いが・・・
  • 有償のサービスで色々試したいが、その度にチャリンチャリン課金されてはたまらない・・・もっとサンプル出して欲しいですね

という、構成管理に向けてまだまだ取っ掛かりの手始めでした。

参考にさせていただいた記事

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2