Nowadays, putting shiny new applications in containers seems to be the way of the future, and for good reason. They offer platform portability, hardware efficiency, and enhanced security. In this tutorial, we are going to take a very simple Flask API, put it in a Docker container, and then test it out using Postman.
To start off, we need to get a few things set up. Below, I created a small API just to use as an example. Please keep in mind that this does not have any error handling nor utilize any authentication. Its usage is only for demonstration purposes. I highly recommend having error handling and user authentication in your APIs.
In the example above, App.py is the entry point for the API. Storage.py is where all our database work is done. Model.py handles the mapping for SqlAlchemy. If you are curious as to what the actual database looked like, table.md shows the create statement for our test table and attribute data types.
Once you have finished building your API and testing it (or if you are using the example above), we need to create a Dockerfile. This file lists out the instructions to build a Docker image. For the example API, the file looks like this:
The file starts out with the FROM instruction which initializes the build stage and sets the base image to python. WORKDIR creates a working directory for the container. COPY adds the requirements.txt file to the file system of the container. Remember that the requirements.txt is a list of all the python modules our API is using. The RUN instruction uses pip to look at that file and install those modules. The second COPY instruction grabs the contents of the src directory which contains our project files (App.py, Storage.py, and Model.py). Lastly, the CMD instruction runs the python command on the app.py in the container.
Now that the Dockerfile is ready to go, we can go ahead and run that build command!
sudo docker build -t api-test .
“api-test” can be whatever you want to name the image, it just has to be lowercase. A successful build will show us this:
To verify that the image was created:
sudo docker images
Now that the image is built, we can go ahead and run it. To do so, we will use the following command:
sudo docker run -d -p 5000:5000 api-test
The output of this command will give us a Container Id.
To see that the container is in fact running:
sudo docker ps -a
If you’re like me and forget to fully test something before trying to run the container, you might get a status that has “Exited” in it. One that I find helpful is running this command with <CONTAINER ID OR CONTAINER NAME> being the Container Id or the Container Name that got assigned to your container when you created it. Either name or id will work just fine.
sudo docker logs <CONTAINER ID OR CONTAINER NAME>
Just like it says, it will show you the logs for that container and any errors that were discovered when trying to run it. Fortunately for us, our example API is in a container and running. So, let’s try and make a couple requests to it. To do so, we’ll utilize Postman. Our first request will be a simple Get request that shows the default page for the API. <HOSTNAME> is whatever the hostname or IP address your container is running on.
http://<HOSTNAME>:5000/
This next request will query the database getting us a list of books and their authors.
http://<HOSTNAME>:5000/booklist/v1/
For something a little more exciting, we can do a POST request as well:
http://<HOSTNAME>:5000/booklist/v1/?name=The Lord of the Rings&author=J R R Tokien
As you probably already noticed, I made a mistake when adding the authors name. You can see the mistake here when we do another GET request on the “booklist/v1/” route:
No worries, because there is a fix for this. We have a PUT request that can be utilized:
http://<HOSTNAME>:5000/booklist/v1/?name=The Lord of the Rings&author=J R R Tolkien
If we take one last peak at the route “booklist/v1/” and do a GET request, this will show the full results:
At this point, we have gone over a couple of common HTTP requests and how they work running inside a Docker container. It really isn’t very different than if the API was running as a standalone app on a server. In our case here though, we get the benefits of the API running in a container. Therefore, there is extra security, portability and efficiency. Feel free to use the example API I provided. Cheers!
More documentation on Docker can be found here
Also published at https://medium.com/python-in-plain-english/containerize-that-flask-api-3a5d3376c761