こんにちは。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を利用することで何度でも同じインフラを構築することができ、なおかつ自動で行ってくれるので、一度コードを書いてしまえばとても楽にインフラを構築できることに感動いたしました。
次回の記事では案件で得たナレッジなどを共有できればと思います。