Automated Deployment to Amazon ECS Fargate
Tags: aws, containerapps, ecs, devops
Published: 2022-10-18 11:17:37

Today's software development lifecycle world requires rapid and uninterrupted way to publish your product. Especially in containers area, there are plenty of methods to deploy your containerized application to your environment. 

In this post, I'd like to mention how to deploy your containerized application to Amazon ECS Fargate - _**a serverless option that run containers without needing to manage your infrastructure**_, in an automated way using AWS CLI and GitLab CI/CD. 

Before begin, below are the required environment. So, I assume that you already have that in use or familiar with tech stack that I mentioned below.

* [Amazon ECS Cluster - Fargate](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html)
* [GitLab CI/CD](https://docs.gitlab.com/ee/ci/)
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html)


Imagine you have created your ECS cluster, then deployed your first release of containerized application to ECS Fargate. But in the continuation, most probably it'll be required to automate deployment of your new release of container. 

To do this, We'll leverage AWS CLI, combining with GitLab CI/CD.

## Step 1 - Create your ECS task definition

We need to create [ECS task definition](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html) which is similar to definitions that refers docker related commands of your containerized application.

myapp-ecs-task-definition.json
```
{
    "family": "myapp-preprod-fargate",
    "executionRoleArn": "arn:aws:iam::<aws_account_id>:role/ecsTaskExecutionRole",
    "taskRoleArn": "arn:aws:iam::<aws_account_id>:role/ecsTaskExecutionRole",
    "cpu": "1 vCPU",
    "memory": "4GB",
    "networkMode": "awsvpc",
    "containerDefinitions": [
        {
            "name": "myapp-preprod",
            "image": "<aws_account_id>.dkr.ecr.eu-central-1.amazonaws.com/myapp:latest",
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/ecs/myapp-preprod-fargate",
                    "awslogs-region": "eu-central-1",
                    "awslogs-stream-prefix": "ecs"
                }
            },
            "portMappings": [
                {
                    "protocol": "tcp",
                    "containerPort": 3000
                }
            ],
            "cpu": 0,
            "essential": true
        }
    ],
    "requiresCompatibilities": [
        "FARGATE"
    ]
}

```
> Pay attention to "image": "<aws_account_id>.dkr.ecr.eu-central-1.amazonaws.com/myapp:latest"

* Here we'll replace "latest" tag with our respectful container image version tag.

## Step 2 - Prepare your gitlab-ci.yml

Now we need to prepare CI/CD side, which will be used to automate deployment of your updated containerized application to ECS Fargate.
 
* [ecs-gitlab-ci.yml](https://gist.github.com/enginaltay/cc8b577b1c62322d42c8991ff1ce6b6d) is a fully ready .gitlab-ci.yml file that includes build & deployment stages. We'll go through the Amazon ECS Fargate related section.

### Setting unique container image version tag

* Below command will replace image tag from **latest** to respectful tag which is **environment name preprod and pipeline number.  **

```
sed -i 's/latest/'"${CI_ENVIRONMENT_NAME}-${CI_PIPELINE_IID}"'/g' myapp-ecs-task-definition.json
```
After running command above, container image will look as below.
```
<aws_account_id>.dkr.ecr.eu-central-1.amazonaws.com/myapp:preprod-1 
```

With this, in every CI/CD pipeline we run, unique container image tag will be injected to ECS task definition file.
 
### Getting ECS task revision number

Now create new task definition and get revision number by registering our updated task definition file.

```
export TASK_REVISION=$(aws ecs register-task-definition \
--family ${ECS_TASK_FAMILY} \
--cli-input-json file://myapp-ecs-task-definition.json \
--region eu-central-1 | jq --raw-output '.taskDefinition.revision')

echo "Registered ECS Task Definition = " $TASK_REVISION
```

After running command above, ECS task definition file with unique revision number will be created.


* _**aws ecs register-task-definition**_ command will basically creates new task definition file by incrementing revision number by one. And will look as below.
 

```
echo "Updated Task Definition = " $ECS_TASK_FAMILY:$TASK_REVISION
myapp-preprod-fargate:1
```

### Updating ECS service - rolling update

In the final step, we need to update ECS service with our updated task revision. Below command will trigger a rolling update for ECS service. 

```
UPDATE_ECS_SERVICE=$(aws ecs update-service \
--cluster $ECS_CLUSTER_NAME \
--service $ECS_SERVICE_NAME \
--task-definition $ECS_TASK_FAMILY:$TASK_REVISION \
--desired-count 1 \
--region eu-central-1 | jq --raw-output '.service.serviceName')

echo "Deployment of $UPDATE_ECS_SERVICE has been completed"
```

That's it! Now you are able to deploy your containerized application with zero downtime, and in an automated way.

In this post, I wanted to mention how to automate deployment process of your container to Amazon ECS Fargate using AWS CLI & GitLab CI/CD.