【AWS】クラウドデザインパターン実践編/仮想IPアドレスによるFloating IPパターン

2013.08.21

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは植木和樹です。本日はクラウドデザインパターン(CDP)を利用してシステム可用性の課題を解決したいと思います。本日取り上げるCDPは「Floating IPパターン」です。

解決したい課題

あるEC2インスタンスに障害が発生した際に、ダウンタイムを最小にして待機系ノードに切り替えたいという要望があったとします。Route53のレコードを書き換えて、ホスト名を待機系のIPアドレスに向け直してあげても良いのですが、DNS情報が伝播するまで時間がかかる場合があります。

そこで仮想IPアドレスを用いた解決方法を検討してみたいと思います。通常時は稼動系ノードに仮想IPアドレスを割り当て、サービスはこの仮想IPアドレスで提供しておきます。障害発生時には稼動系から仮想IPアドレスを外し、待機系ノードに割り当て直すことで瞬時にトラフィックの向け先を変更できます。

本日は「Leveraging Multiple IP Addresses for Virtual IP Address Fail-over in 6 Simple Steps」で紹介されている手順を参考に、仮想IPアドレスの付け替えを行う方法を試してみたいと思います。

なお仮想IPアドレスを使ってサービスの可用性を高める方法を、クラウドデザインパターンでは「Floating IPパターン」と呼びます。「CDP:Floating IPパターン」で概略が説明されていますので、あわせて読んでいただくと良いかと思います。

それでは進めていきましょう。

はじめに

複数のIPアドレスを1つの仮想IP(VIP)アドレス経由でアクセスすることで、高可用フェイルオーバーソリューションを構築する手順を、6つのステップに沿って学びましょう。この記事では必要となるすべてのリソース、すぐに使えるスクリプトを紹介しながら、どのように自己監視設定された2台の高可用(HA)インスタンスを用いて仮想IPソリューションを作成するかの手順を含んでいます。

仮想IPモニターと付け替えスクリプト(vip_monitor.sh)は「AWS re:Invent CPN207: Virtual Networking in the Cloud」でデモンストレーションを行いました。このスクリプトは1台のEC2インスタンスがもう片方のEC2インスタンスを監視し、インスタンスに障害あった場合に”仮想”プライベートIPアドレスを付け替えます。2台のインスタンスはお互いを監視しながら、他方に障害があった場合に仮想IPアドレスを付け替えることで高可用性を実現しています。このスクリプトはサードパーティ製のモニタリングツールと組み合わせたり、2台の監視対象ノードの変わりに監視サーバを別途用いることも容易です。

Floating IP Cloud Design Pattern」について読むことをオススメします。この記事は2台のEC2インスタンスを用いて、このデザインパターンを実装する方法について解説しています。

仮想IPアドレスを持った自己監視インスタンスをセットアップする手順は以下の通りです。

  1. Amazon Virtual Private Cloud(VPC)を作成する
  2. Amazon EC2 AWS Identity and Access Management(IAM)Roleを作成する
  3. Linux EC2インスタンス2台をVPC内のパブリックサブネットに起動する
  4. Elastic IPアドレスを設定する
  5. vip_monitor.sh スクリプトをダウンロードして、設定する
  6. 検証用Webサーバを用いて、設定をテストする

この手順によって下図のような構成が作成されます。pingの応答がなくなると稼働しているノードにIPアドレスが付け替わります。

20130820_floating-ip-pattern_vip

※画像は元サイトから拝借しました

また、この手順によって下記のリソースが作成されます。

  1. パブリックサブネットとInternet Gatewayを持ったVPC
  2. 他方のEC2に障害があった時に仮想IPアドレスを付け替える権限を持ったEC2 IAM Role
  3. 自己監視している2台のLinux EC2インスタンスと2台で共有している仮想IPアドレス
  4. ElasticIPアドレス 3つ(各EC2インスタンスに1つずつと仮想パブリックIPアドレス用に1つ)

Amazon Virtual Private Cloud(VPC)を作成する

ネットワークインフラを準備するところから始めましょう。マネージメントコンソールでVPCの画面を開き、「VPC with a Single Public Subnet Only」をVPCウィザードを用いて作成します。詳しくは「Amazon VPC Getting Started Guide」をご覧ください。

ここで作成したサブネットID(subnet-xxxxxxxx)はメモしておいてください。

EC2インスタンスに出入りするトラフィックをコントロールため、[Security Groups]をクリックしてセキュリティグループを作成します。詳しくは「Amazon VPC documentation」をご覧ください。以下の例では「HA_Monitor」セキュリティグループが作成され、リモート管理用のsshとノード間のICMPを許可しています。また仮想IPアドレスの動作確認に用いるWebサーバのためHTTPも許可しています。

20130820_floating-ip-pattern_000

Amazon EC2 AWS Identity and Access Management(IAM)Roleを作成する

EC2インスタンスで障害が発生した時に、仮想IPアドレスの付け替えできる権限を持ったIAMロールを作成します。マネージメントコンソールのIAMの画面を開き、[Roles]-[Create New Role]ボタンをクリックします。新しく作成するロール名(HA_Monitor)を入力し[Continue]をクリックします。

20130820_floating-ip-pattern_001

[AWS Service Roles]-[Amazon EC2]を選択します。[Custom Policy]を選択し[Select]をクリックします。ポリシー名(HA _Monitor_Policy)を入力し[Policy Document]に以下の内容を貼り付けます。

{
  "Statement": [
  {
    "Action": [
      "ec2:AssignPrivateIpAddresses",
      "ec2:DescribeInstances"
    ],
    "Effect": "Allow",
    "Resource": "*"
  }
  ]
}

20130820_floating-ip-pattern_002

[Continue]をクリックし、最後に[Create Role]するとEC2Roleが作成されます。

2台のLinux EC2インスタンスをVPC内のパブリックサブネットに起動する

2台のLinux EC2インスタンスをVPCに起動します。このインスタンスはHA構成になります。

マネージメントコンソールのEC2の画面を開き、[Launch Instances]をクリックします。Quick Launch Wizardを使ってみましょう。まずインスタンス名(HA Node #1)を入力します。事前に作成済みのキーペアを選択するか新規に作成します。AWSキーペアはインスタンス起動後に(SSHで)安全に接続するための秘密鍵と公開鍵のペアです。キーペアについては「動画:Amazon EC2 - Creating a Key Pair」をご覧ください。

20130820_floating-ip-pattern_003

[Edit details]をクリックし、設定を変更していきます。

20130820_floating-ip-pattern_004

[Instance Details]をクリックします。前の手順で作成した(メモしておいた)サブネットを選択します。なおAWSアカウントの作成時期(2013年5月以前)によっては「Launch into a VPC」という項目をチェックする必要があるかもしれません。

20130820_floating-ip-pattern_005

[Security Settings]をクリックし、先に作成した「HA_Monitor」セキュリティグループを選択します。

20130820_floating-ip-pattern_006

[Advanced Details]をクリックし、IAM Roleに先に作成した「HA_Monitor」IAMロールを選択します。

20130820_floating-ip-pattern_007

画面下にスクロールするとネットワークインターフェースの入力画面が表示されますので、IPアドレス(10.10.0.11)を入力します。さらに「Secondary IP Address」(10.10.0.10)も入力しますが、これが仮想IPアドレスになります。

20130820_floating-ip-pattern_008

入力したら[Save details]をクリックします。

[Launch]をクリックして、インスタンスが起動したら[Close]します。

20130820_floating-ip-pattern_009

同様に2台目のインスタンスについても設定します。1台目との違いは以下の通りです。

  • 2台目のインスタンスに適切な名前を入力します(HA Node #2)
  • [Advanced Details]の画面ではIPアドレスは1つのみ(10.10.0.12)入力し、「Secondary IP Address」は入力しません。

上記以外については1台目と同様に「Launch into a VPC」をチェックし、サブネット、セキュリティグループ、IAMロールを選択します。

Elastic IPアドレスを設定する

EC2インスタンスが起動したら、2台のインスタンスそれぞれと仮想IPアドレス用に計3つのElasticIPアドレスを作成します。個々のEC2インスタンスに(インターネットから)接続するためにEIPを使用します。そして3つ目のEIPは2つのECインスタンス間で付け替えられる仮想IPアドレスとして使用します。

マネージメントコンソールで、EC2の画面のナビゲーション(Network & Securityの下)にある[Elastic IPs]をクリックします。[Allocate New Address]をクリックし、3つのEIPを作成します。なおAWSアカウントの作成時期によっては「VPC用」と選択する必要があるかもしれません。

20130820_floating-ip-pattern_010

1つ目のEIPは「HA Node #1」に割り当てます。デフォルトで割り当てはプライマリ・プライベートIPアドレス(10.10.0.11)にマッピングされます。(IPアドレスの後ろにアスタリクスが付いているのがプライマリアドレスです)。

20130820_floating-ip-pattern_011

2つ目のEIPは「HA Node #2」に割り当てます。

20130820_floating-ip-pattern_013

3つ目のEIPは「HA Node #1」に割り当てますが、セカンダリ・プライベートIPアドレス(10.10.0.10)を選択してください。

20130820_floating-ip-pattern_014

20130820_floating-ip-pattern_015

vip_monitor.sh スクリプトをダウンロードして、設定する

HA Node #1にsshで接続し、rootユーザになります。rootユーザのホームディレクトリに移動し、vip_monitor.sh をダウンロードします。ダウンロードしたファイルには実行権限をつけておきましょう。

$ sudo -s
# cd /root
# wget http://media.amazonwebservices.com/articles/vip_monitor_files/vip_monitor.sh
# chmod a+x vip_monitor.sh

HA Node #1の設定に合うよう以下の変数を編集します。

  • HA_NODE_IP ... HA Node #2のプライマリ・プライベートIPアドレス(10.10.0.12)
  • VIP ... 仮想プライベートIPアドレス(10.10.0.10)
  • REGION ... HAノードが稼働しているリージョン名(ap-northeast-1)

ダウンロードした vip_monitor.sh ですが、このままでは動きません。ENIに割り当てたセカンダリIPを、OSが認識していないためです。そのためifconfigコマンドを使ってIPアドレスを設定する必要があります。後ほどスクリプト全文を掲載しますので、そちらを参考に修正してください。

OS起動時にスクリプトが開始されるようvip_monitor.shをcronに登録します。その後手動でvip_monitor.shを実行します。&をつけてバックグラウンドで実行する点に注意してください。

# echo '@reboot /root/vip_monitor.sh >> /var/log/vip_monitor.log' | crontab
# /root/vip_monitor.sh >> /var/log/vip_monitor.log &

ログファイルを確認し、スクリプトが問題なく実行されているか確認します。

tail /var/log/vip_monitor.log

次にHA Node #2にsshで接続し、HA Node #1と同様にスクリプトをダウンロードし、パーミッションを設定します。vip_monitor.shの変数については以下のように設定します。

  • HA_NODE_IP ... HA Node #1のプライマリ・プライベートIPアドレス(10.10.0.11)
  • VIP ... 仮想プライベートIPアドレス(10.10.0.10)
  • REGION ... HAノードが稼働しているリージョン名(ap-northeast-1)

検証用Webサーバを用いて、設定をテストする

以上で設定が完了しました!それでは設定をテストしてみましょう。HA Node #2のvip_monitor.logを監視しつつ、HA Node #1のOSをリブートしてみましょう。スクリプトが検知して仮想IPアドレスが付け替えが行われます。

HA Node #2

# tail -f /var/log/vip_monitor.log
Wed Aug 21 03:22:51 UTC 2013 -- Starting HA monitor
Wed Aug 21 03:46:50 UTC 2013 -- HA heartbeat failed, taking over VIP
RETURN  true
Wed Aug 21 03:46:53 UTC 2013 -- Restarting network

HA Node #1

# shutdown -r now
Broadcast message from ec2-user@ip-10-10-0-11
        (/dev/pts/0) at 3:46 ...

The system is going down for reboot NOW!

設定を確認するやり方は他にもいくつかあります。例えばHA Node #1をOSリブートしている間、仮想EIPに対してpingを打ち続けてみましょう。pingは一時的に(15秒ほど)繋がらなくなりますが、HA Node #2に仮想IPアドレスが付け変わると反応が復帰すると思います。

他にも、以下のコマンドをそれぞれのノードで実行してみてください。Apacheをインストールして起動し、サンプルのHTMLファイルを作成しています。(前の手順に続けてrootで作業していることを前提としています。sshで再度接続して作業する場合はsudo -sコマンドでrootユーザになるのを忘れないでください。

HA Node #1

# yum -y install httpd
# chkconfig httpd on
# service httpd start
# echo '<h1>HA Node #1<h1>' > /var/www/html/index.html

HA Node #2

# yum -y install httpd
# chkconfig httpd on
# service httpd start
# echo '<h1>HA Node #2<h1>' > /var/www/html/index.html

設定ができたら、仮想EIPにWebブラウザでアクセスしてみましょう。仮想IPを持っているサーバに接続されノード番号が表示されるはずです。このノードをOS再起動し、ブラウザを再読み込みすると仮想IPがもう片方のノードにつけ変わったことが分かると思います。

関連資料

スクリプト

ダウンロードしたvip_monitor.shはそのままでは正しく動きません。ifconfigコマンドでOSにIPアドレスを認識させる必要があります。

vip_monitor.sh

#!/bin/sh
# This script will monitor another HA node and take over a Virtual IP (VIP)
# if communication with the other node fails

# High Availability IP variables
# Other node's IP to ping and VIP to swap if other node goes down
HA_Node_IP=10.10.0.11
VIP=10.10.0.10

# Specify the EC2 region that this will be running in
REGION=us-west-2

# Run aws-apitools-common.sh to set up default environment variables and to
# leverage AWS security credentials provided by EC2 roles
. /etc/profile.d/aws-apitools-common.sh

# Determine the instance and ENI IDs so we can reassign the VIP to the
# correct ENI. Requires EC2 describe-instances and assign-private-ip-address
# permissions. The following example EC2 roles policy will authorize these
# commands:
# {
#   "Statement": [
#   {
#     "Action": [
#       "ec2:AssignPrivateIpAddresses",
#       "ec2:DescribeInstances"
#     ],
#     "Effect": "Allow",
#     "Resource": "*"
#   }
#   ]
# }

Instance_ID=`/usr/bin/curl --silent http://169.254.169.254/latest/meta-data/instance-id`
ENI_ID=`/opt/aws/bin/ec2-describe-instances $Instance_ID --region $REGION | grep eni -m 1 | awk '{print $2;}'`

echo `date` "-- Starting HA monitor"
while [ . ]; do
 pingresult=`ping -c 3 -W 1 $HA_Node_IP | grep time= | wc -l`
 if [ "$pingresult" == "0" ]; then
   echo `date` "-- HA heartbeat failed, taking over VIP"
   /opt/aws/bin/ec2-assign-private-ip-addresses -n $ENI_ID \
     --secondary-private-ip-address $VIP \
     --allow-reassignment --region $REGION
   /sbin/ifconfig eth0:1 $VIP netmask 255.255.255.0
   pingresult=`ping -c 1 -W 1 $VIP | grep time= | wc -l`
   if [ "$pingresult" == "0" ]; then
     echo `date` "-- Restarting network"
     /sbin/service network restart > /dev/null 2>&1
   fi
   sleep 60
 fi
 sleep 2
done

記事の補足

ここまでが元記事で紹介されている仮想IPアドレスを用いたFloating-IPパターンの実装手順です。今回この手順を試していた際に気づいた点がいくつかあるので、ここで補足しておきたいと思います。

このFloating-IPパターンの実装例は同一サブネット(AZ)限定です

2つのHAノードを異なるAZで起動したい場合は、それぞれのAZにサブネットを作成し、インスタンス起動時に別々のサブネットを指定することになります。しかしHAノードに割り当てるセカンダリIPアドレス(仮想IPアドレス)は、プライベートIPアドレスのため「サブネットをまたぐことができません」。つまりAZをまたいだ仮想IPアドレスの付け替えを行うことができません。

単一AZのノードでインスタンス障害が発生した場合のサービス復旧を、極力ダウンタイムを短く行いたいという前提でご検討ください。また待機系ノードに課金が発生する点にもご注意ください。

関連資料 CloudFormation テンプレート使用時の注意点

関連資料で紹介されているCloudFormationのテンプレートを使えばVPCからEC2インスタンスまで5分程度で作成することができます。しかし、リンク先ページで解説されている通り、仮想EIPの作成とセカンダリ・プライベートIPアドレスへの割り当て(「Elastic IPアドレスを設定する」の一部作業)は含まれていません。これはCloudFormationがENIへのセカンダリIPアドレス割り当てに対応していないためと思われます。

スタック作成完了後に、マネージメントコンソールでVPC用EIPを1つ作成し、HA Node #1のセカンダリ・プライベートIPアドレスに割り当ててあげてください。

また、このCloudFormationテンプレートで起動するAmazon LinuxのAMI(ami-2819aa29)が少々古い(2012.03)ため、インストールされている aws-apitools-ec2がIAMロールに対応していません。ログインしたらsudo yum update -yして、アップデートしないとスクリプトがうまく動きません(セカンダリIPの割り当てに失敗します)。1.6.8.1-1.0.amzn1 で動作確認できています。UserDataで実行しているブート処理内にyum update aws-apitools-ec2 -yを追加すれば良いでしょう。

プロダクション環境で使う際に問題になりそうな点

上に掲載したモニタリング用のシェルスクリプト(vip_monitor.sh)を読むと、2台あるHAノードそれぞれが相手にpingを打ち、反応がない場合に仮想IPの付け替えが行われ、その後service network restartしています。なにかしらの理由で相手がなかなかpingに応答しない場合、60秒毎にnetwork restartが実行され通信が瞬断してしまいます。

もし上記スクリプトを本番環境で使われる際には、その辺を考慮の上スクリプトの修正が必要になるかと思いますのでご注意ください。

まとめ

今回の記事ではWebサーバを例としてFloating-IPパターンを実装してきました。ただ対象がWebサーバであればFloating-IPパターンを用いなくても、素直にELBを使うのが良いと思います。しかしプロトコル的にELBを用いることができないケースでは仮想IPアドレスの付け替えによる冗長化を検討する必要があります。

今回のFloating-IPパターンではセカンダリ・プライベートIPアドレス(と関連付けしたElasticIP)を付け替える実装のため、Multi-AZでは使用することができません。Multi-AZ環境で同一IPアドレスによるアクセスを行いたい場合は、固定IPアドレスがグローバルIPなら「ec2-associate-addressコマンドで直接EIPの付け替えを行うスクリプトを作成」し、固定IPアドレスがプライベートIPなら「Routing-Based HAパターン」を検討することになるかと思います。

ELB, ElasticIP, ENI, Route53などサービスをうまく組み合わせることで、要件に応じた冗長化の仕組みを構築することができるのもAWSの特徴です。多種多様な組み合わせが考えられ難しいかもしれませんが、クラウドデザインパターンには先人の知恵が結集されていますので、ぜひ目を通して参考にしてください。