App Deployment To ECS For Beginners

Posted on 20 October 2022 Reading time: 11 min read
App Deployment To ECS For Beginners

Amazon Elastic Container Service (ECS) is a container management service by Amazon Web Services. It can be used to run and manage application running in containers in what is known as a ‘cluster’.

In order to understand how ECS works, you need to understand the underlying technology of the service that ECS manages. ECS supports and Docker and allows you to run distributed applications across different containers by automating processes that you would otherwise spend hours setting up.

Simply put, Docker is a software platform that allows you to build and deploy applications in what is referred to as containers. These are lightweight execution environments running in an operating system but isolated from one another.

What are the benefits of Docker you might ask? That really isn’t the goal of this article but I will mention a few reasons why developers use Docker.

  • It allows for easy sharing of codebase that should run in any operating system that supports Docker

  • It allows for easy testing of application in different environments and library versions. Take for instance, you develop a JavaScript application in Node 16 and want to test if it works in Node 14. You simple just build a Node image tagged v14 and run the application within the container.

  • It is portable - This means that docker containers can run in any environment where Docker is supported wether it’s on your laptop, a PC, a server or in the cloud. I actually run an application in Docker containers on my Raspberry PI at home.

Right, so let’s get into deploying a containerised application in ECS. I have decided to write on this step approach because I encountered some gotchas when I started looking into deploying to ECS and will share them as we go along.

So first thing we will do is create a sample application and test it locally to make sure it works before we create a Docker container that the application will run in. To save time and space, I have written a sample Flask application and have included the link here. https://github.com/stevepop/flask-app.git

You can either clone the application or write the code yourself. Flask is a Python based web development framework so you need to have Python installed on your computer to follow along with this article.

Test Application - To test that the application works, execute the following command within the folder where you downloaded the code. In my own case, I am running this from a folder I named, ‘flask-app’

FLASK_APP=server.py flask run

The above image is what you should see if it runs successfully. Visit http://127.0.0.1:5000. You should see a json object;

{
  message: “Working fine
}

If you add /tasks to that link, you will a json object with an array of task items displayed.

Once we are sure that everything works, it is now time for us to create our Docker container. This is not a Docker primer so I will not be going into the details of the Dockerfile. One thing I will however point out is the line, EXPOSE 5000. This is very important because it will be required for running the application successfully in ECS. With this line, we are telling the container to make this port available to the host environment so that requests can come into the container through that port. When we get to adding a container to ECS, we will see why this is important.

The next step is to build the image. We do this by running the command

docker build -t flask_app .

Make sure you add the . at the end. This is one of the gotchas new beginners to docker fall into. We are basically telling docker that what you need to build this image is in current directory.

Once the image is built, we need to start the container and test if the application runs. This is done by executing the command;

docker run -d --name flask-app -p 5000:5000 flask-app

Let’s break the above command down into it’s integral parts;

sh docker run. We are telling docker to start a new container and run the image we just created with the following parameters;

d - Run the container in the background. Without this, Docker will keep running the application at the console and you will need to terminate it manually.

—name flask-app Name to give the container. Without this, Docker will give it a random name.

p- Expose port 5000 within the container to port 5000 on the host computer.

flask-app - The name of the image to use.

Visit the same link we used for testing the application before we built our container and you should see the same response.

Now that our Docker container is running our application successfully, the next step is to deploy our application to ECS. If you are following along, you need the following;

  • Without the need to say it, you’ll need an AWS account.
  • You will need to create a user on your IAM page. The user must have the following permissions;
    • AmazonEC2ContainerRegistryFullAccess
    • AmazonECS_FullAccess
    • AmazonElasticContainerRegistryPublicFullAccess
  • Finally you need to make sure that you configure your user’s aws_access_key_id and aws_secret_access_key in your .aws/credentials`

One the above are in place, you are good to go.

Step 1 - Create a Repository.

  • Log in to your AWS console and search for ‘ECS’
  • Click on ‘Repositories’ link on the left side menu.
  • On the ‘Create repository’ page, enter a name in the Repository name field and click on ‘create’. -
  • Once the repository is created, click on the name to visit the new repository you just created. At the top of the page is a button labelled, ‘View push commands’. -
  • Depending on your operating system, AWS gives you the steps you need to follow to push your image to your repository along with the explanation of each command. It is simply a matter of copying the commands and executing it in your command line. Make sure you are executing the commands within the folder you created application in.

Step 2 - Create Cluster.

  • Go to ECS page. The quickest way is to enter ‘ecs’ in the search bar.
  • Click on the ‘Clusters’ link on the left side menu.
  • Click on the blue “Create Cluster” button
  • We are using Linux for this tutorial so if you are following along, click on the “EC2 Linux + Networking” box. If you are however using Windows, you will need to select “EC2 Windows + Networking”.
    • Enter Cluster name - In this example, I am using “FlaskAppCluster”
    • Leave the Provisioning Model on ‘On Demand Instance’
    • For “EC2 Instance type” select `t2.micro`
    • You can leave the Key pair as ‘None’. If you would like to ssh into the EC2 instance to troubleshoot or just to look around, you will need to first of all create a key pair. How you do this is beyond the scope of this tutorial but this is created when you create a new user.
    • For Networking;
      • Select the default VPC
      • For subnet, you can select one or more subnet from the dropdown, for example `us-east-1a` and `us-east-1b`
      • Select `enable` for Auto assign public IP
    • Security group - Select default
    • Container instance IAM role
      • Select ecsInstanceRole This is the role the ECS container needs to make calls to the Amazon ECS API actions on your behalf.
  • Click on Create

At this point, ECS will create all the resources required for your container to run using the image that you stored in your ECR repository. You can see your new cluster by clicking on the blue ‘View Cluster’ button. If you cluster created successfully, you should see the status marked as ‘Active’.

Step 3 - Task Definition.
The underlying resource needed to power our container is ready but we need to give ECS specific instructions on how to create our container.

  • On the ‘Clusters’ page, click on ‘Task Definitions’ on the left side menu.
  • Click on the blue ‘Create new Task Definition’ button
  • Click on the EC2 box and click on ‘Next step’
  • Enter the ‘Task definition name’ (For example FlaskAppTask’)
  • Task role: Select `ecsTaskExecutionRole`
  • Network mode: Leave the option on ‘default’
  • In the Task execution IAM role section
    • Task execution role: select `ecsTaskExecutionRole`
    • Task memory - enter 100MB
    • Task CPU - enter ‘1 vCPU’
  • Click on the ‘Add container’ button
    • Enter Container name - (Example - FlaskApp)
    • Image - Open another tab and go to ECS. Click on ‘Repositories’. Click on the new repository you created then copy the URI by clicking on the ‘Copy URI’ icon. Paste the image URI in the image field
    • Port mappings: Remember the command we used to run docker locally? We had this `-p 5000:5000` which means expose port 5000 in the container to port 5000 on the host. We will be doing the same here. Enter 5000 in both fields under ‘Port mappings’
    • Click ‘create’
  • Scroll to the bottom of the page and click the blue ‘Create’ button

Step 4 - Run Task.

Now we have created everything needed to power up our container.

  • On the Task Definition page, click on the ‘Actions’ tab then click on ‘Run Task’.
  • Launch type: Click on ‘EC2’
  • Click on the blue ‘Run Task’ button. You should see a ‘Created tasks successfully’ notification. At the bottom of the screen, you’ll see the status of the task. Click on the link under the ‘Task column’. On the new page, you will see the status of the container(s) and the Status should show ‘RUNNING’.

Step 5 - Test application.

AWS ECS has told us everything is running and that there are no problems. The only way we will know our application is running for sure is to test. First go to your EC2 page. Again, you can quickly navigate there by searching at the top. Once on the page, the first thing we want to do is make sure that we allow traffic on the ports we have specified for the host, which is 5000. This can be done with the following steps;

  • Click on ‘Security Groups’ in the left menu
  • Select the checkbox of ‘default’ security group or the security group you selected when you created your Task Definition
  • At the bottom section of the page, click on the ‘Inbound rules’ tab
  • Click on the ‘Edit inbound rule
  • Click on the ‘Add rule’ button
    • Under type, Select ‘Custom TCP’
    • Protocol should be ‘TCP’
    • Port range - Enter 5000
    • Source - select `0.0.0.0/0`
    • Click on ‘Save rules’

We have now allowed the host of our container to listen on port 5000.

You can now click on the ‘EC2 Dashboard’ on the left menu. Click on Instances. You should see the instance running named `ECS Instance - EC2ContainerService-FlaskAppCluster` where the last part of the name will be the name you gave the cluster.

In the Details tab on the page, you should see the public IPv4 address or Public IPv4 DNS. Copy any of this two and paste in your browser. Add port you allowed in the security group, in our case `:5000`

If you have been following this tutorial, you should see the following;

If you add the `/tasks` route, you should see the following;

Congratulations! You have deployed your first application to Amazon ECS!

Gotchas To Watch Out For

There were a couple of gotchas I encountered which caused some head scratching so if you are reading this, I have probably saved you hours or even days of trying to figure our what the issue is.

Port issues - You encounter a message when you trying to run your container locally that the port is already used. You will need to check applications running on your computer to determine if you are using a port that is already being used. I encountered this problem on my Mac and the frustrating part was that I had no application running on port 5000. It turns out that under ‘Sharing’ in System Preferences, ‘AirPlay Receiver ‘ is using port 5000. Unchecking this solved the problem.

Format error on ECS - This was another one that took me a couple of days to figure out. You may not encounter this issue if you are building your images in Windows or on a Mac Intel based processor. I initially built the container image on my Macbook Air M1 using Docker’s Arm64 version. When I pushed the image to ECR and tried to run the image to create containers, the task failed. I had to dig into the logs to see the cryptic, ‘invalid format error’.

There are two options for overcoming this problem. The first is to run x86_64 Docker images on your ARM64 Mac machine, using emulation. The drawback of this approach is that emulation causes performance issues on the mac. The other option which I adopted was to build the image on an Intel based machine and that worked as expected. Like i mentioned earlier, this will only impact anyone using Macs with M* processors.

This has been a fairly long article but I hope it helps anyone trying to get their feet wet with deployment of applications to ECS.