August 10, 2014

EC2 Nginx で高可用性+ロードバランス(Floating IP パターン)

AWS の ELB はとても高機能だしマネージメントでメンテコストもほとんど無く死角がないのだけど、固定 IP で使えないのがイタイという時がある。

今日日 CNAME じゃだめで固定 IP で無ければならないとかそれはそれでアレなわけだけど、それがマストである、と言うことなら(大変大変大変不本意ではあるが) ELB を使わずに頑張って対応するしかない。

そこで、NGINX Plus をロードバランサーとして使いつつ、CDP で言うところの Floating IP パターンで、高可用性も担保する構成を考えてみる。

LB に NGINX Plus を使ってみる

今回は LB として NGINX Plus を使ってみたい。と、いうのも 政治的な理由 通常の Nginx に追加して、セッションの持続化強化、ヘルスチェック、拡張された状況報告とオンザフライでのロードバランシングプールの設定変更など、魅力的な機能が揃っているから。

Proof of Concept

poc

なんかどっかで見たような図になってしまったが…

構成

  • LB
    • CentOS 6.5(x86_64)
    • NGINX Plus
      • nginx-plus-1.7.3-1.el6.ngx.x86_64
      • ec2-api-tools-1.7.1.0
  • WEB
    • CentOS 6.5(x86_64)
    • httpd(正直何でも良い)

インスタンスの用意

  • LB #1
    • CentOS 6.5(x86_64)
    • ENI #1
      • Private IP: 172.16.10.11
      • Secondary private IP: 172.16.10.10
  • LB #2
    • CentOS 6.5(x86_64)
    • ENI #1
      • Private IP: 172.16.10.12
  • WEB #1, #2
    • WEB #1
      • Private IP: 172.16.10.21
    • WEN #2
      • Private IP: 172.16.10.22

片方の LB に、Secondary private IP: 172.16.10.10 を付けるのがミソ。

NGINX Plus 導入

通常通り、NGINX Plus を導入する。既に導入済みの AMI インスタンスもあるが、今回は CentOS のインスタンスに新規に導入するとしましょう。

手順はサイトの NGINX repository setup instruction を見れば何となく雰囲気はつかめる。CA.crt, nginx-repo.crt, nginx-repo.key の 3つを /etc/ssl/nginx 配下に置く。どれか一つを忘れてもだめっぽい。

[root@ng-lb-1 ~]# mkdir /etc/ssl/nginx/
[root@ng-lb-1 ~]# cp ~/CA.crt /etc/ssl/nginx/
[root@ng-lb-1 ~]# cp ~/nginx-repo.crt /etc/ssl/nginx/
[root@ng-lb-1 ~]# cp ~/nginx-repo.key /etc/ssl/nginx/
[root@ng-lb-1 ~]# yum install -y nginx-plus
[root@ng-lb-1 ~]# cat /etc/nginx/conf.d/default.conf
upstream backend {
    server 172.16.10.21;
    server 172.16.10.22;
}
server {
    listen       80;
    server_name  _;

    access_log  /var/log/nginx/access.log  main;

    location = /favicon.ico {
        log_not_found off;
    }

    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Real-IP $remote_addr;

    location / {
        proxy_pass http://backend;
        health_check interval=5 fails=1 passes=1 uri=/;
    }
}

こんな感じでとりあえず OK。せっかく Plus なのでヘルスチェックも入れてみた。SSL は今回とりあえずおいておく。

ちなみに NGINX Plus のロードバランサ機能は真面目にやろうとすると、結構盛りだくさんな機能があることが分かるがまずはこんなところであろう。

[root@ng-lb-1 ~]# chkconfig nginx on
[root@ng-lb-1 ~]# service nginx start

サービス有効化も忘れずに。

EIP IP 自動付け替え設定

EIP の自動付け替えには ec2-api-tools を使う。CentOS だと当然のことながら最初から入っていないので、手動で導入してやる必要がある。(正直メンドイ…)

[root@ng-lb-1 ~]# yum install -y unzip java-1.6.0-openjdk
[root@ng-lb-1 ~]# mkdir -p /opt/aws
[root@ng-lb-1 ~]# curl -LO http://s3.amazonaws.com/ec2-downloads/ec2-api-tools.zip
[root@ng-lb-1 ~]# unzip ec2-api-tools.zip
[root@ng-lb-1 ~]# mv ec2-api-tools-1.7.1.0 /opt/aws

先に紹介した Developers.IO のネタ を基に、少々 CentOS 用設定を加える。

[root@ng-lb-1 ~]# curl -LO http://media.amazonwebservices.com/articles/vip_monitor_files/vip_monitor.sh
[root@ng-lb-1 ~]# chmod a+x vip_monitor.sh
  • 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
export JAVA_HOME=/etc/alternatives/jre
export EC2_HOME=/opt/aws/ec2-api-tools
export AWS_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXX
export AWS_SECRET_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
export EC2_URL=https://ec2.ap-northeast-1.amazonaws.com
export PATH=$PATH:$JAVA_HOME/bin:$EC2_HOME/bin

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

# Specify the EC2 region that this will be running in
REGION=ap-northeast-1

# 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/ec2-api-tools/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/ec2-api-tools/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
  • 見ることろ
    • 冒頭の大量の export/etc/profile.d/aws-apitools-common.sh をコメントアウトして読み込ませないので追加している。(これって Amazon Linux じゃないと存在しないよね・・・)
    • HA_Node_IP=172.16.10.12 は、対向側になる LB ホストの Private IP を指定する
    • VIP=172.16.10.10Secondary private IP、つまり VIP。
    • 49行目あたりの /sbin/ifconfig eth0:1 $VIP netmask 255.255.255.0 は、基記事でも説明されているとおり。
    • あと、ec2-api-tools への PATH がベタ書きされていたので改修した。
[root@ng-lb-1 ~]# echo '@reboot /root/vip_monitor.sh >> /var/log/vip_monitor.log' | crontab
[root@ng-lb-1 ~]# /root/vip_monitor.sh >> /var/log/vip_monitor.log &

ここまでどちらの LB も共通。勿論2台とも行う。

EIP のアタッチと VIP の有効化

EIP を LB #1 の ENI に Associate する。すると選択肢に 172.16.10.10 があるはずなので、それで決定する。

それからまだ VIP が有効化されていないはずなので、LB #1 で VIP を起こすコマンドを叩いておく。

[root@ng-lb-1 ~]# ifconfig eth0:1 172.16.10.10 netmask 255.255.255.0

確認

ログを tailf とか、作業クライアントから EIP に向けて ping しつつ、LB のインスタンスや WEB のインスタンスを落としてみる。

[root@ng-lb-1 ~]# tailf /var/log/vip_monitor.log
[root@ng-lb-2 ~]# tailf /var/log/vip_monitor.log
Sun Aug 10 02:09:53 JST 2014 -- HA heartbeat failed, taking over VIP
RETURN  true

こんな感じのログが出てれば切り替わりが出来ている。

また、裏の WEB は index.htmlこれは WEB #1 ですこれは WEB #2 です みたいなものを置いておくとテストしやすい。

おわりに

環境を構築してから気がついたのだけど、NGINX plus には nginx-ha なるパッケージが存在する様だ。

どうやら実装は corosync + Pacemaker で、EIP の切替もよしなにやってくれるとのこと(…!)

と、言うわけで AWS 環境で使うならこっちの方法を使う方が自前でしこしこ用意するより楽かもしれません。(今度リベンジする、かもしれない…)

© Kazuhisa Hara 2014-2018