こんにちは、広野です。
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
続編記事

code-server と ALB で AWS Cloud9 代替の研修用 IDE を提供する - 実装編 2 ALB
AWS Cloud9 の代替 IDE を作成しました。私が試みた設計を紹介したいと思います。本記事は実装編 2、ALB です。

code-server と ALB で AWS Cloud9 代替の研修用 IDE を提供する - 実装編 3 EC2
AWS Cloud9 の代替 IDE を作成しました。私が試みた設計を紹介したいと思います。本記事は実装編 3、EC2 です。
まとめ
いかがでしたでしょうか。
今回は実装編 1 VPC でした。一般的な内容なので得るものは少ないと思いましたが、絶対に必要だったので紹介しておきました。
本記事が皆様のお役に立てれば幸いです。



