こんにちは。2024新人の加藤です。
今回はAWS上のCloudFormationと構成管理ツールのAnsibleを使用し、その便利さに感銘を受けたため、具体的に何を行って、どのような部分に感銘を受けたかご紹介したいと思います。
Ansibleとは
Ansibleとは、RedHat社が開発するシステム設定やソフトウェアの自動化を行う構成管理ツールです。
Ansibleにはコントロールノードと管理対象ノードが存在します。
コントロールノードに管理対象ノードのあるべき状態を記載した「Playbook」ファイルを作成し、「Inventory」ファイルに管理対象ノードの情報を記載します。「Inventory」と「Playbook」の内容は以下のようになっています。
- Inventory
リモートホストやグループを管理するファイル
ファイル名は「hosts」 - Playbook
リモートホストの状態を定義するymlファイル
Inventoryで管理されたグループやホスト単位で作成
ファイル名は「main.yml」
また、Playbookファイルは「ロール」形式を取ることによって、効率的にファイル管理をすることができるようになります。ロールとは、Playbookファイルを複数に分割し、別のファイルとして実行・管理できる仕組みのことです。
構成概要
アプリケーションをEC2上にホストし、プライベートネットワーク環境のPCからそのEC2にアクセスしWebページが表示されるか確認します。EC2へのアプリケーションのホストは構成管理ツール(Ansible)を使用し、自動化します。
AWS CloudFormationで各リソースをIaC化し、ルートテンプレートから複数スタックを作成しています。
スタック構造は以下のようになっています。
- Application_Stack:親スタック
- Network_Stack:ネットワークリソースを作成する子スタック(Subnet、Route)
- Application_quickstart_Stack:アプリケーション実行に必要なリソースを作成する子スタック(IAM、SecurityGroup、Route53、ACM、ELB、EC2)
各リソースの詳細内容
各リソースの詳細内容は以下のようになっています。
| No. | カテゴリ | 項目 | 詳細 | 備考 |
|---|---|---|---|---|
| 1 | アプリケーション | Spring Boot |
WebアプリケーションはSpring Bootを使用して作成する ページ概要は以下の通りである
|
|
| 2 | Ansible | Playbook | 以下のAnsible構成図を基に作成する
各ロールの役割を記載する linux_common_config:
web_ui_config:
|
今回はAnsibleをローカルホストから実行するためは、インベントリファイルは作成しない |
| 3 | AWS | IAM | 以下の権限を付与する
|
|
| 4 | VPC | サブネット:構成図を基に作成する
ルートテーブル:作成したサブネットの関連付け、および Gateway へのルーティングを設定する |
InternetGateway、NatGatewayは既に作成されているものを使用する | |
| 5 | SecurityGroup(ELB) | プライベートネットワーク環境(netskopeのグローバルIP)からアクセスできる設定にする | ||
| 6 | SecurityGroup(EC2) | ELBのみからアクセスできる設定にする | ||
| 7 | Route53 | 指定の共通ホストゾーンを利用し、サブドメインのホストゾーンを作成する
サブドメインのホストゾーンには以下を設定する
|
共通ホストゾーンのゾーン情報にサブドメインのNSレコード情報を登録する | |
| 8 | ACM | 東京リージョンに証明書を発行する(パブリック証明書をDNS検証にて発行) | ||
| 9 | ELB | Load balancer typeは「Application」とする | セキュリティ強化のため、アクセスログの有効化を実施する | |
| 10 | EC2 | インスタンスタイプは「t3.micro」とする
AWS::CloudFormation::Init リソースで以下を実施する
UserDataで以下を実施する
|
AMIは既に作成されている共通AMIを使用する | |
| 11 | S3 | 名前は「hrd-d0-s3b-katou」とする
2つの子スタックテンプレート(Network_Stack.yml、Application_quickstart_Stack)とAnsibleのPlaybookファイルを格納しておく |
Ansible
AWS::CloudFormation::Init リソースでAnsibleをEC2上で実行することにより、スタック作成時にアプリケーションが自動で実行されるようになります。また、AnsibleのPlaybookファイルはS3に配置しておき、AWS::CloudFormation::Init リソースでS3からEC2にPlaybookファイルをダウンロードします。
Playbookファイル
# Playbookファイルのrolesを関連付け
- hosts: localhost
become: true
connection: local
roles:
- linux_common_config
- web_ui_config
「linux_common_config」ロールのタスクファイル
# yumリポジトリのアップデート
- name: Update all packages
yum:
name: '*'
update_only: yes
「web_ui_config」ロールのタスクファイル
# Java 17 Amazon Corretoをダウンロード
- name: Install Java 17 Amazon Correto
yum:
name: java-17-amazon-corretto
state: installed
# JarファイルをS3バケットからローカルホストにダウンロード
- name: Copy Jar file from AWS S3 Bucket to localhost
command: "aws s3 cp s3://hrd-d0-s3b-katou/web-ui-teamG-katou-0.0.1-SNAPSHOT.jar /tmp/web_app/"
# Jarファイルをローカルホストから実行
- name: Run command "java -jar"
command: "java -jar /tmp/web_app/web-ui-teamG-katou-0.0.1-SNAPSHOT.jar"
AWS CloudFormation テンプレート
構成図と各リソースの詳細内容を基に作成したテンプレートが以下の3つです。
以下のルートテンプレート(Application_Stack)をCloudFormationでデプロイしてください。
子スタックはネスト構造化してあるので、ルートテンプレートを実行した段階で自動で生成されます。
Application_Stack
AWSTemplateFormatVersion: 2010-09-09
Description: Root stack for 2024 Cloud Training Program - TeamG Kato
Parameters:
#TemplateNetworkのURLを記載
TemplateNetworkUrl:
Description: Network template Object URL
Type: String
Default: "https://hrd-d0-s3b-katou.s3.ap-northeast-1.amazonaws.com/Network_Stack.yml"
#TemplateApplicationQuickstartのURLを記載
TemplateApplicationQuickstartUrl:
Description: Application Quickstart Template Object URL
Type: String
Default: "https://hrd-d0-s3b-katou.s3.ap-northeast-1.amazonaws.com/Application_quickstart_Stack.yml"
#Tagsに使用するパラメータ
SystemId:
Description: System ID
Type: String
Default: "hrd"
EnvironmentId:
Description: Environment ID
Type: String
Default: "d0"
DeveloperId:
Description: Developer ID
Type: String
Default: "katou-y-test"
#サブネット作成に使用するパラメータ
VpcId:
Type: String
Description: VPC ID
Default: "vpc-04ac6f2eb1c774c79"
PublicSubnet1CidrBlock:
Description : CIDR block for Public Subnet1
Type: String
Default: "10.0.225.0/24"
PublicSubnet2CidrBlock:
Description : CIDR block for Public Subnet2
Type: String
Default: "10.0.226.0/24"
PrivateSubnet1CidrBlock:
Description : CIDR block for Private Subnet1
Type: String
Default: "10.0.227.0/24"
#ルートテーブル作成・ルート追加・ルートテーブルとサブネットの関連付けに使用するパラメータ
InternetGatewayId:
Type: String
Description: Internet Gateway ID
Default: "igw-0b1320c68c6590e15"
NatGatewayId:
Type: String
Description: Nat Gateway ID
Default: "nat-0be241ae2ac812138"
#EC2インスタンス作成に使用するパラメータ
InstanceType:
Description: EC2 Instance Type
Type: String
Default: "t3.micro"
#ホストゾーン登録・証明書に使用するパラメータ
SubDomain:
Description: FQDN of the certificate
Type: String
Default: "katou.training.aidiv1.com"
Resources:
#Network_Stackが管理するリソースのスタック
TemplateNetworkStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateNetworkUrl
Parameters:
SystemId: !Ref SystemId
EnvironmentId: !Ref EnvironmentId
DeveloperId: !Ref DeveloperId
VpcId: !Ref VpcId
CidrBlockForPublicSubnet1: !Ref PublicSubnet1CidrBlock
CidrBlockForPublicSubnet2: !Ref PublicSubnet2CidrBlock
CidrBlockForPrivateSubnet1: !Ref PrivateSubnet1CidrBlock
InternetGatewayId: !Ref InternetGatewayId
NatGatewayId: !Ref NatGatewayId
#Application_quickstart_Stackが管理するリソースのスタック
TemplateApplicationQuickstartStack:
DependsOn: TemplateNetworkStack
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref TemplateApplicationQuickstartUrl
Parameters:
SystemId: !Ref SystemId
EnvironmentId: !Ref EnvironmentId
DeveloperId: !Ref DeveloperId
VpcId: !Ref VpcId
PublicSubnet1Id: !GetAtt TemplateNetworkStack.Outputs.PublicSubnet1Id
PublicSubnet2Id: !GetAtt TemplateNetworkStack.Outputs.PublicSubnet2Id
PrivateSubnet1Id: !GetAtt TemplateNetworkStack.Outputs.PrivateSubnet1Id
CidrBlockForPrivateSubnet1: !Ref PrivateSubnet1CidrBlock
InstanceType: !Ref InstanceType
SubDomain: !Ref SubDomain
Network_Stack
AWSTemplateFormatVersion: 2010-09-09
Description: Network stack for 2024 Cloud Training Program - TeamG Kato Resource - Subnet, Route
Parameters:
#Tagsに使用するパラメータ
SystemId:
Description: System ID
Type: String
EnvironmentId:
Description: Environment ID
Type: String
DeveloperId:
Description: Developer ID
Type: String
#サブネット作成に使用するパラメータ
VpcId:
Type: String
Description: VPC ID
CidrBlockForPublicSubnet1:
Description : CIDR block for Public Subnet1
Type: String
CidrBlockForPublicSubnet2:
Description : CIDR block for Public Subnet2
Type: String
CidrBlockForPrivateSubnet1:
Description : CIDR block for Private Subnet1
Type: String
#ルートテーブル作成・ルート追加・ルートテーブルとサブネットの関連付けに使用するパラメータ
InternetGatewayId:
Type: String
Description: Internet Gateway ID
NatGatewayId:
Type: String
Description: Nat Gateway ID
Resources:
#パブリックサブネット1の作成
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VpcId
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: !Ref CidrBlockForPublicSubnet1
Tags:
- Key: Name
Value: !Sub ${SystemId}-${EnvironmentId}-sub-pub1-${DeveloperId}
- Key: Cost
Value: !Sub ${SystemId}-${EnvironmentId}-${DeveloperId}
- Key: Env
Value: !Sub ${SystemId}-${EnvironmentId}
#パブリックサブネット2の作成
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VpcId
AvailabilityZone: !Select [ 1, !GetAZs '' ]
CidrBlock: !Ref CidrBlockForPublicSubnet2
Tags:
- Key: Name
Value: !Sub ${SystemId}-${EnvironmentId}-sub-pub2-${DeveloperId}
- Key: Cost
Value: !Sub ${SystemId}-${EnvironmentId}-${DeveloperId}
- Key: Env
Value: !Sub ${SystemId}-${EnvironmentId}
#プライベートサブネット1の作成
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VpcId
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: !Ref CidrBlockForPrivateSubnet1
Tags:
- Key: Name
Value: !Sub ${SystemId}-${EnvironmentId}-sub-pri1-${DeveloperId}
- Key: Cost
Value: !Sub ${SystemId}-${EnvironmentId}-${DeveloperId}
- Key: Env
Value: !Sub ${SystemId}-${EnvironmentId}
#ルートテーブルの作成
RouteTableForPublicSubnet1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: !Sub ${SystemId}-${EnvironmentId}-rtb-pub1-${DeveloperId}
- Key: Cost
Value: !Sub ${SystemId}-${EnvironmentId}-${DeveloperId}
- Key: Env
Value: !Sub ${SystemId}-${EnvironmentId}
RouteTableForPublicSubnet2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: !Sub ${SystemId}-${EnvironmentId}-rtb-pub2-${DeveloperId}
- Key: Cost
Value: !Sub ${SystemId}-${EnvironmentId}-${DeveloperId}
- Key: Env
Value: !Sub ${SystemId}-${EnvironmentId}
RouteTableForPrivateSubnet1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: !Sub ${SystemId}-${EnvironmentId}-rtb-pri1-${DeveloperId}
- Key: Cost
Value: !Sub ${SystemId}-${EnvironmentId}-${DeveloperId}
- Key: Env
Value: !Sub ${SystemId}-${EnvironmentId}
#ルートテーブルへのルート追加
InternetGatewayRouteForPublicSubnet1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTableForPublicSubnet1
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGatewayId
InternetGatewayRouteForPublicSubnet2:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTableForPublicSubnet2
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGatewayId
NatGatewayRouteForPrivateSubnet1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTableForPrivateSubnet1
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref NatGatewayId
#ルートテーブルとサブネットの関連付け
PublicSubnet1Association:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref RouteTableForPublicSubnet1
PublicSubnet2Association:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref RouteTableForPublicSubnet2
PrivateSubnet1Association:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref RouteTableForPrivateSubnet1
Outputs:
PublicSubnet1Id:
Description: Public Subnet 1 ID
Value: !Ref PublicSubnet1
PublicSubnet2Id:
Description: Public Subnet 2 ID
Value: !Ref PublicSubnet2
PrivateSubnet1Id:
Description: Private Subnet 1 ID
Value: !Ref PrivateSubnet1
Application_quickstart_Stack
AWSTemplateFormatVersion: 2010-09-09
Description: Application Quickstart Stack for 2024 Cloud Training Program - TeamG Kato Resource - IAM, SecurityGroup, Route53, ACM, ELB, EC2
Parameters:
#Tagsに使用するパラメータ
SystemId:
Description: System ID
Type: String
EnvironmentId:
Description: Environment ID
Type: String
DeveloperId:
Description: Developer ID
Type: String
#各種ネットワーク設定パラメータ
VpcId:
Description: VPC ID
Type: String
PublicSubnet1Id:
Description: Public Subnet 1 ID
Type: String
PublicSubnet2Id:
Description: Public Subnet 2 ID
Type: String
PrivateSubnet1Id:
Description: Private Subnet 1 ID
Type: String
CidrBlockForPrivateSubnet1:
Description : CIDR block for Private Subnet1
Type: String
#EC2インスタンス作成に使用するパラメータ
InstanceType:
Description: EC2 Instance Type
Type: String
#ホストゾーン登録・証明書に使用するパラメータ
SubDomain:
Description: FQDN of the certificate
Type: String
Resources:
#IAMロールを作成
IAMRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${SystemId}-${EnvironmentId}-iam-ec2-${DeveloperId}
#どのリソースに対してどんなアクションをするかを記載
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- 'sts:AssumeRole'
#アクセス権限ポリシーを記載
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
- "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
Tags:
- Key: Name
Value: !Sub ${SystemId}-${EnvironmentId}-iam-ec2-${DeveloperId}
- Key: Cost
Value: !Sub ${SystemId}-${EnvironmentId}-${DeveloperId}
- Key: Env
Value: !Sub ${SystemId}-${EnvironmentId}
#インスタンスプロファイルの作成
InstanceProfile:
Type: 'AWS::IAM::InstanceProfile'
Properties:
Path: '/'
Roles:
- !Ref IAMRole # IAMロールへの参照
#セキュリティグループ(ELB)の作成
SecurityGroupForELB:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${SystemId}-${EnvironmentId}-sec-elb-${DeveloperId}
GroupDescription: Security Group for ELB
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: !Sub ${SystemId}-${EnvironmentId}-sec-elb-${DeveloperId}
- Key: Cost
Value: !Sub ${SystemId}-${EnvironmentId}-${DeveloperId}
- Key: Env
Value: !Sub ${SystemId}-${EnvironmentId}
#インバウンドルール・アウトバンドルールを追加
#netskopeのグローバルIPアドレスからELBへのHTTPS通信
SecurityGroupIngress1HTTPSForELB:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupForELB
IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: xxx.xxx.xxx.0/xx
#netskopeのグローバルIPアドレスからELBへのHTTPS通信
SecurityGroupIngress2HTTPSForELB:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupForELB
IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: xxx.xxx.xxx.0/xx
#netskopeのグローバルIPアドレスからELBへのHTTPS通信
SecurityGroupIngress3HTTPSForELB:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupForELB
IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: xxx.xxx.xxx.0/xx
#プライベートサブネットからELBへの8080通信
SecurityGroupIngressCustomTCP8080ForELB:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupForELB
IpProtocol: tcp
FromPort: 8080
ToPort: 8080
CidrIp: !Ref CidrBlockForPrivateSubnet1
#セキュリティグループ(EC2)の作成
SecurityGroupForEC2:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${SystemId}-${EnvironmentId}-sec-ec2-${DeveloperId}
GroupDescription: Security Group for EC2
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: !Sub ${SystemId}-${EnvironmentId}-sec-ec2-${DeveloperId}
- Key: Cost
Value: !Sub ${SystemId}-${EnvironmentId}-${DeveloperId}
- Key: Env
Value: !Sub ${SystemId}-${EnvironmentId}
#ELBからEC2への8080通信
SecurityGroupIngressCustomTCP8080ForEC2:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupForEC2
IpProtocol: tcp
FromPort: 8080
ToPort: 8080
SourceSecurityGroupId: !Ref SecurityGroupForELB
#EC2インスタンスの作成
EC2:
Type: AWS::EC2::Instance
#Metadataの記述
Metadata:
AWS::CloudFormation::Init:
configSets:
Default:
- InstallAnsible
- CreateDirectory
- Download
- RunAnsible
#Ansibleをインストールするためのデータ
InstallAnsible:
packages:
yum:
ansible: []
#各種ディレクトリを作成するための実行コマンドデータ
CreateDirectory:
commands:
mkdirWebApp:
command: "mkdir -p /tmp/web_app"
mkdirAnsible:
command: "mkdir -p /tmp/ansible"
mkdirLinuxCommonConfig:
command: "mkdir -p /tmp/ansible/roles/linux_common_config/tasks"
mkdirWebUiConfig:
command: "mkdir -p /tmp/ansible/roles/web_ui_config/tasks"
#AnsibleのPlaybookをS3バケットからダウンロードするためのデータ
Download:
commands:
downloadWebUiStartup:
command: aws s3 cp s3://hrd-d0-s3b-katou/ansible/web_ui_startup.yml /tmp/ansible/
downloadLinuxCommonConfig:
command: aws s3 cp s3://hrd-d0-s3b-katou/ansible/roles/linux_common_config/tasks/main.yml /tmp/ansible/roles/linux_common_config/tasks/
downloadWebUiConfig:
command: aws s3 cp s3://hrd-d0-s3b-katou/ansible/roles/web_ui_config/tasks/main.yml /tmp/ansible/roles/web_ui_config/tasks/
#AnsibleにおけるPlaybookの実行コマンドデータ
RunAnsible:
commands:
runWebUiStartup:
command: "ansible-playbook -c local /tmp/ansible/web_ui_startup.yml"
Properties:
ImageId: ami-0cb4639ca5eb232fc
InstanceType: !Ref InstanceType
SubnetId: !Ref PrivateSubnet1Id
SecurityGroupIds:
- !Ref SecurityGroupForEC2
IamInstanceProfile: !Ref InstanceProfile
Tags:
- Key: Name
Value: !Sub ${SystemId}-${EnvironmentId}-ec2-${DeveloperId}
- Key: Cost
Value: !Sub ${SystemId}-${EnvironmentId}-${DeveloperId}
- Key: Env
Value: !Sub ${SystemId}-${EnvironmentId}
#UserDataの記述
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum install -y aws-cfn-bootstrap
/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource EC2 --configsets Default --region ${AWS::Region}
#ELBの作成
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub ${SystemId}-${EnvironmentId}-elb-${DeveloperId}
Subnets:
- !Ref PublicSubnet1Id
- !Ref PublicSubnet2Id
SecurityGroups:
- !Ref SecurityGroupForELB
Scheme: internet-facing
Type: application
LoadBalancerAttributes:
- Key: access_logs.s3.enabled
Value: true
- Key: access_logs.s3.bucket
Value: lms-d0-s3-logcollection
Tags:
- Key: Name
Value: !Sub ${SystemId}-${EnvironmentId}-elb-${DeveloperId}
- Key: Cost
Value: !Sub ${SystemId}-${EnvironmentId}-${DeveloperId}
- Key: Env
Value: !Sub ${SystemId}-${EnvironmentId}
#ELBのリスナー設定
ListenerForELB:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref ApplicationLoadBalancer
Port: 443
Protocol: HTTPS
Certificates:
- CertificateArn: !Ref Certificate
DefaultActions:
- Type: forward
TargetGroupArn: !Ref TargetGroup
#ターゲットグループ作成
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
VpcId: !Ref VpcId
Name: !Sub ${SystemId}-${EnvironmentId}-trg-${DeveloperId}
Protocol: HTTP
Port: 8080
HealthCheckProtocol: HTTP
HealthCheckPort: 8080
HealthCheckPath: "/actuator/health"
HealthCheckTimeoutSeconds: 10
HealthCheckIntervalSeconds: 20
HealthyThresholdCount: 3
UnhealthyThresholdCount: 2
Targets:
- Id: !Ref EC2
Port: 8080
TargetType: instance
Tags:
- Key: Name
Value: !Sub ${SystemId}-${EnvironmentId}-trg-${DeveloperId}
- Key: Cost
Value: !Sub ${SystemId}-${EnvironmentId}-${DeveloperId}
- Key: Env
Value: !Sub ${SystemId}-${EnvironmentId}
#Route53のホストゾーン作成
HostedZone:
Type: AWS::Route53::HostedZone
Properties:
HostedZoneConfig:
Comment: "Subdomain of training.aidiv1.com"
Name: !Ref SubDomain
HostedZoneTags:
- Key: Name
Value: !Sub ${SystemId}-${EnvironmentId}-r53-${DeveloperId}
- Key: Cost
Value: !Sub ${SystemId}-${EnvironmentId}-${DeveloperId}
- Key: Env
Value: !Sub ${SystemId}-${EnvironmentId}
#ホストゾーンへのレコード登録
#ELBのAレコード登録
ARecordSetForELB:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId: !Ref HostedZone
Name: !Ref SubDomain
Type: A
AliasTarget:
HostedZoneId: !GetAtt ApplicationLoadBalancer.CanonicalHostedZoneID
DNSName: !GetAtt ApplicationLoadBalancer.DNSName
#training.aidiv1.comへのNSレコード登録
NSRecordSetForParentDomain:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId: Z037543234RUL8GGC5CBK
Name: !Ref SubDomain
Type: NS
TTL: 300
ResourceRecords: !GetAtt HostedZone.NameServers
#Certificate Manager
Certificate:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: !Ref SubDomain
ValidationMethod: DNS
DomainValidationOptions:
- DomainName: !Ref SubDomain
HostedZoneId: !Ref HostedZone
Tags:
- Key: Name
Value: !Sub ${SystemId}-${EnvironmentId}-acm-${DeveloperId}
- Key: Cost
Value: !Sub ${SystemId}-${EnvironmentId}-${DeveloperId}
- Key: Env
Value: !Sub ${SystemId}-${EnvironmentId}
実行画面
CloudFormation上でルートテンプレート「Application_Stack」を実行します。
- AWSマネジメントコンソール上でCloudFormationを検索し、「スタックの作成」を押下します。

- ルートテンプレート「Application_Stack」をアップロードします。

- 適当なスタック名を入力し、下までスクロールし「次へ」を押下します。

- 下までスクロールし、二つのチェックボックスにチェックを入れ、「次へ」を押下します。

- 下までスクロールし、「送信」を押下するとルートテンプレートが実行されます。

- 実行されると親スタックが作成され、子スタックが二つ作成されます。リソースが全て作成されると以下の画面のようになります。

- プライベートネットワーク環境のPCから「https://katou.training.aidiv1.com」にアクセスします。以下のような画面が表示されたらWebアプリケーションの自動実行が成功となります。

注意点
AnsibleとCloudFormationを使用した際に、個人的につまづいた点を共有します。
- AnsibleをS3からEC2へダウンロードする際に、Ansibleのディレクトリ構造を保持したままPlaybookファイルをダウンロードしてください。ディレクトリ構造が保たれていないと、適切なロールのタスクが実行されません。
- AnsibleのPlaybookファイルに「become: true」を記述していないと、権限がなくコマンドが実行されない場合があります。「become: true」は一時的にroot権限を与えるものです。
- Spring Bootアプリケーションのリッスンポート番号は8080なので、私のように「HTTPプロトコルを使用するのでポート番号は80」と決めつけてしまうと痛い目をみます。
- 共通ホストゾーンのゾーン情報にサブドメインのNSレコード情報を登録しないと、共通ホストゾーンからのサブドメインの名前解決がうまくいきません。
- Certificate Managerでサブドメインの証明書をリクエストする際に検証方法としてDNS検証を選んだ場合、CloudFormationではデフォルトで、サブドメインのホストゾーンにCNAMEゾーン情報が登録されます。
最後に
以前行ったハンズオンではAWSをマネジメントコンソール上から画面をポチポチしてインフラ構築をしていたので、一度リソースを削除してから同じインフラを構築しようとなるととても面倒でした。それがCloudFormationとAnsibleを利用することで何度でも同じインフラを構築することができ、なおかつ自動で行ってくれるので、一度コードを書いてしまえばとても楽にインフラを構築できることに感動いたしました。
次回の記事では案件で得たナレッジなどを共有できればと思います。


