Discovering a Flask Web Application

- First, clone an example Flask application:

git clone https://github.com/uptime-formation/microblog/

- Open VSCode with the microblog directory by typing `code microblog` or by launching VSCode with `code` and then clicking on Open Folder.

- In VSCode, you can go to `Terminal > New Terminal` to get a terminal at the bottom of the screen.

- Let's explore the code together in VSCode.

Moving on to Docker

Manually deploying a Flask application each time can be relatively cumbersome. To prevent the dependencies of two Python projects from conflicting, you should normally use a virtual environment like virtualenv to separate these two apps. With Docker, projects are already isolated in containers. Therefore, we will build a container image to package the application and handle it more easily. Make sure Docker is installed.

To know the list of Dockerfile instructions and their usage, refer to the reference manual on Dockerfiles.

  1. In the project folder, add a file named Dockerfile and save it.
  2. Normally, VSCode prompts you to add the Docker extension. It will make our lives easier, so install it. A new icon appears in the left sidebar, where you can see downloaded images and existing containers. The extension also adds useful information to Dockerfile instructions when you hover over a keyword with the mouse.
  3. Add at the top of the file: `FROM ubuntu:latest` This command indicates that our base image is the latest version of the Ubuntu distribution.
  4. We can already build a container from this empty Ubuntu template: `docker build -t microblog .`
  5. Once the build is complete, launch the container.
  6. The container stops immediately. Indeed, it contains no blocking command and we haven't specified any command at launch. To be able to properly observe the container, something needs to be running inside. Add the following line at the end of the file: `CMD [“/bin/sleep”, “3600”]` This line tells the container to wait for 3600 seconds as in the previous TP.
  7. Rebuild the image and relaunch a container.
  8. Display the list of running containers.
  9. Now, let's enter the container via the command line to observe. Use the command: `docker exec -it <container_id> /bin/bash`
  10. You are now inside the container with a command prompt. Use some Linux commands to quickly navigate it (ls, cd…).
  11. This is a standard Linux, but it's not designed to be used as a complete system, just for an isolated application. Now we need to add our Flask application inside. In the Dockerfile, remove the CMD line, then add:
    RUN apt-get update -y
    RUN apt-get install -y python3-pip
  12. Ordered List ItemRebuild your image. If all goes well, proceed. To install Python dependencies and configure the Flask environment variable, add:
    COPY ./requirements.txt /requirements.txt
    RUN pip3 install -r requirements.txt
    ENV FLASK_APP microblog.py
  13. Rebuild your image. If all goes well, proceed.
  14. Next, let's copy the application code inside the container. To do this, add the lines:
    COPY ./ /microblog
    WORKDIR /microblog

The first line instructs to copy all the contents of the current host directory into a directory /microblog inside the container. We didn't copy the requirements at the same time to take advantage of Docker's cache features, avoiding having to redownload the application dependencies every time we modify the app's content.

Then, in the second line, the current directory in the container is moved to /.

  • Rebuild your image. Notice that the build restarts from the modified instruction. Previous layers had been cached by the Docker Engine.
  • Unordered List ItemIf all goes well, proceed.
  • Unordered List ItemFinally, let's add the startup section at the end of the Dockerfile, it's a script called boot.sh:
    CMD ["./boot.sh"]
  • Rebuild the image and launch a container based on the image by opening port 5000 with the command: `docker run -p 5000:5000 microblog`
  • Navigate in the browser to localhost:5000 to admire the microblog prototype.
  • Launch a second container, this time with: `docker run -d -p 5001:5000 microblog`
  • A second instance of the app is now running and accessible at localhost:5001

Docker Hub

Using docker login, docker tag, and docker push, push the microblog image to Docker Hub. Create an account on Docker Hub if necessary.

Solution: Improving the Dockerfile

Using the `python:3-alpine` image and replacing the necessary instructions (no need to install `python3-pip` as this program is now included in the base image), repackage the microblog app into an image tagged `microblog:slim` or `microblog:light`. Compare the size between the two built images.

Varying Configuration Based on Environment

The Flask development server is handy for debugging in a development environment but is not suitable for production. We could create two images for the two situations, but this would go against the DevOps imperative of bringing dev and prod closer together.

To start the application, a boot script named `boot.sh` is utilized. This script handles the initialization of the application environment and starts the appropriate server based on the environment variable `APP_ENVIRONMENT`.

set -e
if [ "$APP_ENVIRONMENT" = 'DEV' ]; then
    echo "Running Development Server"
    exec flask run -h 0.0.0.0
else
    echo "Running Production Server"
    exec gunicorn -b :5000 --access-logfile - --error-logfile - app_name:app
fi

Declaring Environment Variable and Building the Image

  • To start, declare the `APP_ENVIRONMENT` environment variable with the default value `PROD` in the Dockerfile.
  • Build the image with the `build` command.
  • Then, use the right arguments with `docker run` to launch an instance of the application in the PROD configuration and an instance in the DEV environment, reachable on two different ports.
  • Use `docker ps` or read the logs to verify that there is indeed a difference in the launched program.

Exposing the Port

Add the `EXPOSE 5000` instruction to inform Docker that this application is intended to be accessed via its port 5000.

Note: Publishing the port with the `-p host_port:container_port` option remains necessary; the EXPOSE instruction is there for documenting the image.

Improved Dockerfile

In this Dockerfile, use the `HEALTHCHECK` instruction to verify if the application inside the container is healthy.

In a new directory, create a Dockerfile with the following content:

FROM python:alpine
 
RUN apk add curl
RUN pip install flask==0.10.1
 
ADD /app.py /app/app.py
WORKDIR /app
 
HEALTHCHECK CMD curl --fail http://localhost:5000/health || exit 1
 
CMD python

Also, create an app.py file with the following content:

from flask import Flask
 
healthy = True
 
app = Flask(__name__)
 
@app.route('/health')
def health():
    global healthy
 
    if healthy:
        return 'OK', 200
    else:
        return 'NOT OK', 500
 
@app.route('/kill')
def kill():
    global healthy
    healthy = False
    return 'You have killed your app.', 200
 
if __name__ == "__main__":
    app.run(host="0.0.0.0")

Carefully observe the Python code and the `HEALTHCHECK` line of the Dockerfile, then launch the application. Use `docker ps` to see where Docker indicates the health of your application.

Visit the URL /kill of your application in a browser. Repeat `docker ps`. What happened?

(Optional) Add a HEALTHCHECK instruction to the Dockerfile of our microblog application.

Optional: Dissecting an Image

An image is composed of several layers stacked together by the Docker Engine and metadata.

  1. Display the list of images present in your Docker Engine.
  2. Inspect the last image you created (use `docker image –help` to find the command).
  3. Observe the build history of the image with `docker image history <image>`.
  4. As a superuser (`sudo su`), visit the `/var/lib/docker/` directory on the host. In particular, `/var/lib/docker/image/overlay2/layerdb/sha256/`:
    • There you will find a kind of database of all image layers with their ancestors.
    • It's a hierarchical structure.

You can also use the command `docker save your_image -o image.tar`, then use `tar -C decompressed_image/ -xvf image.tar` to decompress a Docker image and explore the different layers of the image.

To explore the image hierarchy, you can install [dive](https://github.com/wagoodman/dive).

Optional: Private Registry

By retrieving the command indicated in the official documentation, create your own registry.

Then find out how to push an image to it.

Finally, delete your image locally and retrieve it from your registry.

Optional: Making the Cow Speak

Create a new Dockerfile to allow a cow to say things using the cowsay command. The goal is to run our program in a container with commands like:

  • `docker run –rm cowsay Hello!`
  • `docker run –rm cowsay -f stegosaurus Yo!`
  • `docker run –rm cowsay -f elephant-in-snake An elephant in a snake.`

Should we use the `ENTRYPOINT` command or the `CMD` command? Consult the Dockerfile reference manual if necessary.

For information, cowsay is installed in `/usr/games/cowsay`.

The list of (essential) cowsay options can be found [here](https://debian-facile.org/doc:jeux:cowsay).

Solution:

  • The `ENTRYPOINT` instruction and managing program input-output in Dockerfiles can be a bit tricky, and sometimes you need good knowledge of Bash and Linux to understand (and carefully read the Docker documentation).
  • Sometimes we use containers just to run once (to retrieve the result in the console or generate files). We then use the –rm option to delete them as soon as they stop.

Optional: Multi-stage Build

Transform the Dockerfile of the dnmonster application located at the following address to perform a multi-stage build to obtain the lightest possible final image: [https://github.com/amouat/dnmonster/](https://github.com/amouat/dnmonster/)

The documentation for multi-stage builds can be found [here](https://docs.docker.com/develop/develop-images/multistage-build/).

teaching_assistant/workflow/images_and_containers.txt · Last modified: 2024/05/17 08:09 by Ralph
Back to top
CC Attribution-Share Alike 4.0 International
chimeric.de = chi`s home Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0