Skip to content

Dockerfile

Goal

This project is about the Dockerfile and the creation of your own images. You will:

  • create your own Dockerfile for a service
  • add a second service to the Dockerfile and connect the services

Tools

  • Try to complete the tasks below with the help of the slides and the cheatsheets.
  • If you have any problems with this, you will find a fold-out block for each task in which the solution is described.

Task 1 - Create a first Dockerfile

  • Create a new subfolder my-first-image of the workspace folder /home/coder/workspace.
  • Create a Dockerfile in the folder with the following properties:
    • Baseimage: ubuntu:20.04
    • Entrypoint: ["/bin/echo", "Hello"]
    • Command: ["world"]
Solution (click on the arrow if you get stuck)
  • Open the explorer of VSCode with Ctrl + B or alternatively by clicking on the file icon (above the magnifying glass) in the left bar.
  • Create a new folder with the name my-first-image via the Explorer.
  • Create a file in the folder with the name Dockerfile.
  • Add the following content to the file:
    FROM ubuntu:20.04
    
    ENTRYPOINT ["/bin/echo", "Hello"]
    CMD ["world"]
    

Task 2 - Building a first image

2.1: Building an image

In the terminal, build an image with the name demo01 from the Dockerfile.

Solution (click on the arrow if you get stuck)
  • Open a terminal (Menu > Terminal > New Terminal).
  • Change to the created folder with cd my-first-image. cd stands for stands for Change Directory.
  • Execute pwd. pwd returns the current directory. You should get the output /home/coder/workspace/my-first-image.
  • If you do not receive the output, change to the directory with cd /home/coder/workspace/my-first-image.
  • Now build the image with:
    docker build -t demo01 .
    
  • You should see an output after the finished build that looks similar to the following:
    Step 3/3 : CMD ["world"]
    ---> Running in fdef47b21535
    Removing intermediate container fdef47b21535
    ---> 6296257370ba
    Successfully built 6296257370ba
    Successfully tagged demo01:latest
    

2.2: Starting the image

Start the image in the foreground (without -d). It should display "Hello world".

Solution (click on the arrow if you get stuck)
  • Start the image with the following command:
    docker run demo01
    

2.3: Starting the image with arguments

Start the image so that it outputs "Hello <name>", where <name> should be your name.

Solution (click on the arrow if you get stuck)
  • Start the image with the following command:
    docker run demo01 <name>
    

Task 3 - Preparation of a small web app

3.1: Create app.py

Create a file app.py with the following content in the folder /home/coder/workspace/my-first-image:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Web App with Python Flask!'

app.run(host='0.0.0.0', port=80)

This Python file defines and starts a small Flask web application. The application is accessible via port 80.

3.2: Create requirements.txt

Create a requirements.txt file with the following content in the /home/coder/workspace/my-first-image folder:

Flask>2<3

Your folder should now look like this:

├── app.py
├── Dockerfile
└── requirements.txt

0 directories, 3 files

Task 4 - Extend Dockerfile for the web app

4.1: Install Python

Install the packages python3 and python3-pip with the help of apt-get.

Solution (click on the arrow if you get stuck)
  • Your Dockerfile should now look like this:
    FROM ubuntu:20.04
    
    RUN apt-get update && apt-get install -y \
            python3 \
            python3-pip \
        && rm -rf /var/lib/apt/lists/*
    

First, the package manager is updated to the latest version. The packages should then be installed. This part of the command is essential. The update and rm commands are best practice and are discussed in more detail in the following chapter. The -y option specifies that yes is automatically answered to the installation question. Finally, we should delete the package cache to keep the image small.

4.2: Install dependencies

  • Expand your Dockerfile so that the requirements.txt file is copied to the image in the /app folder. Use the WORKDIR command to switch to the /app context.
  • Install the dependencies with the following command:
python3 -m pip install -r requirements.txt
Solution (click on the arrow if you get stuck)
  • Ihr Dockerfile sollte nun wie folgt oder ähnlich aussehen:
    FROM ubuntu:20.04
    
    RUN apt-get update && apt-get install -y \
            python3 \
            python3-pip \
        && rm -rf /var/lib/apt/lists/*
    
    WORKDIR /app
    COPY requirements.txt .
    RUN python3 -m pip install -r requirements.txt
    

4.3: Integrate and start webapp

  • Copy the file app.py into the image.
  • Document port 80 as access to the application.
  • Execute python3 app.py when starting the image.
Solution (click on the arrow if you get stuck)
  • Your Dockerfile should now look like this:
    FROM ubuntu:20.04
    
    RUN apt-get update && apt-get install -y \
            python3 \
            python3-pip \
        && rm -rf /var/lib/apt/lists/*
    
    WORKDIR /app
    COPY requirements.txt .
    RUN python3 -m pip install -r requirements.txt
    
    COPY app.py .
    
    EXPOSE 80
    CMD ["python3", "app.py"]
    

4.4: Building and testing the image

  • Build the image with the new Dockerfile.
  • Start the image with port forwarding so that you can access the web app via reach the web app via the browser.
Solution (click on the arrow if you get stuck)
  • Execute the following command to build:
    docker build -t demo01 .
    
  • Start the image as follows:
    docker run -p 8080:80 demo01
    
  • Try to open the webapp in a new tab under http://code-0.labs.corewire.de:8080/.
  • Important:
    • http not https
    • Replace code-0 with your instance.
  • If successful, you can switch back to the VSCode instance and close the container with Ctrl+c.

Task 5 - Change user in container

5.1: Create non-root user

For security reasons, we now want to run the web app in the container with a user with a user who does not have root rights. To do this, we need to create a user. This can be done with the following command:

groupadd --system --gid 10100 app-runner-group \
    && useradd --system --gid 10100 --uid 10100 app-runner-user

Solution (click on the arrow if you get stuck)
  • Your Dockerfile should now look like this:
    [...]
    RUN python3 -m pip install -r requirements.txt
    RUN groupadd --system --gid 10100 app-runner-group \
        && useradd --system --gid 10100 --uid 10100 app-runner-user
    
    COPY app.py .
    [...]
    

5.2: Using non-root users

Now make sure that the webapp is executed with the created user app-runner-user. is executed.

Solution (click on the arrow if you get stuck)
  • Your Dockerfile should now look like this:
    [...]
    RUN groupadd --system --gid 10100 app-runner-group \
        && useradd --system --gid 10100 --uid 10100 app-runner-user
    
    USER app-runner-user
    COPY app.py .
    [...]
    

5.3: Building and testing the image

  • Build the image with the new Dockerfile.
  • Start the image with port forwarding so that you can access the web app via the browser.
Solution (click on the arrow if you get stuck)
  • Execute the following command to build:
    docker build -t demo01 .
    
  • Start the image as follows:
    docker run -p 8080:80 demo01
    
  • Try to open the webapp in a new tab under http://code-0.labs.corewire.de:8080/.
  • Important:
    • http not https
    • Replace code-0 with your instance.
  • If successful, you can switch back to the VSCode instance and close the container with Ctrl+C to close the container.

Privileged ports (ports < 1024) without root rights

Since Docker 20.10.0 (Release: 08.12.2020, Release notes) containers can also use ports < 1024 without requiring root rights.

An alternative solution for older versions to run the container without root rights would be to replace port 80 in the app.py with, for example, port 8080 in app.py and adjust the port forwarding for docker run accordingly.

You have now successfully containerized an existing application. You have defined everything necessary in the Dockerfile for the application to fulfill its purpose without errors.