LoginSignup
0
2

More than 5 years have passed since last update.

ELB(CLB)へのEC2インスタンスのアタッチ/デタッチをAnsibleで手軽に

Posted at

はじめに

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モジュールを読み込むために指定しています。

inventories/aws
[all:vars]
ansible_connection=local
ansible_python_interpreter=python
ansible_host=localhost

[elb_production]
production

[elb_staging]
staging

vars

こんな感じに変数を定義します。

group_vars/elb_production.yml
---
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モジュールで結果を出力します。
roles/elb/tasks/main.yml
---
- 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

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上でコメントアウトする

group_vars/elb_production.yml
---
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デタッチ実行

server01デタッチ実行
$ ansible-playbook -i inventories/aws -l elb_production site.yml

 server01をメンテナンス

再起動など

メンテナンス完了したserver01を2台のELBに再びアタッチする

メンテナンス完了したインスタンスをvars上でコメントインする

group_vars/elb_production.yml
---
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アタッチ実行

server01アタッチ実行
$ ansible-playbook -i inventories/aws -l elb_production site.yml

server02についても同様に

まとめ

いかがでしょうか。あくまでvars上でアタッチ/デタッチ対象を制御するため、シェルスクリプトに引数で対象を渡すより安全に実行できるのではないでしょうか。
普段からAnsibleを利用している環境なら、このようなアドホックな操作をAnsible化するのもありかと思います。
ALB版もそのうち書きたいと思います(ALB移行しなきゃ……)。

参考

AnsibleでAWSリソースを管理するシリーズ

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2