You will learn:

  • Create your own image using docker build
  • Specify the execution order
  • Specify the system to configure

Create a simple web application using the Python scripting language and the Flask framework. We define several functions in the application that can process HTTP requests and return an HTML response.

In our case, we will create an application that can say hello and say the current time:

Application source: app.py

from datetime import date
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1 style = 'color: blue'> Hello There! </h1> <a href='/date'> Date and time </a>"

@app.route("/date")
def today():
    today = date.today()
    return "<p> {} </p>".format(today.strftime("% d.% m.% Y"))

if __name__ == "__main__":
    app.run()

If we want to run such an application in the classic way, we need:

  • have the Python3 interpreter installed,
  • install Flask framework,
  • run the application from the command line to listen on the specified port,
  • Use a web browser to make a request and view the result.

This requires executing multiple commands to get the application to the desired state. If we have a Debian or Ubuntu Linux OS available:

# If necessary, we will install the virtualenv package
sudo apt-get install python3-virtualenv
# Let's create a virtual environment
python3 -m virtualenv ./venv
# We are activating a virtual environment
source ./venv/bin/activate
# We will install in a virtual environment
pip install flask
# We will configure the application
export FLASK_APP=app.py
# Launch the application
flask run --host=0.0.0.0

Docker image preparation

If we want to distribute such an application, we must prepare for different situations and ensure that all dependencies are met - Python is installed and the necessary libraries installed. The procedure for fulfilling dependencies differs depending on the environment we use - it will be different on the Windows environment, different on different Linux environments.

To make it easier, we will prepare a Docker image with the application and all the dependencies.

Docker image consists of several layers. At the lowest level are the operating system files, and above it are the applications. The new layer always covers the old one. Image layering allows us to easily modify existing images without having to do everything again.

We will create a new Docker image using the docker commands. Follow the instructions in the special Dockerfile file to take the existing image, modify it, compile it and save it in the local registry.

Dockerfile

The resulting application image is described in the Dockerfile file. It consists of a set of instructions for assembling the image. The instructions in Dockerfile allow us to easily repeat and modify the application build process.

The advantage is that we can use existing images when creating new images. The image will contain the operating system, necessary libraries and other dependencies. We don't have to start from scratch, but we will take advantage of existing image with Python interpreter installed.

Write the following instructions in Dockerfile:

# Basic image from Docker Hub
# Working Alpine Linux installation with Python interpreter installed
# The advantage is small size
FROM python:3.8-alpine

# Container working directory settings
WORKDIR /app

# Virtual environment is not required

# Install application dependencies directly on the system
RUN pip install flask

# Copy application files to the image
COPY ./app.py /app

# Setting the environment variable
ENV FLASK_APP app.py

# Program to run
ENTRYPOINT [ "flask" ]
# Arguments
CMD ["run", "--host", "0.0.0.0"]
# The flask is launched in development mode as follows:
# flask run --host 0.0.0.0

The ENTRYPOINT command is the name of the command to be executed. CMD is a list of default arguments to the command.

Image compilation

There should be two files in the current directory:

  • app.py
  • Dockerfile

When we have the Dockerfile instructions ready, we will try to compile the image:

docker build -t flaskapp:dev .

The -t (tag) argument specifies the name of the image in the registry. The part before the colon is the name of the image. The part behind the colon is the version of the image.

Verify that the new image is in the list of locally available images:

docker images ls

Launch your own container

If the image is prepared in the list of available images, we can create a new container:

docker run -it --rm flaskapp:dev

Docker takes the image and starts the application according to the instructions in ENTRYPOINT andCMD.

Conditions of startup (program name, arguments, environment variables, etc.) defined in the image can be changed during startup docker run by entering additional arguments.

                       ENV                    ENTRYPOINT  CMD
                        |                              |   |
                        v                              v   v
docker run -it --rm -e VARIABLE=hello --entrypoint echo flaskapp:dev hello world

Note:

in some images, the following shell script is used as ENTRYPOINT:

#!/bin/bash
set -e

# If the first argument is an application name
if ["$ 1" = 'postgres']; then
    # I'll do some configuration
    exec gosu postgres "$ @"
fi
# Otherwise, I will execute the arguments as a shell command
exec "$ @"

In this case, when starting with docker run, we do not have to change the parameter --entrypoint, because the whole run command is read from the CMD arguments.

More about what is the difference between CMD,ENTRYPOINT and RUN.

Configure the application using environment variables

Edit the application source text to take into account the environment variable you defined. If the variable MY_NAME is defined, the application will greet you nicely according to the configured name. Otherwise he will greet our friend Ferko.

Application source: app.py

import os
from flask import Flask
from datetime import date

app = Flask(__name__)

@app.route("/")
def hello():
    name = "Ferko"
    if "MY_NAME" in os.environ:
        name = os.environ["MY_NAME"]
    return "<h1 style = 'color: blue'> Hello There {}! </h1>".format(name)

@app.route("/date")
def date():
    today = date.today()
    return "<p> {} </p>" .format(today.strftime("% d.% m.% Y"))

if __name__ == "__main__":
    app.run()

Create a new image with an improved application.

The environment variable can be set just before the container is launched:

docker run -it --rm -e MY_NAME=Daniel flaskapp:dev

The run of the image-ready application can also be modified with command line arguments.

If we want to get help in the classic command line, we write:

flask --help

In our image it will be:

docker run -it --rm -e MY_NAME=Daniel flaskapp:dev --help

The arguments in the last place replace the arguments entered with CMD.

To run the command line instead of flask, use the--entrypoint parameter, which will overlay the instructions in Dockerfile.

docker run -it --rm --entrypoint /bin/sh flaskapp:dev

Homework

Prepare an application that will count accesses.

Will you be able to find out and record the type of browser?

Additional information

Previous Post Next Post

Week 4: Creating your own container images