こんにちは、広野です。
AWS Cloud9 は研修用途では非常に使い勝手が良かったのですが、AWS が新規アカウントへの提供を終了してしまいました。今回は私が試みた代替ソリューションの実装編です。本記事は VPC を対象にします。特別な設計はないので、別件でも使用可能だと思います。
アーキテクチャ
アーキテクチャ概要編で、以下の図を紹介しておりました。
- code-server サーバを配置する VPC です。何の変哲もない一般的なサブネット構成にしています。
- NAT Gateway は課金節約のため、1 つのパブリックサブネットにしか配置していません。
- code-server サーバは 1 ユーザーあたり 1 台を割り当てます。図では 1 つしかありませんが、人数分作成される想定です。ALB はユーザー全体で共用します。
- code-server のログインパスワードはインストール時に設定しますが、AWS Secrets Manager で生成したものを使用します。
- ALB には HTTPS アクセスさせるため、図には書いていませんが独自ドメインを使用します。そのための SSL 証明書はそのリージョンの AWS Certificate Manager で作成されていることが前提になります。
- ALB から EC2 インスタンスへの通信は 80 番ポート (HTTP) を使用します。
- ALB はインターネットに公開されますが、研修用途を想定して会社のソース IP アドレスからのみアクセスできるようにセキュリティグループを設定しています。
既存 VPC を利用する場合
以下の条件であれば、既存 VPC を使用することが可能です。本記事の内容は飛ばすことができます。
- パブリックサブネットが 2 つある。(ALB 用)
- プライベートサブネットからインターネットにアクセスできる。(NAT Gateway がある) ソフトウェアのインストールや、AWS Systems Manager, AWS Secrets Manager のエンドポイントへのアクセスに使用する。
AWS CloudFormation テンプレートの構成
テンプレートは 3 つに分けています。
- VPC ←今回はここ
- Application Load Balancer
- Amazon EC2 インスタンス
このうち、VPC と Application Load Balancer はユーザー共用になるので 1 回のみ実行します。Amazon EC2 インスタンスはユーザーごとに実行が必要です。
AWS Secrets Manager のシークレットはユーザーごとに作成するので、Amazon EC2 インスタンスと同時に実装します。
テンプレートの範囲を図にすると、以下のようになります。ここに書かれているリソースが作成されます。
AWS CloudFormation テンプレート
AWS CloudFormation テンプレートです。AWS Secrets Manager は、続編記事のテンプレートに入ります。
AWSTemplateFormatVersion: 2010-09-09 Description: The CloudFormation template that creates A VPC, Subnets, an Internet Gateway, a single NAT Gateway in AZ A and Routings. # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: SystemName: Type: String Description: System name. use lower case only. (e.g. example) Default: example MaxLength: 10 MinLength: 1 SubName: Type: String Description: System sub name. use lower case only. (e.g. prod or dev) Default: dev MaxLength: 10 MinLength: 1 VPCCIDR: Type: String Description: IP Address range for your VPC (16 bit mask) Default: 192.168.0.0/16 MaxLength: 14 MinLength: 10 AllowedPattern: ^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){2}0\.0/16$ PublicSubnetACIDR: Type: String Description: IP Address range for your public subnet A (24 bit mask) Default: 192.168.1.0/24 MaxLength: 16 MinLength: 10 AllowedPattern: ^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}0/24$ PublicSubnetCCIDR: Type: String Description: IP Address range for your public subnet C (24 bit mask) Default: 192.168.2.0/24 MaxLength: 16 MinLength: 10 AllowedPattern: ^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}0/24$ PrivateSubnetACIDR: Type: String Description: IP Address range for your private subnet A (24 bit mask) Default: 192.168.101.0/24 MaxLength: 16 MinLength: 10 AllowedPattern: ^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}0/24$ PrivateSubnetCCIDR: Type: String Description: IP Address range for your private subnet C (24 bit mask) Default: 192.168.102.0/24 MaxLength: 16 MinLength: 10 AllowedPattern: ^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}0/24$ Resources: # ------------------------------------------------------------# # VPC # ------------------------------------------------------------# VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VPCCIDR EnableDnsSupport: true EnableDnsHostnames: true InstanceTenancy: default Tags: - Key: Name Value: !Sub vpc-${SystemName}-${SubName} - Key: Cost Value: !Sub ${SystemName}-${SubName} InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub igw-${SystemName}-${SubName} - Key: Cost Value: !Sub ${SystemName}-${SubName} InternetGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC # ------------------------------------------------------------# # Subnet # ------------------------------------------------------------# # Public SubnetA Create PublicSubnetA: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - Fn::GetAZs: !Ref AWS::Region CidrBlock: !Ref PublicSubnetACIDR MapPublicIpOnLaunch: true VpcId: !Ref VPC Tags: - Key: Name Value: !Sub public-a-${SystemName}-${SubName} - Key: Cost Value: !Sub ${SystemName}-${SubName} # Public SubnetC Create PublicSubnetC: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - Fn::GetAZs: !Ref AWS::Region CidrBlock: !Ref PublicSubnetCCIDR MapPublicIpOnLaunch: true VpcId: !Ref VPC Tags: - Key: Name Value: !Sub public-c-${SystemName}-${SubName} - Key: Cost Value: !Sub ${SystemName}-${SubName} # Private SubnetA Create PrivateSubnetA: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - Fn::GetAZs: !Ref AWS::Region CidrBlock: !Ref PrivateSubnetACIDR VpcId: !Ref VPC Tags: - Key: Name Value: !Sub private-a-${SystemName}-${SubName} - Key: Cost Value: !Sub ${SystemName}-${SubName} # Private SubnetC Create PrivateSubnetC: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - Fn::GetAZs: !Ref AWS::Region CidrBlock: !Ref PrivateSubnetCCIDR VpcId: !Ref VPC Tags: - Key: Name Value: !Sub private-c-${SystemName}-${SubName} - Key: Cost Value: !Sub ${SystemName}-${SubName} # ------------------------------------------------------------# # RouteTable # ------------------------------------------------------------# # Public RouteTable A Create PublicRouteTableA: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub rtb-public-a-${SystemName}-${SubName} - Key: Cost Value: !Sub ${SystemName}-${SubName} # Public RouteTable C Create PublicRouteTableC: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub rtb-public-c-${SystemName}-${SubName} - Key: Cost Value: !Sub ${SystemName}-${SubName} # Private RouteTable A Create PrivateRouteTableA: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub rtb-private-a-${SystemName}-${SubName} - Key: Cost Value: !Sub ${SystemName}-${SubName} # Private RouteTable C Create PrivateRouteTableC: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub rtb-private-c-${SystemName}-${SubName} - Key: Cost Value: !Sub ${SystemName}-${SubName} # ------------------------------------------------------------# # Routing to Internet # ------------------------------------------------------------# # PublicRouteA Create PublicRouteA: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PublicRouteTableA DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway # PublicRouteC Create PublicRouteC: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PublicRouteTableC DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway PrivateRouteA: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTableA DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGatewayA PrivateRouteC: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTableC DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGatewayA # ------------------------------------------------------------# # RouteTable Associate # ------------------------------------------------------------# # PublicRouteTable Associate SubnetA PublicSubnetARouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnetA RouteTableId: !Ref PublicRouteTableA # PublicRouteTable Associate SubnetC PublicSubnetCRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnetC RouteTableId: !Ref PublicRouteTableC # PrivateRouteTable Associate SubnetA PrivateSubnetARouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnetA RouteTableId: !Ref PrivateRouteTableA # PrivateRouteTable Associate SubnetC PrivateSubnetCRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnetC RouteTableId: !Ref PrivateRouteTableC # ------------------------------------------------------------# # Elastic IP address for NAT Gateway # ------------------------------------------------------------# EipNatGatewayA: Type: AWS::EC2::EIP Properties: Domain: vpc Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} - Key: Name Value: !Sub eip-ngw-a-${SystemName}-${SubName} # ------------------------------------------------------------# # NAT Gateway # ------------------------------------------------------------# NatGatewayA: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt EipNatGatewayA.AllocationId ConnectivityType: public MaxDrainDurationSeconds: 350 SubnetId: !Ref PublicSubnetA Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} - Key: Name Value: !Sub ngw-a-${SystemName}-${SubName} # ------------------------------------------------------------# # Output Parameters # ------------------------------------------------------------# Outputs: # VPC VpcId: Value: !GetAtt VPC.VpcId
続編記事
続編記事が出来次第、この章を更新します。
まとめ
いかがでしたでしょうか。
今回は実装編 1 VPC でした。一般的な内容なので得るものは少ないと思いましたが、絶対に必要だったので紹介しておきました。
本記事が皆様のお役に立てれば幸いです。