インフラをコード化して自動構築に挑戦してみました!
なんとインフラの構築をコード化できてて、再利用性が高く面倒臭い設定が
ぽちぽちっと一気にできてしまう方法を共有します!
Terraformではありません。 ←ものすごく気になりできるようになりたいがまずは転職成功から!
転職成功したら勉強したいですね!
本当にインフラコード化楽なので、一度しっかりとした構築をコードで残せば再利用できるので
気楽に構築を破壊できて気が楽です
今回はCloudFormationを使用しています
基本的にコストの低いものと必要最低限なもので
PF作成など未経験転職を目指している方向けです
余談↓
NATゲートウェイやVPCエンドポイント使ったら、、恐ろしくて放置できませんw
どこまでコード化したのか
- VPC
- Internet Gateway
- Public Route Table
- Public Subnet
- Private Route Table
- Private Subnet
- Security Group
あとはVPCをIGWにアタッチやPublic Route TableとIGWをアタッチ
あとは各Route Tableをそれぞれのsubnetとassociationなど
必須で細かいところもやってます
できるインフラ構成図
雛形みたいなものですね!
自動化してみよう!
コード説明
cloudformation-vpc.yml
でymlファイルに書いていきます
まず最初に
# CloudFormationテンプレートのバージョンを示します。
AWSTemplateFormatVersion: "2010-09-09"
# テンプレートの概要を記述します,名前みたいなものです
Description: vpc construction template
VPCとそれぞれのサブネットのサイダー(CIDR)を設定する
# キーと値のマッピングを作成,この例ではVPCやサブネットのCIDRブロックが定義してます。
Mappings:
StackConfig:
VPC:
CIDR: 10.1.0.0/16
PublicSubnet1:
CIDR: 10.1.0.0/24
PublicSubnet2:
CIDR: 10.1.1.0/24
PrivateSubnet1:
CIDR: 10.1.2.0/24
PrivateSubnet2:
CIDR: 10.1.3.0/24
後々の設定で直接CIDRを設定するとタイポミスなどが起きてしまう可能性があるからです
このように設定してあげるとミスが減ります。コードを変数に代入するイメージと一緒ですね!
ここから構築スタートまずはVPCから!
Resources
: AWSリソースを定義します!
ここからネストして各それぞれのサービスの設定をしていきます
なので次からのブロックコードはResourcesを書きませんがネストしていると思ってください!
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !FindInMap [StackConfig, VPC, CIDR]
EnableDnsSupport: true # DNS サポートを有効化
EnableDnsHostnames: true # DNS ホスト名を有効化
Tags:
- Key: Name
Value: sample-vpc # VPC に名前タグを付ける
Type: AWS::EC2::VPC
AWSのリソースタイプはVPC
Properties
は設定を定義する感じです
CidrBlock: !FindInMap [StackConfig, VPC, CIDR]
この部分は
Mappingsで設定した、StackConfigのVPCのCIDRなので、、、
CidrBlockには10.1.0.0/16
が入ります。
次にタグでキーが名前値が自分が名付けたいvpcの名前を入れてください。
次インターネットゲートウェイを作成します
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: vpc-igw
ここはそのままですね、IGWを作成しsample-vpc-igw
と名付けています
IGWを作成したVPCとアタッチします
AttachGateway:
# VPC ゲートウェイアタッチメント。上記のVPCとインターネットゲートウェイを接続します
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC # 作成した VPC への参照
InternetGatewayId: !Ref InternetGateway # 作成した IGW への参照
VpcId: !Ref VPC
これは上記で作成したVPCIDを返します。
InternetGatewayId: !Ref InternetGateway
作成した IGWのIDを返す
この二行でインターネットゲートウェイはVPCとアタッチできます
!Refを使用して今回はVPCリソースIDを参照しました
ルートテーブルやサブネット、セキュリティグループを特定のVPCに関連付ける際にも使用します
公開のルートテーブルを作成
PublicRouteTable:
Type: AWS::EC2::RouteTable
DependsOn: AttachGateway
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: public-route-table
DependsOn: AttachGateway
これはIGWがVPCにアタッチされた後に作成してくださいという意味です。
ここでもきました!VpcId: !Ref VPC
です
これはこの公開ルートテーブルが関連づけられるVPCのIDを指定しています
あとは名前でpublic-route-table
としています
IGWと公開するルートテーブルのルートを追加する
PublicRoute:
Type: AWS::EC2::Route # 公開ルートのリソースを定義します。
DependsOn: AttachGateway # インターネットゲートウェイがVPCにアタッチされた後に作成されます。
Properties:
RouteTableId: !Ref PublicRouteTable # このルートが追加されるルートテーブルのIDを指定します。
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway # このルートのターゲットとなるインターネットゲートウェイを指定します。
先ほどと似たようなところはコメントアウトでまとめました
DestinationCidrBlock: 0.0.0.0/0
これは
このルートの宛先CIDRを指定します。ここでは全てのIPアドレスを意味します
次にパブリックサブネット達を作成
PublicSubnet1:
Type: AWS::EC2::Subnet # 公開サブネットのリソースを定義します。
DependsOn: AttachGateway # インターネットゲートウェイがVPCにアタッチされた後に作成されます。
Properties:
AvailabilityZone: "ap-northeast-1a"
VpcId: !Ref VPC # このサブネットが関連付けられるVPCのIDを指定します。
CidrBlock: !FindInMap [StackConfig, PublicSubnet1, CIDR]
Tags:
- Key: Name
Value: Public Subnet a
AvailabilityZone: "ap-northeast-1a"
これはサブネットが配置されるAZを指定してます
久々に来ましたCidrBlock: !FindInMap [StackConfig, PublicSubnet1, CIDR]
これはVPCのサイダーを指定する時に出ましたね
10.1.0.0/24
を意味します。
名前はPublic Subnet aと名付けました
先程のサブネットを公開ルートテーブルに紐づけています
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation # サブネットとルートテーブルの関連付けを定義します。
Properties:
SubnetId: !Ref PublicSubnet1 # 関連付けられるサブネットのIDを指定します。
RouteTableId: !Ref PublicRouteTable # 関連付けられるルートテーブルのIDを指定します。
公開ルートテーブルに先ほど作ったPublic Subnet a
を紐付け(関連づけ)ています
同じ感じでもう一つのパブリックサブネットを作成
PublicSubnet2:
Type: AWS::EC2::Subnet
DependsOn: AttachGateway
Properties:
AvailabilityZone: "ap-northeast-1c"
VpcId: !Ref VPC
CidrBlock: !FindInMap [StackConfig, PublicSubnet2, CIDR]
Tags:
- Key: Name
Value: Public Subnet c
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicRouteTable
まとめてどん!
変更点はAZをaからcへサイダーもPublicSubnet2になっています
名前はPublic Subnet cです
関連づけはPublicSubnet2も同じ公開ルートテーブルに関連づけています
次にプライベートサブネットです大体同じ感じなのでまとめます
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: private-route-table
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: "ap-northeast-1a"
VpcId: !Ref VPC
CidrBlock: !FindInMap [StackConfig, PrivateSubnet1, CIDR]
Tags:
- Key: Name
Value: Private Subnet b
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref PrivateRouteTable
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: "ap-northeast-1c"
VpcId: !Ref VPC
CidrBlock: !FindInMap [StackConfig, PrivateSubnet2, CIDR]
Tags:
- Key: Name
Value: Private Subnet d
PrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet2
RouteTableId: !Ref PrivateRouteTable
ルートテーブル
作成したVPCにprivate-route-tableと名付け
非公開ルートテーブルを作成
サブネット
これらも作成したVPCの中に作ってます
PrivateSubnet1: Private Subnet bと名付け
AZは東京リージョンのaでサイダーもそれぞれ違うものを指定
PrivateSubnet2: Private Subnet dと名付け
AZは東京リージョンのcでサイダーもそれぞれ違うものを指定
作ったサブネットを非公開ルートテーブルと関連づけ
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref PrivateRouteTable
PrivateSubnet1はPrivateRouteTableと関連づけできたなと
みた感じそのまんまのイメージでいいと思います。
ちなみに非公開なのでIGWとはルートはつなげません。
最後にセキュリティグループ作成
まずはパブリックな方から
PublicSecurityGroup:
Type: AWS::EC2::SecurityGroup # 公開セキュリティグループのリソースを定義します。
Properties:
GroupDescription: Allow SSH snd HTTP and HTTPS # グループの説明を設定します。
VpcId: !Ref VPC # このセキュリティグループが関連付けられるVPCのIDを指定します。
SecurityGroupIngress: # セキュリティグループのインバウンドルールを定義します。
- IpProtocol: tcp # プロトコルを指定します。ここではTCPを指定しています。
FromPort: 22 # 開始ポート番号を指定します。ここでは22(SSH)を指定しています。
ToPort: 22 # 終了ポート番号を指定します。ここでは22(SSH)を指定しています。
CidrIp: 0.0.0.0/0 # 許可する送信元CIDR IPアドレス範囲を指定します。ここでは全てのIPアドレスを意味します。
- IpProtocol: tcp
FromPort: 80 # HTTP(80)を許可します。
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443 # HTTPS(443)を許可します。
ToPort: 443
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 3000 # ポート3000を許可します。
ToPort: 3000
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: public-sg # 名前
ブロックコードでコメントアウトで書いた方が自分も理解が深まる気がする
RDS用のセキュリティグループ
RDSSecurityGroup:
Type: AWS::EC2::SecurityGroup # RDS用のセキュリティグループを定義します。
Properties:
GroupDescription: Allow Postgres SQL from ECS only # グループの説明を設定します。
VpcId: !Ref VPC # このセキュリティグループが関連付けられるVPCのIDを指定します。
SecurityGroupIngress: # セキュリティグループのインバウンドルールを定義します。
- IpProtocol: tcp # プロトコルを指定します。ここではTCPを指定しています。
FromPort: 5432 # 開始ポート番号を指定します。ここでは5432(PostgreSQL)を指定しています。
ToPort: 5432 # 終了ポート番号を指定します。ここでは5432(PostgreSQL)を指定しています。
SourceSecurityGroupId: !GetAtt PublicSecurityGroup.GroupId # 許可する送信元セキュリティグループを指定します。ここでは公開セキュリティグループを指定しています。
Tags:
- Key: Name
Value:rds-sg # 名前
DB置き場なのでここでは接続許可できるのは自分のアプリのみとなります。
外部からの通信はできないようにしています
これにて必要最低限の設定をコード化しました!
次はどうやってこのファイルを使うのかを紹介します。
AWSのコンソールへ行ってCloudFormationと検索
デザイナーで表示するとこんな感じの図が
なんもいじってない状態です。この図でインフラ構成図を理解するには僕にはまだ早かったようです
次にスタック名はお好きにどうぞ
他は何もいじってません
送信ボタンを押したらできます
こんな感じでステータスが緑色でcreate_completeになればおk!!
VPC周りが自動で構築してくれています
同じような設定を何度もするのはめんどくさいですし
このように一回作ったりテンプレができれば名前とかサイダーの変更などひょいひょいっと変更するだけで
あとは自動でやってくれます。
終わります!
お疲れ様でした!!!
余談
実はRDSとECS(クラスター、タスク、サービス)も自動化したのですが細かいところの設定がわからなく諦めました
コストの高い部分ですし慎重にいきたい部分なので手動がいいかなと。
一様挑戦したコード達
ECS編
Resources:
MyLogGroup:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: "/ecs/real_world"
MyCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: real_world_ecs
MyTaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: real-world-api
Cpu: '256'
Memory: '512'
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn: !Sub "arn:aws:iam::${AWS::AccountId}:role/ecsTaskExecutionRole"
ContainerDefinitions:
- Name: rails-api
Image: ここにECRでpushしたイメージのURIが入る
Memory: 128
PortMappings:
- ContainerPort: 3000
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: /ecs/real_world
awslogs-region: ap-northeast-1
awslogs-stream-prefix: ecs
MyECSService:
Type: "AWS::ECS::Service"
Properties:
Cluster: !Ref MyCluster
TaskDefinition: !Ref MyTaskDefinition
DesiredCount: 1
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- !Ref PublicSecurityGroup
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
RDS編
MyDBInstance:
Type: "AWS::RDS::DBInstance"
Properties:
Engine: postgres
EngineVersion: "14.6"
DBInstanceIdentifier: "real-world-db"
MasterUsername: "admin"
MasterUserPassword: "password"
AllocatedStorage: "20"
StorageType: "gp2"
DBInstanceClass: "db.t3.micro"
VPCSecurityGroups:
- !Ref RDSSecurityGroup
DBSubnetGroupName: !Ref DBSubnetGroup
PubliclyAccessible: false
MultiAZ: false
AutoMinorVersionUpgrade: false
BackupRetentionPeriod: 0
DBName: "real_world"
DBSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: "My DB Subnet Group"
SubnetIds:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
本当はマルチAZのがいいのはわかっていますがコストが高くて。。。。。orz
細かい設定が大事だから今回は手動にしました。