この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
ちゃだいん(@chazuke4649)です。
先日 Network Load Balancer (以下NLB)の特性を活かしたネットワーク構成を組む機会があったので紹介します。
概要
構成図
背景や要件
- 2つのVPCピアリングでつなげる
- VPC-1のクライアントサーバからVPC-2のターゲットサーバへアクセスしたい
- ターゲット側には複数台存在し、複数同じ受けポートでリッスンしている
- これをクライアント側でポート番号を指定することによって意図したターゲットにアクセスできるように振り分けたい
- 待ち受けポートはhttp/httpsに限らず、SSHなど様々なポートへ通信させる必要がある
- ターゲットによってはVPC-2とサイト間VPN接続でつながっているオンプレ環境とも通信させる必要がある
対応方法
- NLBに中継させる
- NLB側でリスナー側のポートとターゲットグループ側のポートを必要な通信の数だけ用意する
- 一部ターゲットグループの宛先としてオンプレのターゲットサーバのIPを指定する
リスナー・ターゲットグループ組み合わせ一覧
NLBでは以下のようにリスナーとターゲットグループを組み合わせることができます。
No. | リスナー側ポート番号 | アクション | ターゲットグループ名 | ターゲットグループ側ポート番号 | ターゲットIP |
---|---|---|---|---|---|
1 | TCP:10022 | 転送 | tg-10022 | TCP:22 | TargetServer1 |
2 | TCP:10080 | 転送 | tg-10080 | TCP:80 | TargetServer1 |
3 | TCP:20022 | 転送 | tg-20022 | TCP:22 | TargetServer2 |
4 | TCP:20080 | 転送 | tg-20080 | TCP:80 | TargetServer2 |
5 | TCP:30080 | 転送 | tg-30080 | TCP:80 | TargetServer3 |
クライアント側で宛先側のポート指定が可能であれば、それぞれ一意のポート番号単位で、ターゲット側のポート番号とIPの組み合わせを変えるといった方法です。
これを行うと、クライアントとNLB間はリスナー側ポート番号で通信し、NLBとターゲット間はターゲットグループ側ポート番号で通信することが可能です。
やってみた
NLBがメインなので以下リソースの構築は割愛します。
- VPC関連(サブネット、ルートテーブルなど)
- VPCピアリング
- クライアント・ターゲットサーバのEC2インスタンス
- セキュリティグループ
NLBを作成する
今回はTerraformで構築しています。 以下がサンプルのTerraformのtfファイルとなります。
nlb.tf
#########################################################
# NLB: example-nlb
#########################################################
resource aws_lb example-nlb {
name = "example-nlb"
load_balancer_type = "network"
internal = true
enable_deletion_protection = true
enable_cross_zone_load_balancing = true
subnet_mapping {
subnet_id = aws_subnet.private-1a-1.id
private_ipv4_address = "172.31.128.10"
}
subnet_mapping {
subnet_id = aws_subnet.private-1c-1.id
private_ipv4_address = "172.31.136.10"
}
}
#########################################################
# NLB Listener & Target group 10022
#########################################################
resource aws_lb_listener listner-10022 {
load_balancer_arn = aws_lb.example-nlb.arn
port = "10022"
protocol = "TCP"
default_action {
target_group_arn = aws_lb_target_group.tg-10022.arn
type = "forward"
}
}
resource aws_lb_target_group tg-10022 {
name = "example-tg-10022"
port = "22"
protocol = "TCP"
target_type = "ip"
vpc_id = aws_vpc.example.id
deregistration_delay = "300"
depends_on = [aws_lb.example-nlb]
health_check {
port = "traffic-port"
protocol = "TCP"
healthy_threshold = "3"
unhealthy_threshold = "3"
interval = "30"
}
}
resource aws_lb_target_group_attachment tg-10022 {
target_group_arn = aws_lb_target_group.tg-10022.arn
target_id = "172.31.129.10"
port = 22
}
#########################################################
# NLB Listener & Target group 30080
#########################################################
resource aws_lb_listener listner-30080 {
load_balancer_arn = aws_lb.example-nlb.arn
port = "30080"
protocol = "TCP"
default_action {
target_group_arn = aws_lb_target_group.tg-30080.arn
type = "forward"
}
}
resource aws_lb_target_group tg-30080 {
name = "example-tg-30080"
port = "22"
protocol = "TCP"
target_type = "ip"
vpc_id = aws_vpc.example.id
deregistration_delay = "300"
depends_on = [aws_lb.example-nlb]
health_check {
port = "traffic-port"
protocol = "TCP"
healthy_threshold = "3"
unhealthy_threshold = "3"
interval = "30"
}
}
resource aws_lb_target_group_attachment tg-30080 {
target_group_arn = aws_lb_target_group.tg-30080.arn
target_id = "10.0.0.10"
port = 80
# オンプレなどVPC外のIPを指定する場合にallに指定する
availability_zone = "all"
}
- 上記では長くなりすぎるのでリスナー・ターゲットグループ一覧の
No.1
とNo.5
のみ記述しています。(実際はmoduleを使って共通化すべきでしょう) - 11~18行目では、NLBの特徴である静的IPの指定を行っています。今回は2つのサブネットにまたがる形で両サブネットにそれぞれプライベートIPを入力しています
- 99行目では、ターゲットIPがオンプレ側(VPC外)の場合のみ、
availability_zone = "all"
の指定が必要です
疎通確認(HTTP)
まず、クライアントサーバにSSHで入ります。
実際に通信ができるかどうか確認していきます。以下ブログで紹介されているようなコマンドを使用します。
【初心者向け】各OSのTCP通信チェックコマンド入門(2018年 更新版) | DevelopersIO
以下コマンドを実行します。
[ec2-user@ip-192-168-1-214 ~]$ curl -v telnet://172.31.128.10:10080
* Rebuilt URL to: telnet://172.31.128.10:10080/
* Trying 172.31.128.10...
* TCP_NODELAY set
* Connected to 172.31.128.10 (172.31.128.10) port 10080 (#0)
TCPコネクションが貼られていることが確認できました。
ただし、本当にHTTPを返してくれるかわかりません。次は、 curl -I
でHTTPレスポンスヘッダを取得してみます。
[ec2-user@ip-192-168-1-214 ~]$ curl -I 172.31.128.10:10080
HTTP/1.1 200 OK
Date: Fri, 12 Mar 2021 08:59:48 GMT
Server: Apache/2.4.46 ()
Upgrade: h2,h2c
Connection: Upgrade
Last-Modified: Fri, 12 Mar 2021 08:59:11 GMT
ETag: "12-5bd531d172fe5"
Accept-Ranges: bytes
Content-Length: 18
Content-Type: text/html; charset=UTF-8
ターゲットサーバ側で起動しているApahcheのテストページのレスポンスヘッダを取得することができました。HTTPの疎通確認は以上です。
疎通確認(SSH)
次はSSHです。こちらは同じくNLBの1a側のAZの静的プライベートIP宛にポート番号を指定して、SSH接続を試みます。
[ec2-user@ip-192-168-1-214 ~]$ssh -p 10022 -i test.key ec2-user@172.31.128.10
The authenticity of host '54.168.234.185 (54.168.234.185)' can't be established.
ECDSA key fingerprint is SHA256:NfbQno8PS3VjO8BlXdJzIhs7fvfgl4QwFxtQsJHqg6y.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '54.168.234.185' (ECDSA) to the list of known hosts.
__| __|_ )
_| ( / Amazon Linux 2 AMI
___|\___|___|
https://aws.amazon.com/amazon-linux-2/
上記の通り成功しました。
成功ついでに、netstat
コマンドで 本当に22番ポートが使用されているか確認してみます。
[ec2-user@ip-172-31-129-10 ~]$ netstat -n |grep 22
tcp 0 0 172.31.129.10:22 172.31.128.10:44387 SYN_RECV
tcp 0 0 172.31.129.10:22 xxx.xx.xx.xxx:22814 ESTABLISHED
...
上記の通り、2つのうちどちらも22番ポートで受け付けており、NLB側のIPとはTCPのある通信状態(SYN_RECV)を示しています。また、xxx.xx.xx.xxx(ローカルマシンのグローバルIP)とESTABLISHED(TCPコネクションが確立できている状態)であることが確認できました。
トラブルシュート
最初、疎通確認がうまくいかないことがありましたが、以下ポイントを確認すると概ね解決しました。
- ルートテーブルの設定に不備がないか
- ターゲットグループのヘルスチェックがHealthyかどうか
- ターゲットサーバのセキュリティグループでNLBからのIPとポートを許可しているか など
終わりに
NLBを触るとネットワークにおけるトランスポート層の理解が深まることがわかりました。
それではこの辺で。ちゃだいん(@chazuke4649)でした。