Programming inside Containers

An example with Flask and Docker

Reuben Sinha
4 min readDec 5, 2020

Manager: “Hi Leroy! Welcome to the project. You won’t have to spend half a day to setup the environment in your machine, just clone and run!”

Leroy: “Wha-! Are you serious?”

Unlike the tedious, sometimes daunting task of installing software galore for a project, often with ambiguous or incomplete literature and version incompatibilities, containerisation offers a clean and quick alternative for rapid setup of a functional development environment. Let me present my case with the example of a simple flask application.

Step 1. Create Environment inside Image

Build a base image called custom-env-image containing required dependencies for the application using a Dockerfile.

#Using base python image
FROM python:3.10.0a2-slim-buster
#Changing to project directory inside container
WORKDIR /project
#Exposing flask's default port
EXPOSE 5000
#Installing flask library
RUN pip install flask==1.1.2
#Command to execute flask app when container is created.
CMD [“python”, “app.py”]

Additionally, I have also included a CMD instruction to execute the flask application when the container runs.

Step 2. Write some code / Clone code from a repository

The code for a minimal flask application is displayed below. Though robbed of complexity, it can be considered as an example for an existing project, which needs to be extended with some additional code.

#Path: /home/user/project/app.pyfrom flask import Flaskapp = Flask(__name__)@app.route(‘/’)
def hello():
return ‘Hello, world!’
if __name__ == “__main__”:
app.run(host=”0.0.0.0", debug=True)

Step 3. Run container with bind mount volume

In step 1, the development environment was compacted into a base image, which can be effortlessly run to create a container with all required libraries for the application.

Remember! Containers are ephemeral, and immutable. Any modifications made within them, will be futile and destroyed when it is removed. For this reason, the code must be detached from the short-lived container using persistent storage which can be achieved with Bind Mounts.

docker container run \
--name web-app \
 --volume /home/user/project/:/project \
--publish 5000:5000 \
--detach custom-env-image

Step 4. Check the application

Let’s have a peek at the application in its current state. Since, the container has been published in port 5000, it should be accessible from the browser.

Application output prior to modification

Step 5. Modify the application, build on it!

Instead of returning “Hello World”, let’s modify the application to return the html document shown bellow.

<!--#Path: /home/user/project/templates/page.html--><html>
<head>
<title>Message</title>
</head>
<body>
<h1>hi, welcome to the website </h1>
</body>
</html>

Make corresponding changes to the flask application, to return page.html

#Path of flask app /home/user/project/app.py
#Path of html document /home/user/project/templates/page.html
from flask import Flaskapp = Flask(__name__)@app.route(‘/’)
def hello():
#Lets render a html page instead of "Hello, world!"
return render_template('page.html')
if __name__ == “__main__”:
app.run(host=”0.0.0.0", debug=True)

The application gets automatically updated in the browser after a refresh, without having to re-run the container.

Make changes in code, and it runs on the fly!

Step 6. Adding additional Libraries

Seldom does an entire python web application require only the flask library. Installing additional libraries is simple, and can be accomplished by creating a new base image from the old base image. As an example, requests library has been included in the new Dockerfile to create a new base image.

#Using earlier image as base image
FROM custom-env-image
#Installing requests library
RUN pip install requests==2.25.0

(Bonus) Step 7. Handling multiple components of a project

Suppose the project includes additional components such as a database, proxy or API services. Then each component’s environment must be compacted inside separate images with bind mounts if required, allowing us to run them as separate containers using docker-compose.

Conclusion

This is a fun little example, presenting a case for programming inside containers. Perhaps infeasible at present, considering the advantages of a normal development environment such as integration with IDEs, convenient debugging and testing tools, which outweigh the characteristic portability of a containerised development environment.

However with proper plan, design and foresight, programming in containers can be achieved, allowing developers to kick-start their work with a simple clone and run.

--

--