Ansible は、構成管理ツールとして認知されていますが、AWS 関連のモジュールが多数実装されており、各コンポーネントの起動や設定ができます。
このエントリでは、Ansible で、検証環境用の EC2 インスタンスを起動して、その Public DNS をRoute 53 に登録してみます。
以前書いたこのエントリの内容 を Ansible で自動化するイメージですね。
準備
今回は、AWS を操作するので、Python の AWS SDK である boto をインストールしておきます。boto は、pip なり、yum なりでインストールできます。
- OSX
$ pip install boto
- RHEL / CentOS
$ rpm -ivh http://ftp.riken.jp/Linux/fedora/epel/6/i386/epel-release-6-8.noarch.rpm $ yum -y install python-boto
AWS 認証情報
AWS 認証情報を設定します。
playbook に直接記載する方法もあるのですが、ここでは、boto の設定ファイルである ~/.boto
に記述をします。こうしておけば、playbook では、認証情報を指定する必要がありません。
[Credentials] aws_access_key_id = xxxxxxxxxxxxxxx aws_secret_access_key = xxxxxxxxxxxxxxx
EC2 インスタンスの起動
Ansible で EC2 インスタンスを起動するには、ec2
モジュールを使います。
AWS 関連のモジュールを使う場合、対象のインベントリが指定できない場合がある(対象のホストをこのタスクで生成するので)ので、local connection として実行します。
ec2 モジュールには、EC2 インスタンスを起動するためのパラメータを指定します。下記では、VPC で、t2.micro インスタンスを指定しています。各パラメータについては、EC2 インスタンスを起動する際は、お馴染みのものなので、値を指定していきます。あとで識別できるように Name
タグに ansible1
を設定しておきます。
ここで起動したインスタンスの Public DNS を、Route 53 に登録するので、register
を使って、処理結果をec2
という変数に格納しておきます。
- hosts: localhost gather_facts: no connection: local tasks: - name: Create ec2 instanse ec2: key_name: keyA instance_type: t2.micro image: ami-0xxxxx monitoring: yes wait: yes region: ap-northeast-1 group_id: sg-xxxxxx vpc_subnet_id: subnet-xxxxx assign_public_ip: yes instance_tags: Name: ansible1 register: ec2
Route 53 に Public DNS を CNAME に登録
あらかじめ決められた FQDN でアクセスできるように、先ほど起動したインスタンスの Public DNS を CNAME として Route 53 に登録します。
Route 53 の操作には、route53
モジュールを使います。
EC2 インスタンスの情報は、ec2.instances
に格納されているので、これを利用します。下記では、ansible1.dev.example.com
という FQDN に対して、ec2 の Public DNS を CNAME で割り当てています。
- name: Set Public DNS to CNAME in Route53 route53: command: create zone: dev.example.com type: CNAME value: "{{ item.public_dns_name }}" overwrite: yes record: ansible1.dev.example.com ttl: 300 with_items: ec2.instances
playbook の実行
この playbook を ansible-playbook
コマンドで実行します。
local connection を使うのですが、インベントリファイルが必要になるので、作成しておきます。
$ cat > hosts <EOF 127.0.0.1 EOF
では、実行してみましょう。ansible-playbook コマンドを実行すると、2 つのタスクが処理されました。
$ ansible-playbook -i hosts aws.yml PLAY [localhost] ************************************************************** TASK: [Create ec2 instanse] *************************************************** changed: [localhost] TASK: [Set Public DNS to CNAME in Route53] ********************************************* changed: [localhost] => (item={......}) PLAY RECAP ******************************************************************** localhost : ok=2 changed=2 unreachable=0 failed=0
AWS の Management Console を確認すると、Name タグに ansible1 が設定されたインスタンスが生成されていました。
Route 53 を見ると、想定したいた FQDN の CNAME に EC2 インスタンスの Public DNS が設定されていました。
起動済の EC2 を破棄
EC2 インスタンスを起動して、Route 53 に登録するという流れはできました。
ただ、このままだと、この playbook を実行する度に新しいインスタンスが作成されるので、不要なインスタンスが残り続けます。そこで、今回は検証環境ということで、古いインスタンスは破棄した後に、新しいインスタンスを作るという流れにします。
起動済のインスタンスを破棄するには、稼働中のインスタンス ID を取得する必要があります。インスタンス ID はec2_facts
モジュールを実行します。このモジュールは、インスタンス内で実行するので、インスタンスをインベントリファイルに追加する必要があります。
これを手で行うと、インスタンスを起動するたびにインベントリファイルを書き換えることになるので、Dynamic Inventory を利用します。Dynamic Inventory は、インベントリ情報をスクリプト等で動的に生成することができる仕組みです。これを使うことで、AWS から稼働中のインスタンス情報を取得して、各インスタンスをインベントリとして、タスクを実行することができます。
Ansible のソースコードには、稼働中の EC2 インスタンス情報を取得するスクリプト(plugins/inventory/ec2.py, plugins/inventory/ec2.ini)が含まれているので、これを利用します。
https://github.com/ansible/ansible/blob/devel/plugins/inventory/ec2.py
https://github.com/ansible/ansible/blob/devel/plugins/inventory/ec2.ini
ec2.py はインベントリを取得するスクリプトで、ec2.ini がその設定ファイルです。
まず、ec2.ini の設定を変更します。ec2.py は、デフォルトでは取得した情報をキャッシュ仕組みになっているのですが、今回は実行時に最新の情報を取得したいので、このキャッシュを無効にします。
$ vim ec2.ini cache_max_age = 300 # デフォルトは 300 秒なので、0 にする ↓ cache_max_age = 0
次に、playbook にインスタンス情報を取得するタスクを追加します。このタスクは、先頭に記述しておきます。hosts には、tag_Name_ansible1
を指定しています。これは ec2.py で動的に取得したインベントリの内、タグ Name の値が ansible1 のホストを対象にするということです。このように ec2.py では稼働中の全インスタンスを利用するだけでなく、タグやインスタンスタイプなど様々な切り口でインベントリを絞り込むことができます。
--- - hosts: tag_Name_ansible1 gather_facts: no user: root tasks: - ec2_facts:
つづいて、取得したインスタンス情報を使って、インスタンスを破棄します。インスタンスの破棄には ec2
モジュールを利用します。state=absent
を指定することで、instance_ids
で指定したインスタンスが破棄されます。
下記では、with_items で、インスタンスID を指定しているので、タグ名が ansible1 のインスタンスは全て破棄されます。
- hosts: tag_Name_ansible1 gather_facts: no connection: local tasks: - name: Remove ec2 previous instances ec2: state=absent region=ap-northeast-1 instance_ids={{ item }} wait=true with_items: ansible_ec2_instance_id
完成した playbook の実行
playbook が完成しました。実行してみましょう。
-i
オプションで、ec2.py を指定して、下記のように実行します。
実行すると下記の流れでタスクが実行されていきます。これで、何度実行しても起動しているインスタンスは一つのみになりました。
- タグ名=ansible1 の EC2 インスタンス情報取得
- 1 で取得した EC2 インスタンスを破棄
- EC2 インスタンス作成
- 3 で作成したインスタンスの Public DNS を Route 53 に登録
$ ansible-playbook -i ec2.py aws.yml PLAY [tag_Name_ansible1] ****************************************************** TASK: [ec2_facts ] ************************************************************ ok: [xxx.xxx.xxx.xxx] PLAY [tag_Name_ansible1] ****************************************************** TASK: [Remove ec2 previous instances] ***************************************** changed: [xxx.xxx.xxx.xxx] => (item=i-xxxxxx) PLAY [localhost] ************************************************************** TASK: [Create ec2 instanse] *************************************************** changed: [localhost] TASK: [Set Public DNS to CNAME in Route53] ********************************************* changed: [localhost] => (item={...}) PLAY RECAP ******************************************************************** xxx.xxx.xxx.xxx : ok=2 changed=1 unreachable=0 failed=0 localhost : ok=2 changed=2 unreachable=0 failed=0
最終的な playbook は以下です。
--- - hosts: tag_Name_ansible1 gather_facts: no user: root tasks: - ec2_facts: - hosts: tag_Name_ansible1 gather_facts: no connection: local tasks: - name: Remove ec2 previous instances ec2: state=absent region=ap-northeast-1 instance_ids={{ item }} wait=true with_items: ansible_ec2_instance_id - hosts: localhost gather_facts: no connection: local tasks: - name: Create ec2 instanse ec2: key_name: keyA instance_type: t2.micro image: ami-0xxxxx monitoring: yes wait: yes region: ap-northeast-1 group_id: sg-xxxxxx vpc_subnet_id: subnet-xxxxx assign_public_ip: yes instance_tags: Name: ansible1 register: ec2 - name: Set Public DNS to CNAME in Route53 route53: command: create zone: dev.example.com type: CNAME value: "{{ item.public_dns_name }}" overwrite: yes record: ansible1.dev.example.com ttl: 300 with_items: ec2.instances
さいごに
Ansible で AWS を操作してみました。
インスタンス情報の取得やその情報の利用(インスタンス破棄)などは少しコツが必要ですが、それさえ分かれば、思ったとおりに動作しました。はじめは同じことを Terraform で行っていたのですが、プロビジョンには Ansible を使っていたので、どうせなら Ansible で完結させようと思い、試してみました。
このエントリでは、AWS の操作のみ行っていますが、実際は、インスタンス生成後にプロビジョンやデプロイを行う playbook を挟む形になります。こうすれば、EC2 インスタンス生成、プロビジョン、デプロイが Ansible だけで行うことができます。
- Newer: 「Ansibleではじめるサーバ作業の自動化」を発表してきました
- Older: [書評] CakePHPで学ぶ継続的インテグレーション