17日目!
昨日立ち上げた EC2 インスタンス。
皆様もお気づきの通り、1台構成で、何かあったときに可用性が失われます。
また、セキュリティグループのインバウンドルールをCloudFrontからのアクセスのみとしていますが、パブリックサブネットに配置されていて、もしものときを考えると少し心もとないです。
なので、今日から2日に分けてネットワーク構成の変更と可用性向上のために冗長化します。
17日目の要約
ネットワークの構成を変更するよ!
AWS CLI の準備
このあたりをみて、好きなバージョンとお使いのOSにあった環境設定をしてくださいね。
なんなら、 AWS CloudShell で実行するのも楽でよいと思います。
この記事シリーズは、AWS CloudShell で実行し、実行例を載せています。
バージョン1
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv1.html
バージョン2
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2.html
概要
パブリックサブネットに加えて、プライベートサブネットを作成する。そして、ALBを構築する。
さあ、やってみよう!
現在の構成を確認する
EC2 インスタンスやVPC、サブネットなどのネットワーク周りの構成を確認します。
これを、ひとまず、以下の様にします。
※Instance くんにはちょっときえてもらっています。
サブネットを追加する
とりあえず、サブネットを追加していきます。
14日目のサブネットの作成の手順と同じですが、復習を兼ねてみていきます。
実行前に、 VPC の ID を確認しておいてくださいね。
パブリックサブネットを追加する
パブリックサブネットを作成する
まずはパブリックサブネットから作っていきます。
cidr-block の値と availability-zone の値に注意してくださいね。
aws ec2 create-subnet --cidr-block 10.0.1.0/24 --availability-zone ap-northeast-1c \
--vpc-id <VPC ID>
サブネットの作成に成功すると、以下のような json が返ります。
SubnetId の値を確認しておきます。
{
"Subnet": {
"AvailabilityZone": "ap-northeast-1c",
"AvailabilityZoneId": "apne1-az1",
"AvailableIpAddressCount": 251,
"CidrBlock": "10.0.1.0/24",
"DefaultForAz": false,
"MapPublicIpOnLaunch": false,
"State": "available",
"SubnetId": "subnet-*****************",
"VpcId": "vpc-*****************",
"OwnerId": "************",
"AssignIpv6AddressOnCreation": false,
"Ipv6CidrBlockAssociationSet": [],
"SubnetArn": "arn:aws:ec2:ap-northeast-1:************:subnet/subnet-*****************"
}
}
パブリックサブネットの設定を変更する
続いて、EC2 起動時にパブリック IP アドレスが自動で割り当たるように、ec2 modify-subnet-attribute コマンドで設定を変更します。
aws ec2 modify-subnet-attribute --subnet-id <サブネット ID> --map-public-ip-on-launch
こちらは、正常終了しても特に json は返ってきません。
パブリックサブネットにルートテーブルを関連づける
作ったサブネットはパブリックサブネットなので、 Internet Gateway(IGW)に向かうルートがあるルートテーブルを関連づけるために ec2 associate-route-table コマンドを実行します。
aws ec2 associate-route-table --route-table-id <ルートテーブルの ID> \
--subnet-id <サブネットの ID>
関連付けが正常に行えたら、以下のような json が返ります。
{
"AssociationId": "rtbassoc-*****************",
"AssociationState": {
"State": "associated"
}
}
パブリックサブネットの作成は以上です。
プライベートサブネットを追加する
続いて、プライベートサブネットを作成していきます。
1つめ
aws ec2 create-subnet --cidr-block 10.0.10.0/24 --availability-zone ap-northeast-1a \
--vpc-id <VPC ID>
サブネットの作成に成功すると、以下のような json が返ります。
SubnetId の値を確認しておきます。
{
"Subnet": {
"AvailabilityZone": "ap-northeast-1a",
"AvailabilityZoneId": "apne1-az4",
"AvailableIpAddressCount": 251,
"CidrBlock": "10.0.10.0/24",
"DefaultForAz": false,
"MapPublicIpOnLaunch": false,
"State": "available",
"SubnetId": "subnet-*****************",
"VpcId": "vpc-*****************",
"OwnerId": "************",
"AssignIpv6AddressOnCreation": false,
"Ipv6CidrBlockAssociationSet": [],
"SubnetArn": "arn:aws:ec2:ap-northeast-1:************:subnet/subnet-*****************"
}
}
メインのルートテーブルを関連づける
作ったサブネットはプライベートサブネットなので、 メインのルートテーブルを関連づけるために ec2 associate-route-table コマンドを実行します。
aws ec2 associate-route-table --route-table-id <メインのルートテーブルの ID> \
--subnet-id <1つ目のプライベートサブネットの ID>
関連付けが正常に行えたら、以下のような json が返ります。
{
"AssociationId": "rtbassoc-*****************",
"AssociationState": {
"State": "associated"
}
}
2つめ
aws ec2 create-subnet --cidr-block 10.0.11.0/24 --availability-zone ap-northeast-1c \
--vpc-id <VPC ID>
サブネットの作成に成功すると、以下のような json が返ります。
SubnetId の値を確認しておきます。
{
"Subnet": {
"AvailabilityZone": "ap-northeast-1c",
"AvailabilityZoneId": "apne1-az1",
"AvailableIpAddressCount": 251,
"CidrBlock": "10.0.11.0/24",
"DefaultForAz": false,
"MapPublicIpOnLaunch": false,
"State": "available",
"SubnetId": "subnet-*****************",
"VpcId": "vpc-*****************",
"OwnerId": "************",
"AssignIpv6AddressOnCreation": false,
"Ipv6CidrBlockAssociationSet": [],
"SubnetArn": "arn:aws:ec2:ap-northeast-1:************:subnet/subnet-*****************"
}
}
メインのルートテーブルを関連づける
作ったサブネットはプライベートサブネットなので、 メインのルートテーブルを関連づけるために ec2 associate-route-table コマンドを実行します。
aws ec2 associate-route-table --route-table-id <メインのルートテーブルの ID> \
--subnet-id <2つ目のプライベートサブネットの ID>
関連付けが正常に行えたら、以下のような json が返ります。
{
"AssociationId": "rtbassoc-*****************",
"AssociationState": {
"State": "associated"
}
}
ALB を起動する
サブネットの設定が完了したので、 可用性を高め、冗長構成にするために使う ALB の設定をしていきます。
セキュリティグループを作成する
最初に、セキュリティグループを作成します。
作り方は、14日目の手順と同じように、 CloudFront からのアクセスのみを受けるようにします。
ALB 用のセキュリティグループを作成する
ALB 用のセキュリティグループを作成します。
aws ec2 create-security-group --description "ALBs SG" \
--group-name "sg_alb" --vpc-id <VPC ID>
出力される セキュリティグループのIDを確認しておきます。
{
"GroupId": "sg-*****************"
}
次に、インバウンドルールを作成します。
IPRANGE 変数に、CloudFront の IP アドレス範囲一覧を取得、格納します。
そして、その値を用いて、インバウンドルールを作成します。
IPRANGE=`curl https://ip-ranges.amazonaws.com/ip-ranges.json | jq -c '.prefixes[] | select(.service=="CLOUDFRONT") | .ip_prefix'`
for ip in ${IPRANGE}; do iplist_tmp=$iplist_tmp,\{CidrIp=$ip\}; done
iplist=`echo ${iplist_tmp} | sed "s/^,//g"`
aws ec2 authorize-security-group-ingress --group-name sg_alb \
--ip-permissions IpProtocol=tcp,FromPort=80,ToPort=80,IpRanges=[${iplist}]
インバウンドルールが正常に作成できると、以下のような json が返ってきます。
指定したIPアドレス範囲の個数だけルールが列挙されます。
{
"Return": true,
"SecurityGroupRules": [
{
"SecurityGroupRuleId": "sgr-*****************",
"GroupId": "sg-*****************",
"GroupOwnerId": "************",
"IsEgress": false,
"IpProtocol": "tcp",
"FromPort": 80,
"ToPort": 80,
"CidrIpv4": "***.***.***.***/**"
},"(以下略)"
]
}
EC2 用のセキュリティグループを再作成する
14日に作ったセキュリティグループのままだと、ALBからの通信が受け付けられないのと、CloudFrontからのインバウンドルールを消していくのは面倒なので、再作成してしまおうということです。
aws ec2 create-security-group --description "Web Servers SG" \
--group-name "sg_web2" --vpc-id <VPC ID>
出力される セキュリティグループのIDを確認しておきます。
{
"GroupId": "sg-*****************"
}
次に、インバウンドルールを作成します。
ALBからの通信が受け付けられればよいので、ALBのセキュリティグループIDを用いてルールを作成します。
aws ec2 authorize-security-group-ingress --group-name sg_web2 \
--protocol tcp \
--port 80 \
--source-group sg_alb
以下のような json が返ってきます。
{
"Return": true,
"SecurityGroupRules": [
{
"SecurityGroupRuleId": "sgr-*****************",
"GroupId": "sg-*****************",
"GroupOwnerId": "************",
"IsEgress": false,
"IpProtocol": "tcp",
"FromPort": 80,
"ToPort": 80,
"ReferencedGroupInfo": {
"GroupId": "sg-*****************"
}
}
]
}
ALB 用のセキュリティグループのアウトバウンドルールを設定する
次に、ALB側のアウトバウンドルールに、EC2に対する通信のみを認めるようにします。
一旦、フルオープンのルールを削除します。revoke-security-group-egress
コマンドを使います。
aws ec2 revoke-security-group-egress --group-id <確認しておいた ALB のセキュリティグループID> --protocol -1 --port -1 --cidr 0.0.0.0/0
実行に成功すると、以下のような json が返ってきます。
{
"Return": true
}
そして、EC2に向かうアウトバウンドルールを追加するために、authorize-security-group-egress
コマンドを実行します。
aws ec2 authorize-security-group-egress --group-id <確認しておいた ALB のセキュリティグループID> --protocol tcp --port 80 --source-group <確認しておいた EC2用の新しいセキュリティグループID>
以下、余談ですが・・・
インバウンドルールとアウトバウンドルールで必須オプションが異なるのはなんでなんでしょうね・・・?
アウトバウンド側でも --group-name で登録できてもいいのになと。
また、--source-group に セキュリティグループ名で指定すると、エラーになります。なぜ。。。
正常に実行できると、以下のような json が返ってきます。
{
"Return": true,
"SecurityGroupRules": [
{
"SecurityGroupRuleId": "sgr-*****************",
"GroupId": "sg-*****************",
"GroupOwnerId": "************",
"IsEgress": true,
"IpProtocol": "tcp",
"FromPort": 80,
"ToPort": 80,
"ReferencedGroupInfo": {
"GroupId": "sg-*****************"
}
}
]
}
ALB を作成する
セキュリティグループが作成できたら、 ALB を作っていきます。
elbv2 create-load-balancer コマンドを実行します。
aws elbv2 create-load-balancer --name webserver-alb --subnets <1つ目のパブリックサブネットID> <2つ目のパブリックサブネットID> --security-groups <ALB用のセキュリティグループID>
ALB が作成できると、以下のような json が返ります。
{
"LoadBalancers": [
{
"LoadBalancerArn": "arn:aws:elasticloadbalancing:ap-northeast-1:************:loadbalancer/app/webserver-alb/*****************",
"DNSName": "webserver-alb-*********.ap-northeast-1.elb.amazonaws.com",
"CanonicalHostedZoneId": "*****************",
"CreatedTime": "2021-12-16T15:33:06.830000+00:00",
"LoadBalancerName": "webserver-alb",
"Scheme": "internet-facing",
"VpcId": "vpc-*****************",
"State": {
"Code": "provisioning"
},
"Type": "application",
"AvailabilityZones": [
{
"ZoneName": "ap-northeast-1c",
"SubnetId": "subnet-*****************",
"LoadBalancerAddresses": []
},
{
"ZoneName": "ap-northeast-1a",
"SubnetId": "subnet-*****************",
"LoadBalancerAddresses": []
}
],
"SecurityGroups": [
"sg-*****************"
],
"IpAddressType": "ipv4"
}
]
}
LoadBalancerArn と DNSName の値を確認しておいてください。
ターゲットグループを作成する
次にターゲットグループを作っていきます。
aws elbv2 create-target-group --name Web-Server-Target --protocol HTTP --port 80 \
--vpc-id <VPC ID> --health-check-path "/ec2/index.html"
{
"TargetGroups": [
{
"TargetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:************:targetgroup/Web-Server-Target/*****************",
"TargetGroupName": "Web-Server-Target",
"Protocol": "HTTP",
"Port": 80,
"VpcId": "vpc-*****************",
"HealthCheckProtocol": "HTTP",
"HealthCheckPort": "traffic-port",
"HealthCheckEnabled": true,
"HealthCheckIntervalSeconds": 30,
"HealthCheckTimeoutSeconds": 5,
"HealthyThresholdCount": 5,
"UnhealthyThresholdCount": 2,
"HealthCheckPath": "/ec2/index.html",
"Matcher": {
"HttpCode": "200"
},
"TargetType": "instance",
"ProtocolVersion": "HTTP1",
"IpAddressType": "ipv4"
}
]
}
リスナー
そして、ALBとターゲットグループをつなぐ、リスナーの設定を行います。
elbv2 create-listener を実行します。
aws elbv2 create-listener --load-balancer-arn <ALBのARN> \
--protocol HTTP --port 80 \
--default-actions Type=forward,TargetGroupArn=<ターゲットグループのARN>
HTTPで80番ポートへの着信を指定のターゲットグループに転送するよという設定です。
正常に実行できると、以下のような json が返ります。
{
"Listeners": [
{
"ListenerArn": "arn:aws:elasticloadbalancing:ap-northeast-1:************:listener/app/webserver-alb/*****************/*****************",
"LoadBalancerArn": "arn:aws:elasticloadbalancing:ap-northeast-1:************:loadbalancer/app/webserver-alb/*****************",
"Port": 80,
"Protocol": "HTTP",
"DefaultActions": [
{
"Type": "forward",
"TargetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:************:targetgroup/Web-Server-Target/*****************",
"ForwardConfig": {
"TargetGroups": [
{
"TargetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:************:targetgroup/Web-Server-Target/*****************",
"Weight": 1
}
],
"TargetGroupStickinessConfig": {
"Enabled": false
}
}
}
]
}
]
}
Route53 にレコードを追加する
ALB はデフォルト設定だと、インターネット向け通信ができるように DNSName を持っています。この名前をCloudFront のディストリビューションに設定すればよいのですが、管理しやすいように、Route53で CNAME を割り当てます。
ALB_CNAME=web-alb.<ドメイン名>
ALB_NAME=<ALBのDNSName>
HOSTED_ZONE_ID=<ホスティッドゾーンID>
aws route53 change-resource-record-sets --hosted-zone-id ${HOSTED_ZONE_ID} \
--change-batch "{\"Changes\": [ { \"Action\": \"UPSERT\", \"ResourceRecordSet\": { \"Name\": \"${ALB_CNAME}.\", \"Type\": \"CNAME\", \"TTL\": 300, \"ResourceRecords\": [{\"Value\": \"${ALB_NAME}\"}]}}]}"
レコード追加に成功すると、以下の json が返ります。
{
"ChangeInfo": {
"Id": "/change/C0*******************",
"Status": "PENDING",
"SubmittedAt": "2021-12-16T16:31:24.215000+00:00"
}
}
まとめ
今回は、可用性向上と冗長化のための下準備としてサブネットの追加や ALB の作成を行いました。
明日は、EC2 インスタンスの可用性を高め、伸縮性を得るために、AutoScaling を設定します。
- 今回使ったコマンド
- ec2 create-subnet
- ec2 modify-subnet-attribute
- ec2 associate-route-table
- ec2 create-security-group
- ec2 authorize-security-group-ingress
- ec2 revoke-security-group-egress
- ec2 authorize-security-group-egress
- elbv2 create-load-balancer
- elbv2 create-target-group
- elbv2 create-listener
- route53 change-resource-record-sets