How to Deploy an App to AWS using Elastic Beanstalk with Dockers

[This article was first published on Python – Predictive Hacks, and kindly contributed to python-bloggers]. (You can report issue about the content on this page here)
Want to share your content on python-bloggers? click here.

Assume that we work on AWS and we have built a Flask API and we want to deploy it. Let’s say that we work with the Cloud9 IDE and the structure of our project is the following and our goal is to work with Dockers:

How to Deploy an App to AWS using Elastic Beanstalk with Dockers 1

The Dockerfile would look like:

FROM python:3.6
COPY . /appdir
WORKDIR /appdir
RUN pip install Flask==1.0.2
EXPOSE 5000
CMD ["python", "app.py"]

Build the Image

You can type on the terminal the docker build command with a tag of your preference.

docker build --tag flask-tutorial .

Now you can verify that the image exists by typing:

docker images
How to Deploy an App to AWS using Elastic Beanstalk with Dockers 2

Run the Image

You can run the image by typing:

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

Notice that the -d flag means to run it into the background without holding your terminal hostage. We can check that the container is running by typing:

docker ps
How to Deploy an App to AWS using Elastic Beanstalk with Dockers 3

Let’s provide some useful docker commands:

The ps command

To re-iterate with the ps command you can list the docker containers. Some of the more common flags are --no-trunc which will not truncate the output. Another useful flag to keep in mind is -a which will allow us to see all the containers running and stopped (which we mentioned above). The --filter command is also helpful when searching for specific commands. So if we had our running flask-tutorial container we could run the following to filter it out:

docker ps --filter "name=flask-tutorial"

The exec command

Even though we don’t use the exec command it’s important to note. The exec command will allow us to run a command in a running container. So for example if we have our running flask-tutorial container and we wanted to run a command inside it. The following command will create a new file test located in the /tmp directory, in the background:

docker exec -d flask-tutorial touch /tmp/test
​
Verify:
docker exec -it flask-tutorial /bin/ls -l /tmp
total 0
-rw-r--r-- 1 root root 0 Aug 21 16:56 test

The stop command

You show them how to stop our container by it’s name (hence the use the --name flag earlier).

docker stop flask-tutorial

Verify it’s stopped:

docker ps

You should see the following:

   CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

Everything is stopped and we can now think about how we can store our fancy new flask image.

Store your docker images on ECR

The Amazon Elastic Container Registry (ECR) is a fully-managed Docker container registry service that makes it easy to store, manage and deploy Docker container images and it is integrated with Elastic Beanstalk and ECS which also simplifies the process when you deploy to production.

In order to be able to push our Docker image to ECR first we will use the CLI. We first need to authorize our Cloud9 docker client with the AWS CLI’s ecr get-login command:

aws ecr get-login --region us-west-2 --no-include-email

You should see something similar:

docker login -u AWS -p eyJwYXlsb2FkIjoiWnc4NUs2K3dkNVhySXJvQWVlZE8xZUpHOVhaUmNaTEdYTXBrU2U0Y1BYb3NLTit4a1B3c3RteXhwWkNXcDZ0TkxLLzNpRjdZV2JvckxHczZIQmhsSGFwNlh6ZEU2K0tQOGRIRk8vNTl2enZpNDIybGpBTnlHdURHYjdYNHdZczU4TTZSaDVLbEVrUW80VFZDaXFRTzdkS0hnY0YyaTVlZ1c1RjYweVozZllSSHlWdVQvQngrQ2FQV1ZQVlJ1RndMa3RPOThaajBOd3grMDRJdURUQ2pFMzY5dFAxdzVhYjgzOEpjR0JUWFJWVCs3MG1TYUIwTXY3WWJIMCt4VE9ETDhWTEVzaHNuQTh2M1JQLzlUd2gyMFpnalcyRjNKTmpTd2l6dnptbVpDblh3dDZxa2lwNEowM0ZCLzZ5MEtBTHlFYkk3WmlqSFYzem5oSFJRTmVWMVdBR1FoSjdmV1RucHZwSDRqUGNCdndLZWI4aWpKSGs5L2VaUzFSbUlLZU1HS0UzU3FmRks0a2lYbEY2bkNLVEtRRnNLeGpCYnllVDdWbTJHaXJLMENOT0ZQVksrWS9jSlZ3SDJTaFc4VjBEYjdjeG9vK3pXQjFiYlAvNS9XQ0pXeUM2ZHMrM3dSMW1tdnUwdUVEV2U3b0lNdklpVDIrVHMrRTZ6cmxibVVObFhJaTYzUXdXNTJmRGVHZjFEeDFGOTJqU0dCL3hFd3ZYTzVweVZzMEZPYUcrV054cndlc3ZTL293TFJKcElrOHBtOVlwUVBxRzl4NGd2bnNrdFh3QlJOQkZJc0lkbDVDanQzVmxGWXdJaklOZXR4Zlo4M2tzUGNFekxVbW9kOEJtdVNCaGttMU9zeWZhY0RCa1J2QWs3UkVJL21vbXZBdFlYOW03T3A1VHo2V3ExZ3ZSanh5NkQrT2lWUWVHYVJLSlZGK3dpWEp4eWpDM2FwYTYzaTkyRXJ2V2lwd1p4cUQ4QlkzWG40cEtpQzRQUU9ZTGo2WTdMM3l4STVuZDQ1Zm9kL2I4aFBMREszeFlIbHRkMkhFWGJGOFkyd05qcXdld2FzYWw3bXRPTUJ5VzdKOWJJWW5ORm96SVY4TDQ1WitJYUNxN29QRDhrV3crK0lZSU91bit3WExnZ3lMNjFiM3FhRUs3dW04VjRRRWdNZTdtWVd4TnZta1JtWUc3aGFJUHJjTlh3aHZxRjNaQmNNOWVZNlljeENQdnVyYUlSY3BoYjJoWjQrdjFYbDVOL0hUS3UzajNRb1ZQVGtCYnFVNENTTXIyYURtd052LytHRUNxQ2tkSHpFQlE0TlZqcXZtR2dZaFVEcGpSZktIWGp0ajdlQ1hnSHhpb0ZSajdCdnFVZmdtQXlGVWxwZXQ0MklxeXVSSDlTMWpBYzk4VkE3aC9UcEgwd3phOWI3QmNRSnVIbFZVQTJkZDZkM2hvM2RMOU1pQkRtaTF4NHdHUGVTazRweG8zZUhkSWFIZXZlYjROcTdFeEV0OWZwZ3lvajlPRHJNcWw5dzNLbURidG4wVXNDVjJHWHlGalZMSkZITk5RWlFES1VGU2VZQVNUcFNzUFJROHVhR0hJNi9XUTNxUnh1Qk4xWnA0ZSt3cU1oWVV0ME9SNGxBNFFzZExlcVdzMFJqcVFpMDRWdE1yZkhDTVpZd1FGd2diek1xTGdzU29zSnZRSnJVQWEyOWJiZU5TL0xQaUliOTlLbUFjaVRHTE11cXRDZHhkMlBzV3ZCN1k3NU1Wc1NXU2ZBckl1WWh1cjZHSE1TQk9mNTdDbG9ZV0NQVlV2bzNVNnFHUEIwOVAwSUNEczc4ajNGY0x4K1QvSHJpaGNBTjZhZzUwaFdEV0I1TU9nT2dHRDdrRW5VTGxMSkRBRnp4c3l2Q252N2pwcFo5R2RlSTBINWtiR1hIZFRwQXJEZzZYeUlLYXVMQVFMczdWcUF4TUNwU3djdmluQXNDR0dWQ3M4MUtGUjZjQTA5bmVrYWgvUVpxQkhhdHA0QW1scGNDQlJSMGEvTFVRPT0iLCJkYXRha2V5IjoiQVFFQkFIajZsYzRYSUp3LzdsbjBIYzAwRE1lazZHRXhIQ2JZNFJJcFRNQ0k1OEluVXdBQUFINHdmQVlKS29aSWh2Y05BUWNHb0c4d2JRSUJBREJvQmdrcWhraUc5dzBCQndFd0hnWUpZSVpJQVdVREJBRXVNQkVFRERJWDFaUm1YUmR5QmZtM1VBSUJFSUE3UldmRHhZSW1zdDhOejdVM01KREpjMlRVbSszSE13RXIyMWUzZ0lEOWI1QnhPZXptNnhRL3JNTjRMejV6UGxnM3RhTFRUMzNlWEEwS3d6MD0iLCJ2ZXJzaW9uIjoiMiIsInR5cGUiOiJEQVRBX0tFWSIsImV4cGlyYXRpb24iOjE1NzIwNzgxMTV9 https://323009296932.dkr.ecr.us-west-2.amazonaws.com

You copy and paste the entire output from above. Paste it back in to the terminal and press enter. You should see the following. You can ignore the warnings:

WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /home/ec2-user/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
​
Login Succeeded

We will need to create an ECR repository to push our image.

aws ecr create-repository --repository-name flask-tutorial

You should see something similar to the output:

{
    "repository": {
        "repositoryUri": "323009296932.dkr.ecr.us-west-2.amazonaws.com/flask-tutorial", 
        "registryId": "323009296932", 
        "imageTagMutability": "MUTABLE", 
        "repositoryArn": "arn:aws:ecr:us-west-2:323009296932:repository/flask-tutorial", 
        "repositoryName": "flask-tutorial", 
        "createdAt": 1572035197.0
    }
}

This is the repositoryUri that we need to use if we ever have to reference our image in future deployments.

The tag command

We are going to re-tag our image to include our registryId. This makes it easier to manage if we have many accounts or work with other registry providers like docker hub etc.

docker tag flask-tutorial:latest 323009296932.dkr.ecr.us-west-2.amazonaws.com/flask-tutorial:latest 

Note that your ECR’s repositoryUri will be different.

And if we re-run the docker images we will get the see the new one.

docker images
How to Deploy an App to AWS using Elastic Beanstalk with Dockers 4

The push command

Now we can push the image:

docker push 323009296932.dkr.ecr.us-west-2.amazonaws.com/flask-tutorial:latest

Once you do this you should see something similar to the following:

The push refers to repository [323009296932.dkr.ecr.us-west-2.amazonaws.com/flask-tutorial]
ff4ff4b2b471: Pushed 
cd1f247135f6: Pushed 
dcea7e243648: Pushed 
d431d6bc3307: Pushed 
ff19fb54200e: Pushed 
69168fb9b7b0: Pushed 
31f78d833a92: Pushed 
2ea751c0f96c: Pushed 
7a435d49206f: Pushed 
9674e3075904: Pushed 
831b66a484dc: Pushed 
latest: digest: sha256:8e9ab6d360fb225cde3d5f4adad29c4d0bee5097a951d32aa15ccbe8560200d2 size: 2636

Now the flask image is stored in ECR. Using the aws ecr list-images command:

You can now show them something similar to this on your screen:

{
    "imageIds": [
        {
            "imageTag": "latest", 
            "imageDigest": "sha256:8e9ab6d360fb225cde3d5f4adad29c4d0bee5097a951d32aa15ccbe8560200d2"
        }
    ]
}

Deploy to the cloud using the Elastic Beanstalk console

Now, we will deploy the Flask application to Elastic Beanstalk. We will do the so-called ZIP deployment. We go into the working directory and we type:

zip flask_application.zip app.py Dockerfile

On the left panel expand the flask folder and right click on the flask_application.zip and choose Download

  1. Choose AWS Cloud9 and choose Go To Your Dashboard. Choose Services and choose Elastic Beanstalk..
  2. At the top right choose Create Application.
  3. Paste in flask-app for Application name.
  4. For Platform choose Docker. Leave the docker settings as is.
  5. For Application code choose Upload your code. Leave Local file selected. Choose Choose File and browse to where the flask_application.zip was downloaded.
  6. Choose Create application.
  7. Eventually you should see the Health go to Ok with a green circle and a check mark to indicate everything is healthy.
  8. At the top you should see your application as well with the URL. Click that and at the top you should see the Environment ID and URL. Copy the URL.
  9. Choose the URL (which should open in a new browser tab) and you should see your example Flask Application.

Voilà, your application is up!

References

[1] Building Containerized Applications on AWS on Coursera

To leave a comment for the author, please follow the link and comment on their blog: Python – Predictive Hacks.

Want to share your content on python-bloggers? click here.