NLBでプライベートなEC2やRDSの負荷分散が可能に!
ウィスキー、シガー、パイプをこよなく愛する大栗です。
先日ALBでIPアドレスをターゲットとしてオンプレミスのリソースなどに対して負荷分散ができるようになりました。先日NLBでもIPアドレスをターゲットとして設定できるようになったためご紹介します。
IPアドレスターゲット
NLBが負荷分散するターゲットをインスタンスIDではなくIPアドレスを指定することが可能な機能です。ALBでも同様の機能がありますが、一部異なる部分があります。
指定可能なIPアドレス
サポートされているIPアドレスは、以下の範囲です。
- ターゲットグループが配置されたVPCのCIDR
- 10.0.0.0/8(RFC 1918)
- 100.64.0.0/10(RFC 6598)
- 172.16.0.0/12(RFC 1918)
- 192.168.0.0/16(RFC 1918)
登録可能なリソース
サポートされているCIDRブロックの範囲で、ClassicLinkインスタンス、IPアドレスとポートを指定できるAWSリソース、Direct ConnectやソフトウェアVPNコネクションで接続したオンプレミスのリソースをターゲットグループに登録できます。残念ながらALBとは異なり、VPC PeeringやハードウェアVPNコネクション(VPCのVPNコネクション)の先は登録できないので注意が必要です。
また、登録できるリソースで『IPアドレスとポートを指定できるAWSリソース』という点が非常に重要になります。IPアドレスとポートが指定できれば、EC2以外のリソースも登録ができる訳です!!!RDSやElastiCacheなどのVPC内のリソースの負荷分散が可能になっています。今までソフトウェアロードバランサを使用していた箇所をマネージドサービスで代替できるということです。
ネットワーク構成
NLBのバックエンドサーバとして登録するEC2は、クライアントへのネットワーク到達性(IGWやNAT GWへのルート)が必要です。しかし、IPアドレスターゲットの場合はNLBへの到達性があればクライアントへのネットワーク到達性が不要になっています。オンプレミスのリソースを登録するために動作が変わっていると思われますが、VPC内のリソースもIPアドレスターゲットであれば同様の動作をします。
今までは、以下の2パターンの構成でNLBを構築できました。
- EC2がパブリックサブネットでグローバルIPを持っているパターン
- NAT Gateway経由でインターネットに出られるパターン
EC2がパブリックサブネットでグローバルIPを持っているパターン
- Internet GatewayからNLBへ通信
- NLBからEC2へ通信
- EC2からInternet Gatewayへ通信
注意: 3.の部分で、VPC Flow LogsではEC2 -> NLB -> Internet Gatewayのように見える。また外部への通信はNLBのElastic IPを使用する。
NAT Gateway経由でインターネットに出られるパターン
- Internet GatewayからNLBへ通信
- NLBからEC2へ通信
- EC2からNAT Gatewayへ通信
- NAT GatewayからInternet Gatewayへ通信
注意: 3.と4.の部分で、VPC Flow LogsではEC2 -> NLB -> Internet Gatewayのように見える。また外部への通信はNLBのElastic IPを使用する。
IPターゲットのパターン
IPターゲットの場合は、EC2のサブネットからインターネットへ出られないルートであってもNLBを経由して出ていくように見える動作となります。
- Internet GatewayからNLBへ通信
- NLBからEC2へ通信
- EC2からNLBへ通信
- NLBからInternet Gatewayへ通信
この動作により完全なプライベートサブネットのリソースに対してもNLBを利用することができます。
やってみる
EC2インスタンスとRDSの負荷分散をやってみます。
EC2インスタンスのIPターゲット登録
まずは普通にEC2の負荷分散を行います。
ここでは、以下の前提とします。
リージョン:東京リージョン OS:amzn-ami-hvm-2017.03.1.20170812-x86_64-gp2 (ami-4af5022c)を元にhttpdをインストール済みのものを使用
また、ネットワーク構成は以下の図のようになります。
- フロントエンドサブネット(Internet Gatewayにルーティングしている)
- Frontend Subnet1:10.20.0.0/24
- Frontend Subnet2:10.20.1.0/24
- アプリケーションサブネット(VPC内のみにルーティングしてる)
- Application Subnet1:10.20.100.0/24
- Application Subnet2:10.20.101.0/24
- データストアサブネット(VPC内のみにルーティングしてる)
- Datastore Subnet1:10.20.200.0/24
- Datastore Subnet2:10.20.201.0/24
EC2の起動
構成図のようにアプリケーションサブネットにEC2を起動します。EC2では事前にhttpdを導入したものを使用します。UserDataで以下を設定して、インスタンスIDを/index.html
に設定します。セキュリティグループはフロントエンドサブネット(10.20.0.0/24、10.20.0.0/24)とアクセス元IPアドレスに対してポート80を許可した物を使用します。
#!/bin/bash curl -s http://169.254.169.254/latest/meta-data/instance-id | tee /var/www/html/index.html > /dev/null sudo service httpd start
ターゲットグループの作成
次にターゲットグループを作成します。
- ターゲットグループ名:EC2-IP(任意で設定してください)
- プロトコル:TCP(NLBの場合はTCPのみです)
- ポート:80(HTTPのため)
- ターゲットの種類:ip
- VPC:対象のVPCを選択してください
ヘルスチェックの設定を以下のように実施しました。
- ヘルスチェックの設定
- プロトコル:TCP
- ヘルスチェックの詳細設定
- ポート:トラフィックポート
- 正常のしきい値:3
- 非正常のしきい値:3(変更不能)
- タイムアウト:10(変更不能)
- 間隔:10秒
ヘルスチェックのプロトコルはHTTPを使用せずにTCPにしています。NLBのヘルスチェックは分散型で行われているためヘルスチェクのアクセスが多いので、負荷を下げるためにTCPにしています。自分の計測結果ですが、間隔を10秒にした時で180回/分以上、間隔を30秒にした時で120回/分以上のヘルスチェックが来る模様です。
ターゲットの登録
作成したターゲットグループのターゲットを登録します。ターゲットタブの編集
をクリックします。上部メニューの+
をクリックします。ネットワークに対象のVPCを選択し、IPにEC2のIPアドレスを設定してリストに追加
をクリックします。2台のEC2を追加して登録
をクリックしてと登録します。
ロードバランサーの作成
次にNLBを作成します。
- 名前:EC2-IP
- スキーマ:インターネット向け
- リスナー
- ロードバランサーのプロトコル:TCP(変更不能)
- ロードバランサーのポート:80
- アベイラビリティーゾーン
- アベイラビリティー:ap-northeast-1a(Frontend Subnet1)
- アベイラビリティー:ap-northeast-1c(Frontend Subnet2)
以下のようにルーティングの設定を行います。
- ターゲットグループ
- ターゲットグループ:既存のターゲットグループ
- 名前:EC2-IP(作成したターゲットグループを選択します)
- プロトコル:TCP(変更不能)
- ポート:80(変更不能)
- ターゲットの種類:ip(変更不能)
- ヘルスチェック
- プロトコル:TCP (変更不能)
ターゲットのEC2を確認します。
ロードバランサーの詳細を確認して作成
をクリックします。
しばらく経つとターゲットがhealthyになります。
パブリックIPが無くインターネット値のアクセスもできないEC2に対してNLBで負荷分散が行なえます。
RDSのIPターゲット登録
次にRDSの負荷分散を行います。
ここではAuroraで、Writer1台、ReaderをAZごとに2台ずつの計4台で起動して、以下のように構成します。VPCのネットワークは「EC2インスタンスのIPターゲット登録」と同じものです。
DBサブネットグループの作成
まずはDBサブネットグループを作成します。ここではデータストアサブネットの2個のサブネットを登録します。
DBパラメータグループの作成
次に専用のDBパラメータグループを作成します。
作成したDBパラメータグループのmax_connect_errors
を編集します。ここで最大値の18446744073709547520を入力します。これはNLBがヘルスチェックを行うと不正接続と判定されるため、アクセスを拒否しないための設定となります。
セキュリティグループの作成
セキュリテイグループも作成します。
インバウンドのルールはアプリケーションサブネットのCIDRを許可します。NLBのヘルスチェックはIPアドレスでしか許可できないためです。
Auroraの起動
Auroraを起動します。
これまでに作成した、Dbサブネットグループ、DBパラメータグループ、セキュリティグループを設定します。配置するAZ(サブネット)は、以下の通りです。
- Writer(writer-11):ap-northeast-1a(Datastore Subnet1)
- Reader11(target-11):ap-northeast-1a(Datastore Subnet1)
- Reader12(target-12):ap-northeast-1a(Datastore Subnet1)
- Reader31(target-31):ap-northeast-1c(Datastore Subnet2)
- Reader32(target-31):ap-northeast-1c(Datastore Subnet2)
ターゲットグループの作成
RDS用のターゲットグループを作成します。
- ターゲットグループ名:RDS-IP(任意で設定してください)
- プロトコル:TCP(NLBの場合はTCPのみです)
- ポート:3306(MySQLのため)
- ターゲットの種類:ip
- VPC:対象のVPCを選択してください
ヘルスチェックの設定を以下のように設定します。
- ヘルスチェックの設定
- プロトコル:TCP
- ヘルスチェックの詳細設定
- ポート:トラフィックポート
- 正常のしきい値:3
- 非正常のしきい値:3(変更不能)
- タイムアウト:10(変更不能)
- 間隔:10秒
ターゲットの登録
ターゲットグループを作成したらターゲットを登録します。ここで注意が必要です。各々のDBインスタンスのインスタンスエンドポイントに対して名前解決をして、IPアドレスを確認します。確認したIPアドレスをターゲットグループに登録します。
- Reader11(target-11):10.20.200.146
- Reader12(target-12):10.20.200.254
- Reader31(target-31):10.20.201.23
- Reader32(target-31):10.20.201.142
ロードバランサーの作成
次にNLBを作成します。
- 名前:aurora-nlb
- スキーマ:内部
- リスナー
- ロードバランサーのプロトコル:TCP(変更不能)
- ロードバランサーのポート:3306
- アベイラビリティーゾーン
- アベイラビリティー:ap-northeast-1a(Application Subnet1)
- アベイラビリティー:ap-northeast-1c(Application Subnet2)
以下のようにルーティングの設定を行います。
- ターゲットグループ
- ターゲットグループ:既存のターゲットグループ
- 名前:RDS-IP(作成したターゲットグループを選択します)
- プロトコル:TCP(変更不能)
- ポート:3306(変更不能)
- ターゲットの種類:ip(変更不能)
- ヘルスチェック
- プロトコル:TCP (変更不能)
ターゲットのRDSのIPアドレスをを確認して、ロードバランサーの詳細を確認してロードバランサーを作成します。
しばらく経つとターゲットがhealthyになります。
アプリケーションサブネットのEC2からNLB経由でAuroraにアクセスしてみます。普通にログイン可能です。
$ mysql -uawsuser -pmypassword -h aurora-nlb-a1b2c3d4e5f6g7h8.elb.ap-northeast-1.amazonaws.com Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 60667 Server version: 5.6.10 MySQL Community Server (GPL) Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
何度かアクセスして、Auroraのhostnameを確認してみます。1000回確認してみました。地道にやるのは面倒なので、少しシェル芸を使います、
$ for i in `seq 1 1000`; do echo "show variables like 'hostname';" | mysql -uawsuser -pmypassword -h aurora-nlb-a1b2c3d4e5f6g7h8.elb.ap-northeast-1.amazonaws.com -N; done | sort | uniq -c 240 hostname ip-172-23-1-228 259 hostname ip-172-23-1-55 256 hostname ip-172-23-2-123 245 hostname ip-172-23-2-124
かなり綺麗に分散されています。
比較のために読み込みエンドポイントで同様に確認してみます。
$ for i in `seq 1 1000`; do echo "show variables like 'hostname';" | mysql -uawsuser -pmypassword -h target-cluster-01.cluster-ro-a1b2c3d4e5f6.ap-northeast-1.rds.amazonaws.com -N; done | sort | uniq -c 157 hostname ip-172-23-1-228 611 hostname ip-172-23-1-55 59 hostname ip-172-23-2-123 173 hostname ip-172-23-2-124
読み込みエンドポイントの場合は偏りが大きくなってしまいます。これは読み込みエンドポイントのTTLが5秒に設定されていることに起因します。
NLBでReaderの負荷分散を行うことで理想的に分けられることが確認できました。
さいごに
自分でNLBの紹介ブログを書いていてなんなんですが、SSL/TLSが使用できなかったり、バックエンドEC2のセキュリティグループを外部公開する必要があったり、正直NLBを使う場面は極めて限定的でALBとくらべてほとんど出番が無いと考えていました。
しかし、今回IPアドレスのターゲット登録ができるようになりEC2以外のリソースも負荷分散できるようになりました。大規模システムなどではDBの負荷分散が偏ると、負荷で順々に落ちていきサービスの全面ダウンが発生するようなことがあります。Auroraで読み込みエンドポイントを使用すると負荷の偏りが発生することがありましたが、NLBを使用するとほぼ理想的な分散となります。CLBやALB1ではEC2に対してしか負荷分散を行えませんでしたが、NLBではVPC内のリソースに対して自由にターゲット登録を行えます。今まで自分でソフトウェアロードバランサを使用していた部分でもマネージドサービスで運用する事ができることが期待できますね。
- ドキュメントによるとALBでもIPアドレスとポートを指定できるAWSリソースをターゲットに登録できる記載があるのですが、EC2以外でhttpでアクセスできるVPC内のリソースが思い当たりません。。。 http://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-target-groups.html#target-type ↩