0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ひろ亭Advent Calendar 2021

Day 17

AWS CLIで Web サイトを構築、管理、運用する(17日目)

Posted at

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、サブネットなどのネットワーク周りの構成を確認します。

image.png

これを、ひとまず、以下の様にします。
image.png
※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
0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?