Why Use Containers?
When I was first learning software engineering, one of the most frustrating barriers to learning was setting up local and production environments to run my applications.
I wanted to follow industry best practices, but configuring a local postgres or a daemonizing nginx server on a digital ocean droplet ate up a lot of time that I wanted to spend building projects or working on tutorials.
Learning to run applications in containers has transformed the way I work as a software engineer. It allows me to easily package and run nearly anything I want in a consistent way across local and production environments.
Moving from local development to production is as simple as building my Dockerfile, pushing the image up to a registry, and telling my container orchestrator to run it.
If these words don’t mean anything to you right now that’s ok. We’ll cover container registries and orchestration soon enough.
For now, let’s start by exploring an example of how to containerize and run a python web server.
Configure Filesystem
pwd and highlight project directory
mkdir containerize-python-server
cd !$
mkdir src
Within your project directory, create a src
directory for our application code.
Create Server
create simple_server.py
In the src
directory, create a file called simple_server.py
. This file will run our simple http server:
from http.server import HTTPServer, SimpleHTTPRequestHandler
We will use an HTTPServer class and request handler from the python standard library to keep things as simple as possible.
def run(server_class=HTTPServer, handler_class=SimpleHTTPRequestHandler):
"""Entrypoint for python server"""
server_address = ("0.0.0.0", 8000)
httpd = server_class(server_address, handler_class)
print("launching server...")
httpd.serve_forever()
Define a function called run
that instantiates and runs an instance of HTTPServer
. Notice the server_address
argument tells our HTTPServer instance to listen on port 8000 of our local machine.
if __name__ == "__main__":
run()
We will execute our run
function whenever this python module is invoked.
Validate Server Functionality
python3 src/simple_server.py
We can run the server on our local machine by executing it as a python file.
from a new terminal
curl -iv localhost:8000
Validate the server is running by opening a new tab and curling port 8000 to confirm you get a response.
Looks good!
We know that it works, so let’s kill the server by hitting ctrl-c
.
Create Dockerfile
create Dockerfile
Now let’s add a Dockerfile to define the container image we will use to run our web server:
FROM python:3.8
Use an image with Python 3.8 pre-installed as our base. This will give us a python 3.8 interpreter in the container.
ENV SRC_DIR /usr/bin/src/webapp/src
COPY src/* ${SRC_DIR}/
WORKDIR ${SRC_DIR}
Copy the files in our project directory’s src
folder into the container at /usr/bin/src/webapp/src
. Then set the image’s working directory to the src
directory.
ENV PYTHONUNBUFFERED=1
Set PYTHONUNBUFFERED=1
as an environment variable so that python sends print and log statements directly to stdout
. If we did not set this, we would not see logs from our container because they would be sent to a buffer.
CMD ["python", "simple_server.py"]
Finally, use the CMD
directive to tell the container a default command to execute when we run it. In this case, we execute our simple_server.py
script.
Build Image
docker build . -t simple_server
Build the image and tag it as simple_server
with the -t
flag.
Run Image
docker run -p 8000:8000 simple_server
Run the image and forward port 8000
on your local machine to port 8000
in the docker container with the -p
flag.
Validate Server Functionality
from a different terminal
curl -iv localhost:8000
Validate your containerized server is running with a curl command just like before.
And you’re done! You’ve containerized a python server.
You can kill the dockerized process by hitting ctrl-c
.
In my next videos I will show you how to run a django application entirely with containers.