Amazon ECS task definition across both EC2 and AWS Fargate

Nathan Peck profile picture
Nathan Peck
Senior Developer Advocate at AWS

About

One of the convenient features of Amazon ECS is that it is agnostic when it comes to capacity type. You can create an ECS task definition that deploys to both AWS Fargate and Amazon EC2 instances at the same time.

This pattern shows the key parts of the task definition that make this possible.

CloudFormation template

The following template creates a task definition that is compatible across both ECS on EC2 and ECS on AWS Fargate. It then deploys the task definition on both capacity types.

Note the following settings, which enable cross compatability in the task definition:

  • RequiresCompatibilities - This is set to an array that has both EC2 and FARGATE
  • NetworkMode - In order to be compatible across both EC2 and Fargate the only valid value is awsvpc
  • Cpu and Memory - The requested task size much match a valid AWS Fargate task size
File: task-definition.ymlLanguage: yml
AWSTemplateFormatVersion: '2010-09-09'
Description: An example task definition that can deployed onto both
             Amazon EC2 and AWS Fargate

Parameters:
  Cluster:
    Type: String
    Description: The name of the ECS cluster to deploy into
  Ec2CapacityProvider:
    Type: String
    Description: The name of an EC2 capacity provider in the cluster.
  ServiceName:
    Type: String
    Default: nginx
    Description: Name of the service
  VpcId:
    Type: AWS::EC2::VPC::Id
    Description: The virtual private network into which to launch all resources
  SubnetIds:
    Type: List<AWS::EC2::Subnet::Id>
    Description: List of subnet IDs where the EC2 instances will be launched

Resources:

  # This task definition has settings which allow it to
  # be used on both AWS Fargate and Amazon EC2 capacity
  SampleTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: nginx
      RequiresCompatibilities:
        - EC2
        - FARGATE
      NetworkMode: awsvpc
      Cpu: 256
      Memory: 512
      ContainerDefinitions:
        - Name: nginx
          Image: public.ecr.aws/nginx/nginx:mainline

  # Deploy the task definition as a service on EC2 capacity
  Ec2Service:
    Type: AWS::ECS::Service
    Properties:
      ServiceName: !Sub '${ServiceName}-on-ec2'
      Cluster: !Ref 'Cluster'
      DeploymentConfiguration:
        MaximumPercent: 200
        MinimumHealthyPercent: 75
      CapacityProviderStrategy:
        - Base: 0
          CapacityProvider: !Ref Ec2CapacityProvider
          Weight: 1
      NetworkConfiguration:
        AwsvpcConfiguration:
          SecurityGroups:
            - !Ref ServiceSecurityGroup
          Subnets:
            - !Select [ 0, !Ref SubnetIds ]
            - !Select [ 1, !Ref SubnetIds ]
      DesiredCount: 2
      TaskDefinition: !Ref 'SampleTaskDefinition'

  # Deploy the task definition as a service on AWS Fargate capacity
  FargateService:
    Type: AWS::ECS::Service
    Properties:
      ServiceName: !Sub '${ServiceName}-on-fargate'
      Cluster: !Ref 'Cluster'
      LaunchType: FARGATE
      DeploymentConfiguration:
        MaximumPercent: 200
        MinimumHealthyPercent: 75
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: ENABLED
          SecurityGroups:
            - !Ref ServiceSecurityGroup
          Subnets:
            - !Select [ 0, !Ref SubnetIds ]
            - !Select [ 1, !Ref SubnetIds ]
      DesiredCount: 2
      TaskDefinition: !Ref 'SampleTaskDefinition'

  # Security group that limits network access
  # to the task
  ServiceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for service
      VpcId: !Ref VpcId

This template requires the following parameters:

  • Cluster - The name of an ECS cluster on this account. This cluster should have EC2 capacity available in it. All ECS clusters come with AWS Fargate support already built-in. For an example of how to deploy an ECS cluster with EC2 capacity there is a pattern for an ECS cluster using a EC2 capacity provider.
  • Ec2CapacityProvider - The name of an EC2 capacity provider on this cluster. Again see the ECS cluster with EC2 capacity provider pattern.
  • VpcId - A virtual private cloud ID. This can be the default VPC that comes with your AWS account. Example: vpc-79508710
  • SubnetIds - A comma separated list of subnets from the VPC. Example: subnet-b4676dfe,subnet-c71ebfae

Usage

Deploy this template with a command like this:

Language: sh
aws cloudformation deploy \
  --template-file task-definition.yml \
  --stack-name task-across-ec2-and-fargate \
  --capabilities CAPABILITY_IAM \
  --parameter-overrides \
     Cluster=capacity-provider-environment-BaseStack-18PANC6K9E7D8-ECSCluster-NNBNpIh5AkZO \
     Ec2CapacityProvider=capacity-provider-environment-BaseStack-18PANC6K9E7D8-CapacityProvider-FI323ISAaRbn \
     VpcId=vpc-79508710 \
     SubnetIds=subnet-b4676dfe,subnet-c71ebfae

JSON task definition

If you prefer to create the ECS task definition using JSON, then this snippet is a minimal example of the properties necessary for a task definition to be compatible across both AWS Fargate and Amazon EC2:

File: task-definition.jsonLanguage: json
{
  "family": "nginx",
  "requiresCompatibilities": [
    "EC2",
    "FARGATE"
  ],
  "cpu": "256",
  "memory": "512",
  "networkMode": "awsvpc",
  "containerDefinitions": [
    {
      "name": "nginx",
      "image": "public.ecr.aws/nginx/nginx:mainline"
    }
  ]
}

Register this task definition using the following command:

Language: sh
aws ecs register-task-definition \
  --cli-input-json file://task-definition.json