From fa56a10dfab8fec29ac099babe97e731b830afbc Mon Sep 17 00:00:00 2001 From: Karl Wallbom Date: Fri, 3 Apr 2020 15:44:33 +0200 Subject: [PATCH] feat: add dedicated lb template and move as default (#13) Co-authored-by: Karl Wallbom --- apps/ecs-fargate-shared/README.md | 63 +++++++ apps/ecs-fargate-shared/latest.yaml | 253 ++++++++++++++++++++++++++++ apps/ecs-fargate/README.md | 4 +- apps/ecs-fargate/latest.yaml | 130 +++++++++++--- 4 files changed, 421 insertions(+), 29 deletions(-) create mode 100644 apps/ecs-fargate-shared/README.md create mode 100644 apps/ecs-fargate-shared/latest.yaml diff --git a/apps/ecs-fargate-shared/README.md b/apps/ecs-fargate-shared/README.md new file mode 100644 index 0000000..0610cf7 --- /dev/null +++ b/apps/ecs-fargate-shared/README.md @@ -0,0 +1,63 @@ +# Gurum - ECS Fargate Application on Shared Load Balancer + +ECS Fargate Application running on a Shared Load Balancer. +[https://aws.amazon.com/documentation/codepipeline/](https://aws.amazon.com/documentation/codepipeline/) + +## Table of contents + +* [Parameters](#parameters) + * [Generic](#generic) + * [Prescribed](#prescribed) +* [Examples](#examples) + * [minimal](#minimal) + * [complete](#complete) + +## Parameters + +### Generic + +These parameters are required, but generic or require privileged access to the underlying AWS account. + +Name | Description | Default | Accepted Values +-------------- | --------------- | --------------- | --------------- +DesiredCount|How many instances of this task to run across our cluster|1|Int +HealthCheckPath|The health check path to register with the Application Load Balancer|/|String +ServiceDiscoveryTTL|The amount of time, in seconds, that you want DNS resolvers to cache the settings for this record.|60|Double + +### Prescribed + +These are parameters that are prescribed by the plan and are not configurable, should adjusting any of these be required please choose a plan that makes them available. + +Name | Description | Value +-------------- | --------------- | --------------- +BucketName|Must contain only lowercase letters, numbers, periods (.), and hyphens. If set to Auto, a bucket name will be generated (-),Cannot end in numbers|Auto + +## Examples + +***Note:*** Examples do not include generic parameters, if you have not setup defaults for these you will need to add +them as additional parameters + +### Minimal + +```yaml +environments: + - name: dev + config: + DesiredCount: 1 + HealthCheckPath: '/' + ServiceDiscoveryTTL: 60 +``` + +### Complete + +```yaml +environments: + - name: dev + config: + DesiredCount: 4 + HealthCheckPath: '/health' + ServiceDiscoveryTTL: 60 + env_vars: + environment: prod + YourVar: AnotherEnvVar +``` diff --git a/apps/ecs-fargate-shared/latest.yaml b/apps/ecs-fargate-shared/latest.yaml new file mode 100644 index 0000000..611b841 --- /dev/null +++ b/apps/ecs-fargate-shared/latest.yaml @@ -0,0 +1,253 @@ +# This is a sample, non-production-ready template. +# +# © 2019 Amazon Web Services, In​c. or its affiliates. All Rights Reserved. +# +# This AWS Content is provided subject to the terms of the +# AWS Customer Agreement available at http://aws.amazon.com/agreement +# or other written agreement between Customer and either +# Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +AWSTemplateFormatVersion: "2010-09-09" +Description: Platform App on Shared Load Balancer + +Parameters: + + # User Parameters - Dynamically generated from the API + DesiredCount: + Description: How many instances of this task should we run across our cluster? + Type: Number + Default: 1 + + Priority: + Description: The Application Load Balancer rule priority + Type: Number + Default: 1 + + HealthCheckPath: + Description: The health check path to register with the Application Load Balancer + Type: String + Default: / + + DockerImage: + Description: Docker image to start the container with + Type: String + Default: nginx:latest + + ServiceDiscoveryTTL: + Type: String + Description: The amount of time, in seconds, that you want DNS resolvers to cache the settings for this record. + Default: '60' + + # Platform Parameters - Resolved from SSM. + PlatformDomainName: + Type: AWS::SSM::Parameter::Value + Description: Name of the Hosted Zone to register service with + Default: /gurum/platform/domain-name + + PlatformVPC: + Type: AWS::SSM::Parameter::Value + Description: Platform VPC + Default: /gurum/platform/vpc + + PlatformPrivateSubnets: + Type: AWS::SSM::Parameter::Value> + Description: Platform Private Subnets + Default: /gurum/platform/subnets/private + + PlatformCluster: + Type: AWS::SSM::Parameter::Value + Description: Platform ECS Cluster + Default: /gurum/platform/ecs + + PlatformLoadBalancerDnsName: + Type: AWS::SSM::Parameter::Value + Description: Platform Load Balancer DNS Name + Default: /gurum/platform/loadbalancer/dns-name + + PlatformLoadBalancerHostedZoneId: + Type: AWS::SSM::Parameter::Value + Description: Platform Load Balancer Hosted Zone ID + Default: /gurum/platform/loadbalancer/hosted-zone-id + + PlatformLoadBalancerListener: + Type: AWS::SSM::Parameter::Value + Description: The Application Load Balancer listener to register with + Default: /gurum/platform/loadbalancer/listener-arn + + PlatformLoadBalancerSecurityGroup: + Type: AWS::SSM::Parameter::Value + Description: Platform Load Balancer Security Group + Default: /gurum/platform/loadbalancer/security-group + + PlatformNamespaceId: + Type: AWS::SSM::Parameter::Value + Description: Platform Namespace ID + Default: /gurum/platform/service-discovery/namespace-id + +Resources: + + Service: + Type: AWS::ECS::Service + DependsOn: ListenerRule + Properties: + ServiceName: + Ref: AWS::StackName + Cluster: !Ref PlatformCluster + DeploymentConfiguration: + MaximumPercent: 200 + MinimumHealthyPercent: 50 + DesiredCount: !Ref DesiredCount + TaskDefinition: !Ref TaskDefinition + LaunchType: FARGATE + LoadBalancers: + - ContainerName: web + ContainerPort: 80 + TargetGroupArn: !Ref TargetGroup + NetworkConfiguration: + AwsvpcConfiguration: + AssignPublicIp: DISABLED + SecurityGroups: + - !GetAtt ApplicationServiceSecurityGroup.GroupId + Subnets: !Ref PlatformPrivateSubnets + ServiceRegistries: + - + Port: 80 + RegistryArn: !GetAtt ServiceDiscoveryService.Arn + + TaskDefinition: + Type: AWS::ECS::TaskDefinition + Properties: + Family: + Ref: AWS::StackName + Cpu: "1024" + Memory: "2048" + NetworkMode: awsvpc + RequiresCompatibilities: + - FARGATE + TaskRoleArn: !GetAtt ApplicationContainerRole.Arn + ExecutionRoleArn: !GetAtt ApplicationExecutionRole.Arn + ContainerDefinitions: + - Name: web + Essential: true + Image: !Ref DockerImage + Memory: 2048 + PortMappings: + - ContainerPort: 80 + LogConfiguration: + LogDriver: awslogs + Options: + awslogs-group: !Ref CloudWatchLogsGroup + awslogs-region: !Ref AWS::Region + awslogs-stream-prefix: 'app' + + CloudWatchLogsGroup: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: !Sub 'app-${AWS::StackName}' + RetentionInDays: 365 + + ApplicationServiceSecurityGroup: + Type: "AWS::EC2::SecurityGroup" + Properties: + GroupName: !Sub 'app-sg-${AWS::StackName}' + GroupDescription: !Sub '(${AWS::StackName}) Application Service Security Group' + SecurityGroupIngress: + - SourceSecurityGroupId: !Ref PlatformLoadBalancerSecurityGroup + FromPort: 80 + ToPort: 80 + IpProtocol: tcp + VpcId: !Ref PlatformVPC + + TargetGroup: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + Properties: + Name: + Ref: AWS::StackName + TargetType: ip + Port: 80 + Protocol: HTTP + VpcId: !Ref PlatformVPC + TargetGroupAttributes: + - Key: deregistration_delay.timeout_seconds + Value: "50" + Matcher: + HttpCode: 200-299 + HealthCheckIntervalSeconds: 10 + HealthCheckPath: !Ref HealthCheckPath + HealthCheckProtocol: HTTP + HealthCheckTimeoutSeconds: 5 + HealthyThresholdCount: 2 + + ListenerRule: + Type: AWS::ElasticLoadBalancingV2::ListenerRule + Properties: + ListenerArn: !Ref PlatformLoadBalancerListener + Priority: !Ref Priority + Conditions: + - Field: host-header + Values: + - !Join ['', [!Ref 'AWS::StackName', ., !Ref 'PlatformDomainName']] + Actions: + - TargetGroupArn: !Ref TargetGroup + Type: forward + + ServiceDNSRecord: + Type: AWS::Route53::RecordSet + Properties: + HostedZoneName: !Join ['', [!Ref 'PlatformDomainName', .]] + Comment: !Sub 'DNS Record for ${AWS::StackName}' + Name: !Join ['', [!Ref 'AWS::StackName', ., !Ref 'PlatformDomainName']] + Type: A + AliasTarget: + HostedZoneId: !Ref PlatformLoadBalancerHostedZoneId + DNSName: !Ref PlatformLoadBalancerDnsName + + ServiceDiscoveryService: + Type: AWS::ServiceDiscovery::Service + Properties: + DnsConfig: + NamespaceId: !Ref PlatformNamespaceId + DnsRecords: + - Type: SRV + TTL: !Ref ServiceDiscoveryTTL + HealthCheckCustomConfig: + FailureThreshold: 1 + Name: + Ref: AWS::StackName + + # IAM Role that the container tasks assume to gain access to AWS Services such as S3 etc. + ApplicationContainerRole: + Type: "AWS::IAM::Role" + Properties: + RoleName: !Sub 'platform-role-${AWS::StackName}' + ManagedPolicyArns: + - arn:aws:iam::aws:policy/AmazonS3FullAccess + AssumeRolePolicyDocument: + Version: "2008-10-17" + Statement: + - Effect: Allow + Principal: + Service: ecs-tasks.amazonaws.com + Action: sts:AssumeRole + + # Execution role for EC2 to assume to pull images and write to CloudWatch Logs + ApplicationExecutionRole: + Type: "AWS::IAM::Role" + Properties: + RoleName: !Sub 'platform-execution-role-${AWS::StackName}' + ManagedPolicyArns: + - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy + AssumeRolePolicyDocument: + Version: "2008-10-17" + Statement: + - Effect: Allow + Principal: + Service: ecs-tasks.amazonaws.com + Action: sts:AssumeRole + +Outputs: + Endpoint: + Value: !Join ['', [!Ref 'AWS::StackName', ., !Ref 'PlatformDomainName']] + + ServiceRole: + Value: !GetAtt ApplicationContainerRole.Arn \ No newline at end of file diff --git a/apps/ecs-fargate/README.md b/apps/ecs-fargate/README.md index 0610cf7..b3b84d9 100644 --- a/apps/ecs-fargate/README.md +++ b/apps/ecs-fargate/README.md @@ -1,6 +1,6 @@ -# Gurum - ECS Fargate Application on Shared Load Balancer +# Gurum - ECS Fargate Application on Dedicated Load Balancer -ECS Fargate Application running on a Shared Load Balancer. +ECS Fargate Application running on a Dedicated Load Balancer. [https://aws.amazon.com/documentation/codepipeline/](https://aws.amazon.com/documentation/codepipeline/) ## Table of contents diff --git a/apps/ecs-fargate/latest.yaml b/apps/ecs-fargate/latest.yaml index 611b841..f702d98 100644 --- a/apps/ecs-fargate/latest.yaml +++ b/apps/ecs-fargate/latest.yaml @@ -8,7 +8,7 @@ # Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. AWSTemplateFormatVersion: "2010-09-09" -Description: Platform App on Shared Load Balancer +Description: Platform App on Dedicated Load Balancer Parameters: @@ -44,11 +44,21 @@ Parameters: Description: Name of the Hosted Zone to register service with Default: /gurum/platform/domain-name + PlatformDomainCertificate: + Type: AWS::SSM::Parameter::Value + Description: ARN to the Platform Domain ACM Certificate. + Default: /gurum/platform/domain-certificate + PlatformVPC: Type: AWS::SSM::Parameter::Value Description: Platform VPC Default: /gurum/platform/vpc + PlatformPublicSubnets: + Type: AWS::SSM::Parameter::Value> + Description: Platform Private Subnets + Default: /gurum/platform/subnets/public + PlatformPrivateSubnets: Type: AWS::SSM::Parameter::Value> Description: Platform Private Subnets @@ -59,26 +69,6 @@ Parameters: Description: Platform ECS Cluster Default: /gurum/platform/ecs - PlatformLoadBalancerDnsName: - Type: AWS::SSM::Parameter::Value - Description: Platform Load Balancer DNS Name - Default: /gurum/platform/loadbalancer/dns-name - - PlatformLoadBalancerHostedZoneId: - Type: AWS::SSM::Parameter::Value - Description: Platform Load Balancer Hosted Zone ID - Default: /gurum/platform/loadbalancer/hosted-zone-id - - PlatformLoadBalancerListener: - Type: AWS::SSM::Parameter::Value - Description: The Application Load Balancer listener to register with - Default: /gurum/platform/loadbalancer/listener-arn - - PlatformLoadBalancerSecurityGroup: - Type: AWS::SSM::Parameter::Value - Description: Platform Load Balancer Security Group - Default: /gurum/platform/loadbalancer/security-group - PlatformNamespaceId: Type: AWS::SSM::Parameter::Value Description: Platform Namespace ID @@ -117,8 +107,7 @@ Resources: TaskDefinition: Type: AWS::ECS::TaskDefinition Properties: - Family: - Ref: AWS::StackName + Family: !Ref AWS::StackName Cpu: "1024" Memory: "2048" NetworkMode: awsvpc @@ -152,7 +141,7 @@ Resources: GroupName: !Sub 'app-sg-${AWS::StackName}' GroupDescription: !Sub '(${AWS::StackName}) Application Service Security Group' SecurityGroupIngress: - - SourceSecurityGroupId: !Ref PlatformLoadBalancerSecurityGroup + - SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup FromPort: 80 ToPort: 80 IpProtocol: tcp @@ -181,7 +170,7 @@ Resources: ListenerRule: Type: AWS::ElasticLoadBalancingV2::ListenerRule Properties: - ListenerArn: !Ref PlatformLoadBalancerListener + ListenerArn: !Ref LoadBalancerListener Priority: !Ref Priority Conditions: - Field: host-header @@ -199,8 +188,8 @@ Resources: Name: !Join ['', [!Ref 'AWS::StackName', ., !Ref 'PlatformDomainName']] Type: A AliasTarget: - HostedZoneId: !Ref PlatformLoadBalancerHostedZoneId - DNSName: !Ref PlatformLoadBalancerDnsName + HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID + DNSName: !GetAtt LoadBalancer.DNSName ServiceDiscoveryService: Type: AWS::ServiceDiscovery::Service @@ -245,7 +234,94 @@ Resources: Service: ecs-tasks.amazonaws.com Action: sts:AssumeRole + # Load Balancer for the app + LoadBalancer: + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + Properties: + Name: !Sub 'app-lb-${AWS::StackName}' + Subnets: !Ref PlatformPublicSubnets + SecurityGroups: + - !Ref LoadBalancerSecurityGroup + Tags: + - Key: Name + Value: !Sub 'app-lb-${AWS::StackName}' + + LoadBalancerListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + LoadBalancerArn: !Ref LoadBalancer + Port: 443 + Protocol: HTTPS + Certificates: + - CertificateArn: !Ref PlatformDomainCertificate + DefaultActions: + - Type: forward + TargetGroupArn: !Ref TargetGroup + + LoadBalancerHttpRedirectListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + LoadBalancerArn: !Ref LoadBalancer + Port: 80 + Protocol: HTTP + DefaultActions: + - Type: redirect + RedirectConfig: + Host: "#{host}" + Path: "/#{path}" + Port: '443' + Protocol: "HTTPS" + Query: "#{query}" + StatusCode: HTTP_301 + + # This security group defines who/where is allowed to access the Application Load Balancer. + # By default, we've opened this up to the public internet (0.0.0.0/0) but can you restrict + # it further if you want. + LoadBalancerSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + VpcId: !Ref PlatformVPC + GroupDescription: Access to the load balancer that sits in front of ECS + SecurityGroupIngress: + # Allow access from anywhere to our ECS services + - CidrIp: 0.0.0.0/0 + IpProtocol: "-1" + Tags: + - Key: Name + Value: !Sub 'app-lb-sg-${AWS::StackName}' + + # We define a default target group here, as this is a mandatory Parameters + # when creating an Application Load Balancer Listener. This is not used, instead + # a target group is created per-service in each service template (../services/*) + DefaultTargetGroup: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + Properties: + VpcId: !Ref PlatformVPC + Port: 443 + Protocol: HTTPS + Outputs: + + LoadBalancer: + Description: A reference to the Application Load Balancer + Value: !Ref LoadBalancer + + LoadBalancerListener: + Description: A reference to a port listener + Value: !Ref LoadBalancerListener + + LoadBalancerCanonicalHostedZoneID: + Description: A reference to a port listener + Value: !GetAtt LoadBalancer.CanonicalHostedZoneID + + LoadBalancerDNSName: + Description: A reference to a port listener + Value: !GetAtt LoadBalancer.DNSName + + LoadBalancerSecurityGroup: + Description: A reference to the security group for load balancers + Value: !Ref LoadBalancerSecurityGroup + Endpoint: Value: !Join ['', [!Ref 'AWS::StackName', ., !Ref 'PlatformDomainName']]