こんにちは、ラーニングエクスペリエンス課の小倉です。
いつも書いているハンズオンブログですが、今回は サーバーワークス Advent Calendar 2023 シリーズ1 の 10日目のブログとして投稿します。
サーバーワークスでは、自由に勉強会を開催してスキルアップをしています。その中で私は毎週月曜日の朝、「30分AWSハンズオン」という30分でできるAWSハンズオンを2021年9月から継続して開催しています。その内容をブログで定期的に紹介していきます。AWSをご利用のみなさまのスキルアップにお役立ていただければと考えています。
7回目は、「Transit Gatewayを使ってVPC間通信をしてみよう」をやります。
ハンズオンは、以下の流れで実施します。
1.CloudFormationでハンズオン環境の作成
2.Transit Gatewayを作成してVPCへ接続
3.VPCのルートテーブルにTransit Gatwayあてのルートを追加
4.EC2 1にログインして、EC2 2にpingを実行
使用するAWSサービス
Amazon VPC
VPCとは、AWS内で作成できる仮想ネットワークです。同じAWSアカウントで複数のVPCを作成することができますが、それぞれが独立したネットワークのため、直接通信することはできず、インターネットゲートウェイ経由での通信となります。VPC間で直接通信をするためにはいくつか方法があるのですが、その一つが今回のハンズオンで取り扱うTransit Gatewayです。
参考サイト: [AWS Black Belt Online Seminar]Amazon VPC
AWS Transit Gateway
Transit Gatewayとは、VPCやオンプレミスを単一のゲートウェイへ簡単に接続できるサービスです。Transit Gatewayに接続できるのは、Amazon VPC、AWS VPN、AWS Direct Connect Gatewayです。接続するVPCやオンプレミスでCIDRブロックが重複してしまうとルーティングできなくなり、接続することができなくなるため、アドレス設計が重要です。
VPC間を接続する機能としてVPCピアリングというものがあるのですが、それぞれのVPC間での設定が必要となり、VPCの数が多くなるほど管理が煩雑になります。
例えば、VPCが5つあってすべてのVPCとVPCピアリングをする場合、VPCピアリングを10回設定します(左図)。それに対してTransit Gatewayを導入するとすべてのVPCをTransit Gatewayに接続してルートテーブルを設定するだけでVPC間の通信ができるようになり、構成が簡略化されます。
参考サイト: [AWS Black Belt Online Seminar]AWS Transit Gateway
構成図
今回の構成図です。
VPCを2つ作成し、それぞれのVPC内に疎通確認用のEC2を1台ずつ作成します。
Transit Gatewayを作成し、2つのVPCをTransit Gatewayに接続し、Transit Gateway経由でVPC間通信をできるようにします。
ハンズオン手順
1. CloudFormationでハンズオン環境を作成します
まず、ハンズオン環境をCloudFormationを使って準備します。CloudFormationとはコードでインフラを管理できるAWSサービスです。以下のコードをコピーしてローカルのメモ帳などのテキストエディタに貼りつけて、ファイル名 handson7.yml で保存します。このコードを利用するとVPC、サブネット、EC2を作成してくれます。
AWSTemplateFormatVersion: "2010-09-09" Description: Provision handson environment Parameters: ImageId: Type: AWS::SSM::Parameter::Value<String> Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64 Resources: VPC1: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.1.0/24 EnableDnsSupport: true EnableDnsHostnames: true InstanceTenancy: default Tags: - Key: Name Value: handson1-vpc InternetGateway1: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: handson1-igw InternetGatewayAttachment1: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref InternetGateway1 VpcId: !Ref VPC1 PublicSubnetA1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a CidrBlock: 10.0.1.0/26 VpcId: !Ref VPC1 Tags: - Key: Name Value: handson1-subnet-public-a PrivateSubnetA1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a CidrBlock: 10.0.1.64/26 VpcId: !Ref VPC1 Tags: - Key: Name Value: handson1-subnet-private-a PublicRouteTable1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC1 Tags: - Key: Name Value: handson1-rtb-public PrivateRouteTable1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC1 Tags: - Key: Name Value: handson1-rtb-private PublicRoute1: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PublicRouteTable1 DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway1 PublicSubnetARouteTableAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnetA1 RouteTableId: !Ref PublicRouteTable1 PrivateSubnetARouteTableAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnetA1 RouteTableId: !Ref PrivateRouteTable1 SecurityGroupforEC21: Type: AWS::EC2::SecurityGroup Properties: GroupName: handson1-ec2-sg GroupDescription: handson1-ec2-sg VpcId: !Ref VPC1 SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 0.0.0.0/0 - IpProtocol: icmp FromPort: -1 ToPort: -1 CidrIp: 0.0.0.0/0 Tags: - Key: Name Value: handson1-ec2-sg WebAppInstance1: Type: AWS::EC2::Instance Properties: Tags: - Key: Name Value: handson1-ec2 ImageId: !Ref ImageId InstanceType: t3.nano DisableApiTermination: false EbsOptimized: false BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeSize: 8 UserData: Fn::Base64: "#!/bin/bash -v\nexec > >(tee /var/log/user-data.log || logger -t user-data -s 2> /dev/console) 2>&1\nreboot" NetworkInterfaces: - AssociatePublicIpAddress: "true" DeleteOnTermination: "true" DeviceIndex: "0" SubnetId: !Ref PublicSubnetA1 GroupSet: - !Ref SecurityGroupforEC21 VPC2: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.2.0/24 EnableDnsSupport: true EnableDnsHostnames: true InstanceTenancy: default Tags: - Key: Name Value: handson2-vpc InternetGateway2: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: handson2-igw InternetGatewayAttachment2: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref InternetGateway2 VpcId: !Ref VPC2 PublicSubnetA2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a CidrBlock: 10.0.2.0/26 VpcId: !Ref VPC2 Tags: - Key: Name Value: handson2-subnet-public-a PrivateSubnetA2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a CidrBlock: 10.0.2.64/26 VpcId: !Ref VPC2 Tags: - Key: Name Value: handson2-subnet-private-a PublicRouteTable2: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC2 Tags: - Key: Name Value: handson2-rtb-public PrivateRouteTable2: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC2 Tags: - Key: Name Value: handson2-rtb-private PublicRoute2: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PublicRouteTable2 DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway2 PublicSubnetARouteTableAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnetA2 RouteTableId: !Ref PublicRouteTable2 PrivateSubnetARouteTableAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnetA2 RouteTableId: !Ref PrivateRouteTable2 SecurityGroupforEC22: Type: AWS::EC2::SecurityGroup Properties: GroupName: handson2-ec2-sg GroupDescription: handson2-ec2-sg VpcId: !Ref VPC2 SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 0.0.0.0/0 - IpProtocol: icmp FromPort: -1 ToPort: -1 CidrIp: 0.0.0.0/0 Tags: - Key: Name Value: handson2-ec2-sg WebAppInstance2: Type: AWS::EC2::Instance Properties: Tags: - Key: Name Value: handson2-ec2 ImageId: !Ref ImageId InstanceType: t3.nano DisableApiTermination: false EbsOptimized: false BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeSize: 8 UserData: Fn::Base64: "#!/bin/bash -v\nexec > >(tee /var/log/user-data.log || logger -t user-data -s 2> /dev/console) 2>&1\nreboot" NetworkInterfaces: - AssociatePublicIpAddress: "true" DeleteOnTermination: "true" DeviceIndex: "0" SubnetId: !Ref PublicSubnetA2 GroupSet: - !Ref SecurityGroupforEC22
CloudFormationで上記のコードを実行すると以下の構成ができあがります。
AWSマネジメントコンソールにログインし、画面上の検索窓で「CloudFormation」と入力し、サービスの下に表示された [CloudFormation] をクリックし、CloudFormationのコンソール画面を開きます。また今回のハンズオンは東京リージョンで実施しますので、右上のリージョンが東京になっていない場合は東京に変更しておきましょう。
スタック一覧の画面が表示されるので、右上の [スタックの作成] - [新しいリソースを使用 (標準)] をクリックします。 もしスタック一覧が表示されない場合は、左上のハンバーガーメニュー(三本線のアイコン)をクリックし、ナビゲーションペイン(左メニュー)を表示し、スタックをクリックしてスタック一覧を表示します。
スタックの作成画面が表示されるので、以下を設定して [次へ] をクリックします。
・テンプレートソース: テンプレートファイルのアップロード
・テンプレートファイルのアップロード: ファイルの選択をクリックし、先ほどローカルに保管した handson7.yml を選択
スタックの詳細を指定の画面で、スタック名に tgw-handson と入力して、右下の [次へ] をクリックします。 パラメータの変更は不要です。
スタックオプションの設定画面が表示されるので、ここはなにも変更せずに下にスクロールして右下の [次へ] をクリックします。
レビューの画面が表示されるので、下にスクロールして右下の [送信] をクリックします。
[送信] をクリックすると、CloudFormationによって、AWSリソースの作成が始まります。ステータスが CREATE_IN_PROGRESS から CREATE_COMPLETE になったら作成完了です。3分ほどかかりますので、作成完了まで待ちます。
【作成中】
【作成完了】
2. Transit Gatewayを作成してVPCへ接続します
まず、Transit Gatewayを作成します。
画面上の検索窓で「VPC」と入力し、サービスの下に表示された [VPC] をクリックしVPCのコンソール画面を開きます。また今回のハンズオンは東京リージョンで実施しますので、右上のリージョンが東京になっていない場合は東京に変更しておきましょう。
ナビゲーションペイン(左メニュー)の [Transit Gateway] をクリックし、Transit Gateway一覧の画面を表示した後、画面右上の [Transit Gatewayを作成] をクリックします。
Transit Gatewayを作成の画面で、以下を入力し、[Transit Gatewayを作成] をクリックします。
・名前タグ: yyyymmdd-handson-tgw (yyyymmdd は年月日で、例えば2023年12月10日なら 20231210 となります)
作成したTransit Gatewayが一覧に表示され、状態が Pending から Available に変わったら利用可能となります。
次の手順がAvailableの状態じゃないとできませんので、Availableになるまで2分ほど待ちましょう。
Transit Gatwayの作成が終わったので、次に作成したTransit GatewayをVPCに接続します。
ナビゲーションペイン(左メニュー)の [Transit Gatewayアタッチメント] をクリックし、Transit Gatewayアタッチメント一覧の画面を表示した後、画面右上の [Transit Gatewayアタッチメントを作成] をクリックします。
Transit Gatewayアタッチメントを作成の画面で、以下を入力し、[Transit Gatewayアタッチメントを作成] をクリックします。
詳細
・名前タグ: yyyymmdd-handson1-vpc (yyyymmdd は年月日で、例えば2023年12月10日なら 20231210 となります)
・Transit Gateway ID: yyyymmdd-handson-tgw を選択
・アタッチメントタイプ: VPC
VPCアタッチメント
・VPC ID: handson1-vpc を選択
・サブネットID: ap-northeast-1 にチェックを入れ、handson1-subnet-private-aを選択
作成したTransit Gatewayアタッチメントが一覧に表示され、状態が Pending から Available に変わったら利用可能となります。
同様の手順で handson2-vpcへのアタッチメントを作成します。
画面右上の [Transit Gatewayアタッチメントを作成] をクリックします。
Transit Gatewayアタッチメントを作成の画面で、以下を入力し、[Transit Gatewayアタッチメントを作成] をクリックします。
詳細
・名前タグ: yyyymmdd-handson2-vpc (yyyymmdd は年月日で、例えば2023年12月10日なら 20231210 となります)
・Transit Gateway ID: yyyymmdd-handson-tgw を選択
・アタッチメントタイプ: VPC
VPCアタッチメント
・VPC ID: handson2-vpc を選択
・サブネットID: ap-northeast-1 にチェックを入れ、handson2-subnet-private-aを選択
これでTransit Gatewayアタッチメントの作成が終わり、Transit GatewayとVPCの接続が終わりました。
3. VPCのルートテーブルにTransit Gatwayあてのルートを追加します
Transit GatewayとVPCの接続が終わりましたが、今のままだとまだ通信することはできません。それぞれのVPC内のルートテーブルにTransit Gatewayあてのルートを追加する必要があります。
ナビゲーションペイン(左メニュー)の [ルートテーブル] をクリックし、ルートテーブル一覧の中から handson1-rtb-public にチェックを入れます。ルートテーブルの検索窓で、handson1などと検索すると探しやすいです。
画面下のルートタブをクリックし、[ルートを編集] をクリックします。
ルートを編集の画面で、[ルートを追加] をクリックします。
追加された行の送信先に 10.0.2.0/24、ターゲットに Transit Gateway を選択し、先ほど作成したTransit Gatewayアタッチメント(yyyymmdd-handson1-vpc) を選択します。追加が終わりましたら、[変更を保存] をクリックします。
同様にhandson2-vpcのルートテーブルも編集します。
ナビゲーションペイン(左メニュー)の [ルートテーブル] をクリックし、ルートテーブル一覧の中から handson2-rtb-public にチェックを入れます。画面下のルートタブをクリックし、[ルートを編集] をクリックします。
ルートを編集の画面で、[ルートを追加] をクリックします。
追加された行の送信先に 10.0.1.0/24、ターゲットに Transit Gateway を選択し、先ほど作成したTransit Gatewayアタッチメント(yyyymmdd-handson2-vpc) を選択します。追加が終わりましたら、[変更を保存] をクリックします。
これでルートの追加が完了しましたので、疎通確認ができる状態となりました。
4. EC2 1にログインして、EC2 2にpingを実行します
CloudFormationで作成したEC2にログインし、別のVPCのEC2にpingを実行します。
画面上の検索窓で「EC2」と入力し、サービスの下に表示された [EC2] をクリックしEC2のコンソール画面を開きます。また今回のハンズオンは東京リージョンで実施しますので、右上のリージョンが東京になっていない場合は東京に変更しておきましょう。
ナビゲーションペイン(左メニュー)の [インスタンス] をクリックして、インスタンス一覧の画面を表示した後、CloudFormationで作成されたEC2インスタンス(handson2-ec2)にチェックを入れ、接続先のプライベートIPv4アドレスを控えます。
CloudFormationで作成されたEC2インスタンス(handson1-ec2)にチェックを入れ、右上の [接続] をクリックします。
インスタンスに接続の画面で、以下を設定して、右下の [接続] をクリックします。
・EC2 Instance Connect タブを選択
・接続タイプは、 EC2 Instance Connect を使用して接続する を選択
・ユーザー名は、ec2-user
接続をクリックするとEC2インスタンスにログインできます。
プロンプトで ping <プライベートIPv4アドレス> を実行して、疎通できることを確認します。
以下のようなメッセージが1秒おきに表示されれば疎通できています。コマンドを停止するときは、キーボードの ctrl + c を押します。
64 bytes from 10.0.2.38: icmp_seq=1 ttl=126 time=1.67 ms
これでVPC間をTransit Gateway経由で通信できることが確認できました。
5. 後片付け
AWSのリソースは従量課金のため、作ったまま放置しておくとお金がかかってしまいます。そのため、ハンズオンが終わったら不要なリソースは削除しておきましょう。
Transit Gatewayアタッチメントを削除します。
VPCのコンソール画面を開き、ナビゲーションペイン(左メニュー)の [Transit Gatewayアタッチメント] をクリックし、Transit Gatewayアタッチメント一覧の画面を表示します。作成したTransit Gatewayアタッチメント (yyyymmdd-handson1-vpc) にチェックを入れ、[アクション] - [Transit Gateway アタッチメントを削除] をクリックします。まとめて削除はできませんので、1つずつ削除します。
削除の確認画面がでますので、入力フィールドに 削除 と入力し、[削除] をクリックします。
もう一つ (yyyymmdd-handson2-vpc) も同様の手順で削除します。Transit Gatewayアタッチメントの削除が完了しないと次のTransit Gatewayの削除ができないので、状態が Deleted になるまで待ちます。
次にTransit Gatewayを削除します。 ナビゲーションペイン(左メニュー)の [Transit Gateway] をクリックし、Transit Gateway一覧の画面を表示します。作成したTransit Gateway (yyyymmdd-handson-tgw) にチェックを入れ、[アクション] - [Transit Gateway を削除]をクリックします。
削除の確認画面がでますので、入力フィールドに 削除 と入力し、[削除] をクリックします。
最後にCloudFormationでスタックの削除をします。
CloudFormationでまとめて作成したリソース(VPC、EC2)は、まとめて削除できます。
CloudFormationのコンソール画面を開き、ナビゲーションペイン(左メニュー)の [スタック] をクリックしてスタック一覧を開きます。ハンズオン環境構築のスタック(tgw-handson) を選択し、 [削除] をクリックします。
確認画面が表示されますので、右下の [削除] をクリックします。
5分ほど経つと削除が完了します。
これで今回のハンズオンは以上となります。
まとめ
今回はTransit Gatewayを使ってVPC間通信をしてみました。Transit Gatewayに接続するだけで複数の宛先に通信できるようになります。複数のVPCやオンプレミスを接続している環境であればTransit Gatewayを利用することで運用負荷を軽減できますので、このハンズオンを通して使い方の基本的を習得しましょう。
参考資料
小倉 大(記事一覧)
アプリケーションサービス部ラーニングエクスペリエンス課 札幌在住
AWSトレーニングの講師をしています。
最近は5歳の息子と遊ぶのが楽しいです!
Twitter: @MasaruOgura