Introduction
In my latest efforts to learn more about DevOps tools and practices, I set out to deploy the Docker image for my FastAPI app API to AWS. You can read about the process of building the image in my last post.
This post is part of a series where I create a basic CRUD app and improve upon the development and deployment of the app using DevOps tools and techniques.
Title |
---|
Creating a Notes App with FARM Stack |
Containerize a FastAPI App with Docker |
Deploying a FastAPI Container to AWS ECS |
Setting Up GitHub Actions to Deploy to ECS |
Using Terraform for ECS/EC2 |
Initially, I was overwhelmed by the many ways to run containers on AWS. I mistakenly decided to try Lambda - partially to use the most “serverless” method I could, but mostly to be as cheap as possible. Many painful hours of not getting it to work later, and I decided to try out ECS instead. Either way, first I needed to push my Docker image to AWS ECR.
ECR Setup
I was able to do this part using the AWS CLI. First I created a new ECR repository:
aws ecr create-repository --repository-name notes-api
Next, AWS gave instructions on how to authenticate the local Docker utility to my AWS:
aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin <AWS ID>.dkr.ecr.us-west-2.amazonaws.com
I was able to get my AWS id using this command:
aws sts get-caller-identity
Then, AWS required a specific taging convention:
docker tag notes-api:latest <AWS ID>.dkr.ecr.us-west-2.amazonaws.com/notes-api:latest
Finally, I was able to push the image to ECR using:
docker push <AWS ID>.dkr.ecr.us-west-2.amazonaws.com/notes-api:latest
I verified that my image showed up in the web console and copied the image URI, so it was time to spin up an ECS cluster.
ECS Setup
I wasn’t finding any real resources for setting up ECS using the CLI, so I did this part using the web console. I hate having to rely on a graphical interface, so I plan on learning some form of IaC for this in the future.
First, I went to the old web interface because it matched the guides I was using. Then I went to Create Cluster and selected EC2 Linux + Networking.
I then changed the following fields:
- Cluster Name: notes-api
- EC2 instance type: t3.nano
- Key pair: an existing key pair - this isn’t necessary, but I wanted to have it so that I could connect to the ec2 instance if I needed to troubleshoot, which I ended up needing.
- VPC: I chose one I had setup previously
- Security group: I chose one I had setup previously
- Auto assign public IP: Enabled
After hitting create, I went to Task Definitions then Create new Task Definition and selected EC2.
For this section I entered the following:
- Task definition name: notes-api-task
- Task Memory: 100
- Task CPU: 1024
And under Container definitions, I clicked Add container, gave it a name, pasted my container URI from the ECR page, and set a port mapping of 80 to 8000 to match the Uvicorn service that runs in the container. I left the rest as defaults and clicked Add.
Back at the Create Cluster page, I left the rest as default and clicked Create.
Then, I went back to the Clusters page, selected my “notes-api” cluster, selected the Tasks tab, and clicked Run new Task. On the next screen, I selected EC2 and clicked Run Task
At this point, I went to the EC2 console and found the public IP for my new EC2 instance. With that, I was able to verify that the root GET method, which was a simple string return, was working. The other routes, which used an external API call to Mongo Atlas, were not.
I was able to get into the EC2 instance and read the output from the Docker container, which described a failed connection to the Mongo API. At first I thought it must be the Security Group firewall blocking it on AWS, so I first tried opening the port it needed (27017), and opening the whole thing when that didn’t work. Eventually I logged into my Atlas console and realized that there was an IP allow list on that end. Once I added the EC2 instance’s IP, everything worked! Of course I removed the allow all rule from the Security Group, leaving the 27017 port open.
Adding the Front-End
Now that the API was fully functioning, I just needed to put up my front-end. This was quite easy as I had done this before and wrote down how to host it on S3 using the CLI in a previous post.
Once the front-end was up and a few minutes after adding the DNS record, I had my app fully running at http://notes.rickt.io/
Conclusion
This one took a lot of effort to get through. I had been struggling to focus and spend my time productively working on this ongoing project. One thing that has helped a lot is picking up a used ThinkPad and working outside of my home at coffee shops. Not only has it given me the ability to stay on task, but it has forced me to become even more familiar with Git when I switch from one workstation to the other.
I’m also immensely grateful to my partner, Jen Hernandez for all her support in this endeavor.
Cheers!
Rick
Resources
- Github repo for this app - https://github.com/rickthackeray/notes \
- AWS cloud demos' ECS guide - https://www.youtube.com/watch?v=PgyFrkJaNys \