EC2インスタンスのVirtualHost(Apache)からサイトを分離する
はじめに
ApacheのVirtualHostで複数の静的ウェブサイトや動的ウェブサイトを運用するEC2インスタンスからサイトを分離してみました。
分離前の構成図
分離前の構成は以下の通りです。
example.com
site1.example.com
site2.example.com
3つのウェブサイトのリクエストを処理できるようにEC2インスタンスのApacheのVirtualHostで設定されています。
<VirtualHost *:80> ServerName example.com DocumentRoot /var/www/html/ </VirtualHost> <VirtualHost *:80> ServerName site1.example.com DocumentRoot /var/www/site1/ </VirtualHost> <VirtualHost *:80> ServerName site2.example.com DocumentRoot /var/www/site2/ </VirtualHost>
Route53で3つのウェブサイトのDNSレコードのValueをALB DNS名とします。ApacheのVirtualHostがHostヘッダを見て振り分けしてくれるのでALBのリスナールールは不要です。
ウェブサイト分離の動きを見るために、CFnテンプレート site.yml
を用意しました。ウェブサイトのコンテンツは フリーホームページ.netを使わせていただきました。(ありがとうございます)
site.yml
AWSTemplateFormatVersion: "2010-09-09" Description: Template generated by rain Parameters: billingTag: Type: String domainName: Type: String domainName1: Type: String domainName2: Type: String ec2Ami: Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 env: Type: String hostedZoneId: Type: String instanceClass: Type: String instanceType: Type: String masterPassword: Type: String NoEcho: true sysName: Type: String vpcCidr: Type: String Resources: Alb: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: IpAddressType: ipv4 Name: !Sub ${env}-${sysName}-alb Scheme: internet-facing SecurityGroups: - !Ref AlbSG Subnets: - !Ref MyPublicSubnet1 - !Ref MyPublicSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-api - Key: BillingGroup Value: !Ref billingTag Type: application AlbAcm: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName DomainValidationOptions: - DomainName: !Ref domainName HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbAcm1: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName1 DomainValidationOptions: - DomainName: !Ref domainName1 HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm1 - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbAcm2: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName2 DomainValidationOptions: - DomainName: !Ref domainName2 HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm2 - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: Certificates: - CertificateArn: !Ref AlbAcm DefaultActions: - FixedResponseConfig: ContentType: text/plain MessageBody: Unauthorized Access StatusCode: "403" Type: fixed-response LoadBalancerArn: !Ref Alb Port: 443 Protocol: HTTPS SslPolicy: ELBSecurityPolicy-2016-08 AlbListenerCertificate1: Type: AWS::ElasticLoadBalancingV2::ListenerCertificate Properties: Certificates: - CertificateArn: !Ref AlbAcm1 ListenerArn: !Ref AlbListener AlbListenerCertificate2: Type: AWS::ElasticLoadBalancingV2::ListenerCertificate Properties: Certificates: - CertificateArn: !Ref AlbAcm2 ListenerArn: !Ref AlbListener AlbListnerRule1: Type: AWS::ElasticLoadBalancingV2::ListenerRule Properties: Actions: - TargetGroupArn: !Ref TargetGroup Type: forward Conditions: - Field: path-pattern PathPatternConfig: Values: - '*' ListenerArn: !Ref AlbListener Priority: 1 AlbRecordSet: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Join - "" - - dualstack. - !GetAtt Alb.DNSName EvaluateTargetHealth: false HostedZoneId: Z14GRHDCWA56QT HostedZoneId: !Ref hostedZoneId Name: !Ref domainName Type: A AlbRecordSet1: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Join - "" - - dualstack. - !GetAtt Alb.DNSName EvaluateTargetHealth: false HostedZoneId: Z14GRHDCWA56QT HostedZoneId: !Ref hostedZoneId Name: !Ref domainName1 Type: A AlbRecordSet2: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Join - "" - - dualstack. - !GetAtt Alb.DNSName EvaluateTargetHealth: false HostedZoneId: Z14GRHDCWA56QT HostedZoneId: !Ref hostedZoneId Name: !Ref domainName2 Type: A AlbSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Application load barancer. GroupName: !Sub ${env}-${sysName}-alb-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - CidrIp: 0.0.0.0/0 FromPort: 443 IpProtocol: tcp ToPort: 443 Tags: - Key: Name Value: !Sub ${env}-${sysName}-alb-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC DbInstance1: Type: AWS::RDS::DBInstance DeletionPolicy: Delete Properties: AutoMinorVersionUpgrade: false DBClusterIdentifier: !Ref clusterAurora DBInstanceClass: !Ref instanceClass DBInstanceIdentifier: !Sub ${env}-${sysName}-db1 DBParameterGroupName: !Ref ParameterGroupAurora EnablePerformanceInsights: false Engine: aurora-mysql MonitoringInterval: 0 PubliclyAccessible: false Tags: - Key: Name Value: !Sub ${env}-${sysName}-db1 - Key: BillingGroup Value: !Ref billingTag DbInstance2: Type: AWS::RDS::DBInstance DeletionPolicy: Delete Properties: AutoMinorVersionUpgrade: false DBClusterIdentifier: !Ref clusterAurora DBInstanceClass: !Ref instanceClass DBInstanceIdentifier: !Sub ${env}-${sysName}-db2 DBParameterGroupName: !Ref ParameterGroupAurora EnablePerformanceInsights: false Engine: aurora-mysql MonitoringInterval: 0 PubliclyAccessible: false Tags: - Key: Name Value: !Sub ${env}-${sysName}-db2 - Key: BillingGroup Value: !Ref billingTag Instance1: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: 3000 VolumeSize: 8 VolumeType: gp3 CreditSpecification: CPUCredits: standard IamInstanceProfile: !Ref ec2Profile ImageId: !Ref ec2Ami InstanceType: !Ref instanceType SecurityGroupIds: - !Ref ec2SG SubnetId: !Ref MyPublicSubnet1 Tags: - Key: Name Value: !Sub ${env}-${sysName}-instance1 - Key: BillingGroup Value: !Ref billingTag UserData: !Base64 Fn::Sub: | #!/bin/bash yum install -y httpd cp -p /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.org cat <<EOF >> /etc/httpd/conf/httpd.conf <VirtualHost *:80> ServerName ${domainName} DocumentRoot /var/www/html/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName1} DocumentRoot /var/www/site1/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName2} DocumentRoot /var/www/site2/ </VirtualHost> EOF mkdir /var/www/{site1,site2} cd /var/www/html/ curl -O https://free-hp.net/clinic/cl_002/clinic_002_DL.zip && unzip clinic_002_DL.zip && mv clinic_002_DL/* . && rmdir clinic_002_DL && rm clinic_002_DL.zip cd /var/www/site1/ curl -O https://free-hp.net/restaurant/re_002/restaurant_002_DL.zip && unzip restaurant_002_DL.zip && mv restaurant_002_DL/* . && rmdir restaurant_002_DL && rm restaurant_002_DL.zip cd /var/www/site2/ curl -O https://free-hp.net/school/sc_001/school_001_DL.zip && unzip school_001_DL.zip && mv school_001_DL/* . && rmdir school_001_DL && rm school_001_DL.zip systemctl start httpd Instance2: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: 3000 VolumeSize: 8 VolumeType: gp3 CreditSpecification: CPUCredits: standard IamInstanceProfile: !Ref ec2Profile ImageId: !Ref ec2Ami InstanceType: !Ref instanceType SecurityGroupIds: - !Ref ec2SG SubnetId: !Ref MyPublicSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-instance2 - Key: BillingGroup Value: !Ref billingTag UserData: !Base64 Fn::Sub: | #!/bin/bash yum install -y httpd cp -p /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.org cat <<EOF >> /etc/httpd/conf/httpd.conf <VirtualHost *:80> ServerName ${domainName} DocumentRoot /var/www/html/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName1} DocumentRoot /var/www/site1/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName2} DocumentRoot /var/www/site2/ </VirtualHost> EOF mkdir /var/www/{site1,site2} cd /var/www/html/ curl -O https://free-hp.net/clinic/cl_002/clinic_002_DL.zip && unzip clinic_002_DL.zip && mv clinic_002_DL/* . && rmdir clinic_002_DL && rm clinic_002_DL.zip cd /var/www/site1/ curl -O https://free-hp.net/restaurant/re_002/restaurant_002_DL.zip && unzip restaurant_002_DL.zip && mv restaurant_002_DL/* . && rmdir restaurant_002_DL && rm restaurant_002_DL.zip cd /var/www/site2/ curl -O https://free-hp.net/school/sc_001/school_001_DL.zip && unzip school_001_DL.zip && mv school_001_DL/* . && rmdir school_001_DL && rm school_001_DL.zip systemctl start httpd MyInternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-igw - Key: BillingGroup Value: !Ref billingTag MyPrivateRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref MyInternetGateway RouteTableId: !Ref MyPrivateRouteTable MyPrivateRouteTable: Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-rtb - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 10 - !Cidr - !GetAtt MyVPC.CidrBlock - 12 - 8 Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-subnet-1 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPrivateRouteTable SubnetId: !Ref MyPrivateSubnet1 MyPrivateSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 11 - !Cidr - !GetAtt MyVPC.CidrBlock - 12 - 8 Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-subnet-2 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPrivateRouteTable SubnetId: !Ref MyPrivateSubnet2 MyPublicRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref MyInternetGateway RouteTableId: !Ref MyPublicRouteTable MyPublicRouteTable: Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-rtb - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 0 - !Cidr - !GetAtt MyVPC.CidrBlock - 2 - 8 MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-subnet-1 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPublicRouteTable SubnetId: !Ref MyPublicSubnet1 MyPublicSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 1 - !Cidr - !GetAtt MyVPC.CidrBlock - 2 - 8 MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-subnet-2 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPublicRouteTable SubnetId: !Ref MyPublicSubnet2 MyVPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref vpcCidr Tags: - Key: Name Value: !Sub ${env}-${sysName}-vpc - Key: BillingGroup Value: !Ref billingTag MyVPCGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref MyInternetGateway VpcId: !Ref MyVPC ParameterGroupAurora: Type: AWS::RDS::DBParameterGroup Properties: Description: !Sub ${env}-${sysName}-parametergroup Family: aurora-mysql5.7 RdsSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Rds Instances. GroupName: !Sub ${env}-${sysName}-rds-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - FromPort: 3306 IpProtocol: tcp SourceSecurityGroupId: !Ref ec2SG ToPort: 3306 Tags: - Key: Name Value: !Sub ${env}-${sysName}-rds-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC SubnetGroupRds: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: !Sub ${env}-${sysName}-rds-SubnetGroup DBSubnetGroupName: !Sub ${env}-${sysName}-rds-subnetgroup SubnetIds: - !Ref MyPrivateSubnet1 - !Ref MyPrivateSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-rds-SubnetGroup - Key: BillingGroup Value: !Ref billingTag TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Name: !Sub ${env}-${sysName}-ec2 Port: 80 Protocol: HTTP Tags: - Key: Name Value: !Sub ${env}-${sysName}-tg - Key: BillingGroup Value: !Ref billingTag TargetType: instance Targets: - Id: !Ref Instance1 - Id: !Ref Instance2 VpcId: !Ref MyVPC clusterAurora: Type: AWS::RDS::DBCluster DeletionPolicy: Delete Properties: BackupRetentionPeriod: 7 DBClusterIdentifier: !Sub ${env}-${sysName}-cluster DBClusterParameterGroupName: !Ref clusterParameterGroupAurora DBSubnetGroupName: !Ref SubnetGroupRds DeletionProtection: false EnableCloudwatchLogsExports: - audit - error - general - slowquery Engine: aurora-mysql EngineVersion: 5.7.mysql_aurora.2.10.0 MasterUserPassword: !Ref masterPassword MasterUsername: root Port: 3306 PreferredBackupWindow: 17:00-18:00 PreferredMaintenanceWindow: tue:18:00-tue:19:00 StorageEncrypted: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-cluster - Key: BillingGroup Value: !Ref billingTag VpcSecurityGroupIds: - !Ref RdsSG clusterParameterGroupAurora: Type: AWS::RDS::DBClusterParameterGroup Properties: Description: !Sub ${env}-${sysName}-cluster-parametergroup Family: aurora-mysql5.7 Parameters: character_set_client: utf8 character_set_connection: utf8 character_set_database: utf8 character_set_results: utf8 character_set_server: utf8 general_log: 1 server_audit_events: CONNECT,QUERY,QUERY_DCL,QUERY_DDL,QUERY_DML,TABLE server_audit_logging: 1 slow_query_log: 1 time_zone: Asia/Tokyo ec2Profile: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !Ref ec2Role ec2Role: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - ec2.amazonaws.com Version: "2012-10-17" ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore - arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess ec2SG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: ec2 Instances. GroupName: !Sub ${env}-${sysName}-ec2-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - FromPort: 80 IpProtocol: tcp SourceSecurityGroupId: !Ref AlbSG ToPort: 80 Tags: - Key: Name Value: !Sub ${env}-${sysName}-ec2-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC
Rainを使ったデプロイで説明していきます。
rain deploy -y ./site.yml site --params \ env=prd,\ sysName=example,\ billingTag=example,\ vpcCidr=10.0.0.0/16,\ domainName=example.com,\ domainName1=site1.example.com,\ domainName2=site2.example.com,\ hostedZoneId=Z10116024XXXXXXXXXXX,\ instanceType=t3.micro,\ instanceClass=db.t3.small,\ masterPassword=Passw0rd
Rainを使わない場合は、各パラメータを参考にコンソールなどからデプロイしてください。
静的ウェブサイトの分離
site2.example.com
が静的ウェブサイトだとして、VirtualHostから分離していきます。静的ウェブサイトであればS3ホスティングの利用が考えられますが、S3ホスティングではhttpsプロトコルが利用できません。
ドキュメントにもある通り、Amazon CloudFront+S3で静的ウェブサイトをHTTPSで利用できるようにします。site2.example.com
を分離した構成図は以下です。
オリジンのS3バケットには、 site2.example.com
のコンテンツを格納します。CloudFrontのOrigin Access Identity(以下、OAI)でCloudFront DistributionのみS3バケットのアクセスを許可します。CloudFront+S3リソースが出来上がった後、Route53で site2.example.com
レコードのValueをCloudFront URL dxxxxxxxxxxxx.cloudfront.net
に書き換えます。
CloudFront+S3リソースの作成
まずは、site2.example.com
のオリジンのS3バケットを作成する為、先程の site.yml
テンプレートに site2Bucket
リソースを追記しました。
site2Bucket: DeletionPolicy: Delete UpdateReplacePolicy: Delete Type: AWS::S3::Bucket Properties: BucketName: !Ref domainName2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-site2-contents-bucket - Key: BillingGroup Value: !Ref billingTag
上記リソースを追記した site.yml
テンプレートです。
site.yml(S3バケットリソース作成)
AWSTemplateFormatVersion: "2010-09-09" Description: Template generated by rain Parameters: billingTag: Type: String domainName: Type: String domainName1: Type: String domainName2: Type: String ec2Ami: Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 env: Type: String hostedZoneId: Type: String instanceClass: Type: String instanceType: Type: String masterPassword: Type: String NoEcho: true sysName: Type: String vpcCidr: Type: String Resources: Alb: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: IpAddressType: ipv4 Name: !Sub ${env}-${sysName}-alb Scheme: internet-facing SecurityGroups: - !Ref AlbSG Subnets: - !Ref MyPublicSubnet1 - !Ref MyPublicSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-api - Key: BillingGroup Value: !Ref billingTag Type: application AlbAcm: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName DomainValidationOptions: - DomainName: !Ref domainName HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbAcm1: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName1 DomainValidationOptions: - DomainName: !Ref domainName1 HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm1 - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbAcm2: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName2 DomainValidationOptions: - DomainName: !Ref domainName2 HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm2 - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: Certificates: - CertificateArn: !Ref AlbAcm DefaultActions: - FixedResponseConfig: ContentType: text/plain MessageBody: Unauthorized Access StatusCode: "403" Type: fixed-response LoadBalancerArn: !Ref Alb Port: 443 Protocol: HTTPS SslPolicy: ELBSecurityPolicy-2016-08 AlbListenerCertificate1: Type: AWS::ElasticLoadBalancingV2::ListenerCertificate Properties: Certificates: - CertificateArn: !Ref AlbAcm1 ListenerArn: !Ref AlbListener AlbListenerCertificate2: Type: AWS::ElasticLoadBalancingV2::ListenerCertificate Properties: Certificates: - CertificateArn: !Ref AlbAcm2 ListenerArn: !Ref AlbListener AlbListnerRule1: Type: AWS::ElasticLoadBalancingV2::ListenerRule Properties: Actions: - TargetGroupArn: !Ref TargetGroup Type: forward Conditions: - Field: path-pattern PathPatternConfig: Values: - '*' ListenerArn: !Ref AlbListener Priority: 1 AlbRecordSet: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Join - "" - - dualstack. - !GetAtt Alb.DNSName EvaluateTargetHealth: false HostedZoneId: Z14GRHDCWA56QT HostedZoneId: !Ref hostedZoneId Name: !Ref domainName Type: A AlbRecordSet1: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Join - "" - - dualstack. - !GetAtt Alb.DNSName EvaluateTargetHealth: false HostedZoneId: Z14GRHDCWA56QT HostedZoneId: !Ref hostedZoneId Name: !Ref domainName1 Type: A AlbRecordSet2: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Join - "" - - dualstack. - !GetAtt Alb.DNSName EvaluateTargetHealth: false HostedZoneId: Z14GRHDCWA56QT HostedZoneId: !Ref hostedZoneId Name: !Ref domainName2 Type: A AlbSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Application load barancer. GroupName: !Sub ${env}-${sysName}-alb-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - CidrIp: 0.0.0.0/0 FromPort: 443 IpProtocol: tcp ToPort: 443 Tags: - Key: Name Value: !Sub ${env}-${sysName}-alb-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC DbInstance1: Type: AWS::RDS::DBInstance DeletionPolicy: Delete Properties: AutoMinorVersionUpgrade: false DBClusterIdentifier: !Ref clusterAurora DBInstanceClass: !Ref instanceClass DBInstanceIdentifier: !Sub ${env}-${sysName}-db1 DBParameterGroupName: !Ref ParameterGroupAurora EnablePerformanceInsights: false Engine: aurora-mysql MonitoringInterval: 0 PubliclyAccessible: false Tags: - Key: Name Value: !Sub ${env}-${sysName}-db1 - Key: BillingGroup Value: !Ref billingTag DbInstance2: Type: AWS::RDS::DBInstance DeletionPolicy: Delete Properties: AutoMinorVersionUpgrade: false DBClusterIdentifier: !Ref clusterAurora DBInstanceClass: !Ref instanceClass DBInstanceIdentifier: !Sub ${env}-${sysName}-db2 DBParameterGroupName: !Ref ParameterGroupAurora EnablePerformanceInsights: false Engine: aurora-mysql MonitoringInterval: 0 PubliclyAccessible: false Tags: - Key: Name Value: !Sub ${env}-${sysName}-db2 - Key: BillingGroup Value: !Ref billingTag Instance1: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: 3000 VolumeSize: 8 VolumeType: gp3 CreditSpecification: CPUCredits: standard IamInstanceProfile: !Ref ec2Profile ImageId: !Ref ec2Ami InstanceType: !Ref instanceType SecurityGroupIds: - !Ref ec2SG SubnetId: !Ref MyPublicSubnet1 Tags: - Key: Name Value: !Sub ${env}-${sysName}-instance1 - Key: BillingGroup Value: !Ref billingTag UserData: !Base64 Fn::Sub: | #!/bin/bash yum install -y httpd cp -p /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.org cat <<EOF >> /etc/httpd/conf/httpd.conf <VirtualHost *:80> ServerName ${domainName} DocumentRoot /var/www/html/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName1} DocumentRoot /var/www/site1/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName2} DocumentRoot /var/www/site2/ </VirtualHost> EOF mkdir /var/www/{site1,site2} cd /var/www/html/ curl -O https://free-hp.net/clinic/cl_002/clinic_002_DL.zip && unzip clinic_002_DL.zip && mv clinic_002_DL/* . && rmdir clinic_002_DL && rm clinic_002_DL.zip cd /var/www/site1/ curl -O https://free-hp.net/restaurant/re_002/restaurant_002_DL.zip && unzip restaurant_002_DL.zip && mv restaurant_002_DL/* . && rmdir restaurant_002_DL && rm restaurant_002_DL.zip cd /var/www/site2/ curl -O https://free-hp.net/school/sc_001/school_001_DL.zip && unzip school_001_DL.zip && mv school_001_DL/* . && rmdir school_001_DL && rm school_001_DL.zip systemctl start httpd Instance2: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: 3000 VolumeSize: 8 VolumeType: gp3 CreditSpecification: CPUCredits: standard IamInstanceProfile: !Ref ec2Profile ImageId: !Ref ec2Ami InstanceType: !Ref instanceType SecurityGroupIds: - !Ref ec2SG SubnetId: !Ref MyPublicSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-instance2 - Key: BillingGroup Value: !Ref billingTag UserData: !Base64 Fn::Sub: | #!/bin/bash yum install -y httpd cp -p /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.org cat <<EOF >> /etc/httpd/conf/httpd.conf <VirtualHost *:80> ServerName ${domainName} DocumentRoot /var/www/html/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName1} DocumentRoot /var/www/site1/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName2} DocumentRoot /var/www/site2/ </VirtualHost> EOF mkdir /var/www/{site1,site2} cd /var/www/html/ curl -O https://free-hp.net/clinic/cl_002/clinic_002_DL.zip && unzip clinic_002_DL.zip && mv clinic_002_DL/* . && rmdir clinic_002_DL && rm clinic_002_DL.zip cd /var/www/site1/ curl -O https://free-hp.net/restaurant/re_002/restaurant_002_DL.zip && unzip restaurant_002_DL.zip && mv restaurant_002_DL/* . && rmdir restaurant_002_DL && rm restaurant_002_DL.zip cd /var/www/site2/ curl -O https://free-hp.net/school/sc_001/school_001_DL.zip && unzip school_001_DL.zip && mv school_001_DL/* . && rmdir school_001_DL && rm school_001_DL.zip systemctl start httpd MyInternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-igw - Key: BillingGroup Value: !Ref billingTag MyPrivateRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref MyInternetGateway RouteTableId: !Ref MyPrivateRouteTable MyPrivateRouteTable: Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-rtb - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 10 - !Cidr - !GetAtt MyVPC.CidrBlock - 12 - 8 Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-subnet-1 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPrivateRouteTable SubnetId: !Ref MyPrivateSubnet1 MyPrivateSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 11 - !Cidr - !GetAtt MyVPC.CidrBlock - 12 - 8 Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-subnet-2 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPrivateRouteTable SubnetId: !Ref MyPrivateSubnet2 MyPublicRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref MyInternetGateway RouteTableId: !Ref MyPublicRouteTable MyPublicRouteTable: Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-rtb - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 0 - !Cidr - !GetAtt MyVPC.CidrBlock - 2 - 8 MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-subnet-1 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPublicRouteTable SubnetId: !Ref MyPublicSubnet1 MyPublicSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 1 - !Cidr - !GetAtt MyVPC.CidrBlock - 2 - 8 MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-subnet-2 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPublicRouteTable SubnetId: !Ref MyPublicSubnet2 MyVPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref vpcCidr Tags: - Key: Name Value: !Sub ${env}-${sysName}-vpc - Key: BillingGroup Value: !Ref billingTag MyVPCGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref MyInternetGateway VpcId: !Ref MyVPC ParameterGroupAurora: Type: AWS::RDS::DBParameterGroup Properties: Description: !Sub ${env}-${sysName}-parametergroup Family: aurora-mysql5.7 RdsSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Rds Instances. GroupName: !Sub ${env}-${sysName}-rds-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - FromPort: 3306 IpProtocol: tcp SourceSecurityGroupId: !Ref ec2SG ToPort: 3306 Tags: - Key: Name Value: !Sub ${env}-${sysName}-rds-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC SubnetGroupRds: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: !Sub ${env}-${sysName}-rds-SubnetGroup DBSubnetGroupName: !Sub ${env}-${sysName}-rds-subnetgroup SubnetIds: - !Ref MyPrivateSubnet1 - !Ref MyPrivateSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-rds-SubnetGroup - Key: BillingGroup Value: !Ref billingTag TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Name: !Sub ${env}-${sysName}-ec2 Port: 80 Protocol: HTTP Tags: - Key: Name Value: !Sub ${env}-${sysName}-tg - Key: BillingGroup Value: !Ref billingTag TargetType: instance Targets: - Id: !Ref Instance1 - Id: !Ref Instance2 VpcId: !Ref MyVPC clusterAurora: Type: AWS::RDS::DBCluster DeletionPolicy: Delete Properties: BackupRetentionPeriod: 7 DBClusterIdentifier: !Sub ${env}-${sysName}-cluster DBClusterParameterGroupName: !Ref clusterParameterGroupAurora DBSubnetGroupName: !Ref SubnetGroupRds DeletionProtection: false EnableCloudwatchLogsExports: - audit - error - general - slowquery Engine: aurora-mysql EngineVersion: 5.7.mysql_aurora.2.10.0 MasterUserPassword: !Ref masterPassword MasterUsername: root Port: 3306 PreferredBackupWindow: 17:00-18:00 PreferredMaintenanceWindow: tue:18:00-tue:19:00 StorageEncrypted: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-cluster - Key: BillingGroup Value: !Ref billingTag VpcSecurityGroupIds: - !Ref RdsSG clusterParameterGroupAurora: Type: AWS::RDS::DBClusterParameterGroup Properties: Description: !Sub ${env}-${sysName}-cluster-parametergroup Family: aurora-mysql5.7 Parameters: character_set_client: utf8 character_set_connection: utf8 character_set_database: utf8 character_set_results: utf8 character_set_server: utf8 general_log: 1 server_audit_events: CONNECT,QUERY,QUERY_DCL,QUERY_DDL,QUERY_DML,TABLE server_audit_logging: 1 slow_query_log: 1 time_zone: Asia/Tokyo ec2Profile: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !Ref ec2Role ec2Role: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - ec2.amazonaws.com Version: "2012-10-17" ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore - arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess ec2SG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: ec2 Instances. GroupName: !Sub ${env}-${sysName}-ec2-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - FromPort: 80 IpProtocol: tcp SourceSecurityGroupId: !Ref AlbSG ToPort: 80 Tags: - Key: Name Value: !Sub ${env}-${sysName}-ec2-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC site2Bucket: DeletionPolicy: Delete UpdateReplacePolicy: Delete Type: AWS::S3::Bucket Properties: BucketName: !Ref domainName2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-site2-contents-bucket - Key: BillingGroup Value: !Ref billingTag
これでデプロイしていきます。
rain deploy -y ./site.yml site --params \ env=prd,\ sysName=example,\ billingTag=example,\ vpcCidr=10.0.0.0/16,\ domainName=example.com,\ domainName1=site1.example.com,\ domainName2=site2.example.com,\ hostedZoneId=Z10116024XXXXXXXXXXX,\ instanceType=t3.micro,\ instanceClass=db.t3.small,\ masterPassword=Passw0rd
S3バケットの作成後、 /var/www/site2/
にあるコンテンツをS3にアップロードしておきます。マネジメントコンソール、AWS CLIどちらでも良いです。
CloudFront、OAI、ACMの作成
次にCloudFront Distribution、OAI、ACMを作成します。ACMを作成する意図は、CloufFrontリソースはバージニアリージョンのACMのみ割当できる為です。他のリージョンのACMとCloudFrontは割当できません。なのでCloudFront、OAI、ACMをバージニアリージョンで作成します。
site2.yml
AWSTemplateFormatVersion: "2010-09-09" Description: Template generated by rain Parameters: billingTag: Type: String hostedZoneId: Type: String domainName: Type: String domainName2: Type: String domainName2Bucket: Type: String env: Type: String sysName: Type: String Resources: CloudFrontDistribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Aliases: - !Sub ${domainName2} Origins: - ConnectionAttempts: 3 ConnectionTimeout: 10 DomainName: !Sub ${domainName2Bucket} Id: !Sub ${domainName2Bucket} OriginPath: "" S3OriginConfig: OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CloudFrontCloudFrontOriginAccessIdentity}" OriginGroups: Quantity: 0 DefaultCacheBehavior: AllowedMethods: - HEAD - GET CachedMethods: - HEAD - GET Compress: true CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 SmoothStreaming: false TargetOriginId: !Sub ${domainName2Bucket} ViewerProtocolPolicy: redirect-to-https Comment: "" PriceClass: PriceClass_All Enabled: true ViewerCertificate: AcmCertificateArn: !Ref cloudfrontAcm MinimumProtocolVersion: TLSv1.2_2021 SslSupportMethod: sni-only Restrictions: GeoRestriction: RestrictionType: none HttpVersion: http2 DefaultRootObject: index.html IPV6Enabled: false CloudFrontCloudFrontOriginAccessIdentity: Type: AWS::CloudFront::CloudFrontOriginAccessIdentity Properties: CloudFrontOriginAccessIdentityConfig: Comment: !Sub "access-identity-${domainName2Bucket}" cloudfrontAcm: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Sub ${domainName2} DomainValidationOptions: - DomainName: !Sub ${domainName} HostedZoneId: !Sub ${hostedZoneId} Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS
rainコマンドは、 -r, --retion
オプションでリージョンを指定できます。バージニアリージョン(us-east-1)を指定してスタックをデプロイします。
domainName2Bucketパラメータには site2Bucket
リソースの仮想ホスト形式(バケット名.s3.Region.amazonaws.com or バケット名.s3.amazonaws.com)の値を指定しましょう。
rain deploy -y -r us-east-1 ./site2.yml site2 --params \ env=prd,\ sysName=example,\ billingTag=example,\ domainName=example.com,\ domainName2=site2.example.com,\ hostedZoneId=Z10116024XXXXXXXXXXX,\ domainName2Bucket=site2.example.com.s3.ap-northeast-1.amazonaws.com
S3バケットポリシー、Route53レコードの更新
作成したOAIを指定して site2Bucket
のバケットポリシーを設定します。このポリシーにより、CloudFront DistributionのみS3バケットにアクセスできます。併せて site2.example.com
レコードをALBからCloudFront Distributionに変更します。
〜 AlbRecordSet2: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Sub ${domain2Cloudfront} # CloudFront URL EvaluateTargetHealth: false HostedZoneId: Z2FDTNDATAQYW2 # cloudfront.netのHostedZoneID HostedZoneId: !Ref hostedZoneId Name: !Ref domainName2 Type: A 〜 site2BucketPolicy: # バケットポリシー追加 Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref site2Bucket PolicyDocument: Statement: - Action: s3:GetObject Effect: Allow Resource: !Sub arn:aws:s3:::${site2Bucket}/* Principal: AWS: !Sub arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${site2OAI} # OAIを指定
上記リソースを追記した site.yml
テンプレートです。
site.yml(S3バケットポリシー作成、Route53レコード変更)
AWSTemplateFormatVersion: "2010-09-09" Description: Template generated by rain Parameters: billingTag: Type: String domainName: Type: String domainName1: Type: String domainName2: Type: String ec2Ami: Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 env: Type: String hostedZoneId: Type: String instanceClass: Type: String instanceType: Type: String masterPassword: Type: String NoEcho: true sysName: Type: String vpcCidr: Type: String domain2Cloudfront: Type: String site2OAI: Type: String Resources: Alb: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: IpAddressType: ipv4 Name: !Sub ${env}-${sysName}-alb Scheme: internet-facing SecurityGroups: - !Ref AlbSG Subnets: - !Ref MyPublicSubnet1 - !Ref MyPublicSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-api - Key: BillingGroup Value: !Ref billingTag Type: application AlbAcm: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName DomainValidationOptions: - DomainName: !Ref domainName HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbAcm1: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName1 DomainValidationOptions: - DomainName: !Ref domainName1 HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm1 - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbAcm2: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName2 DomainValidationOptions: - DomainName: !Ref domainName2 HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm2 - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: Certificates: - CertificateArn: !Ref AlbAcm DefaultActions: - FixedResponseConfig: ContentType: text/plain MessageBody: Unauthorized Access StatusCode: "403" Type: fixed-response LoadBalancerArn: !Ref Alb Port: 443 Protocol: HTTPS SslPolicy: ELBSecurityPolicy-2016-08 AlbListenerCertificate1: Type: AWS::ElasticLoadBalancingV2::ListenerCertificate Properties: Certificates: - CertificateArn: !Ref AlbAcm1 ListenerArn: !Ref AlbListener AlbListenerCertificate2: Type: AWS::ElasticLoadBalancingV2::ListenerCertificate Properties: Certificates: - CertificateArn: !Ref AlbAcm2 ListenerArn: !Ref AlbListener AlbListnerRule1: Type: AWS::ElasticLoadBalancingV2::ListenerRule Properties: Actions: - TargetGroupArn: !Ref TargetGroup Type: forward Conditions: - Field: path-pattern PathPatternConfig: Values: - '*' ListenerArn: !Ref AlbListener Priority: 1 AlbRecordSet: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Join - "" - - dualstack. - !GetAtt Alb.DNSName EvaluateTargetHealth: false HostedZoneId: Z14GRHDCWA56QT HostedZoneId: !Ref hostedZoneId Name: !Ref domainName Type: A AlbRecordSet1: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Join - "" - - dualstack. - !GetAtt Alb.DNSName EvaluateTargetHealth: false HostedZoneId: Z14GRHDCWA56QT HostedZoneId: !Ref hostedZoneId Name: !Ref domainName1 Type: A AlbRecordSet2: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Sub ${domain2Cloudfront} EvaluateTargetHealth: false HostedZoneId: Z2FDTNDATAQYW2 HostedZoneId: !Ref hostedZoneId Name: !Ref domainName2 Type: A AlbSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Application load barancer. GroupName: !Sub ${env}-${sysName}-alb-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - CidrIp: 0.0.0.0/0 FromPort: 443 IpProtocol: tcp ToPort: 443 Tags: - Key: Name Value: !Sub ${env}-${sysName}-alb-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC DbInstance1: Type: AWS::RDS::DBInstance DeletionPolicy: Delete Properties: AutoMinorVersionUpgrade: false DBClusterIdentifier: !Ref clusterAurora DBInstanceClass: !Ref instanceClass DBInstanceIdentifier: !Sub ${env}-${sysName}-db1 DBParameterGroupName: !Ref ParameterGroupAurora EnablePerformanceInsights: false Engine: aurora-mysql MonitoringInterval: 0 PubliclyAccessible: false Tags: - Key: Name Value: !Sub ${env}-${sysName}-db1 - Key: BillingGroup Value: !Ref billingTag DbInstance2: Type: AWS::RDS::DBInstance DeletionPolicy: Delete Properties: AutoMinorVersionUpgrade: false DBClusterIdentifier: !Ref clusterAurora DBInstanceClass: !Ref instanceClass DBInstanceIdentifier: !Sub ${env}-${sysName}-db2 DBParameterGroupName: !Ref ParameterGroupAurora EnablePerformanceInsights: false Engine: aurora-mysql MonitoringInterval: 0 PubliclyAccessible: false Tags: - Key: Name Value: !Sub ${env}-${sysName}-db2 - Key: BillingGroup Value: !Ref billingTag Instance1: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: 3000 VolumeSize: 8 VolumeType: gp3 CreditSpecification: CPUCredits: standard IamInstanceProfile: !Ref ec2Profile ImageId: !Ref ec2Ami InstanceType: !Ref instanceType SecurityGroupIds: - !Ref ec2SG SubnetId: !Ref MyPublicSubnet1 Tags: - Key: Name Value: !Sub ${env}-${sysName}-instance1 - Key: BillingGroup Value: !Ref billingTag UserData: !Base64 Fn::Sub: | #!/bin/bash yum install -y httpd cp -p /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.org cat <<EOF >> /etc/httpd/conf/httpd.conf <VirtualHost *:80> ServerName ${domainName} DocumentRoot /var/www/html/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName1} DocumentRoot /var/www/site1/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName2} DocumentRoot /var/www/site2/ </VirtualHost> EOF mkdir /var/www/{site1,site2} cd /var/www/html/ curl -O https://free-hp.net/clinic/cl_002/clinic_002_DL.zip && unzip clinic_002_DL.zip && mv clinic_002_DL/* . && rmdir clinic_002_DL && rm clinic_002_DL.zip cd /var/www/site1/ curl -O https://free-hp.net/restaurant/re_002/restaurant_002_DL.zip && unzip restaurant_002_DL.zip && mv restaurant_002_DL/* . && rmdir restaurant_002_DL && rm restaurant_002_DL.zip cd /var/www/site2/ curl -O https://free-hp.net/school/sc_001/school_001_DL.zip && unzip school_001_DL.zip && mv school_001_DL/* . && rmdir school_001_DL && rm school_001_DL.zip systemctl start httpd Instance2: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: 3000 VolumeSize: 8 VolumeType: gp3 CreditSpecification: CPUCredits: standard IamInstanceProfile: !Ref ec2Profile ImageId: !Ref ec2Ami InstanceType: !Ref instanceType SecurityGroupIds: - !Ref ec2SG SubnetId: !Ref MyPublicSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-instance2 - Key: BillingGroup Value: !Ref billingTag UserData: !Base64 Fn::Sub: | #!/bin/bash yum install -y httpd cp -p /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.org cat <<EOF >> /etc/httpd/conf/httpd.conf <VirtualHost *:80> ServerName ${domainName} DocumentRoot /var/www/html/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName1} DocumentRoot /var/www/site1/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName2} DocumentRoot /var/www/site2/ </VirtualHost> EOF mkdir /var/www/{site1,site2} cd /var/www/html/ curl -O https://free-hp.net/clinic/cl_002/clinic_002_DL.zip && unzip clinic_002_DL.zip && mv clinic_002_DL/* . && rmdir clinic_002_DL && rm clinic_002_DL.zip cd /var/www/site1/ curl -O https://free-hp.net/restaurant/re_002/restaurant_002_DL.zip && unzip restaurant_002_DL.zip && mv restaurant_002_DL/* . && rmdir restaurant_002_DL && rm restaurant_002_DL.zip cd /var/www/site2/ curl -O https://free-hp.net/school/sc_001/school_001_DL.zip && unzip school_001_DL.zip && mv school_001_DL/* . && rmdir school_001_DL && rm school_001_DL.zip systemctl start httpd MyInternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-igw - Key: BillingGroup Value: !Ref billingTag MyPrivateRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref MyInternetGateway RouteTableId: !Ref MyPrivateRouteTable MyPrivateRouteTable: Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-rtb - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 10 - !Cidr - !GetAtt MyVPC.CidrBlock - 12 - 8 Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-subnet-1 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPrivateRouteTable SubnetId: !Ref MyPrivateSubnet1 MyPrivateSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 11 - !Cidr - !GetAtt MyVPC.CidrBlock - 12 - 8 Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-subnet-2 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPrivateRouteTable SubnetId: !Ref MyPrivateSubnet2 MyPublicRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref MyInternetGateway RouteTableId: !Ref MyPublicRouteTable MyPublicRouteTable: Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-rtb - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 0 - !Cidr - !GetAtt MyVPC.CidrBlock - 2 - 8 MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-subnet-1 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPublicRouteTable SubnetId: !Ref MyPublicSubnet1 MyPublicSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 1 - !Cidr - !GetAtt MyVPC.CidrBlock - 2 - 8 MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-subnet-2 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPublicRouteTable SubnetId: !Ref MyPublicSubnet2 MyVPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref vpcCidr Tags: - Key: Name Value: !Sub ${env}-${sysName}-vpc - Key: BillingGroup Value: !Ref billingTag MyVPCGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref MyInternetGateway VpcId: !Ref MyVPC ParameterGroupAurora: Type: AWS::RDS::DBParameterGroup Properties: Description: !Sub ${env}-${sysName}-parametergroup Family: aurora-mysql5.7 RdsSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Rds Instances. GroupName: !Sub ${env}-${sysName}-rds-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - FromPort: 3306 IpProtocol: tcp SourceSecurityGroupId: !Ref ec2SG ToPort: 3306 Tags: - Key: Name Value: !Sub ${env}-${sysName}-rds-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC SubnetGroupRds: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: !Sub ${env}-${sysName}-rds-SubnetGroup DBSubnetGroupName: !Sub ${env}-${sysName}-rds-subnetgroup SubnetIds: - !Ref MyPrivateSubnet1 - !Ref MyPrivateSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-rds-SubnetGroup - Key: BillingGroup Value: !Ref billingTag TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Name: !Sub ${env}-${sysName}-ec2 Port: 80 Protocol: HTTP Tags: - Key: Name Value: !Sub ${env}-${sysName}-tg - Key: BillingGroup Value: !Ref billingTag TargetType: instance Targets: - Id: !Ref Instance1 - Id: !Ref Instance2 VpcId: !Ref MyVPC clusterAurora: Type: AWS::RDS::DBCluster DeletionPolicy: Delete Properties: BackupRetentionPeriod: 7 DBClusterIdentifier: !Sub ${env}-${sysName}-cluster DBClusterParameterGroupName: !Ref clusterParameterGroupAurora DBSubnetGroupName: !Ref SubnetGroupRds DeletionProtection: false EnableCloudwatchLogsExports: - audit - error - general - slowquery Engine: aurora-mysql EngineVersion: 5.7.mysql_aurora.2.10.0 MasterUserPassword: !Ref masterPassword MasterUsername: root Port: 3306 PreferredBackupWindow: 17:00-18:00 PreferredMaintenanceWindow: tue:18:00-tue:19:00 StorageEncrypted: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-cluster - Key: BillingGroup Value: !Ref billingTag VpcSecurityGroupIds: - !Ref RdsSG clusterParameterGroupAurora: Type: AWS::RDS::DBClusterParameterGroup Properties: Description: !Sub ${env}-${sysName}-cluster-parametergroup Family: aurora-mysql5.7 Parameters: character_set_client: utf8 character_set_connection: utf8 character_set_database: utf8 character_set_results: utf8 character_set_server: utf8 general_log: 1 server_audit_events: CONNECT,QUERY,QUERY_DCL,QUERY_DDL,QUERY_DML,TABLE server_audit_logging: 1 slow_query_log: 1 time_zone: Asia/Tokyo ec2Profile: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !Ref ec2Role ec2Role: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - ec2.amazonaws.com Version: "2012-10-17" ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore - arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess ec2SG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: ec2 Instances. GroupName: !Sub ${env}-${sysName}-ec2-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - FromPort: 80 IpProtocol: tcp SourceSecurityGroupId: !Ref AlbSG ToPort: 80 Tags: - Key: Name Value: !Sub ${env}-${sysName}-ec2-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC site2Bucket: DeletionPolicy: Delete UpdateReplacePolicy: Delete Type: AWS::S3::Bucket Properties: BucketName: !Ref domainName2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-site2-contents-bucket - Key: BillingGroup Value: !Ref billingTag site2BucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref site2Bucket PolicyDocument: Statement: - Action: s3:GetObject Effect: Allow Resource: !Sub arn:aws:s3:::${site2Bucket}/* Principal: AWS: !Sub arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${site2OAI}
domain2Cloudfront、site2OAIパラメータを追加してデプロイします。
rain deploy -y ./site.yml site --params \ env=prd,\ sysName=example,\ billingTag=example,\ vpcCidr=10.0.0.0/16,\ domainName=example.com,\ domainName1=site1.example.com,\ domainName2=site2.example.com,\ hostedZoneId=Z10116024XXXXXXXXXXX,\ instanceType=t3.micro,\ instanceClass=db.t3.small,\ masterPassword=Passw0rd,\ domain2Cloudfront=dxxxxxxxxxxxx.cloudfront.net,\ site2OAI=E36GW62SYZ8RPJ
site2.example.com
にhttpsでアクセスできることを確認できたらOKです。
動的ウェブサイトの分離
site1.example.com
が動的ウェブサイトの場合(例えばhttpd,tomcat)の分離先として、別のEC2インスタンスもしくはECSなど選択肢があります。今回はsite1.example.com
を動的ウェブサイトに見立てて、ECS(Fargate)に分離してみます。
ECSサービス作成前の事前準備
事前準備としてECRプライベートリポジトリ、コンテナログを出力する為のCloudwatchロググループとタスク実行ロールを作成し、タスク定義で指定します。
site1Ecr: Type: AWS::ECR::Repository Properties: RepositoryName: site1 ImageScanningConfiguration: ScanOnPush: "true" LifecyclePolicy: LifecyclePolicyText: | { "rules": [ { "rulePriority": 1, "description": "Delete images", "selection": { "tagStatus": "any", "countType": "imageCountMoreThan", "countNumber": 10 }, "action": { "type": "expire" } } ] } Tags: - Key: Name Value: !Sub ${env}-${sysName}-site1-ecr - Key: BillingGroup Value: !Ref billingTag ecsLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub "/ecs/logs/${env}/${sysName}" ecsTaskExecutionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub ${env}-${sysName}-ecs-task-exec-role Path: / AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: ecs-tasks.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy site1TaskDefinition: Type: AWS::ECS::TaskDefinition Properties: ContainerDefinitions: - Essential: true Image: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/site1:latest" LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref ecsLogGroup awslogs-region: !Ref AWS::Region awslogs-stream-prefix: site1 Name: site1 PortMappings: - ContainerPort: 80 HostPort: 80 Protocol: tcp Family: site1 ExecutionRoleArn: !GetAtt ecsTaskExecutionRole.Arn NetworkMode: awsvpc RequiresCompatibilities: - FARGATE Cpu: "256" Memory: "512"
上記リソースを追記した site.yml
テンプレートです。
site.yml(ECR、CloudWatch LogGroup、IAMロール、ECSタスク定義の追加)
AWSTemplateFormatVersion: "2010-09-09" Description: Template generated by rain Parameters: billingTag: Type: String domainName: Type: String domainName1: Type: String domainName2: Type: String ec2Ami: Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 env: Type: String hostedZoneId: Type: String instanceClass: Type: String instanceType: Type: String masterPassword: Type: String NoEcho: true sysName: Type: String vpcCidr: Type: String domain2Cloudfront: Type: String site2OAI: Type: String Resources: Alb: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: IpAddressType: ipv4 Name: !Sub ${env}-${sysName}-alb Scheme: internet-facing SecurityGroups: - !Ref AlbSG Subnets: - !Ref MyPublicSubnet1 - !Ref MyPublicSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-api - Key: BillingGroup Value: !Ref billingTag Type: application AlbAcm: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName DomainValidationOptions: - DomainName: !Ref domainName HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbAcm1: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName1 DomainValidationOptions: - DomainName: !Ref domainName1 HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm1 - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbAcm2: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName2 DomainValidationOptions: - DomainName: !Ref domainName2 HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm2 - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: Certificates: - CertificateArn: !Ref AlbAcm DefaultActions: - FixedResponseConfig: ContentType: text/plain MessageBody: Unauthorized Access StatusCode: "403" Type: fixed-response LoadBalancerArn: !Ref Alb Port: 443 Protocol: HTTPS SslPolicy: ELBSecurityPolicy-2016-08 AlbListenerCertificate1: Type: AWS::ElasticLoadBalancingV2::ListenerCertificate Properties: Certificates: - CertificateArn: !Ref AlbAcm1 ListenerArn: !Ref AlbListener AlbListenerCertificate2: Type: AWS::ElasticLoadBalancingV2::ListenerCertificate Properties: Certificates: - CertificateArn: !Ref AlbAcm2 ListenerArn: !Ref AlbListener AlbListnerRule1: Type: AWS::ElasticLoadBalancingV2::ListenerRule Properties: Actions: - TargetGroupArn: !Ref TargetGroup Type: forward Conditions: - Field: path-pattern PathPatternConfig: Values: - '*' ListenerArn: !Ref AlbListener Priority: 1 AlbRecordSet: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Join - "" - - dualstack. - !GetAtt Alb.DNSName EvaluateTargetHealth: false HostedZoneId: Z14GRHDCWA56QT HostedZoneId: !Ref hostedZoneId Name: !Ref domainName Type: A AlbRecordSet1: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Join - "" - - dualstack. - !GetAtt Alb.DNSName EvaluateTargetHealth: false HostedZoneId: Z14GRHDCWA56QT HostedZoneId: !Ref hostedZoneId Name: !Ref domainName1 Type: A AlbRecordSet2: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Sub ${domain2Cloudfront} EvaluateTargetHealth: false HostedZoneId: Z2FDTNDATAQYW2 HostedZoneId: !Ref hostedZoneId Name: !Ref domainName2 Type: A AlbSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Application load barancer. GroupName: !Sub ${env}-${sysName}-alb-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - CidrIp: 0.0.0.0/0 FromPort: 443 IpProtocol: tcp ToPort: 443 Tags: - Key: Name Value: !Sub ${env}-${sysName}-alb-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC DbInstance1: Type: AWS::RDS::DBInstance DeletionPolicy: Delete Properties: AutoMinorVersionUpgrade: false DBClusterIdentifier: !Ref clusterAurora DBInstanceClass: !Ref instanceClass DBInstanceIdentifier: !Sub ${env}-${sysName}-db1 DBParameterGroupName: !Ref ParameterGroupAurora EnablePerformanceInsights: false Engine: aurora-mysql MonitoringInterval: 0 PubliclyAccessible: false Tags: - Key: Name Value: !Sub ${env}-${sysName}-db1 - Key: BillingGroup Value: !Ref billingTag DbInstance2: Type: AWS::RDS::DBInstance DeletionPolicy: Delete Properties: AutoMinorVersionUpgrade: false DBClusterIdentifier: !Ref clusterAurora DBInstanceClass: !Ref instanceClass DBInstanceIdentifier: !Sub ${env}-${sysName}-db2 DBParameterGroupName: !Ref ParameterGroupAurora EnablePerformanceInsights: false Engine: aurora-mysql MonitoringInterval: 0 PubliclyAccessible: false Tags: - Key: Name Value: !Sub ${env}-${sysName}-db2 - Key: BillingGroup Value: !Ref billingTag Instance1: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: 3000 VolumeSize: 8 VolumeType: gp3 CreditSpecification: CPUCredits: standard IamInstanceProfile: !Ref ec2Profile ImageId: !Ref ec2Ami InstanceType: !Ref instanceType SecurityGroupIds: - !Ref ec2SG SubnetId: !Ref MyPublicSubnet1 Tags: - Key: Name Value: !Sub ${env}-${sysName}-instance1 - Key: BillingGroup Value: !Ref billingTag UserData: !Base64 Fn::Sub: | #!/bin/bash yum install -y httpd cp -p /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.org cat <<EOF >> /etc/httpd/conf/httpd.conf <VirtualHost *:80> ServerName ${domainName} DocumentRoot /var/www/html/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName1} DocumentRoot /var/www/site1/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName2} DocumentRoot /var/www/site2/ </VirtualHost> EOF mkdir /var/www/{site1,site2} cd /var/www/html/ curl -O https://free-hp.net/clinic/cl_002/clinic_002_DL.zip && unzip clinic_002_DL.zip && mv clinic_002_DL/* . && rmdir clinic_002_DL && rm clinic_002_DL.zip cd /var/www/site1/ curl -O https://free-hp.net/restaurant/re_002/restaurant_002_DL.zip && unzip restaurant_002_DL.zip && mv restaurant_002_DL/* . && rmdir restaurant_002_DL && rm restaurant_002_DL.zip cd /var/www/site2/ curl -O https://free-hp.net/school/sc_001/school_001_DL.zip && unzip school_001_DL.zip && mv school_001_DL/* . && rmdir school_001_DL && rm school_001_DL.zip systemctl start httpd Instance2: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: 3000 VolumeSize: 8 VolumeType: gp3 CreditSpecification: CPUCredits: standard IamInstanceProfile: !Ref ec2Profile ImageId: !Ref ec2Ami InstanceType: !Ref instanceType SecurityGroupIds: - !Ref ec2SG SubnetId: !Ref MyPublicSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-instance2 - Key: BillingGroup Value: !Ref billingTag UserData: !Base64 Fn::Sub: | #!/bin/bash yum install -y httpd cp -p /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.org cat <<EOF >> /etc/httpd/conf/httpd.conf <VirtualHost *:80> ServerName ${domainName} DocumentRoot /var/www/html/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName1} DocumentRoot /var/www/site1/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName2} DocumentRoot /var/www/site2/ </VirtualHost> EOF mkdir /var/www/{site1,site2} cd /var/www/html/ curl -O https://free-hp.net/clinic/cl_002/clinic_002_DL.zip && unzip clinic_002_DL.zip && mv clinic_002_DL/* . && rmdir clinic_002_DL && rm clinic_002_DL.zip cd /var/www/site1/ curl -O https://free-hp.net/restaurant/re_002/restaurant_002_DL.zip && unzip restaurant_002_DL.zip && mv restaurant_002_DL/* . && rmdir restaurant_002_DL && rm restaurant_002_DL.zip cd /var/www/site2/ curl -O https://free-hp.net/school/sc_001/school_001_DL.zip && unzip school_001_DL.zip && mv school_001_DL/* . && rmdir school_001_DL && rm school_001_DL.zip systemctl start httpd MyInternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-igw - Key: BillingGroup Value: !Ref billingTag MyPrivateRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref MyInternetGateway RouteTableId: !Ref MyPrivateRouteTable MyPrivateRouteTable: Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-rtb - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 10 - !Cidr - !GetAtt MyVPC.CidrBlock - 12 - 8 Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-subnet-1 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPrivateRouteTable SubnetId: !Ref MyPrivateSubnet1 MyPrivateSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 11 - !Cidr - !GetAtt MyVPC.CidrBlock - 12 - 8 Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-subnet-2 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPrivateRouteTable SubnetId: !Ref MyPrivateSubnet2 MyPublicRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref MyInternetGateway RouteTableId: !Ref MyPublicRouteTable MyPublicRouteTable: Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-rtb - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 0 - !Cidr - !GetAtt MyVPC.CidrBlock - 2 - 8 MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-subnet-1 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPublicRouteTable SubnetId: !Ref MyPublicSubnet1 MyPublicSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 1 - !Cidr - !GetAtt MyVPC.CidrBlock - 2 - 8 MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-subnet-2 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPublicRouteTable SubnetId: !Ref MyPublicSubnet2 MyVPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref vpcCidr Tags: - Key: Name Value: !Sub ${env}-${sysName}-vpc - Key: BillingGroup Value: !Ref billingTag MyVPCGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref MyInternetGateway VpcId: !Ref MyVPC ParameterGroupAurora: Type: AWS::RDS::DBParameterGroup Properties: Description: !Sub ${env}-${sysName}-parametergroup Family: aurora-mysql5.7 RdsSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Rds Instances. GroupName: !Sub ${env}-${sysName}-rds-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - FromPort: 3306 IpProtocol: tcp SourceSecurityGroupId: !Ref ec2SG ToPort: 3306 Tags: - Key: Name Value: !Sub ${env}-${sysName}-rds-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC SubnetGroupRds: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: !Sub ${env}-${sysName}-rds-SubnetGroup DBSubnetGroupName: !Sub ${env}-${sysName}-rds-subnetgroup SubnetIds: - !Ref MyPrivateSubnet1 - !Ref MyPrivateSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-rds-SubnetGroup - Key: BillingGroup Value: !Ref billingTag TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Name: !Sub ${env}-${sysName}-ec2 Port: 80 Protocol: HTTP Tags: - Key: Name Value: !Sub ${env}-${sysName}-tg - Key: BillingGroup Value: !Ref billingTag TargetType: instance Targets: - Id: !Ref Instance1 - Id: !Ref Instance2 VpcId: !Ref MyVPC clusterAurora: Type: AWS::RDS::DBCluster DeletionPolicy: Delete Properties: BackupRetentionPeriod: 7 DBClusterIdentifier: !Sub ${env}-${sysName}-cluster DBClusterParameterGroupName: !Ref clusterParameterGroupAurora DBSubnetGroupName: !Ref SubnetGroupRds DeletionProtection: false EnableCloudwatchLogsExports: - audit - error - general - slowquery Engine: aurora-mysql EngineVersion: 5.7.mysql_aurora.2.10.0 MasterUserPassword: !Ref masterPassword MasterUsername: root Port: 3306 PreferredBackupWindow: 17:00-18:00 PreferredMaintenanceWindow: tue:18:00-tue:19:00 StorageEncrypted: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-cluster - Key: BillingGroup Value: !Ref billingTag VpcSecurityGroupIds: - !Ref RdsSG clusterParameterGroupAurora: Type: AWS::RDS::DBClusterParameterGroup Properties: Description: !Sub ${env}-${sysName}-cluster-parametergroup Family: aurora-mysql5.7 Parameters: character_set_client: utf8 character_set_connection: utf8 character_set_database: utf8 character_set_results: utf8 character_set_server: utf8 general_log: 1 server_audit_events: CONNECT,QUERY,QUERY_DCL,QUERY_DDL,QUERY_DML,TABLE server_audit_logging: 1 slow_query_log: 1 time_zone: Asia/Tokyo ec2Profile: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !Ref ec2Role ec2Role: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - ec2.amazonaws.com Version: "2012-10-17" ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore - arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess ec2SG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: ec2 Instances. GroupName: !Sub ${env}-${sysName}-ec2-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - FromPort: 80 IpProtocol: tcp SourceSecurityGroupId: !Ref AlbSG ToPort: 80 Tags: - Key: Name Value: !Sub ${env}-${sysName}-ec2-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC site2Bucket: DeletionPolicy: Delete UpdateReplacePolicy: Delete Type: AWS::S3::Bucket Properties: BucketName: !Ref domainName2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-site2-contents-bucket - Key: BillingGroup Value: !Ref billingTag site2BucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref site2Bucket PolicyDocument: Statement: - Action: s3:GetObject Effect: Allow Resource: !Sub arn:aws:s3:::${site2Bucket}/* Principal: AWS: !Sub arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${site2OAI} site1Ecr: Type: AWS::ECR::Repository Properties: RepositoryName: site1 ImageScanningConfiguration: ScanOnPush: "true" LifecyclePolicy: LifecyclePolicyText: | { "rules": [ { "rulePriority": 1, "description": "Delete images", "selection": { "tagStatus": "any", "countType": "imageCountMoreThan", "countNumber": 10 }, "action": { "type": "expire" } } ] } Tags: - Key: Name Value: !Sub ${env}-${sysName}-site1-ecr - Key: BillingGroup Value: !Ref billingTag ecsLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub "/ecs/logs/${env}/${sysName}" ecsTaskExecutionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub ${env}-${sysName}-ecs-task-exec-role Path: / AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: ecs-tasks.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy site1TaskDefinition: Type: AWS::ECS::TaskDefinition Properties: ContainerDefinitions: - Essential: true Image: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/site1:latest" LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref ecsLogGroup awslogs-region: !Ref AWS::Region awslogs-stream-prefix: site1 Name: site1 PortMappings: - ContainerPort: 80 HostPort: 80 Protocol: tcp Family: site1 ExecutionRoleArn: !GetAtt ecsTaskExecutionRole.Arn NetworkMode: awsvpc RequiresCompatibilities: - FARGATE Cpu: "256" Memory: "512"
デプロイしていきます。
rain deploy -y ./site.yml site --params \ env=prd,\ sysName=example,\ billingTag=example,\ vpcCidr=10.0.0.0/16,\ domainName=example.com,\ domainName1=site1.example.com,\ domainName2=site2.example.com,\ hostedZoneId=Z10116024XXXXXXXXXXX,\ instanceType=t3.micro,\ instanceClass=db.t3.small,\ masterPassword=Passw0rd,\ domain2Cloudfront=dxxxxxxxxxxxx.cloudfront.net,\ site2OAI=E36GW62SYZ8RPJ
次にECRリポジトリにイメージをアップロードします。site1.example.com
コンテンツ(https://free-hp.net/restaurant/re_002/restaurant_002_DL.zip)を含むコンテナイメージをhttpd公式イメージをベースに作成します。
FROM httpd:2.4 COPY ./school_001_DL/ /usr/local/apache2/htdocs/
コンテナイメージのpushはドキュメントを参照してください。
ECSサービスの実行
事前準備ができたのでECSサービスを設定します。併せてALBのリスナールールで site1.example.com
のリクエストを振り分けるルールと振り分け先のターゲットグループを設定します。
site1EcsSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: site1 ecs. GroupName: !Sub ${env}-${sysName}-site1-ecs-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - FromPort: 80 IpProtocol: tcp SourceSecurityGroupId: !Ref AlbSG ToPort: 80 Tags: - Key: Name Value: !Sub ${env}-${sysName}-site1-ecs-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC site1TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Name: !Sub ${env}-${sysName}-site1-ecs-tg Port: 80 Protocol: HTTP TargetType: ip VpcId: !Ref MyVPC Tags: - Key: Name Value: !Sub ${env}-${sysName}-site1-ecs-tg - Key: BillingGroup Value: !Ref billingTag cluster: Type: AWS::ECS::Cluster Properties: ClusterName: !Sub ${env}-${sysName}-cluster ClusterSettings: - Name: containerInsights Value: enabled Tags: - Key: Name Value: !Sub ${env}-${sysName}-cluster - Key: BillingGroup Value: !Ref billingTag site1ServiceDefinition: Type: AWS::ECS::Service Properties: Cluster: !Ref cluster DesiredCount: 0 LaunchType: FARGATE LoadBalancers: - TargetGroupArn: !Ref site1TargetGroup ContainerPort: 80 ContainerName: site1 NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: ENABLED SecurityGroups: - !Ref site1EcsSG Subnets: - !Ref MyPublicSubnet1 - !Ref MyPublicSubnet2 ServiceName: !Sub ${env}-${sysName}-site1 TaskDefinition: !Sub ${site1TaskDefinition}
上記リソースを追記した site.yml
テンプレートです。
site.yml(ECSサービス、ターゲットグループ、ALBリスナールールの変更)
AWSTemplateFormatVersion: "2010-09-09" Description: Template generated by rain Parameters: billingTag: Type: String domainName: Type: String domainName1: Type: String domainName2: Type: String ec2Ami: Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 env: Type: String hostedZoneId: Type: String instanceClass: Type: String instanceType: Type: String masterPassword: Type: String NoEcho: true sysName: Type: String vpcCidr: Type: String domain2Cloudfront: Type: String site2OAI: Type: String Resources: Alb: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: IpAddressType: ipv4 Name: !Sub ${env}-${sysName}-alb Scheme: internet-facing SecurityGroups: - !Ref AlbSG Subnets: - !Ref MyPublicSubnet1 - !Ref MyPublicSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-api - Key: BillingGroup Value: !Ref billingTag Type: application AlbAcm: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName DomainValidationOptions: - DomainName: !Ref domainName HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbAcm1: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName1 DomainValidationOptions: - DomainName: !Ref domainName1 HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm1 - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbAcm2: Type: AWS::CertificateManager::Certificate Properties: DomainName: !Ref domainName2 DomainValidationOptions: - DomainName: !Ref domainName2 HostedZoneId: !Ref hostedZoneId Tags: - Key: Name Value: !Sub ${env}-${sysName}-albacm2 - Key: BillingGroup Value: !Ref billingTag ValidationMethod: DNS AlbListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: Certificates: - CertificateArn: !Ref AlbAcm DefaultActions: - FixedResponseConfig: ContentType: text/plain MessageBody: Unauthorized Access StatusCode: "403" Type: fixed-response LoadBalancerArn: !Ref Alb Port: 443 Protocol: HTTPS SslPolicy: ELBSecurityPolicy-2016-08 AlbListenerCertificate1: Type: AWS::ElasticLoadBalancingV2::ListenerCertificate Properties: Certificates: - CertificateArn: !Ref AlbAcm1 ListenerArn: !Ref AlbListener AlbListenerCertificate2: Type: AWS::ElasticLoadBalancingV2::ListenerCertificate Properties: Certificates: - CertificateArn: !Ref AlbAcm2 ListenerArn: !Ref AlbListener AlbListnerRule1: Type: AWS::ElasticLoadBalancingV2::ListenerRule Properties: Actions: - TargetGroupArn: !Ref TargetGroup Type: forward Conditions: - Field: path-pattern PathPatternConfig: Values: - '*' ListenerArn: !Ref AlbListener Priority: 1 AlbRecordSet: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Join - "" - - dualstack. - !GetAtt Alb.DNSName EvaluateTargetHealth: false HostedZoneId: Z14GRHDCWA56QT HostedZoneId: !Ref hostedZoneId Name: !Ref domainName Type: A AlbRecordSet1: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Join - "" - - dualstack. - !GetAtt Alb.DNSName EvaluateTargetHealth: false HostedZoneId: Z14GRHDCWA56QT HostedZoneId: !Ref hostedZoneId Name: !Ref domainName1 Type: A AlbRecordSet2: Type: AWS::Route53::RecordSet Properties: AliasTarget: DNSName: !Sub ${domain2Cloudfront} EvaluateTargetHealth: false HostedZoneId: Z2FDTNDATAQYW2 HostedZoneId: !Ref hostedZoneId Name: !Ref domainName2 Type: A AlbSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Application load barancer. GroupName: !Sub ${env}-${sysName}-alb-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - CidrIp: 0.0.0.0/0 FromPort: 443 IpProtocol: tcp ToPort: 443 Tags: - Key: Name Value: !Sub ${env}-${sysName}-alb-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC DbInstance1: Type: AWS::RDS::DBInstance DeletionPolicy: Delete Properties: AutoMinorVersionUpgrade: false DBClusterIdentifier: !Ref clusterAurora DBInstanceClass: !Ref instanceClass DBInstanceIdentifier: !Sub ${env}-${sysName}-db1 DBParameterGroupName: !Ref ParameterGroupAurora EnablePerformanceInsights: false Engine: aurora-mysql MonitoringInterval: 0 PubliclyAccessible: false Tags: - Key: Name Value: !Sub ${env}-${sysName}-db1 - Key: BillingGroup Value: !Ref billingTag DbInstance2: Type: AWS::RDS::DBInstance DeletionPolicy: Delete Properties: AutoMinorVersionUpgrade: false DBClusterIdentifier: !Ref clusterAurora DBInstanceClass: !Ref instanceClass DBInstanceIdentifier: !Sub ${env}-${sysName}-db2 DBParameterGroupName: !Ref ParameterGroupAurora EnablePerformanceInsights: false Engine: aurora-mysql MonitoringInterval: 0 PubliclyAccessible: false Tags: - Key: Name Value: !Sub ${env}-${sysName}-db2 - Key: BillingGroup Value: !Ref billingTag Instance1: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: 3000 VolumeSize: 8 VolumeType: gp3 CreditSpecification: CPUCredits: standard IamInstanceProfile: !Ref ec2Profile ImageId: !Ref ec2Ami InstanceType: !Ref instanceType SecurityGroupIds: - !Ref ec2SG SubnetId: !Ref MyPublicSubnet1 Tags: - Key: Name Value: !Sub ${env}-${sysName}-instance1 - Key: BillingGroup Value: !Ref billingTag UserData: !Base64 Fn::Sub: | #!/bin/bash yum install -y httpd cp -p /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.org cat <<EOF >> /etc/httpd/conf/httpd.conf <VirtualHost *:80> ServerName ${domainName} DocumentRoot /var/www/html/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName1} DocumentRoot /var/www/site1/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName2} DocumentRoot /var/www/site2/ </VirtualHost> EOF mkdir /var/www/{site1,site2} cd /var/www/html/ curl -O https://free-hp.net/clinic/cl_002/clinic_002_DL.zip && unzip clinic_002_DL.zip && mv clinic_002_DL/* . && rmdir clinic_002_DL && rm clinic_002_DL.zip cd /var/www/site1/ curl -O https://free-hp.net/restaurant/re_002/restaurant_002_DL.zip && unzip restaurant_002_DL.zip && mv restaurant_002_DL/* . && rmdir restaurant_002_DL && rm restaurant_002_DL.zip cd /var/www/site2/ curl -O https://free-hp.net/school/sc_001/school_001_DL.zip && unzip school_001_DL.zip && mv school_001_DL/* . && rmdir school_001_DL && rm school_001_DL.zip systemctl start httpd Instance2: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: 3000 VolumeSize: 8 VolumeType: gp3 CreditSpecification: CPUCredits: standard IamInstanceProfile: !Ref ec2Profile ImageId: !Ref ec2Ami InstanceType: !Ref instanceType SecurityGroupIds: - !Ref ec2SG SubnetId: !Ref MyPublicSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-instance2 - Key: BillingGroup Value: !Ref billingTag UserData: !Base64 Fn::Sub: | #!/bin/bash yum install -y httpd cp -p /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.org cat <<EOF >> /etc/httpd/conf/httpd.conf <VirtualHost *:80> ServerName ${domainName} DocumentRoot /var/www/html/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName1} DocumentRoot /var/www/site1/ </VirtualHost> <VirtualHost *:80> ServerName ${domainName2} DocumentRoot /var/www/site2/ </VirtualHost> EOF mkdir /var/www/{site1,site2} cd /var/www/html/ curl -O https://free-hp.net/clinic/cl_002/clinic_002_DL.zip && unzip clinic_002_DL.zip && mv clinic_002_DL/* . && rmdir clinic_002_DL && rm clinic_002_DL.zip cd /var/www/site1/ curl -O https://free-hp.net/restaurant/re_002/restaurant_002_DL.zip && unzip restaurant_002_DL.zip && mv restaurant_002_DL/* . && rmdir restaurant_002_DL && rm restaurant_002_DL.zip cd /var/www/site2/ curl -O https://free-hp.net/school/sc_001/school_001_DL.zip && unzip school_001_DL.zip && mv school_001_DL/* . && rmdir school_001_DL && rm school_001_DL.zip systemctl start httpd MyInternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-igw - Key: BillingGroup Value: !Ref billingTag MyPrivateRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref MyInternetGateway RouteTableId: !Ref MyPrivateRouteTable MyPrivateRouteTable: Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-rtb - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 10 - !Cidr - !GetAtt MyVPC.CidrBlock - 12 - 8 Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-subnet-1 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPrivateRouteTable SubnetId: !Ref MyPrivateSubnet1 MyPrivateSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 11 - !Cidr - !GetAtt MyVPC.CidrBlock - 12 - 8 Tags: - Key: Name Value: !Sub ${env}-${sysName}-private-subnet-2 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPrivateSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPrivateRouteTable SubnetId: !Ref MyPrivateSubnet2 MyPublicRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref MyInternetGateway RouteTableId: !Ref MyPublicRouteTable MyPublicRouteTable: Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-rtb - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 0 - !Cidr - !GetAtt MyVPC.CidrBlock - 2 - 8 MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-subnet-1 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPublicRouteTable SubnetId: !Ref MyPublicSubnet1 MyPublicSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 1 - !Cidr - !GetAtt MyVPC.CidrBlock - 2 - 8 MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-public-subnet-2 - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC MyPublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref MyPublicRouteTable SubnetId: !Ref MyPublicSubnet2 MyVPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref vpcCidr Tags: - Key: Name Value: !Sub ${env}-${sysName}-vpc - Key: BillingGroup Value: !Ref billingTag MyVPCGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref MyInternetGateway VpcId: !Ref MyVPC ParameterGroupAurora: Type: AWS::RDS::DBParameterGroup Properties: Description: !Sub ${env}-${sysName}-parametergroup Family: aurora-mysql5.7 RdsSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Rds Instances. GroupName: !Sub ${env}-${sysName}-rds-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - FromPort: 3306 IpProtocol: tcp SourceSecurityGroupId: !Ref ec2SG ToPort: 3306 Tags: - Key: Name Value: !Sub ${env}-${sysName}-rds-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC SubnetGroupRds: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: !Sub ${env}-${sysName}-rds-SubnetGroup DBSubnetGroupName: !Sub ${env}-${sysName}-rds-subnetgroup SubnetIds: - !Ref MyPrivateSubnet1 - !Ref MyPrivateSubnet2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-rds-SubnetGroup - Key: BillingGroup Value: !Ref billingTag TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Name: !Sub ${env}-${sysName}-ec2 Port: 80 Protocol: HTTP Tags: - Key: Name Value: !Sub ${env}-${sysName}-tg - Key: BillingGroup Value: !Ref billingTag TargetType: instance Targets: - Id: !Ref Instance1 - Id: !Ref Instance2 VpcId: !Ref MyVPC clusterAurora: Type: AWS::RDS::DBCluster DeletionPolicy: Delete Properties: BackupRetentionPeriod: 7 DBClusterIdentifier: !Sub ${env}-${sysName}-cluster DBClusterParameterGroupName: !Ref clusterParameterGroupAurora DBSubnetGroupName: !Ref SubnetGroupRds DeletionProtection: false EnableCloudwatchLogsExports: - audit - error - general - slowquery Engine: aurora-mysql EngineVersion: 5.7.mysql_aurora.2.10.0 MasterUserPassword: !Ref masterPassword MasterUsername: root Port: 3306 PreferredBackupWindow: 17:00-18:00 PreferredMaintenanceWindow: tue:18:00-tue:19:00 StorageEncrypted: true Tags: - Key: Name Value: !Sub ${env}-${sysName}-cluster - Key: BillingGroup Value: !Ref billingTag VpcSecurityGroupIds: - !Ref RdsSG clusterParameterGroupAurora: Type: AWS::RDS::DBClusterParameterGroup Properties: Description: !Sub ${env}-${sysName}-cluster-parametergroup Family: aurora-mysql5.7 Parameters: character_set_client: utf8 character_set_connection: utf8 character_set_database: utf8 character_set_results: utf8 character_set_server: utf8 general_log: 1 server_audit_events: CONNECT,QUERY,QUERY_DCL,QUERY_DDL,QUERY_DML,TABLE server_audit_logging: 1 slow_query_log: 1 time_zone: Asia/Tokyo ec2Profile: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !Ref ec2Role ec2Role: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - ec2.amazonaws.com Version: "2012-10-17" ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore - arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess ec2SG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: ec2 Instances. GroupName: !Sub ${env}-${sysName}-ec2-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - FromPort: 80 IpProtocol: tcp SourceSecurityGroupId: !Ref AlbSG ToPort: 80 Tags: - Key: Name Value: !Sub ${env}-${sysName}-ec2-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC site2Bucket: DeletionPolicy: Delete UpdateReplacePolicy: Delete Type: AWS::S3::Bucket Properties: BucketName: !Ref domainName2 Tags: - Key: Name Value: !Sub ${env}-${sysName}-site2-contents-bucket - Key: BillingGroup Value: !Ref billingTag site2BucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref site2Bucket PolicyDocument: Statement: - Action: s3:GetObject Effect: Allow Resource: !Sub arn:aws:s3:::${site2Bucket}/* Principal: AWS: !Sub arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${site2OAI} site1Ecr: Type: AWS::ECR::Repository Properties: RepositoryName: site1 ImageScanningConfiguration: ScanOnPush: "true" LifecyclePolicy: LifecyclePolicyText: | { "rules": [ { "rulePriority": 1, "description": "Delete images", "selection": { "tagStatus": "any", "countType": "imageCountMoreThan", "countNumber": 10 }, "action": { "type": "expire" } } ] } Tags: - Key: Name Value: !Sub ${env}-${sysName}-site1-ecr - Key: BillingGroup Value: !Ref billingTag ecsLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub "/ecs/logs/${env}/${sysName}" ecsTaskExecutionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub ${env}-${sysName}-ecs-task-exec-role Path: / AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: ecs-tasks.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy site1TaskDefinition: Type: AWS::ECS::TaskDefinition Properties: ContainerDefinitions: - Essential: true Image: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/site1:latest" LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref ecsLogGroup awslogs-region: !Ref AWS::Region awslogs-stream-prefix: site1 Name: site1 PortMappings: - ContainerPort: 80 HostPort: 80 Protocol: tcp Family: site1 ExecutionRoleArn: !GetAtt ecsTaskExecutionRole.Arn NetworkMode: awsvpc RequiresCompatibilities: - FARGATE Cpu: "256" Memory: "512" site1EcsSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: site1 ecs. GroupName: !Sub ${env}-${sysName}-site1-ecs-sg SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: 0 IpProtocol: "-1" ToPort: 0 SecurityGroupIngress: - FromPort: 80 IpProtocol: tcp SourceSecurityGroupId: !Ref AlbSG ToPort: 80 Tags: - Key: Name Value: !Sub ${env}-${sysName}-site1-ecs-sg - Key: BillingGroup Value: !Ref billingTag VpcId: !Ref MyVPC site1TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Name: !Sub ${env}-${sysName}-site1-ecs-tg Port: 80 Protocol: HTTP TargetType: ip VpcId: !Ref MyVPC Tags: - Key: Name Value: !Sub ${env}-${sysName}-site1-ecs-tg - Key: BillingGroup Value: !Ref billingTag cluster: Type: AWS::ECS::Cluster Properties: ClusterName: !Sub ${env}-${sysName}-cluster ClusterSettings: - Name: containerInsights Value: enabled Tags: - Key: Name Value: !Sub ${env}-${sysName}-cluster - Key: BillingGroup Value: !Ref billingTag site1ServiceDefinition: Type: AWS::ECS::Service Properties: Cluster: !Ref cluster DesiredCount: 0 LaunchType: FARGATE LoadBalancers: - TargetGroupArn: !Ref site1TargetGroup ContainerPort: 80 ContainerName: site1 NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: ENABLED SecurityGroups: - !Ref site1EcsSG Subnets: - !Ref MyPublicSubnet1 - !Ref MyPublicSubnet2 ServiceName: !Sub ${env}-${sysName}-site1 TaskDefinition: !Sub ${site1TaskDefinition}
最後のデプロイです。
rain deploy -y ./site.yml site --params \ env=prd,\ sysName=example,\ billingTag=example,\ vpcCidr=10.0.0.0/16,\ domainName=example.com,\ domainName1=site1.example.com,\ domainName2=site2.example.com,\ hostedZoneId=Z10116024XXXXXXXXXXX,\ instanceType=t3.micro,\ instanceClass=db.t3.small,\ masterPassword=Passw0rd,\ domain2Cloudfront=dxxxxxxxxxxxx.cloudfront.net,\ site2OAI=E36GW62SYZ8RPJ
site1.example.com
にアクセスしてレスポンスが帰ってきたらOKです。お疲れ様でした。
終わりに
VirtualHostからの分離方法を整理してみました。VirtualHostだけじゃなく、サイトのリプレースも同様の流れになるので参考にしていただければ幸いです。また、S3、ECR,ECSはCodePipelineのビルド、デプロイ先として指定できるのでCI/CDの導入も一緒にご検討ください。