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
  • ec2.ini
    • 取得するEC2情報に関してのコンフィグファイル

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タグ情報などでも管理が可能になります。便利すね。