読者です 読者をやめる 読者になる 読者になる

okochangの馬鹿でありがとう

ふらふら適当に世間を生きる日々でございます

EC2停止/起動時にELBから自分自身を除外/登録する

こんにちは@oko_changです。
ELB配下のEC2インスタンスをstop状態にする場合、停止する前にELBの分散対象から外してからstopします。
ELBからEC2へは定期的にHealth Checkが行われ、失敗がUnhealthy Thresholdで設定した回数に達すると分散対象から除外します。
そのため、ELBの分散対象から外さずにstopしたりすると若干ながらダウンタイムが発生します。
また、そのまま起動したとしてもELBからは正常にトラフィックを分散されないので注意が必要です。
本番環境ではあまりないかもしれませんが、テスト中や夜間に一部のサーバを停止する事はあるかもしれないので簡単なスクリプトを作ってみました。

環境

構成

停止する時はスクリプト経由でstopし、起動時はrc.localやcronとかに設定してスクリプトを実行するイメージです。
f:id:okochang:20130829100148p:plain

停止時に使うスクリプト

# -*- coding: utf-8 -*- 
require 'net/http'
require 'aws-sdk'

ec2_region = 'ec2.' + Net::HTTP.get('169.254.169.254', '/latest/meta-data/placement/availability-zone').chop + '.amazonaws.com'
elb_region = 'elasticloadbalancing.' + Net::HTTP.get('169.254.169.254', '/latest/meta-data/placement/availability-zone').chop + '.amazonaws.com'
instance_id = Net::HTTP.get('169.254.169.254', '/latest/meta-data/instance-id')

@ec2 = AWS::EC2.new(
  :ec2_endpoint => ec2_region
).client

@elb = AWS::ELB.new(
  :elb_endpoint => elb_region
).client

## インスタンスに割り当てられたタグを取得します
def check_tag_set(instance_id)
  tag_set = @ec2.describe_instances(:instance_ids => [instance_id])[:instance_index][instance_id][:tag_set]
  return tag_set
end

## タグの一覧から割り当てるELBの名前を取得します
def get_elbs_name(instance_id)
  tag_set = check_tag_set(instance_id)
  if tag_set.empty?
    puts "There is no tags"
  else 
    tag_set.each do |tag|
      if tag[:key] == 'elb'
        @elb_config = tag[:value].split(",")
      end
    end
    return @elb_config
  end
end

## タグの中に割り当てるELBがあったかチェックします
def check_elbs_name(instance_id)
  if get_elbs_name(instance_id).nil?
    puts "Instance is not registered to elb"
    @ec2.stop_instances(:instance_ids => [instance_id])
    exit 0
  end
end

## ELBに割り当てられていれば除外してから停止
def deregister_instance_from_lb(instance_id)
  get_elbs_name(instance_id).each do |elb_name|
    instances = @elb.describe_load_balancers(:load_balancer_names => [elb_name])[:load_balancer_descriptions][0][:instances]
    instances.each do |instance|
      if instance[:instance_id] == instance_id
        @elb.deregister_instances_from_load_balancer(:load_balancer_name => elb_name, :instances => [:instance_id => instance[:instance_id]])
        puts "deregister from #{elb_name}"
      end
    end
  end
  @ec2.stop_instances(:instance_ids => [instance_id])
  exit 0
end

## 実行します
check_elbs_name(instance_id)
deregister_instance_from_lb(instance_id)

起動時に使うスクリプト

# -*- coding: utf-8 -*- 
require 'net/http'
require 'aws-sdk'

ec2_region = 'ec2.' + Net::HTTP.get('169.254.169.254', '/latest/meta-data/placement/availability-zone').chop + '.amazonaws.com'
elb_region = 'elasticloadbalancing.' + Net::HTTP.get('169.254.169.254', '/latest/meta-data/placement/availability-zone').chop + '.amazonaws.com'
instance_id = Net::HTTP.get('169.254.169.254', '/latest/meta-data/instance-id')

@ec2 = AWS::EC2.new(
  :ec2_endpoint => ec2_region
).client

@elb = AWS::ELB.new(
  :elb_endpoint => elb_region
).client

## インスタンスに割り当てられたタグを取得します
def check_tag_set(instance_id)
  tag_set = @ec2.describe_instances(:instance_ids => [instance_id])[:instance_index][instance_id][:tag_set]
  return tag_set
end

## タグの一覧から割り当てるELBの名前を取得します
def get_elbs_name(instance_id)
  tag_set = check_tag_set(instance_id)
  if tag_set.empty?
    puts "There is no tags"
    exit 0
  else 
    tag_set.each do |tag|
      if tag[:key] == 'elb'
        @elb_config = tag[:value].split(",")
      end
    end
    return @elb_config
  end
end

## タグの中に割り当てるELBがあったかチェックします
def check_elbs_tag(instance_id)
  if get_elbs_name(instance_id).nil?
    puts "There is no elb to register"
    exit 0
  end
end

## インスタンスがすでにELBに登録されているかチェックします
def check_registration_status(instance_id)
  get_elbs_name(instance_id).each do |elb_name|
    if @elb.describe_load_balancers(:load_balancer_names => [elb_name])[:load_balancer_descriptions][0][:instances].include?({:instance_id=>instance_id})
      puts "Instance already registered to elb"
      exit 0
    end
  end
end

## ELBに割り当てます
def register_instance_to_lb(instance_id)
  get_elbs_name(instance_id).each do |elb_name|
    @elb.register_instances_with_load_balancer(:load_balancer_name => elb_name, :instances => [:instance_id => instance_id])
    puts "Instance registered to #{elb_name}"
  end
  exit 0
end

## 実行します
check_elbs_tag(instance_id)
check_registration_status(instance_id)
register_instance_to_lb(instance_id)

まとめ

今回の場合はタグにELBの情報を事前に入れている事が前提ですが、stop時に自分自身(EC2)が登録されているELBを取得してタグに情報を保存しておき、起動時はそのタグを見てELBに登録するようにすればもう少し楽になりそう。