はじめに
ELB配下のサーバをメンテナンス時にデタッチしてまたアタッチするという運用をされている方は多いと思います。
ELBへのデタッチ/アタッチ操作をメンテナンスのたびに行うこと自体、モダンではないことはわかっていますが、そんな温かみのある運用をすぐに捨てられない以上、操作をAnsbleで自動化することにより、手作業やコマンドのコピペミスによる事故を防ぐ効果はあると思います。
SSL証明書の関係で1つのEC2インスタンスが複数のELBにアタッチされている場合などは特に威力を発揮するかと。
今回はとりあえずCLBです。
概要
- ELB(CLB)に定義したEC2インスタンスをアタッチ
- 定義したEC2インスタンスと実際にアタッチされているEC2インスタンスの差分を取得
- 上記差分EC2インスタンスをELB(CLB)からデタッチ
- 結果を出力
ポイント
- varsにはELBにアタッチしたいEC2インスタンスIDを定義しておく
- デタッチの際は一時的にコメントアウトする
- AnsibleによるEC2インスタンスのアタッチは冪等性が担保されるため、実行することで定義した通りになる
- 定義と実際のELBにアタッチされたEC2インスタンスの差分=デタッチすべきEC2インスタンスとなる
注意
EC2インスタンスが1つも定義されていない場合は、アタッチ処理でエラーとなります。
エラーハンドリングを実装していないためですが、実際の運用では事故になるため、あえて実装していません。
前提
- AWS関連のモジュール実行にはbotoが必要です。
- Ansible環境はvirtualenvで用意することとします。
- credential情報は環境変数でセットしてある必要があります。
sample
ディレクトリ構成
site.yml
inventories/
|--aws
roles/
|--elb/
| |--tasks/
| | |--main.yml
group_vars/
|--group.yml
inventory
インベントリにはターゲットホストを記載しますが、AWSモジュールはlocalhostで実行されるため全てansible_host=localhost
となります。
グループ、ホスト名は実行時に読み込まれるgroup_vars,host_vars(今回は未使用)を切り替えるために利用します。
varsに記載したELB全てに対して処理を実行するため、作業単位にグループを分けるといいでしょう。
ansible_python_interpreter=python
は、virtualenv環境でpip install
したboto
モジュールを読み込むために指定しています。
[all:vars]
ansible_connection=local
ansible_python_interpreter=python
ansible_host=localhost
[elb_production]
production
[elb_staging]
staging
vars
こんな感じに変数を定義します。
---
my_vars:
aws:
common:
region: ap-northeast-1
elb:
- name: production_elb01
instances:
- i-XXXXXXXXXXXXXXXXX #server01
- i-XXXXXXXXXXXXXXXXX #server02
- name: production_elb02
instances:
- i-XXXXXXXXXXXXXXXXX #server01
- i-XXXXXXXXXXXXXXXXX #server02
Role
- まず、varsに定義されたEC2インスタンスをそれぞれのELBにアタッチします。すでにアタッチ済みのものはAnsibleによりスキップされます。
- 次に、
ec2_elb_facts
モジュールにより、ELBにアタッチされているEC2インスタンス情報を取得します。 - 取得したEC2インスタンス情報と、varsに定義されたEC2インスタンスとの差分を
difference
フィルタで取得し、elb_diff_list
を作成します。これらは、ELBにアタッチされているが、varsには定義されていないEC2インスタンスなのでデタッチ対象となります。 -
elb_diff_list
に基づいてデタッチを実行します。 - 再度
ec2_elb_facts
モジュールにより、ELBにアタッチされているEC2インスタンス情報を取得します。 -
debug
モジュールで結果を出力します。
---
- name: ELBにインスタンスをアタッチ
ec2_elb:
instance_id: "{{ item.1 }}"
ec2_elbs: "{{ item.0.name }}"
state: present
with_subelements:
- "{{ my_vars.aws.elb }}"
- instances
- name: Gathering ELB facts
ec2_elb_facts:
region: "{{ my_vars.aws.common.region }}"
names: "{{ item.name }}"
register: elb_facts
with_items: "{{ my_vars.aws.elb }}"
- name: ELBアタッチ済みインスタンスとvarsの差分からデタッチ対象インスタンスlist作成
set_fact:
elb_diff_list: >-
{%- set tmplist = [] -%}
{%- for i in range(elb_facts.results|length) -%}
{%- set diffelb = elb_facts.results[i].elbs[0].instances | difference(my_vars.aws.elb[i].instances) -%}
{%- set tmpelb = {"name": elb_facts.results[i].elbs[0].name, "instances": diffelb } -%}
{%- set _ = tmplist.append(tmpelb) -%}
{%- endfor -%}
{{ tmplist }}
- debug: var=elb_diff_list
- name: ELBからインスタンスをデタッチ
ec2_elb:
instance_id: "{{ item.1 }}"
ec2_elbs: "{{ item.0.name }}"
state: absent
with_subelements:
- "{{ elb_diff_list }}"
- instances
- name: Gathering ELB facts
ec2_elb_facts:
region: "{{ my_vars.aws.common.region }}"
names: "{{ item.name }}"
register: elb_facts_result
with_items: "{{ my_vars.aws.elb }}"
- name: 結果出力
debug:
msg: "Result: {{ item.elbs[0].name }}: {{ item.elbs[0].instances }}"
with_items: "{{ elb_facts_result.results }}"
site.yml
---
- name: elb
hosts: elb-production
connection: local
roles:
- role: elb
tags:
- elb
実行
$ ansible-playbook -i inventories/aws -l elb_production site.yml
実際の作業イメージ
server01を2台のELBからデタッチする
デタッチ対象のインスタンスをvars上でコメントアウトする
---
my_vars:
aws:
common:
region: ap-northeast-1
elb:
- name: production_elb01
instances:
# - i-XXXXXXXXXXXXXXXXX #server01 デタッチするインスタンスをコメントアウト
- i-XXXXXXXXXXXXXXXXX #server02
- name: production_elb02
instances:
# - i-XXXXXXXXXXXXXXXXX #server01 デタッチするインスタンスをコメントアウト
- i-XXXXXXXXXXXXXXXXX #server02
server01デタッチ実行
$ ansible-playbook -i inventories/aws -l elb_production site.yml
server01をメンテナンス
再起動など
メンテナンス完了したserver01を2台のELBに再びアタッチする
メンテナンス完了したインスタンスをvars上でコメントインする
---
my_vars:
aws:
common:
region: ap-northeast-1
elb:
- name: production_elb01
instances:
- i-XXXXXXXXXXXXXXXXX #server01 アタッチするインスタンスをコメントイン
- i-XXXXXXXXXXXXXXXXX #server02
- name: production_elb02
instances:
- i-XXXXXXXXXXXXXXXXX #server01 アタッチするインスタンスをコメントイン
- i-XXXXXXXXXXXXXXXXX #server02
server01アタッチ実行
$ ansible-playbook -i inventories/aws -l elb_production site.yml
server02についても同様に
まとめ
いかがでしょうか。あくまでvars上でアタッチ/デタッチ対象を制御するため、シェルスクリプトに引数で対象を渡すより安全に実行できるのではないでしょうか。
普段からAnsibleを利用している環境なら、このようなアドホックな操作をAnsible化するのもありかと思います。
ALB版もそのうち書きたいと思います(ALB移行しなきゃ……)。