[macOS] AWS CLIのインストール
AWS CLIをmacOS 12.1にインストールする手順を備忘録として残しておく。
環境
- HW: MacBook Pro(14"/2021)
- CPU: Apple M1 Max
- MEM: 32GB
- OS: macOS Monterey 12.1
AWS CLIのインストール
AWS公式サイトのインストール手順にしたがってAWS CLIをインストールする。
AWSCLIV2.pkg
をダウンロード:
$ mkdir awscli && cd awscli $ curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
AWSCLIV2.pkg
をインストール:
$ sudo installer -pkg ./AWSCLIV2.pkg -target / Password: ******** installer: Package name is AWS Command Line Interface installer: Installing at base path / installer: The install was successful. $ hash -r $ aws --version aws-cli/2.4.7 Python/3.8.8 Darwin/21.2.0 exe/x86_64 prompt/off $ which aws /usr/local/bin/aws
認証情報の登録
公式サイトのクイック設定ガイドにしたがって、awx configure
コマンドで設定する:
※あらかじめACCESS_KEYとSECRET_KEY情報を取得しておく
$ aws configure AWS Access Key ID [None]: <AWS_ACCESS_KEY> AWS Secret Access Key [None]: <AWS_SECRET_KEY> Default region name [None]: ap-northeast-1 Default output format [None]: json
AWS接続に利用するdefaultプロファイルが$HOME/.aws/
ディレクトリ配下に作成される:
$ cd $HOME $ tree .aws .aws ├── config └── credentials
$HOME/.aws/config:
[default] region = ap-northeast-1 output = json
$HOME/.aws/credentials:
[default] aws_access_key_id = <AWS_ACCESS_KEY> aws_secret_access_key = <AWS_SECRET_KEY>
AWSへのアクセス確認
作成したプロファイルを利用して、awsコマンドでAWSのリソースを操作できることを確認する:
$ aws ec2 describe-instances --output=table --query='Reservations[].Instances[].{InstanceId: InstanceId, Name: Tags[?Key==`Name`].Value|[0]}' ------------------------------------------------------------- | DescribeInstances | +----------------------+------------------------------------+ | InstanceId | Name | +----------------------+------------------------------------+ | i-***************** | hsaito-aap125 | | i-***************** | hsaito-aap210 | +----------------------+------------------------------------+
[macOS] Terraformのインストール
TerraformをmacOS 12.1にインストールする手順を備忘録として書き留めておく。
環境
- HW: MacBook Pro(14"/2021)
- CPU: Apple M1 Max
- MEM: 32GB
- OS: macOS Monterey 12.1
Terraformのインストール
Download Terraformにしたがって、Homebrewのtapを登録してインストールする。
$ brew tap hashicorp/tap $ brew install hashicorp/tap/terraform $ hash -r $ terraform -v Terraform v1.1.2 on darwin_arm64
動作確認
Quick start tutorialにしたがって動作確認を実施。
Docker Desktopの起動と作業ディレクトリの作成:
$ open -a Docker $ mkdir test && cd test
テスト用のマニフェストファイル(main.tf)の作成:
terraform { required_providers { docker = { source = "kreuzwerker/docker" version = "~> 2.15.0" } } } provider "docker" {} resource "docker_image" "nginx" { name = "nginx:latest" keep_locally = false } resource "docker_container" "nginx" { image = docker_image.nginx.latest name = "tutorial" ports { internal = 80 external = 8000 } }
main.tfでTerraformのInitialize:
$ terraform init
terraform init
を実行すると、Terraformのワーキングディレクトリが作成される:
$ tree -a . . ├── .terraform │ └── providers │ └── registry.terraform.io │ └── kreuzwerker │ └── docker │ └── 2.15.0 │ └── darwin_arm64 │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ └── terraform-provider-docker_v2.15.0 ├── .terraform.lock.hcl └── main.tf
Docker Desktopを起動してmain.tfをApplyすると、テスト用のNginxコンテナが起動する:
$ open -a Docker $ terraform apply Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create ... Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes
Nginxコンテナが起動していることを確認:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 74241ca394eb eeb9db34b331 "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:8000->80/tcp tutorial $ curl -o /dev/null -s -w '%{http_code}\n' https://www.google.co.jp 200
テスト環境の削除
main.tfを配置したディレクトリでterraform destroy
コマンドで環境を削除:
$ terraform destroy docker_image.nginx: Refreshing state... [id=sha256:eeb9db34b33190cac170b27d857e9b23511d396a2609c1a54e93025634afed0anginx:latest] docker_container.nginx: Refreshing state... [id=74241ca394eb095063b2cd2565725b0e72108cc4c6c0f5985b507506a3fc12ed] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: - destroy ... Do you really want to destroy all resources? Terraform will destroy all your managed infrastructure, as shown above. There is no undo. Only 'yes' will be accepted to confirm. Enter a value: yes ...
はじめてのAnsibleのモジュール開発
はじめに
Ansibleのモジュールは、ansibleコマンドやansible-playbookコマンドから利用されるスクリプトです。
モジュールは、引数を受け取り、何らかの処理を実行した後に、標準出力にJSON形式で実行結果を出力します。
+---------+ +----------------+ | ansible +---{引数を格納したファイル}--->| module program | +----^----+ +--------+-------+ | | +----------{戻り値:stdout(JSON形式}-----------+
引数
引数は以下のようにハンドリングされます。(※v2系の場合)
- モジュールには、引数としてkey=value形式のパラメータを渡すことができます
- 引数は、パラメータファイル(ファイル名:args)に書き込まれます
- Ansibleは、モジュールスクリプト実行時の引数(sys.argv[1])として、このファイル名を指定することで、パラメータファイル名をモジュールに渡します
- モジュールは、このファイルを実行時に読み込んで、パラメータを利用します
戻り値
JSON形式のデータ構造を標準出力(stdout)に出力することで、モジュールの実行結果をansibleコマンドやansible-playbookコマンドに渡すことができます。
はじめてのAnsibleモジュール
本家のチュートリアルのサンプルモジュール(timetest.py)は、以下のとおりです。このモジュールは、実行環境に対して変更を加えないので、changedパラメータはFalseとして設定しています。
※本家のコードに対してchangedパラメータを追加しています
#!/usr/bin/python import datetime import json date = str(datetime.datetime.now()) print json.dumps({ "changed": False, "time" : date })
このサンプルモジュールは、引数は受け取らず、現在時刻を取得して戻り値として返します。この戻り値は、前述の通りJSON形式である必要があるため、json.dumps()を利用して、データ構造をJSON形式に変換して標準出力に出力しています。
このサンプルモジュール(timetest.py)をカレントディレクトリに配置して、ansibleコマンドを実行してみましょう。
% echo "127.0.0.1 ansible_connection=local" | tee hosts 127.0.0.1 ansible_connection=local % ansible localhost -i hosts -M . -m timetest.py localhost | SUCCESS => { "changed": false, "time": "2017-01-19 14:01:30.328960" }
想定通りの実行結果(現在時刻)が得られました。
モジュールにパラメータを指定する
モジュールに渡されたパラメータは、モジュールと同じディレクトリに“args”というファイル名で配置されます。Ansibleは、モジュール実行時に、このファイル名を最初の引数に指定して実行します。(※v2系の場合)
#!/usr/bin/python """ Description: Sample module for checking how to handle command-line options. """ from __future__ import print_function import json import shlex import sys def main(): result = dict() result['changed'] = False result['message'] = 'Hello, World!' with open(sys.argv[1], 'rb') as args_file: args_buffer = args_file.read() for arg in shlex.split(args_buffer): (key, value) = arg.split('=') if key == 'name': result['message'] = 'Hello, {name:s}!'.format(name=value) print(json.dumps(result)) if __name__ == '__main__': main()
それでは、以下のサンプルモジュール(paramtest.py)をカレントディレクトリに配置して、ansibleコマンドを実行してみましょう。
% ansible localhost -i hosts -M . -m paramtest.py -a name=Hideki localhost | SUCCESS => { "changed": false, "message": "Hello, Hideki!" }
argsファイルの実際の内容は、以下のようにスペースで区切られた key=value形式となっています。このサンプルモジュールも、実行環境に変更を加えないため、changedパラメータはFalseとしています。
_ansible_version=2.2.1.0 _ansible_selinux_special_fs='['"'"'fuse'"'"', '"'"'nfs'"'"', '"'"'vboxsf'"'"', '"'"'ramfs'"'"']' name=Hideki _ansible_module_name=paramtest.py _ansible_verbosity=0 _ansible_syslog_facility=LOG_USER _ansible_diff=False _ansible_debug=False _ansible_check_mode=False _ansible_no_log=False
ここから、キーであるnameの値を取り出して戻り値にセットしています。
実行環境の状態を変化させる
次に、実行環境を変更するモジュールを書いていましょう。このサンプルモジュール(touchtest.py)は、以下のような仕様とします。
- nameパラメータに指定したファイル名で空ファイルを作成する(changed: True)
- nameパラメータに指定したファイル名と同名のファイルが存在していたら何もしない(changed: False)
#!/usr/bin/python """ Description: Sample module for checking how to change your system. """ from __future__ import print_function import json import shlex import os import sys def main(): result = dict() result['changed'] = False filename = None with open(sys.argv[1], 'rb') as args_file: args_buffer = args_file.read() for arg in shlex.split(args_buffer): (key, value) = arg.split('=') if key == 'name': filename = value if not os.path.exists(filename): result['changed'] = True target_fd = open(filename, 'w') target_fd.close() result['filename'] = filename print(json.dumps(result)) if __name__ == '__main__': main()
それでは、以下のサンプルモジュール(touchtest.py)をカレントディレクトリに配置して、ansibleコマンドを実行してみましょう。
まずはカレントディレクトリに存在していないファイル名(hello)を指定します。
% ansible localhost -i hosts -M . -m touchtest.py -a name=hello localhost | SUCCESS => { "changed": true, "filename": "hello" }
作成されたファイル(hello)のステータスを確認してみましょう。
% stat hello 16777220 44124007 -rw-r--r-- 1 saitou staff 0 0 "Jan 19 16:11:25 2017" "Jan 19 16:11:25 2017" "Jan 19 16:11:25 2017" "Jan 19 16:11:25 2017" 4096 0 0 hello
新たに空ファイルが作成されました。この処理では、実行環境に変更を加える(空ファイルが作成される)ため、changedパラメータをTrueに設定しています。
次に、全く同じ作業を実施してみます。
% ansible localhost -i hosts -M . -m touchtest.py -a name=hello localhost | SUCCESS => { "changed": false, "filename": "hello" }
ファイル(hello)のステータスを確認してみましょう。
% stat hello 16777220 44124007 -rw-r--r-- 1 saitou staff 0 0 "Jan 19 16:11:25 2017" "Jan 19 16:11:25 2017" "Jan 19 16:11:25 2017" "Jan 19 16:11:25 2017" 4096 0 0 hello
すでにファイルが存在しているため、os.path.exists(filename)の評価結果がTrueとなり、ファイルに関する操作がスキップされます。この場合は、実行環境が変化しませんから、changedパラメータにFalseをセットしてやります。
このようにして、1つ1つ丁寧にモジュールの中の処理で冪等性を担保してやるわけです。
Ansibleモジュールのお作法にしたがう
Ansibleが推奨しているモジュールの書式にしたがって、touchtest.pyを書き直してみましょう。
モジュールの基本構造
Ansibleでは、モジュールの作成が楽になるよう、さまざまな機能がライブラリとして提供されています。これらを利用すれば、引数のハンドリングや、戻り値のフォーマットなど、モジュールに共通する処理を自身で書く必要がなくなります。
このようなライブラリを利用する場合のモジュール基本構造が、本家のDeveloping Modulesというドキュメントで紹介されています。
from ansible.module_utils.basic import AnsibleModule def main(): module = AnsibleModule( argument_spec=dict( state=dict(default='present', choices=['present', 'absent']), name=dict(required=True), enabled=dict(required=True, type='bool'), something=dict(aliases=['whatever']) ) ) if __name__ == '__main__': main()
お作法にしたがってモジュールを書いてみる
それでは、上記の基本構造を利用して、さきほどのtouchtest.pyを書き直してみましょう。ただ書き直すだけではなく、Ansibleのモジュールらしく、いくつか仕様を追加しています。
- stateパラメータがpresentの場合
- nameパラメータに指定したファイル名で空ファイルを作成する(changed: True)
- nameパラメータに指定したファイル名と同名のファイルが存在していたら何もしない(changed: False)
- stateパラメータがabsentの場合
- nameパラメータに指定したファイルが存在していれば削除する(changed: True)
- nameパラメータに指定したファイルだ存在しなければ何もしない(changed: False)
この仕様にしたがって、書き直したサンプルモジュール(myfile.py)は、以下の通りです。
#!/usr/bin/python # -*- coding: utf-8 -*- """ Description: Sample module for checking how to change your system. """ import os from ansible.module_utils.basic import AnsibleModule def main(): module = AnsibleModule( argument_spec=dict( state=dict(default='present', choices=['present', 'absent']), name=dict(required=True) ) ) state = module.params['state'] filename = module.params['name'] changed = False if state == 'absent': if os.path.exists(filename): changed = True try: os.unlink(filename) except OSError as err: module.fail_json(msg='{err}'.format(err=err)) module.exit_json(changed=changed, filename=filename) if not os.path.exists(filename): changed = True try: target_fd = open(filename, 'w') except IOError as err: module.fail_json(msg='{err}'.format(err=err)) target_fd.close() module.exit_json(changed=changed, filename=filename) if __name__ == '__main__': main()
AnsibleModuleクラスのオブジェクトインスタンスを利用することで、このクラスが提供している、引数やJSON形式での戻り値のハンドリングを行ってくれるメソッドなどが利用できるようになります。
まずは、state==presentから。1回目の実行ではファイルが作成されるので、changed: Trueとなり、2回目の実行では、すでにファイルが存在していて新たに作成されることはありませんから、changed: Falseになっています。
% ansible localhost -i hosts -M . -m myfile.py -a "name=hello state=present" localhost | SUCCESS => { "changed": true, "filename": "hello" } % ansible localhost -i hosts -M . -m myfile.py -a "name=hello state=present" localhost | SUCCESS => { "changed": false, "filename": "hello"
続いてstate==absentで実行してみましょう。1回目の実行ではファイルが削除されてchanged: True、2回目の実行ではファイルが存在していませんから、changed: Falseになっています。
% ansible localhost -i hosts -M . -m myfile.py -a "name=hello state=absent" localhost | SUCCESS => { "changed": true, "filename": "hello" } % ansible localhost -i hosts -M . -m myfile.py -a "name=hello state=absent" localhost | SUCCESS => { "changed": false, "filename": "hello" }
作成や削除に失敗した場合の処理についても、サンプルとして記述してあります。例えばディレクトリに対してstate=absentで削除処理を適用しようとすると、削除処理に失敗して、以下のようなエラーがfail_json()で戻されます。
% file hello hello: directory % ansible localhost -i hosts -M . -m myfile.py -a "name=hello state=absent" localhost | FAILED! => { "changed": false, "failed": true, "msg": "[Errno 1] Operation not permitted: 'hello'" }
AnsibleModuleクラスの機能(ほんの一部)
サンプルモジュール(myfile.py)では、ほんの一部しか利用していませんが、AnsibleModuleクラスは、モジュール開発を支援するために、さまざまな共通機能を提供してくれます。
以下では、myfile.pyで利用している機能のみを紹介します。
モジュールパラメータ処理: AnsibleModule().params
module = AnsibleModule( argument_spec=dict( state=dict(default='present', choices=['present', 'absent']), name=dict(required=True) ) )
AnsibleModuleクラスのオブジェクトインスタンス生成時に、argument_specにモジュールパラメータを渡すことで、argsファイルに記述されているモジュールのオプションパラメータを読み込んで辞書型の変数(params)に格納してくれます。
ここでは、stateとnameという2つのパラメータを取得するように指定していあす。辞書型で指定するオプションパラメータの要件には、以下のようなキーワードを指定できます。
- aliased: 互換性を確保するのに必要なパラメータの別名を記述したリスト
- choices: stateパラメータのように"present"や"absent"などのキーワード指定うためのキーワードリスト
- default: 無指定だった場合のデフォルト値
- required: 指定必須のパラメータかどうか(True or False)
- type: int、bool、listなどのような渡されるパラメータ値の型
モジュール内では、以下のように渡されたオプションパラメータを利用することができます。
state = module.params['state'] filename = module.params['name']
正常終了: AnsibleModule.exit_json(changed, result)
モジュールが正常終了した場合に、JSON形式でメッセージを標準出力して終了します。
module.exit_json(changed=changed, result=filename)
モジュールの動作結果として、実行環境に何らかの変更が加わった場合は、changed_にTrueを、変更がなかった場合はFalseを設定してやります。
resultには、必要に応じて利用者に知らせたい情報を設定します。
異常終了: AnsibleModule.fail_json(msg)
モジュールが異常終了した場合に、JSON形式でメッセージを標準出力して終了します。
module.fail_json(msg='{err}'.format(err=err))
異常終了時に利用者に知らせたい情報を、msgに設定してやります。
冪等性問題
Ansibleは、冪等性が担保されていると良く言われていますが、これもモジュールの作り次第です。
冪等性を実現するために、モジュールの作成者は、AnsibleModuleクラスが提供している共通機能を上手に活用して、冪等性の担保するコードを実装してやる必要があります。
参考情報
OpenStackとAnsible Towerでメリークリスマス
OpenStackとAnsible Towerでメリークリスマス
みなさんメリークリスマス。 この記事は、OpenStack Advent Calendar 2016の25日目の記事です。
OpenStack Advent Calendar 2016 最終日の今日は、OpenStack管理下にインスタンスを作成する際に、Ansible Towerを利用してPlaybookを適用する方法を紹介します。
OpenStackとAnsibleとAnsible Tower
OpenStack
OpenStackは、言わずと知れたクラウド基盤管理システムです。オープンな開発コミュニティ、そして全ての機能を外部から管理可能な、オープンなAPIを持っています。
このAPIを利用することで、従来のオンプレミスな仮想化基盤では難しかった、CI/CDに代表されるツールチェーンのような仕組みをインフラ分野にも適用することが可能となます。いわゆるInfrastructure as Codeを実現するのに、OpenStackが一役買ってくれるという訳ですね。
Ansible
Ansibleは、現代を代表するITオートメーションツールです。コア部分の実体は、単なるコマンドラインツールなのですが、この単純でシンプルな構造が非常に使い勝手が良く、運用の現場での利用が急速に進んでいます。
世の中では利点ばかりが強調されがちですが、シンプルであるがゆえに、Ansibleのコア部分が削ぎ落とした機能がいくつかあります。 実際に運用していると、サーバのオートスケール時の自動構築や、ツールチェーンに組み込みたい場合、そしてシステム監査のような場面で、以下の3点は、なにか対策があるといいのになぁと思ってりしています。
- 外部APIを持っていない
- コマンドの実行権限管理はOS任せ
- Playbookやログなどが分散してしまう
それぞれ、いろいろと考えて対処しているのですが、これを公式に解決してくれるのがAnsible Towerです。
Ansible Tower
Ansible Towerは、Playbookによるジョブ管理システムです。具体的には以下の機能を提供してくれます。現時点では有償ですが、10ノードまで管理できる無償のトライアル版が提供されいますので、今回は、このトライアル版を利用してAnsible TowerとOpenStackの連携方法についてご紹介します。
- コールバック機能も含めた外部API機能
- Gitなどのソフトウェア構成管理システム上に置いたPlaybookを利用したジョブ管理機能
- ジョブ実行ログの集中管理機能
- ユーザ・グループレベルでの実行権限管理
インストール手順は、特に難しということはありません。公式ドキュメントや、@ITのコチラの記事を参照してみてください。
Tower起動させると、こんな感じのリッチなWebダッシュボードから操作できるようになります。
紹介する構成
testserverをデプロイする際に、Ansible Towerの管理下にあるPlaybookをジョブとして実行する方法を紹介します。
172.16.165.0/24 +------+--------------+---------------------+ |.130 |.66 | | | | +-----+----+ +-----+-----+ | router | | OpenStack | +-----+----+ +-----------+ | | |.1 192.168.0.0/24 +-----++---------------+--------------------+ |.15 | | | | | +----+----+ +------+------+ | tower | | testserver | +---------+ +-------------+
ジョブとして実行するPlaybook
playbookは、以下の構成でGitHubに置いて、これをAnsible Towerから利用します。
playbook ├── site.yml └── web ├── files │ ├── index.html │ └── josug2016.png └── tasks └── main.yml
playbookで実行するタスク(playbook/web/tasks/main.yml)は、以下の通りです。パッケージ更新後にnginxをインストールし、コンテンツを配置した後で、最後にnginxをリスタートしています。
--- # tasks file for web - block: - name: update all packages apt: upgrade: yes update_cache: yes - name: install nginx apt: name: nginx update_cache: no - name: copy contents copy: src: "{{ item }}" dest: "/usr/share/nginx/html/{{ item }}" with_items: - index.html - josug2016.png - name: restart nginx service: name: nginx state: restarted become: yes become_user: root
Ansible TowerでのOpenStack連携設定
それでは、Ansible Towerを操作して、実際にOpenStackとの連携をみてみましょう。
Ansible TowerからOpenStack環境を利用するための設定項目は4つあります。
- プロジェクトの作成
- Credentialの作成
- Dynamic Inventoryを利用するInventory Groupの作成
- Playbookを実行するJob Templateの作成
1. プロジェクトの作成
今回のAdvent Calendar用に1つプロジェクトを作成します。
SCM URLには、GitHub上に配置してあるPlaybookのパスを指定しています。
2. Credentialの作成
利用するCredentialは2つあります。
1つは、Dynamic InventoryからOpenStackのAPIを利用するためのopenstackと、
そしてもう1つは、起動したインスタンスにログインしてPlaybookを適用するためのubuntuです。
3. Inventory Groupの作成
OpenStack環境で管理されている仮想マシン群を、Ansibleから操作するためには Dynamic Inventory を利用するのが常套手段です。当然ながらAnsible Towerからも Dynamic Inventory が利用できます。
ここでは、すでに作成済みのCredential(openstack)を利用して、Dynamic Inventory(webservice)を作成しています。
4. Job Templateの作成
ubuntuとopenstackという2つのCredentialとInventoryを指定して、Job Templateを作成します。更に、管理対象ホストからジョブ起動させるトリガーを引けるように、Callbackを許可します。
このCallback URLに、操作対象ホストからcurlなどでアクセスすることにより、ジョブをキックすることができます。
curl --data "host_config_key=設定キー" https://192.168.0.241:443/api/v1/job_templates/15/callback/ --insecure
OpenStack管理下に仮想マシンインスタンスを起動
openstackコマンドで、インスタンスを起動させる際に、user-dataとして実行するスクリプト(userdata.sh)は、以下の通りです。
#!/bin/bash -v curl --data "host_config_key=08e4d419bebc422a2bfacbd4bfd469fb" https://192.168.0.15:443/api/v1/job_templates/15/callback/ --insecure # # [EOF] #
実際に、このuser-dataを利用してインスタンスを起動します。
openstack --os-cloud=josug server create \ --flavor m1.small \ --image trusty-server-cloudimg-amd64 \ --key-name ぼくの鍵 \ --nic net-id=ぼくのネットワークID \ --security-group ぼくのSecGroup \ --user-data ./userdata.sh \ testserver
起動時のTowerとの連携は以下の通りです。
- boot => OpenStack APIを利用して、Computeノード上にインスタンス(testserver)を起動する
- callback => 起動プロセスの最後に実行されるスクリプト(userdata.sh)からAnsible TowerのCallback URLにアクセスしてジョブをキックする
- playbook => Ansible Towerにジョブとして登録されているPlaybookを実行する
client OpenStack Tower testserver(compute) + + + + | 1.boot | | | +----------------------> | | | | | | | | | | | | | 1.boot | | +--------------------------------------------> | | | | | | | | | | | 2.callback | | | <----------------------+ | | | | | | | | | | | 3.playbook | | | +----------------------> | | | | | | | | + + + +
さらに進んだ利用方法
今回は、openstackコマンドのuser-dataオプションから、Callback URLキックするスクリプトを利用しましたが、こちらの記事のように、Heatのテンプレートからも同様にuser-dataに指定することで、操作対象ホストからCallbackでPlaybookをキックできます。オートスケール時の初期設定にも利用できて、さらに便利に使えますね。
クリスマスプレゼント間に合わず
Ansible Towerは、現在のところオープンソースではありません。しかし、Ansible WorksをRed Hatが買収されて以降、Red Hatのポリシーに従って、オープンソース化に向けた努力を続けているそうです。
きっと来年のクリスマスまでには、Ansible Towerがオープンソースソフトウェアとして広く利用できるに違いない!と期待を込めて、このカレンダー記事を書きました。
それでは皆さん、メリークリスマス:-)
cryptographyをpipインストールする際の fatal error: 'openssl/opensslv.h' file not found 対策メモ
OSX環境(10.11.6 Beta(15G12a))で、cryptographyをpipでインストールしようとした際に遭遇したエラーに対する対策メモ。
いまおもえばpastebinにでも貼っておいたほうがよかった。
問題
brewでインストールしたOpenSSLのヘッダファイル(opensslv.h)を見つけてくれず、コンパイル時にエラーとなる。
% pip install cryptography Collecting cryptography Using cached cryptography-1.4.tar.gz <...> building '_openssl' extension creating build/temp.macosx-10.11-intel-2.7/build creating build/temp.macosx-10.11-intel-2.7/build/temp.macosx-10.11-intel-2.7 cc -fno-strict-aliasing -fno-common -dynamic -arch i386 -arch x86_64 -g -Os -pipe -fno-common -fno-strict-aliasing -fwrapv -DENABLE_DTRACE -DMACOSX -DNDEBUG -Wall -Wstrict-prototypes -Wshorten-64-to-32 -DNDEBUG -g -fwrapv -Os -Wall -Wstrict-prototypes -DENABLE_DTRACE -arch i386 -arch x86_64 -pipe -I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c build/temp.macosx-10.11-intel-2.7/_openssl.c -o build/temp.macosx-10.11-intel-2.7/build/temp.macosx-10.11-intel-2.7/_openssl.o build/temp.macosx-10.11-intel-2.7/_openssl.c:423:10: fatal error: 'openssl/opensslv.h' file not found #include <openssl/opensslv.h> ^ 1 error generated. error: command 'cc' failed with exit status 1 ---------------------------------------- Failed building wheel for cryptography Failed to build cryptography Installing collected packages: cryptography Running setup.py install for cryptography Complete output from command /Users/saitou/venv/2.7.10/ansible/bin/python -c "import setuptools, tokenize;__file__='/private/var/folders/kk/ftd09wdd0sz27nbm8zwzjsdh0000gn/T/pip-build-u8oOQQ/cryptography/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /var/folders/kk/ftd09wdd0sz27nbm8zwzjsdh0000gn/T/pip-d3jDDM-record/install-record.txt --single-version-externally-managed --compile --install-headers /Users/saitou/venv/2.7.10/ansible/include/site/python2.7/cryptography: <...> building '_openssl' extension cc -fno-strict-aliasing -fno-common -dynamic -arch i386 -arch x86_64 -g -Os -pipe -fno-common -fno-strict-aliasing -fwrapv -DENABLE_DTRACE -DMACOSX -DNDEBUG -Wall -Wstrict-prototypes -Wshorten-64-to-32 -DNDEBUG -g -fwrapv -Os -Wall -Wstrict-prototypes -DENABLE_DTRACE -arch i386 -arch x86_64 -pipe -I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c build/temp.macosx-10.11-intel-2.7/_openssl.c -o build/temp.macosx-10.11-intel-2.7/build/temp.macosx-10.11-intel-2.7/_openssl.o build/temp.macosx-10.11-intel-2.7/_openssl.c:423:10: fatal error: 'openssl/opensslv.h' file not found #include <openssl/opensslv.h> ^ 1 error generated. error: command 'cc' failed with exit status 1 ---------------------------------------- <...>
対策
本家のHomebrewの項に書いてあった...
% brew install openssl % env CRYPTOGRAPHY_OSX_NO_LINK_FLAGS=1 LDFLAGS="$(brew --prefix openssl)/lib/libssl.a $(brew --prefix openssl)/lib/libcrypto.a" CFLAGS="-I$(brew --prefix openssl)/include" pip install cryptography
恐らく一週間後には覚えてなさそうなのでメモしておく。
きょうのdevstack用local.conf - 2016.5.13時点でのmaster向け
Environment
VMware FusionのGuestOSにUbuntu16.04をインストールし、その上にdevstackでOpenStack開発環境を構築する。
- GuestOS: ubuntu 16.04lts
- VMware Fusion 8.1.1
- SPEC: vCPU x 2 / MEM 8GB / HDD 100GB / Network VMNET12,13
注意)GuestOSのメモリは少なくとも4GBは確保しておく。それ以下のサイズだとOOM Killerが発動し、mysqldあたりからkillされていく...
インストールされるコンポーネント
コアコンポーネント
- cinder
- glance
- keystone
- neutron
- nova
- swift
その他のBigTentコンポーネント
- barbican
- ceilometer
- designate
- heat
- magnum
- murano
local.conf
- 管理用NIC: ens33
- サービス用NIC: ens34
- PASSWORD,VERSION,PUBLIC_INTERFACEは環境に応じて変更する
- 2回目以降はOFFLINE=True,RECLONE=Falseで実行すれば時間短縮!
[[local|localrc]] #OFFLINE=True RECLONE=True HOST_IP=10.0.12.3 SERVICE_HOST=$HOST_IP MYSQL_HOST=$HOST_IP RABBIT_HOST=$HOST_IP GLANCE_HOSTPORT=$HOST_IP:9292 PASSWORD=changeme ADMIN_PASSWORD=$PASSWORD DATABASE_PASSWORD=$PASSWORD RABBIT_PASSWORD=$PASSWORD SERVICE_PASSWORD=$PASSWORD SWIFT_HASH=JRBj7ukxMG4tckek VERSION=master CEILOMETER_BRANCH=$VERSION CINDER_BRANCH=$VERSION GLANCE_BRANCH=$VERSION KEYSTONE_BRANCH=$VERSION HEAT_BRANCH=$VERSION HEAT_CFNTOOLS_BRANCH=$VERSION HEAT_TEMPLATES_BRANCH=$VERSION HORIZON_BRANCH=$VERSION NEUTRON_BRANCH=$VERSION NOVA_BRANCH=$VERSION SWIFT_BRANCH=$VERSION # Log settings LOGDIR=$DEST/logs SCREEN_LOGDIR=$LOGDIR SCREEN_HARDSTATUS="%{= rw} %H %{= wk} %L=%-w%{= bw}%30L> %n%f %t*%{= wk}%+Lw%-17< %-=%{= gk} %y/%m/%d %c" LOGFILE=$LOGDIR/devstack.log LOGDAYS=1 LOG_COLOR=False # Neutron settings disable_service n-net ENABLED_SERVICES+=,q-svc,q-dhcp,q-meta,q-agt,q-l3,q-lbaasv2,q-fwaas,q-vpn,q-qos,q-flavors,octavia,o-cw,o-hk,o-hm,o-api # Ceilometer settings ENABLED_SERVICES+=,ceilometer-acompute,ceilometer-acentral,ceilometer-collector,ceilometer-api # Swift settings ENABLED_SERVICES+=,s-proxy,s-object,s-container,s-account # Heat settings ENABLED_SERVICES+=,heat,h-api,h-api-cfn,h-api-cw,h-eng # Ceilometer settings enable_plugin ceilometer https://git.openstack.org/openstack/ceilometer # Magnum settings enable_plugin magnum https://git.openstack.org/openstack/magnum $VERSION enable_plugin magnum-ui https://git.openstack.org/openstack/magnum-ui $VERSION # Barbican settings enable_plugin barbican https://git.openstack.org/openstack/barbican $VERSION # Designate settings enable_plugin designate https://git.openstack.org/openstack/designate $VERSION ENABLED_SERVICES+=,designate,designate-central,designate-api,designate-pool-manager,designate-zone-manager,designate-mdns # Murano settings MURANO_APPS=io.murano.apps.apache.Tomcat,io.murano.apps.Guacamole enable_plugin murano https://git.openstack.org/openstack/murano $VERSION ENABLED_SERVICES+=,murano-cfapi,g-glare ## Neutron options Q_USE_SECGROUP=True FLOATING_RANGE="10.0.13.0/24" FIXED_RANGE="192.168.0.0/24" NETWORK_GATEWAY="192.168.0.1" Q_FLOATING_ALLOCATION_POOL=start=10.0.13.129,end=10.0.13.254 PUBLIC_NETWORK_GATEWAY="10.0.13.2" Q_L3_ENABLED=True PUBLIC_INTERFACE=ens34 # Open vSwitch provider networking configuration Q_USE_PROVIDERNET_FOR_PUBLIC=True OVS_PHYSICAL_BRIDGE=br-ex PUBLIC_BRIDGE=br-ex OVS_BRIDGE_MAPPINGS=public:br-ex # # [EOF] #
OpenStackとAnsibleと私
OpenStack Advent Calendar 2015とAnsible Advent Calendar 2015の12月25日の記事です。
沖縄でハンズオンをやってきました
12/14-18で開催されたOkinawa Open Days 2015で、OpenStackとAnsibleとDockerを組み合わせたハンズオンセミナーをやってきました。
OpenStackとAnsibleのAdvent Calendarの最終日としてふさわしいかどうかわかりませんが、ハンズオンに向けての準備で得られた知見を、ここにメモしておきたいと思います。
"OpenStack"と"Ansible v2"
Ansibleは標準でOpenStackを操作するためのモジュールを持っています。 このモジュールを利用すれば、OpenStackが提供するAPIを利用して、仮想マシンインスタンス、ネットワーク、ボリュームなどの仮想リソースの管理をAnsibleのPlaybookを利用して実施することができます。 また、OpenStack管理下にある仮想マシンインスタンス群の管理を容易にするために、ダイナミックインベントリも提供しています。 このOpenStack向けに提供される標準モジュールとダイナミックインベントリは、v2で大きく進化しており、v1向けのモジュールよりも簡単便利に使えるようになりました。
Ansible v2向けモジュールの進化
v2向けのOpenStack用モジュールは、github上にある公式リポジトリから入手可能です。
従来のv1向けのモジュールやダイナミックインベントリは、OpenStackの各コンポーネントが提供するクライアントライブラリ(python-novaclientやpython-neutronclientなど)を直接利用していましたが、v2向けのモジュールは、shadeライブラリを利用するよう全面的に刷新されました。
v1向けOpenStack用モジュールの特徴
例えば、nova-computeモジュールでは、python-novaclientを直接利用していました。 OpenStackの各コンポーネントが提供するクライアントモジュール(pyhon-*client)を直接利用しており、OpenStack環境へのログイン情報をパラメータとして指定する必要がありました。 さらに、OpenStackのクライアントモジュールはコンポーネント毎に認証方式が微妙に違っており、この差異はAnsibleのモジュール側の実装で吸収していました。
例) nova_computeモジュール
+--------------+ +-------------------+ +-----------+
| nova_compute +----+ python-novaclient +==(API)=>+ openstack |
+--------------+ +-------------------+ +-----------+
例) quantum_networkモジュール
+-----------------+ +----------------------+ +-----------+
| neutron_network +----+ python-neutronclient +==(API)=>+ openstack |
+-----------------+ +----------------------+ +-----------+
v2向けOpenStack用モジュールで改善された点
v2向けのモジュールでは、shadeライブラリを利用しています。shadeはOpenStackクライアントライブラリ(python-*client)の上に一枚被さる形で各クライアントライブラリの認証部分など共通に利用する部分の差異を吸収して抽象化したレイヤーを、上位のプログラムに対して提供してくれます。 さらに、v1時代のモジュールのように、OpenStackへの接続情報をPlaybookに書く必要はなく、外部の設定ファイルに複数のOpenStack環境の認証情報を記述して実行時に選択することで、接続先を切り替えるも可能です。
+--------------+ +-------+ +----------------+ +-----------+
| os_* modules +----+ shade +----+ python-*client +==(API)=>+ openstack |
+--------------+ +---+---+ +----------------+ +-----------+
|
+--------+---------+ +-------------------------------------+
| os-client-config +----+ $HOME/.config/openstack/clouds.yaml |
+------------------+ +-------------------------------------+
shadeが利用しているos-client-configについては、OSS とクラウドの狭間で: 環境変数1つで OpenStack 環境を切り替える os-client-config で、もときさんが詳しく紹介してくれています。必読です。
v1向けモジュールのClientクラスインスタンスの初期化
nova_computeモジュールと、quantum_networkモジュールの例を以下に紹介します。ログイン情報をモジュールパラメータとして指定する必要があります。 さらにpython-novaclientとpython-neutronclientのClientクラスインスタンスは、生成する際の引数が微妙に違います。
nova_compute.py
...
41: options:
42: login_username:
43: description:
44: - login username to authenticate to keystone
45: required: true
45: default: admin
46: login_password:
47: description:
48: - Password of login user
49: required: true
50: default: 'yes'
51: login_tenant_name:
52: description:
53: - The tenant name of the login user
54: required: true
55: default: 'yes'
...
534: def main():
...
572: nova = nova_client.Client(module.params['login_username'],
573: module.params['login_password'],
574: module.params['login_tenant_name'],
575: module.params['auth_url'],
576: region_name=module.params['region_name'],
577: service_type='compute')
...
quantum_network.py
...
37: options:
38: login_username:
39: description:
40: - login username to authenticate to keystone
41: required: true
42: default: admin
43: login_password:
44: description:
45: - Password of login user
46: required: true
47: default: 'yes'
48: login_tenant_name:
49: description:
50: - The tenant name of the login user
51: required: true
52: default: 'yes'
...
130: def _get_ksclient(module, kwargs):
131: try:
132: kclient = ksclient.Client(username=kwargs.get('login_username'),
133: password=kwargs.get('login_password'),
134: tenant_name=kwargs. get('login_tenant_name'),
135: auth_url=kwargs.get('auth_url'))
...
v2向けモジュールのClientクラスインスタンスの初期化
v2向けモジュールでは、認証情報をパラメータとして直接モジュールに渡す必要はありません。shadeがos-client-configを利用して設定ファイルを取得して利用してくれるからです。 ちなみに、何もしなくてもopenrcなどによって反映された環境変数(OS_USERNAMEなど)を接続情報として利用してくれます。なにこれ便利すぎる!
v2向けのOpenStack操作用モジュール(os_*モジュール)は、認証情報をパラメータとしてセットする必要がありません。さらに、内部的にはnovaを操作する場合(例:os_nova_flavorモジュール)も、neutronを操作する場合(例:os_portモジュール)も同一の方法で利用することができます。
os_nova_flavor.py
...
201: cloud = shade.operator_cloud(**module.params)
...
os_port.py
...
336: cloud = shade.openstack_cloud(**module.params)
...
設定ファイル($HOME/.config/openstack/clouds.yaml)
以下の例では、libertyとdevstackの2つの環境を定義しており、os_*モジュールのcloudパラメータに環境名(ここではlibertyまたはdevstack)を指定することで、接続先となるOpenStack環境を切り替えることが可能です。 また、cloudパラメータに何もしていしなかった場合は、openrcなどで反映されている環境変数(OS_USERNAMEなど)が利用されます。
clouds:
liberty:
auth:
auth_url: http://192.168.0.1:5000
username: saitou
password: changeme
project_domain_id: default
user_domain_id: default
project_name: SYSTEM00
identity_api_version: '3'
devstack:
auth:
auth_url: http://172.16.31.1:35357
username: admin
password: changeme
project_domain_id: default
user_domain_id: default
project_name: admin
identity_api_version: '3'
region_name: RegionOne
利用方法
この例では、os_*モジュールにはcloudパラメータを指定していません。 この状態だと、ansible-playbookコマンドの実行環境で反映されている環境変数(OS_USERNAMEなど)が接続情報として利用されます。
- 公開鍵を登録する(os_keypairモジュール)
- セキュリティグループを作成する(os_security_groupモジュール)
- セキュリティグループにルールを登録する(os_security_group_ruleモジュール)
- 仮想マシンインスタンスを起動する(os_serverモジュール)
- 仮想マシンインスタンスにフローティングIPアドレスを割り当てる(quantum_floating_ipモジュール)
比較のために利用しているquantum_floating_ipモジュールはv1向けのモジュールです。v1向けのモジュールは、実行時にOpenStack環境への接続情報がパラメータとして必要となるため、わざわざ環境変数から取得するようvarsセクションでlookup()しています。
Playbook: create_instance.yml
---
- hosts: localhost
vars:
ansible_python_interpreter: /home/centos/handson/bin/python
os_auth_url: "{{ lookup('env','OS_AUTH_URL') }}"
os_username: "{{ lookup('env','OS_USERNAME') }}"
os_password: "{{ lookup('env','OS_PASSWORD') }}"
os_project_name: "{{ lookup('env','OS_TENANT_NAME') }}"
os_region_name: "{{ lookup('env','OS_REGION_NAME') }}"
keypairs:
- name: "step-server"
public_key_file: "/home/centos/.ssh/id_rsa.pub"
secgroups:
- name: "eplite"
desc: "secgroup for eplite"
rules:
- protocol: "icmp"
port_range_min: -1
port_range_max: -1
remote_ip_prefix: "0.0.0.0/0"
- protocol: "tcp"
port_range_min: 22
port_range_max: 22
remote_ip_prefix: "0.0.0.0/0"
- protocol: "tcp"
port_range_min: 80
port_range_max: 80
remote_ip_prefix: "0.0.0.0/0"
- protocol: "tcp"
port_range_min: 3306
port_range_max: 3306
remote_ip_prefix: "0.0.0.0/0"
servers:
- name: "{{ hostname }}"
key_name: "step-server"
flavor: "m1.small"
image: "Docker01"
secgroups:
- "eplite"
nics:
- net-name: "work-net"
auto_ip: no
ext_net: "ext-net_1214"
int_net: "work-net"
tasks:
- name: import keypairs
os_keypair:
state=present
name="{{ item.name }}"
public_key_file="{{ item.public_key_file }}"
with_items: keypairs
- name: create security group
os_security_group:
state=present
name="{{ item.name }}"
description="{{ item.desc}}"
with_items: secgroups
- name: add rules to secgroup
os_security_group_rule:
state=present
security_group="{{ item[0].name }}"
protocol="{{ item[1].protocol }}"
port_range_min="{{ item[1].port_range_min }}"
port_range_max="{{ item[1].port_range_max }}"
remote_ip_prefix="{{ item[1].remote_ip_prefix }}"
with_subelements:
- secgroups
- rules
- name: create servers
os_server:
state: present
timeout: 200
name: "{{ item.name }}"
key_name: "{{ item.key_name }}"
flavor: "{{ item.flavor }}"
image: "{{ item.image }}"
security_groups: "{{ item.secgroups }}"
nics: "{{ item.nics }}"
auto_ip: "{{ item.auto_ip }}"
with_items: servers
- name: create and assign floating_ip to server
quantum_floating_ip:
state=present
login_username="{{ os_username }}"
login_password="{{ os_password }}"
login_tenant_name="{{ os_project_name }}"
network_name="{{ item.ext_net }}"
instance_name="{{ item.name }}"
internal_network_name="{{ item.int_net }}"
with_items: servers
Playbookを実行してみる
OpenStackモジュールを利用する前に、まずはshadeのインストールをしなければなりません。ここではpipを利用してvirtualenv環境にインストールしていますが、みなさんの環境にあわせてインストールしておいてください。
$ cd ~ && virtualenv develop
(develop)$ pip install shade functools32
-eオプションで指定したホスト名で、仮想マシンインスタンスを作成します。
$ source openrc
$ ansible-playbook -i ansible_hosts -e "hostname=web00" create_instance.yml
OpenStack管理機能を提供するv2向けモジュール
仮想マシンインスタンスを起動するという目的を達成するために、必要最低限の機能を提供していたv1向けモジュールに対して、v2向けのモジュールは大幅に機能追加されています。 これらv2向けモジュールのほとんど(すべて検証したわけではないけれど)は、Ansible v1.9.4でも正常動作するため、みなさんのv1環境でも恩恵を受けることができます。
是非試してみてください。
v1向けOpenStack用モジュールリスト
- glance_image.py
- keystone_user.py
- nova_compute.py
- nova_keypair.py
- quantum_floating_ip_associate.py
- quantum_floating_ip.py
- quantum_network.py
- quantum_router_gateway.py
- quantum_router_interface.py
- quantum_router.py
- quantum_subnet.py
v2向けOpenStack用モジュールリスト
- os_auth.py
- os_client_config.py
- os_floating_ip.py
- os_image_facts.py
- os_image.py
- os_ironic_node.py
- os_ironic.py
- os_keypair.py
- os_network.py
- os_networks_facts.py
- os_nova_flavor.py
- os_object.py
- os_port.py
- os_router.py
- os_security_group.py
- os_security_group_rule.py
- os_server_actions.py
- os_server_facts.py
- os_server.py
- os_server_volume.py
- os_subnet.py
- os_subnets_facts.py
- os_user_group.py
- os_user.py
- os_volume.py
刷新されたダイナミックインベントリプログラム
Ansibleのソースコードには、ダイナミックインベントリのサンプルプログラムが含まれています。v2のソースコードにも、もちろんOpenStack環境用のダイナミックインベントリプログラムが含まれているのですが、今回はこのプログラムも全面刷新され、shadeを利用するようになりました。
v1時代のOpenStack環境用ダイナミックインベントリプログラムは、あくまでもサンプル的な内容でしたが、v2は違います。一度取得した情報を一定時間キャッシュして再利用することで、問い合わせ回数を削減するなどの工夫がされており、そのまま特に手を加えることなく利用できるレベルの仕上がりで、足りない機能を自身で追加するにしても、コードがシンプルで改修もしやすくなっています。
実際に利用してみる
まずは、上記サイトからダイナミックインベントリプログラムと設定ファイルを入手します。
※以降、すでにshadeはインストール済みであるものと仮定します。
テスト環境
ここでは以下のような環境を例に、ダイナミックインベントリプログラムの利用例を紹介します。
- OpenStackコントローラ: 10.0.0.2
- ユーザ: saitou
- パスワード: changeme
- ドメイン: default
- プロジェクト名: OOD2015
- 環境名: liberty
設定ファイルの取得と編集
(develop)$ sudo mkdir /etc/ansible/ (develop)$ cd /etc/ansible (develop)$ sudo wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/openstack.yml
/etc/ansible/openstack.yml
clouds:
liberty:
auth:
auth_url: http://10.0.0.2:5000
username: saitou
password: changeme
project_domain_id: default
user_domain_id: default
project_name: OOD2015
identity_api_version: '3'
ダイナミックインベントリプログラムの取得とテスト実行
公式サイトからダイナミックインベントリプログラムを取得して、実行権を付与しておきます。
(develop)$ cd ~
(develop)$ wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib /inventory/openstack.py
(develop)$ chmod +x openstack.py
早速テストしてみます。
(develop)$ ./openstack.py --list
実行結果は以下の通り。ちゃんと _meta も使ってくれています。素敵!
{
"": [
"9da2a418-61bd-4118-8eac-1e3d4d639c73",
"9da2a418-61bd-4118-8eac-1e3d4d639c73",
"ea5bd227-3b9c-4a43-80ea-8feb34eb6eb5",
"ea5bd227-3b9c-4a43-80ea-8feb34eb6eb5",
"f40e9f60-2f9c-4405-99dc-50f37b988ef6",
"f40e9f60-2f9c-4405-99dc-50f37b988ef6"
],
"_meta": {
"hostvars": {
"9da2a418-61bd-4118-8eac-1e3d4d639c73": {
"ansible_ssh_host": "10.0.1.143",
"openstack": {
"HUMAN_ID": true,
"NAME_ATTR": "name",
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "nova",
"OS-EXT-STS:power_state": 4,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "stopped",
"OS-SRV-USG:launched_at": "2015-12-06T07:17:22.000000",
"OS-SRV-USG:terminated_at": null,
"accessIPv4": "10.0.1.143",
"accessIPv6": ""
...以下略...
これをansibleから利用してみます。まずはallで全インスタンスにpingモジュールを適用してみる。
(develop)$ ansible all -i ./openstack.py -m ping -u centos
ea5bd227-3b9c-4a43-80ea-8feb34eb6eb5 | success >> {
"changed": false,
"ping": "pong"
}
9da2a418-61bd-4118-8eac-1e3d4d639c73 | success >> {
"changed": false,
"ping": "pong"
}
f40e9f60-2f9c-4405-99dc-50f37b988ef6 | success >> {
"changed": false,
"ping": "pong"
}
Ansibleのダイナミックインベントリは、ホスト名やフレーバなどの情報を元に自動でグループを作ってくれるようなので、これもテストしてみます。
(develop)$ ansible step -i ./openstack.py -m ping -u centos
9da2a418-61bd-4118-8eac-1e3d4d639c73 | success >> {
"changed": false,
"ping": "pong"
}
(develop)$ ansible docker7 -i ./openstack.py -m ping -u centos
ea5bd227-3b9c-4a43-80ea-8feb34eb6eb5 | success >> {
"changed": false,
"ping": "pong"
}
ホスト名でのグループ指定もできてる!便利! 実はshadeはAnsibleのダイナミックインベントリを処理するため(としか思えない)に利用できるOpenStackInventoryクラスを持っています。 Ansible v2に含まれるダイナミックインベントリは、このクラスを利用しているため、v1用と比較して、コード自体が非常に簡潔になっておりカスタマイズもしやすくなっていますので、カスタマイズベースとしても使う価値ありです!
まとめ
Ansible v2では、OpenStack環境との親和性が飛躍的に高まりました。 OpenStackモジュールは刷新されており、shadeのおかげでシンプルでわかりやすい構造となっています。 OpenStack環境でAnsibleを利用しているひとは、切り替える価値が十分ありますので、是非試してみてください。
とは言っても
肝心のv2ってまだリリースされてないですよねぇ サンタはリリース情報をもって来てくれないかもしれないですが、みなさん素敵なクリスマスを:-)