Cloud Systems (Part 2): Containerizing a Website

Cloud engineering is taking over software development. In a lot of ways, this is great; It allows us to build and deploy more complicated applications with less difficulty, and maintaining those applications becomes less troublesome too. We can release updates more quickly than ever, ensuring that we can stay on top of feature requests and security issues. That said, the rise of cloud engineering has also introduced a lot of complexity in the form of dozens of services even within just one cloud provider. Figuring out where to start can be tough, so let’s take a practical tour! In this series, I’ll walk you through building a personal website and deploying it using modern cloud engineering practices.

In part one of this series, we built a personal website and it to AWS S3. That works perfectly well for a static, single-page application with minimal interaction, but if you want server-side routing or database interaction, things have to get a little bit more complicated. In part two of this series, we’ll be adding a couple more pages to our personal website, adding server-side routing, and containerizing it with Docker.


So, what’s a container and why would you want to use one? You can think of a container here the exact same way you think of containers on a shipping barge. On that barge is a bunch of shipping containers, and inside each shipping container is a bunch of packages. The barge itself is your computer (or your cloud environment), and the shipping containers house your applications.

It may also help to think of them as smaller, more lightweight incarnations of virtual machines. While a virtual machine virtualizes the machine’s physical hardware through the use of a hypervisor, a container virtualizes only the operating system. A virtual machine is usually several gigabytes in size, whereas a container is usually less than a gigabyte. The small size and speed of deployment as compared to a traditional virtual machine has given rise to the popularity of designing applications as collections of microservices (several single-purpose services running in containers that, when working together, make up your complete application) instead of monoliths (all of your application code and services running as a single unit, often within a single file).

Deploying your application using containers allows you to package your application code alongside everything required to run it, including its dependencies and an operating system. You know how sometimes you hand code off to someone else, but it doesn’t run for them, so you go “Well, it works on my machine,” and just shrug? Using a container is pretty similar to just deploying your machine. The most popular container engine today is Docker, so that’s what we’ll be using today.


Before we go into containerizing and deploying our website, let’s make it a little bit more useful. For this, we’re going to be using Flask, a lightweight web framework for Python. Fork and clone this GitHub repository to get the full sample code for part two of this series.

Expanding the Website

Our file structure gets more complicated now that we are building a more dynamic website with server-side routing and throwing it into a Docker container. You will have something like this:

├── Pulumi.yaml
├── requirements.txt
└── website
    ├── Dockerfile
    ├── requirements.txt
    └── app
        ├── static
        │   ├── normalize.css
        │   ├── style.css
        │   └── background.jpg
        └── templates
            ├── base.html
            ├── index.html
            ├── portfolio.html
            └── about.html

Everything contained within the app directory is the website itself. We now have server-side routing, and a few different pages. To run it locally and see what the new website looks like, pip3 install flask to install the Flask web framework and then run python3 from the app directory. Flask will return an IP address where your website is running; navigate there in your browser and click around a bit!

from flask import Flask, render_template

app = Flask(__name__)

def home():
    return render_template("index.html")

def about():
    return render_template("about.html")

def portfolio():
    return render_template("portfolio.html")

if __name__ == '__main__':"", port=80, debug=True)

Where before our entire website was made up of a single HTML file, we now have several, and we’re using Flask to handle the routing between them. In this server file, three routs are defined: home, about, and portfolio. Flask handles serving these up for us, and associating a route with a particular HTML file. This way, we get a nice URL instead of something like Building a website this way also means we have the ability to apply some logic to each of these routes, such as adding database interaction, user login, and passing conditional variables from the server to the templates that will be rendering each page. We aren’t doing any of that yet, but we will!

At the bottom, we’re binding the Flask application to You can change that to any other unoccupied local IP address and port you like.


Flask can make use of a templating engine called Jinja2 to make adding pages to your website a bit less repetitive. You start with one template, in this case called base.htmlthat contains any HTML you want to exist on every single page.


{% block content %}{% endblock %}


Leave a Comment