Source Allies Logo

Sharing Our Passion for Technology

& Continuous Learning

<   Back to Blog

ECS Anywhere First Impressions

Run containers anywhere

AWS ECS Anywhere First Impressions

It isn't often that we read the mass marketing emails that we all get everyday. AWS sends out an email each week (usually on Tuesday) with a list of recent updates and new services. I actually get a lot of value out of reading it over.

A couple weeks ago I saw in that email that "ECS Anywhere" was now in "GA". I had a hunch what it was so I looked into it and I am pleased to report that it is an awesomely useful feature.

At a high level, it allows us to take a Linux machine (bare bone or VM) that is running anywhere outside AWS and hook it into the ECS control plane so that Docker containers can be deployed to it. There are a few key properties of this setup that are worth mentioning:

  1. The host does not need ingress access from AWS, only egress to AWS
  2. The initiation of the deployment is triggered from the AWS side so it can be put in Cloudformation like any other task
  3. The task is pretty much just like any other, it has an IAM role just like other tasks.

The main marketing for the feature focuses on its ability to extend compute capacity. I, however, see it as a way to help migrate systems to the cloud more easily.

Most systems, in my experience, cannot be simply lift-and-shifted to the cloud or rebuilt there greenfield practically. There are usually on-premise databases, mainframes, and other things that cannot move. Organizations are also hesitant to open up their datacenter networks to AWS.

ECS anywhere opens up a couple of key use cases:

First, it can facilitate a conversion to containers and breaking down a monolith. We continue to deploy it on-premise but use AWS and Cloudformation to manage the deployment. This allows the application to run local to the other parts of the system but in a way that is easily moved into ECS(EC2) or Fargate at a later time.

Second, it can be used as a data sync bridge. Deploy a small application that can run on-premise and connect to existing data sources, or listen to existing queues and relay data changes into cloud event streams such as Kinesis, SQS, Kafka, etc. A good example of this would be to deploy Debezium using it. From a security perspective, this is very slick because we don't need to manage IAM keys. Instead, simply leverage a task role that is assumed by the container.

To setup ECS anywhere only a few pieces are needed. First, we need to create an ECS cluster. The following Cloudformation template sets this up:

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'

Resources:
  Cluster:
    Type: AWS::ECS::Cluster
    Properties: {}
  
  RemoteHostRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - sts:AssumeRole
            Principal:
              Service:
                - ssm.amazonaws.com
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role'
        - 'arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore'

Outputs:
  RemoteHostRoleName:
    Value: !Ref RemoteHostRole

Next, we connect the on-premise machine into the cluster. Go to the ECS cluster page and under the "Instances" tab click the register button. Select the role created above and there will be a command that can be run on the VM. The provided script will install all the necessary software and hook the instance into the ECS cluster. You should then see the cluster listed on the instances tab.

Finally, we can deploy an application to our cluster similar to any other ECS task:

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  Cluster:
    Type: String
    Description: Id of the cluster to target

Resources:
  LogGroup:
    Type: AWS::Logs::LogGroup

  InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - sts:AssumeRole
            Principal:
              Service:
                - ecs-tasks.amazonaws.com
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/CloudWatchLogsFullAccess'

  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties: 
      RequiresCompatibilities:
        - EXTERNAL
      ContainerDefinitions:
        - Name: "nginx"
          Image: "public.ecr.aws/nginx/nginx:latest"
          Memory: 256
          Cpu: 256
          Essential: true
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-region: !Ref AWS::Region
              awslogs-group: !Ref LogGroup
          PortMappings:
            - ContainerPort: 80
              HostPort: 8080
              Protocol: "tcp"
      ExecutionRoleArn: !GetAtt InstanceRole.Arn
      NetworkMode: bridge
      Family: "nginx"
  
  Service:
    Type: AWS::ECS::Service
    Properties:
      Cluster: !Ref Cluster
      DesiredCount: 1
      TaskDefinition: !Ref TaskDefinition
      LaunchType: EXTERNAL
      DeploymentConfiguration:
        MaximumPercent: 100
        MinimumHealthyPercent: 0

This strategy should make it a lot easier to migrate existing applications to AWS by making on-premise services more consistent with cloud deployed services. I'm excited to try some of the above strategies on one of my next migration projects.

References: