NSWI153: Advanced Web Applications

Containers : Docker

Preliminaries

Please read this section at least one day prior to the seminar. This section outlines what you are expected to know, be able to do, or prepare in advance. Following these instructions will help you get the most out of the seminar and ensure smooth participation.

Preliminaries : Knowledge, Skills, and Competence


Before the start of the practical, you should be able to:

  • Explain relation between a Dockerfile, a Container and an Image.

Preliminaries : Using your own computer


You need to bring your own computer to the practical. If you can not you will need to work with your collogue.

Before the start of the practical, make sure that:

  • Make sure you have Docker installed.
  • Make sure you can successfully execute following command
    
              docker run hello-world
            
  • Run following commands to download images we use during practicals.
    
              # Ubuntu image
              docker pull ubuntu:noble
    
              # NodeJS
              docker pull node:25.9.0-slim
    
              # Nginx
              docker pull nginx:1.29.8
            

Objectives

  • Docker Concepts
    Images / Containers / Volumes / Ports / Networks
  • Docker CLI
    run / exec / inspect / ...
  • Dockerfile
    Basic commands / Users / Volumes
    Build / Run / Tag / Publish
    Multistage build

Demonstration: Behind the scene

Containers are not magic, more like a magic trick. What really happens when we run following command?


      docker run hello-world
    

Behind the scene

  • Containers are created by running/starting Images. An image contains a definition of a container. This includes mostly file system layers and some metadata.
    We can employ pull to download an image.
  • Docker client (CLI, Docker Desktop, ...) and Docker daemon.
  • Image versions (architectures, tags), repository, GitHub and scratch image.
  • Export file system:
    
            # Create a container from an image, does not run the container!
            # We may need to add a random command at the end.
            docker create --name="{containername}" image:tag
    
            # Export file system from the container.
            docker export {container identifier} -o dump.tar
            
  • dive - a tool for exploring a docker image

Demonstration: Working with containers

Interactive container

  • Create a new container:
    
              docker run -it ubuntu:noble bash
            
  • Create a file inside the container.
  • Execute into the container from another terminal.
    
              docker exec -it {container} bash
            
  • List running containers.
    
              docker ps
            
  • Stop and recreate the container and check for the file. Explain what and why happened.
    
              docker kill {container}
            

Persistent storage

Sometimes we need persistent storage across containers. The solution is to save data outside of the container.

Compare Named Volumes / Bind Mounts

What about mapping Windows directory to Docker? It is possible via WSL. However not all functionality is available and there may be performance issues.


      # There may be issues ...
      npm run dev
    

Custom HTTP server

  • NginX
  • Create a directory with a simple HTML file.
    
              <!doctype html><title>Title</title><p>Content</p>
            
  • Start a container with a volume mapped to this directory.
    
              # PowerShell
              docker run -it --rm -d -p 8080:80 -v "${PWD}:/usr/share/nginx/html" nginx:1.29.8
    
              # Bash
              docker run -it --rm -d -p 8080:80 -v ${PWD}:/usr/share/nginx/html nginx:1.29.8
            
  • Visit http://localhost:8080/site.html.
  • Modify the site.html file and refresh the page.
  • Stop the container, this time we combine two commands together. The next command is PowerShell specific.
    
              docker kill $(docker ps -q)
            

We need with PHP

Start with nginx:1.29.8, or even ubuntu:noble, and create custom image which can serve PHP files.

Do not try this at home!

How To Install Linux, Nginx, MySQL, PHP (LEMP stack) in Ubuntu 16.04. Is it easier to install and configure PHP with Apache server?


        # Update package index.
        apt-get update
        # Install packages.
        apt install php-fpm
        # ...
      

Now we can pause the image and create a container.


        # Pause the image.
        docker pause {container}
        # Create a container.
        docker commit {container} server-php:latest
      

This probably takes a while ...

Demonstration: Dockerfile

Basic commands

Dockerfile provides a better way to create and share definition of a Docker image. The are a number of commands documentation, as well as best-practices.
We need only a few basic commands:

More commands ...

Exercise: Container for frontend

We start with your frontend application that can list books using the Library API. Your task is to add a "production-grade" Docker deployment.

Assignment: Container for frontend

GitLab: ./practical-06/


Following slides should guide you towards a reasonable Dockerfile.
Beware of potential pitfalls!
You are allowed to modify the application, but try to keep the changes minimal.

You need to be able build and run the Dockerfile using following commands in the "./practical-06/" directory.


      docker build -t nswi153/frontend .
      docker run -p 8080:80 nswi153/frontend
    

The application

Start by with your frontend from "JavaScript : User Interface" practical. Alternatively, you can download and unpack a sample web application.

Continue to the next slide once you are done >>>

Building the application

  • Start with node:25.9.0-slim. Do not use node or node:latest!
  • Select working directory, mind the Linux file system conventions.
  • Set BACKEND_BASE_URL to point to your API from "PHP : Doctrine" practical.
  • Copy files and build the project. Why is this bad?
    
              COPY . .
              RUN npm install
              RUN npm build
          
  • Do not ignore .dockerignore.
    
              .*
              node_modules
              dist
            
  • How do we host the application?

Continue to the next slide once you are done >>>

Multistage build

We do not need the build-related libraries and code. We only need the content of the this directory.


      FROM {image} as build

      FROM {another-image}
      COPY --from=build {source} {target}
    

Continue to the next slide once you are done >>>

Hosting the application

  • Create new stage using nginx:1.29.8.
  • Copy the distribution directory to /usr/share/nginx/html.
  • This does not expose the port automatically, it is just about the metadata.
    
              EXPOSE 80
            

Continue to the next slide once you are done >>>

API proxy

The backend API is not part of our image, we need to proxy to other container / locations / ...

The Nginx configuration (container specific) is stored in /etc/nginx/conf.d/default.conf. We need to change this file and add instruction for a proxy.


      server {
        listen       80;
        server_name  localhost;
        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }
        location /api/ {
          proxy_pass https://webik.ms.mff.cuni.cz/~skoda/nswi153/practical-03/api/;
        }
      }
    

But, we need the backend to be set using ENV variable not to be hardcoded. This is Nginx specific.

Continue to the next slide once you are done >>>

Configurable API Proxy

Files placed to /etc/nginx/templates/ are processed by 20-envsubst-on-templates.sh This script substitutes environment variables in the Docker image at startup (runtime).


      COPY ./nginx/default.conf.template /etc/nginx/templates/

      # We need to use env variable as we need it to be available at runtime.
      ENV BACKEND_BASE_URL=http://server/
    

We need to set the ENV variable when running the container!


      docker run -p 8080:80 -e BACKEND_BASE_URL="https://webik.ms.mff.cuni.cz/~skoda/nswi153/practical-03/" nswi153/frontend
    

Congratulations, if you have followed the instructions, you have just reached the end of this exercise.

Demonstration: There is more ...

Docker run command

List of selected Docker arguments:

  • -it - Run with interactive console.
  • --rm - Remove container when finished.
  • --volume - Map a directory or a file into the container.
  • --port - Map a host port into the container.

There are single letter alternatives and additional commands.

More Dockerfile commands ...

Some more commands you should know.

  • CMD ... - Sets the command to be executed when running a container from an image. The objective is to provide defaults for an executing container. There is shell form (CMD command params...) and execute mode (CMD ["command", ...]). It provides defaults for container execution.
  • ENTRYPOINT ... - There are same forms as for CMD. Command line arguments to "docker run" will be appended after all elements in an exec form ENTRYPOINT, and will override all elements specified using CMD. The shell form of ENTRYPOINT ignores any CMD or docker run command line arguments. We can change ENTRYPOINT from command line using "--entrypoint" argument Use for "executable" containers.
  • HEALTHCHECK ... - Defines healthcheck.
  • VOLUME ["/data"] - Array or string form. After the image is started, Docker create new mounting point and initialize it with the content of the directory.
  • ...
Questions, ideas, or any other feedback?

Please feel free to use the anonymous feedback form.