EC2 External Inventory Scriptを使って、EC2のDynamic Invetoryを利用してみる
AnsibleでInventoryファイルをイチイチ管理するのがめんどくさい。ということで、「EC2 External Inventory Script」を試してみました。これはAWSのEC2に対するdynamic investoryを作成してくれるAnsibleのスクリプトになります。公式のドキュメントはこちら。
Working With Dynamic Inventory — Ansible Documentation
EC2の情報を収集してくる訳で、その際に利用するIAMユーザを用意しておく必要があります。今回は、IAMポリシー AmazonEC2ReadOnlyAccess
をアタッチしたプログラムアクセス用のIAMユーザを作成しました。
上で作ったIAMユーザを、Ansible実行環境のawscliに、名前付きプロファイルで設定してあげて。
$ aws configure --profile ec2readonly AWS Access Key ID [None]: XXXXXXXXXXXXXXXXXXXX AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Default region name [None]: ap-northeast-1 Default output format [None]:
pythonスクリプト内でbotoを利用するので、botoがいることを確認。
$ python --version Python 2.7.15rc1 $ $ python3 --version Python 3.6.7 $ $ pip3 list | grep boto boto (2.49.0) botocore (1.12.122)
これで事前の準備は終了です。
次に、実際のInventory Scriptをダウンロードしてきます。以下2ファイルをダウンロードしてきます。
$ wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/ec2.py
$ chmod u+x ./ec2.py
$
$ wget wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/ec2.ini
それぞれファイルの説明。
ec2.py
で情報を取得できるかどうか確認してみます。--list
オプションをつけて実行すると、取得結果を表示してくれます。併せて --profile
オプションにawscliプロファイル情報を指定すると、指定したプロファイル情報でawsにアクセスしてくれます。
$ ./ec2.py --list --profile ec2readonly Traceback (most recent call last): File "./ec2.py", line 171, in <module> from ansible.module_utils import ec2 as ec2_utils ImportError: No module named ansible.module_utils
まあ、エラーになっちゃうんですが。僕の場合、OS上にPython2系と3系が共存していて、AnsibleをPython3系側にインストールしたため、この実行方法ではPython2系を見に行ってしまい、「ansibleのモジュールないよ」と言われている状況でした。なので、明示的にPython3系を指定して実行すると、
$ python3 ./ec2.py --list --profile ec2readonly ERROR: "Forbidden", while: getting ElastiCache clusters
まあ、違うエラーになっちゃうんですが。ElastiCacheの情報取得部分でエラーってるようなので、「ElastiCacheの情報なんて取得しなくていいよ」と、 ec2.ini
内でelasticacheの取得設定を除外してあげます。こんな感じですね。
# To exclude ElastiCache instances from the inventory, uncomment and set to False. elasticache = False
その後、改めて実行すると取得できるようになりました。
$ python3 ./ec2.py --list --profile ec2readonly { "_meta": { "hostvars": { "54.250.244.136": { "ansible_host": "54.250.244.136", "ec2__in_monitoring_element": false, ...
毎回Python3であることを明示的に指定するのはだるいので、 ec2.py
のシバン行の記載をPython3系に書き換えてしまいました。こんな感じですね。
#!/usr/bin/env python3
それでは、実際にこのdynamic inventoryの仕組みを利用してansibleを実行してみます。Ansibleであるので、対象サーバにssh接続できないといけない訳ですが、今回はssh_configを用意する方法でなく、静的なinventoryファイルを用意してssh接続する方法を試してみます。
Ansibleのプロジェクトフォルダに、以下のようなフォルダ構成でファイルを用意します。
. └── inventories ├── public │ ├── ec2.ini │ ├── ec2.py │ └── hosts └── private ├── ec2.ini ├── ec2.py └── hosts
まずInternetから、Public IPを持つEC2に対して、グローバルIP向けにssh接続してみます。 inventories/public/hosts
に、以下のようなAnsible Inventory用環境変数を定義します。
[all:vars] ansible_ssh_port=22 ansible_ssh_private_key_file=/mnt/c/_/work/ssh-key/aws/aws-key.pem ansible_ssh_user=ec2-user ansible_ssh_common_args='-o StrictHostKeyChecking=no'
変数名を見ればどんな設定をしているかわかると思いますが、利用できる変数に関する公式マニュアルはこちら。
Working with Inventory — Ansible Documentation
で、ルートのディレクトリから以下の通りコマンド実行すると、ec2.py結果のDynamic Inventoryの情報に対し、静的指定したhostsファイルの情報を付加して、ssh接続をしてくれます。コマンド冒頭にて、 AWS_PROFILE=ec2readonly
にて、ec2.py実行時に利用されるAWSプロファイル情報を指定しています。
$ AWS_PROFILE=ec2readonly ansible -i inventories/public all -m ping 176.34.16.70 | SUCCESS => { "changed": false, "ping": "pong" }
続いて、Internetから踏み台経由でssh接続してみます。
inventories/public/ec2.ini
にて、 vpc_destination_variable
の値を、 private_ip_address
と指定。デフォルトではPublic IPとなる設定になっています。
vpc_destination_variable = private_ip_address
inventories/private/hosts
にて、以下のような踏み台サーバ・アクセス経由での、Ansible Inventory用環境変数を定義します。
[all:vars] ansible_ssh_port=22 ansible_ssh_user=ec2-user ansible_ssh_common_args='-o StrictHostKeyChecking=no -o ProxyCommand="ssh -i /mnt/c/_/work/ssh-key/aws/aws-key.pem -W %h:%p -q ec2-user@176.34.16.70"' [key_aws_key:vars] ansible_ssh_private_key_file=/mnt/c/_/work/ssh-key/aws/aws-key.pem [key_sample_key:vars] ansible_ssh_private_key_file=/mnt/c/_/work/ssh-key/aws/sample-key.pem
ec2.pyでは、EC2で利用されているKeyPair毎にグルーピングしてEC2情報を取得してくれるため、上記のように利用する秘密鍵の情報を複数定義することができます。 ansible_ssh_common_args
で定義したProxyCommandにて、踏み台サーバへの接続設定をしています。
利用するinventoryファルダとしてprivateを指定すれば、接続できるようになります。
$ AWS_PROFILE=ec2readonly ansible -i inventories/private all -m ping 172.31.52.157 | SUCCESS => { "changed": false, "ping": "pong" } 172.31.52.134 | SUCCESS => { "changed": false, "ping": "pong" } 172.31.38.3 | SUCCESS => { "changed": false, "ping": "pong" }
ec2.py
では、色々な単位でEC2をグルーピングして出力してくれ、上記のKeyPairのほか、RegionやAZ、EC2タグ情報などでも管理が可能になります。便利すね。