Swift Software Group

Launch ECS container using AWS ECS CLI

The Amazon ECS Command Line Interface (CLI) is a command line tool for Amazon Elastic Container Service (Amazon ECS) that provides high-level commands to simplify creating, updating, and monitoring clusters and tasks from a local development environment.

Make sure you do this setup first:

  1. Setup macOS for AWS Cloud DevOps
  2. AWS Authentication

Install AWS ECS CLI

Install:

mkdir -p ~/ecs-cli
pushd ~/ecs-cli

# Download
curl -Lo ecs-cli https://amazon-ecs-cli.s3.amazonaws.com/ecs-cli-darwin-amd64-latest
curl -Lo ecs-cli.asc https://amazon-ecs-cli.s3.amazonaws.com/ecs-cli-darwin-amd64-latest.asc

# Verify signature
gpg --keyserver hkp://keys.gnupg.net --recv BCE9D9A42D51784F
gpg --verify ecs-cli.asc ecs-cli

# Copy to path
sudo cp ecs-cli /usr/local/bin
sudo chmod a+x /usr/local/bin/ecs-cli

popd

Test:

ecs-cli --version

Define Variables

image="ecs-test"
tag="latest"
service_name="test"

ECS CLI Setup

Configuration information is stored in the ~/.ecs directory on macOS and Linux systems and in C:\Users\<username>\AppData\local\ecs on Windows systems.

Create a profile using your access key and secret key:

export AWS_ACCESS_KEY_ID=$(aws configure get aws_access_key_id)
export AWS_SECRET_ACCESS_KEY=$(aws configure get aws_secret_access_key)
export AWS_REGION=$(aws configure get region)

ecs-cli configure profile --profile-name swift

ECS Cluster Configuration

Create a cluster configuration:

export AWS_REGION=$(aws configure get region)

ecs-cli configure \
  --config-name test-ecs \
  --region $AWS_REGION \
  --cluster test-ecs \
  --default-launch-type EC2
  
ecs-cli configure default --config-name test-ecs  

Launch ECS Cluster

This will take a few minutes to complete:

ecs-cli up \
  --ecs-profile swift \
  --cluster-config test-ecs \
  --keypair aws-ec2-key \
  --capability-iam \
  --instance-type t2.micro \
  --size 1 \
  --port 22

In addition to EC2 Instances, other resources created by default include:

Create ECS Repository

# registry (in the form $registryId.dkr.ecr.$region.amazonaws.com)
region=$(aws configure get region)
registryId=$(aws ecr describe-registry | jq -r '.registryId')
registry="$registryId.dkr.ecr.$region.amazonaws.com"

# repository
repository=$(aws ecr describe-repositories | jq -r ".repositories[] | select(.repositoryName==\"$image\") | .repositoryUri")

# create if it does not exist
[ -z "$repository" ] && aws ecr create-repository --repository-name $image
repository=$(aws ecr describe-repositories | jq -r ".repositories[] | select(.repositoryName==\"$image\") | .repositoryUri")

Create Docker image

Dockerfile

Create a Dockerfile with the following contents:

cat << "EOT" > ./Dockerfile
# See https://hub.docker.com/_/oraclelinux for all supported 
# Oracle Linux tags from Docker Hub.

# this image will be the actual running container
FROM  oraclelinux:8

LABEL Name=ecs-test

## System Config
ENV TZ=America/Los_Angeles

# Packages
RUN yum -y install bind-utils 

CMD ["/bin/ping", "localhost"]
EOT

Build Image

echo "Build image ..."
docker build --tag ${image}:${tag} .

Tag Image

repository=$(aws ecr describe-repositories | jq -r ".repositories[] | select(.repositoryName==\"$image\") | .repositoryUri")

echo "Tag image ..."
docker tag ${image}:${tag} ${repository}:${tag}

Publish Image

# refresh AWS token for docker
aws ecr get-login-password --region $region \
  | docker login --username AWS --password-stdin $registry

# push image
echo "Push image ..."
docker push ${repository}:${tag}

Verify

# verify
red='\e[0;31m'    
green='\e[0;32m'    
clear='\e[0m'

pushed_tag=$(reg tags ${repository} | grep "${tag}")
if [ "${pushed_tag}" != "${tag}" ]; then
    printf "${red}Failed${clear}"; echo
else    
    printf "${green}Success${clear}"; echo
fi

Launch ECS Task

Service Description

Create docker-compose.yml file:

cat << EOT > ./docker-compose.yml
version: '3'

services:
  ecs-test:
    image: $repository:$tag

    logging:
      driver: awslogs
      options: 
        awslogs-group: test-ecs
        awslogs-region: us-west-2
        awslogs-stream-prefix: test
EOT

Task Definition

Create ecs-params.yml file:

cat << EOT > ./ecs-params.yml
version: 1
task_definition:
  services:
    ecs-test:
      cpu_shares: 100
      mem_limit: 524288000
EOT

Launch Service

# create service
ecs-cli compose \
    --project-name $service_name \
    --file ./docker-compose.yml \
    --ecs-params ./ecs-params.yml \
    service up \
    --create-log-groups \
    --cluster-config test-ecs \
    --ecs-profile swift

Login to the first task of the test service:

CLUSTER=test-ecs
SERVICE=test

TASK_ARN=$( aws ecs list-tasks --cluster=$CLUSTER --service-name=$SERVICE | jq -r '.taskArns[0]' )
CONTAINER_INSTANCE_ARN=$( aws ecs describe-tasks --cluster=$CLUSTER --tasks $TASK_ARN | jq -r '.tasks[0].containerInstanceArn' )

EC2_INSTANCE=$( aws ecs describe-container-instances --cluster=$CLUSTER --container-instances $CONTAINER_INSTANCE_ARN | jq -r '.containerInstances[0].ec2InstanceId' )
EC2_IP=$( aws ec2 describe-instances --instance-ids $EC2_INSTANCE | jq -r '.Reservations[0].Instances[0].PublicIpAddress' )

EC2_KEY="~/.ssh/aws-ec2-key"

ssh -i $EC2_KEY ec2-user@$EC2_IP -t 'bash -c "docker exec -it $( docker ps -a -q -f name=ecs-'$SERVICE' | head -n 1 ) bash"'

Check task logs:

task_id=$( aws ecs list-tasks --cluster=test-ecs --service-name=test | jq -r '.taskArns[0]' | cut -d "/" -f 3 )
ecs-cli logs --task-id $task_id

Cleanup

WARNING: These scripts destroy the created AWS resources. Proceed with caution!!!

Stop services

Stop running tasks and remove service:

ecs-cli compose --project-name test service down

Delete cluster

ecs-cli down \
  --force  \
  --ecs-profile swift \
  --cluster-config test-ecs

Delete log group

aws logs delete-log-group --log-group-name test-ecs

Delete image

repo=ecs-test
registryId=$(aws ecr describe-registry | jq -r '.registryId')

aws ecr batch-delete-image \
  --registry-id $registryId \
  --repository-name $repo \
  --image-ids imageTag=latest

Delete image repository

This will delete all images in the repo and the repo itself

repo="ecs-test"
registryId=$(aws ecr describe-registry | jq -r '.registryId')

aws ecr delete-repository \
  --force \
  --registry-id $registryId \
  --repository-name $repo