概要
お名前.comで購入したドメインについて、ALBを使ってホストベースのルーティングを行う。
今回のルーティング先はECSのサービスとする。
複数のドメインを使ってホストベースルーティングする場合でも、1台のALBで対応できる。また、ACMのSSL証明書も1つで済む。
事前準備
事前にお名前.comでexample.com
とtest.com
のドメインを購入しているとする。
(もちろん、example.com
だけでもOK)
お名前.comでは、NSレコードを使ってAWSのRoute53のホストゾーンにドメインないしはサブドメインの管理を委任することができる。
ここでは、Route53でexample.com
およびtest.com
のホストゾーンを作成し、そのNSレコードをお名前.comで設定する。
実装
Route53でexample.com
およびにtest.com
対してホストゾーンを作ったが、これらのドメインに対してはAliasレコード、サブドメインであるwww.example.com
に対してはCNAMEレコードを設定し、向き先は全てALBとする。
ALBのリスナールールで、リクエストのヘッダーに含まれるドメインに応じたルーティングを設定できる。
ACMの設定もSubjectAlternativeNames
を指定すると、複数のドメインに対して有効となる。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
DomainName1:
Description: FQDN of the HostZone example.com
Type: String
Default: example.com
DomainName2:
Description: FQDN of the HostZone test.com
Type: String
Default: test.com
SubjectAlternativeName:
Description: FQDN of the Subdomains. # www.example.com, acc.examle.comなど全てをカバー
Type: String
Default: '*.example.com'
SubDomain:
Description: FQDN of the Subdomain
Type: String
Default: www.example.com
HostZoneId1:
Description: host zone id of example.com
Type: String
Default: <input-your-hostzone-id>
HostZoneId2:
Description: host zone id of test.com
Type: String
Default: <input-your-hostzone-id>
EnvironmentName:
Type: String
Default: alb-ecs
VpcId:
Type: String
Default: <input-your-vpc-id>
PublicSubnet1:
Type: String
Default: <input-your-subnet-id>
PublicSubnet2:
Type: String
Default: <input-your-subnet-id>
PublicSubnet3:
Type: String
Default: <input-your-subnet-id>
PublicAlbSG:
Type: String
Default: <input-your-security-group-id-for-ALB>
PrivateSubnet1:
Type: String
Default: <input-your-subnet-id>
PrivateSubnet2:
Type: String
Default: <input-your-subnet-id>
PrivateSubnet3:
Type: String
Default: <input-your-subnet-id>
EcsTaskSG:
Type: String
Default: <input-your-security-group-id-for-ECS-task>
ClusterArn:
Type: String
Default: <input-your-ECS-cluster-ARN>
ExecutionRoleArn:
Type: String
Default: <input-your-ECS-role-ARN>
TaskRoleArn:
Type: String
Default: <input-your-Task-role-ARN>
ServiceName1:
Type: String
Default: <input-your-service-name-for-application1>
ServiceName2:
Type: String
Default: <input-your-service-name-for-application2>
ServiceName3:
Type: String
Default: <input-your-service-name-for-application3>
ImageUrl1:
Type: String
Default: <input-your-image-url-of-application1>
ImageUrl2:
Type: String
Default: <input-your-image-url-of-application2>
ImageUrl3:
Type: String
Default: <input-your-image-url-of-application2>
ContainerPort:
Type: Number
Default: <inout-your-port-number-of-task>
ContainerCpu:
Type: Number
Default: 256
Description: How much CPU to give the container. 1024 is 1 CPU
ContainerMemory:
Type: Number
Default: 512
Description: How much memory in megabytes to give the container
DesiredCount:
Type: Number
Default: 1
Description: How many copies of the service task to run
Resources:
# 複数のドメインやサブドメインに対しても、ACMは一つで対応可
ACMCertificate:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: !Sub ${DomainName1}
SubjectAlternativeNames:
- !Sub ${SubjectAlternativeName}
- !Sub ${DomainName2}
DomainValidationOptions:
- HostedZoneId: !Sub ${HostZoneId1}
DomainName: !Sub ${DomainName1}
ValidationMethod: DNS
PublicLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Scheme: internet-facing
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: '30'
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
- !Ref PublicSubnet3
SecurityGroups:
- !Ref PublicAlbSG
Domain1Record: # example.com
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneId: !Sub ${HostZoneId1}
Comment: Zone apex alias targeted to public LoadBalancer.
RecordSets:
- Name: !Sub ${DomainName1}
Type: A
AliasTarget:
HostedZoneId: !GetAtt PublicLoadBalancer.CanonicalHostedZoneID
DNSName: !GetAtt PublicLoadBalancer.DNSName
Domain1SubDomainRecord: # www.example.com
Type: AWS::Route53::RecordSet
DependsOn: PublicLoadBalancer
Properties:
HostedZoneId: !Sub ${HostZoneId1}
Name: !Sub ${SubDomain}
Type: CNAME
TTL: 60
ResourceRecords:
- !GetAtt PublicLoadBalancer.DNSName
Domain2Record: # test.com
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneId: !Sub ${HostZoneId2}
Comment: Zone apex alias targeted to public LoadBalancer.
RecordSets:
- Name: !Sub ${DomainName2}
Type: A
AliasTarget:
HostedZoneId: !GetAtt PublicLoadBalancer.CanonicalHostedZoneID
DNSName: !GetAtt PublicLoadBalancer.DNSName
LogGroup1:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub ${EnvironmentName}-service-${ServiceName1}
LogGroup2:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub ${EnvironmentName}-service-${ServiceName2}
LogGroup3:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub ${EnvironmentName}-service-${ServiceName3}
TaskDefinition1:
Type: AWS::ECS::TaskDefinition
Properties:
Family: !Ref ServiceName1
Cpu: !Ref ContainerCpu
Memory: !Ref ContainerMemory
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn: !Ref ExecutionRoleArn
TaskRoleArn: !Ref TaskRoleArn
ContainerDefinitions:
- Name: !Ref ServiceName1
Cpu: !Ref ContainerCpu
Memory: !Ref ContainerMemory
Essential: true
Image: !Ref ImageUrl1
PortMappings:
- ContainerPort: !Ref ContainerPort
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Sub ${EnvironmentName}-service-${ServiceName1}
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: !Ref ServiceName1
TaskDefinition2:
Type: AWS::ECS::TaskDefinition
Properties:
Family: !Ref ServiceName2
Cpu: !Ref ContainerCpu
Memory: !Ref ContainerMemory
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn: !Ref ExecutionRoleArn
TaskRoleArn: !Ref TaskRoleArn
ContainerDefinitions:
- Name: !Ref ServiceName2
Cpu: !Ref ContainerCpu
Memory: !Ref ContainerMemory
Essential: true
Image: !Ref ImageUrl2
PortMappings:
- ContainerPort: !Ref ContainerPort
LogConfiguration:
LogDriver: 'awslogs'
Options:
awslogs-group: !Sub ${EnvironmentName}-service-${ServiceName2}
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: !Ref ServiceName2
TaskDefinition3:
Type: AWS::ECS::TaskDefinition
Properties:
Family: !Ref ServiceName3
Cpu: !Ref ContainerCpu
Memory: !Ref ContainerMemory
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn: !Ref ExecutionRoleArn
TaskRoleArn: !Ref TaskRoleArn
ContainerDefinitions:
- Name: !Ref ServiceName3
Cpu: !Ref ContainerCpu
Memory: !Ref ContainerMemory
Essential: true
Image: !Ref ImageUrl3
PortMappings:
- ContainerPort: !Ref ContainerPort
LogConfiguration:
LogDriver: 'awslogs'
Options:
awslogs-group: !Sub ${EnvironmentName}-service-${ServiceName3}
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: !Ref ServiceName3
EcsService1:
Type: AWS::ECS::Service
DependsOn:
- HTTPSLoadBalancerListener
Properties:
ServiceName: !Ref ServiceName1
Cluster: !Ref ClusterArn
LaunchType: FARGATE
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 75
DesiredCount: !Ref DesiredCount
HealthCheckGracePeriodSeconds: 300
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: DISABLED # in case of no public ip is needed
SecurityGroups:
- !Ref EcsTaskSG
Subnets:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
- !Ref PrivateSubnet3
TaskDefinition: !Ref TaskDefinition1
LoadBalancers:
- ContainerName: !Ref ServiceName1
ContainerPort: !Ref ContainerPort
TargetGroupArn: !Ref TargetGroup1
EcsService2:
Type: AWS::ECS::Service
DependsOn:
- HTTPSLoadBalancerListener
Properties:
ServiceName: !Ref ServiceName2
Cluster: !Ref ClusterArn
LaunchType: FARGATE
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 75
DesiredCount: !Ref DesiredCount
HealthCheckGracePeriodSeconds: 300
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: DISABLED
SecurityGroups:
- !Ref EcsTaskSG
Subnets:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
- !Ref PrivateSubnet3
TaskDefinition: !Ref TaskDefinition2
LoadBalancers:
- ContainerName: !Ref ServiceName2
ContainerPort: !Ref ContainerPort
TargetGroupArn: !Ref TargetGroup2
EcsService3:
Type: AWS::ECS::Service
DependsOn:
- HTTPSLoadBalancerListener
Properties:
ServiceName: !Ref ServiceName3
Cluster: !Ref ClusterArn
LaunchType: FARGATE
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 75
DesiredCount: !Ref DesiredCount
HealthCheckGracePeriodSeconds: 300
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: DISABLED
SecurityGroups:
- !Ref EcsTaskSG
Subnets:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
- !Ref PrivateSubnet3
TaskDefinition: !Ref TaskDefinition3
LoadBalancers:
- ContainerName: !Ref ServiceName3
ContainerPort: !Ref ContainerPort
TargetGroupArn: !Ref TargetGroup3
HTTPLoadBalancerListener: # 80=>443に転送
Type: AWS::ElasticLoadBalancingV2::Listener
DependsOn:
- PublicLoadBalancer
Properties:
DefaultActions:
- Type: redirect
RedirectConfig:
Protocol: HTTPS
Port: 443
Host: '#{host}'
Path: '/#{path}'
Query: '#{query}'
StatusCode: HTTP_301
LoadBalancerArn: !Ref PublicLoadBalancer
Port: 80
Protocol: HTTP
HTTPSLoadBalancerListener: # SSL設定してTargetGroupに転送
Type: AWS::ElasticLoadBalancingV2::Listener
DependsOn: PublicLoadBalancer
Properties:
LoadBalancerArn: !Ref PublicLoadBalancer
Port: 443
Protocol: HTTPS
DefaultActions:
- Type: forward
TargetGroupArn: !Ref TargetGroup1
SslPolicy: ELBSecurityPolicy-2016-08
Certificates:
- CertificateArn: !Ref ACMCertificate
ListenerRule1:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
ListenerArn: !Ref HTTPSLoadBalancerListener
Priority: 2
Conditions:
- Field: host-header
HostHeaderConfig:
Values:
- !Ref DomainName1
Actions:
- Type: forward
TargetGroupArn: !Ref TargetGroup1
TargetGroup1:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
DependsOn: PublicLoadBalancer
Properties:
HealthCheckIntervalSeconds: 300
HealthCheckPath: / # input healthcheck path
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 120
HealthyThresholdCount: 2
TargetType: ip #fargateなのでVPC内のipをtargetにする。EC2であればinstanceがtargetになる
Name: !Ref ServiceName1
Port: !Ref ContainerPort
Protocol: HTTP
UnhealthyThresholdCount: 2
VpcId: !Ref VpcId
ListenerRule2:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
ListenerArn: !Ref HTTPSLoadBalancerListener
Priority: 1
Conditions:
- Field: host-header
HostHeaderConfig:
Values:
- !Ref SubDomain
Actions:
- Type: forward
TargetGroupArn: !Ref TargetGroup2
TargetGroup2:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
DependsOn: PublicLoadBalancer
Properties:
HealthCheckIntervalSeconds: 300
HealthCheckPath: / # input healthcheck path
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 120
HealthyThresholdCount: 2
TargetType: ip #fargateなのでVPC内のipをtargetにする。EC2であればinstanceがtargetになる
Name: !Ref ServiceName2
Port: !Ref ContainerPort
Protocol: HTTP
UnhealthyThresholdCount: 2
VpcId: !Ref VpcId
ListenerRule3:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
ListenerArn: !Ref HTTPSLoadBalancerListener
Priority: 3
Conditions:
- Field: host-header
HostHeaderConfig:
Values:
- !Ref DomainName2
Actions:
- Type: forward
TargetGroupArn: !Ref TargetGroup3
TargetGroup3:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
DependsOn: PublicLoadBalancer
Properties:
HealthCheckIntervalSeconds: 300
HealthCheckPath: / # input healthcheck path
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 120
HealthyThresholdCount: 2
TargetType: ip
Name: !Ref ServiceName3
Port: !Ref ContainerPort
Protocol: HTTP
UnhealthyThresholdCount: 2
VpcId: !Ref VpcId
Xserverでドメインを取得した場合
Xserverでドメインを取得した場合、お名前.comのようにRoute53のNSレコードを設定することができない。
つまりRoute53のホストゾーンは使わない。
(上記Cloudformationで言えば、Domain1Record
,Domain1SubDomainRecord
,Domain2Record
が不要。)
この場合、Xserverの設定画面で、使用する全てのDNSに対してCNAMEを設定してALBに向けさせる。
ACMの証明書を使う場合、Xserverの設定画面で証明書の認証設定が必要。下記参照。