AWS ECS: Use tasks and services to define deployment containers

You can download this article in PDF format via the link below to support us.
Download the guide in PDF formatturn off

This article is part 3 of part 4 of the guide to running Docker containers on AWS ECS. ECS stands for Elastic Container Service. It is a managed container service that can run docker containers. Although AWS also provides container management through Kubernetes (EKS), it also has its own proprietary solution (ECS).

The guide will cover:

  • Create an ECS cluster.
  • Set up the image registry (ECR) and push the docker image to the registry.
  • Use task and service definitions to deploy containers to the cluster.
  • Create a pipeline to update the services running on the ECS cluster.

This is part 1 and part 2:

Using ECS ​​to run Docker containers on AWS-part 1

Run Docker container on AWS ECS-upload Docker image to ECR-part 2

Part 3 of this guide will cover “Deploying containers to clusters using task and service definitions.”

In this demo, we will use a simple hello-world image pushed from Docker Hub to the ECR registry. We will create a task and service definition and deploy it to the ECS cluster.

Requirements/prerequisites

  • An AWS account.
  • A user is created on the account, and the user has permission to provision resources on the account.
  • Created a Route 53 hosted zone with your custom domain (it can be a public zone or a private zone according to user requirements).
  • Import or generate your site certificate to ACM (Amazon Certificate Manager).
  • Set up an AWS ECS cluster
  • Upload your Docker image to the ECR registry.

Create AWS Application Load Balancer and target group

ALB (Application Load Balancer) is an AWS-hosted load balancer that can route traffic according to the OSI layer 7 protocol. We will use a load balancer to expose our world service endpoints. We have provided an AWS guide on creating an application load balancer on the link below:

  • Use CloudFormation to create and configure AWS Application Load Balancer

Therefore, I will not introduce ALB too much. We will use the CloudFormation template below to create and configure our ECS ALB. The template will be provided;

  • ALB (application load balancer).
  • ALB security group.
  • Target group.
  • ALB listener and listener rules.

N/B: If readers/users wish to expose their services internally, they should create a load balancer on a private subnet. Otherwise, the load balancer should face the Internet and be created in a public subnet. For high-availability load balancers, users should configure them in different subnets in different availability zones.

AWSTemplateFormatVersion: "2010-09-09"
Description: "Create ALB, Target Groups and ALB security group"
Parameters:
    VPC:
        Type: String
        Description: The vpc to launch the service
        Default: vpc-ID

    PublicSubnet1:
        Type: String
        Description: The subnet where to launch the service
        Default: subnet-ID

    PublicSubnet2:
        Type: String
        Description: the subnet where to Launch the service
        Default: subnet-ID

Resources:            
    ALBSecurityGroup:
        Type: "AWS::EC2::SecurityGroup"
        Properties:
            GroupDescription: "security group for ALB"
            GroupName: "test-prod-ALB-SG"
            Tags: 
              - 
                Key: "Project"
                Value: "test-blog"
              - 
                Key: "createdBy"
                Value: "Maureen Barasa"
              - 
                Key: "Environment"
                Value: "test"
              - 
                Key: "Name"
                Value: "test-ALB-SG"
            VpcId: !Ref VPC
            SecurityGroupIngress: 
              - 
                CidrIp: "0.0.0.0/0"
                FromPort: 80
                IpProtocol: "tcp"
                ToPort: 80
              - 
                CidrIp: "0.0.0.0/0"
                FromPort: 443
                IpProtocol: "tcp"
                ToPort: 443
    
    ApplicationLoadBalancer:
        Type: "AWS::ElasticLoadBalancingV2::LoadBalancer"
        Properties:
            Name: "test-Application-Load-Balancer"
            Scheme: "internet-facing"
            Type: "application"
            Subnets: 
              - !Ref PublicSubnet1
              - !Ref PublicSubnet2
            SecurityGroups: 
              - !Ref ALBSecurityGroup
            IpAddressType: "ipv4"
            LoadBalancerAttributes: 
              - 
                Key: "access_logs.s3.enabled"
                Value: "true"
              - 
                Key: "idle_timeout.timeout_seconds"
                Value: "60"
              - 
                Key: "deletion_protection.enabled"
                Value: "false"
              - 
                Key: "routing.http2.enabled"
                Value: "true"
              - 
                Key: "routing.http.drop_invalid_header_fields.enabled"
                Value: "false"
            Tags: 
              - 
                Key: "Project"
                Value: "test-blog"
              - 
                Key: "createdBy"
                Value: "Maureen Barasa"
              - 
                Key: "Environment"
                Value: "test"
              - 
                Key: "Name"
                Value: "test-Application-Load-Balancer"

    HTTPSListener:
        Type: "AWS::ElasticLoadBalancingV2::Listener"
        Properties:
            LoadBalancerArn: !Ref ApplicationLoadBalancer
            Port: 443
            Protocol: "HTTPS"
            SslPolicy: "ELBSecurityPolicy-2016-08"
            Certificates: 
              - 
                CertificateArn: arn:aws:acm:eu-central-1:*************:certificate/************
                
            DefaultActions: 
              - 
                Order: 1
                TargetGroupArn: !Ref TestTargetGroup
                Type: "forward"

    HTTPListener:
        Type: "AWS::ElasticLoadBalancingV2::Listener"
        Properties:
            LoadBalancerArn: !Ref ApplicationLoadBalancer
            Port: 80
            Protocol: "HTTP"
            DefaultActions: 
              - 
                Order: 1
                RedirectConfig: 
                    Protocol: "HTTPS"
                    Port: "443"
                    Host: "#{host}"
                    Path: "/#{path}"
                    Query: "#{query}"
                    StatusCode: "HTTP_301"
                Type: "redirect"
                
    TestTargetGroup:
        Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
        Properties:
            HealthCheckIntervalSeconds: 30
            HealthCheckPath: "/"
            Port: 80
            Protocol: "HTTP"
            HealthCheckPort: "traffic-port"
            HealthCheckProtocol: "HTTP"
            HealthCheckTimeoutSeconds: 5
            UnhealthyThresholdCount: 2
            TargetType: "ip"
            Matcher: 
                HttpCode: "200"
            HealthyThresholdCount: 5
            VpcId: !Ref VPC
            Name: "target-group-1"
            HealthCheckEnabled: true
            TargetGroupAttributes: 
              - 
                Key: "stickiness.enabled"
                Value: "false"
              - 
                Key: "deregistration_delay.timeout_seconds"
                Value: "300"
              - 
                Key: "stickiness.type"
                Value: "lb_cookie"
              - 
                Key: "stickiness.lb_cookie.duration_seconds"
                Value: "86400"
              - 
                Key: "slow_start.duration_seconds"
                Value: "0"
              - 
                Key: "load_balancing.algorithm.type"
                Value: "round_robin"

               
    TestListenerRule1:
        Type: "AWS::ElasticLoadBalancingV2::ListenerRule"
        Properties:
            Priority: "1"
            ListenerArn: !Ref HTTPSListener
            Conditions: 
              - 
                Field: "host-header"
                Values: 
                  - "test1.helloworld.com"
            Actions: 
              - 
                Type: "forward"
                TargetGroupArn: !Ref TestTargetGroup
                Order: 1
                ForwardConfig: 
                    TargetGroups: 
                      - 
                        TargetGroupArn: !Ref TestTargetGroup
                        Weight: 1
                    TargetGroupStickinessConfig: 
                        Enabled: false

Outputs:        
    ALB:
        Description: The created loadbalancer
        Value: !Ref ApplicationLoadBalancer

    TargetGroup:
        Description: The created TargetGroup 
        Value: !Ref TestTargetGroup

    LoadBalancerSecurityGroup:
        Description: the securty group for the ALB
        Value: !Ref ALBSecurityGroup

Make sure to replace the certificate with the generated certificate ARN under the HTTPS listener. Similarly, according to the listener rules, we should replace the host header with the record set created by the user on the Route 53 hosted zone.

The tags and names of resources should also be customized according to user requirements.

ECS task and service definition

The ECS task definition defines the requirements of Docker containers.It defines the image to be used, CPU and memory requirements, etc.

The ECS service definition defines how to run the application/service.It defines the startup type, the cluster in which the service will run, the target group used for ALB, the task definition to be used, etc.

Create ECS task execution role

N/B: The task execution role is usually created on the AWS account.One can search for it ecsTaskExecutionRole. If you haven’t created one on your account, please create one using the CloudFormation template below.

AWSTemplateFormatVersion: "2010-09-09"
Description: "Template to create ECS Task Execution Role"

Resources:
  ECSTaskExecutionRole:
    Type: 'AWS::IAM::Role'
    Properties:
      Description: The ECS task execution Role
      RoleName: AWSECSTaskExecutionRole
      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
      Tags: 
        - 
          Key: "Project"
          Value: "test-blog"
        - 
          Key: "Environment"
          Value: "test"
        - 
          Key: "createdBy"
          Value: "Maureen Barasa"
        - 
          Key: "Name"
          Value: "AWSECSTaskExecutionRole"
          
Outputs:
  IAMRole:
    Description: the role created
    Value: !Ref ECSTaskExecutionRole
    Export: 
      Name: !Sub "${AWS::StackName}-rolename"

Create ECS tasks and service definitions

Use the following template to create task and service definitions.

N/B: This template creates tasks and service definitions for the Fargate cluster. In addition, for the task role and task execution role arn, please use arn for the role created above; if it exists, use arn for the following tasks: ecsTaskExecutionRole.

To create a task and service definition for an EC2 cluster, replace the LaunchType on the “Service Definition” with EC2. In terms of task definition, EC2 can work in any network mode; awsvpc, bridge or host. Fargate is only available in awsvpc mode.

AWSTemplateFormatVersion: "2010-09-09"
Description: "hello-world task and service definition"

Parameters:
    VPC:
        Type: String
        Description: The vpc to launch the service
        Default: vpc-ID

    Subnet1:
        Type: String
        Description: The subnet where to launch the service
        Default: subnet-ID

    Subnet2:
        Type: String
        Description: The subnet where to launch the service
        Default: subnet-ID

Resources:
    CWLoggroup:
        Type: AWS::Logs::LogGroup
        Properties:
            LogGroupName: ecs-hello-world-Loggroup

    TaskDefinition:
        Type: "AWS::ECS::TaskDefinition"
        Properties:
            ContainerDefinitions: 
              - 
                Essential: true
                Image: 429758582529.dkr.ecr.eu-central-1.amazonaws.com/hello-world:latest
                LogConfiguration: 
                    LogDriver: "awslogs"
                    Options: 
                        awslogs-group: !Ref CWLoggroup
                        awslogs-region: !Ref AWS::Region
                        awslogs-stream-prefix: "ecs"
                Name: "Hello_World"
                PortMappings: 
                  - 
                    ContainerPort: 80
                    HostPort: 80
                    Protocol: "tcp"
            Family: "Hello_World"
            TaskRoleArn: arn:aws:iam::429758582529:role/AWSECSTaskExecutionRole
            ExecutionRoleArn: arn:aws:iam::429758582529:role/AWSECSTaskExecutionRole
            NetworkMode: "awsvpc"
            RequiresCompatibilities: 
              - "FARGATE"
            Cpu: "256"
            Memory: "512"

    ServiceDefinition:
        Type: "AWS::ECS::Service"
        Properties:
            ServiceName: "hello-world"
            Cluster: "arn:aws:ecs:eu-central-1:429758582529:cluster/eu-central-1-test-ECS-Fargate-Cluster"
            LoadBalancers: 
              - 
                TargetGroupArn: "arn:aws:elasticloadbalancing:eu-central-1:************:targetgroup/test-fargate-hello/*********"
                ContainerName: "Hello_World"
                ContainerPort: 80
            DesiredCount: 1
            LaunchType: "FARGATE"
            PlatformVersion: "1.4.0"
            TaskDefinition: !Ref TaskDefinition
            DeploymentConfiguration: 
                MaximumPercent: 200
                MinimumHealthyPercent: 100
            NetworkConfiguration: 
                AwsvpcConfiguration: 
                    AssignPublicIp: "ENABLED"
                    SecurityGroups: 
                      - "sg-ID"
                    Subnets: 
                      - !Ref Subnet1
                      - !Ref Subnet2
            HealthCheckGracePeriodSeconds: 300
            SchedulingStrategy: "REPLICA"

Outputs:
  HelloTaskDefinition:
    Description: The created name of the ECS TaskDefinition 
    Value: !Ref TaskDefinition

  HelloService:
    Description: The ECS service
    Value: !Ref Service

N/B: CloudFormation templates should be customized according to user requirements. We can customize:

  • The name of the resource to be provisioned.
  • For the container definition, we can change the image name, port mapping, etc.
  • The user/reader should also replace the cluster and target group arn to reflect his/her own value.

After completion, you should follow the steps below to run the service on the ECS cluster.ECS cluster with running tasks and services

Now you can access the service through the created domain name.

Other AWS guidelines:

  • How to mount AWS EFS file system on EC2 instance
  • Create an AWS S3 upload and list object policy that does not perform delete operations
  • How to set up AWS VPC network using CloudFormation

Happy Building! ! !

You can download this article in PDF format via the link below to support us.
Download the guide in PDF formatturn off

Sidebar