Advanced
Web Applications


Docker

Trust me, I'm ...


# Download the Ubuntu image ~ 30MB.
docker pull ubuntu:noble

# NodeJS
docker pull node:21.7.2-slim

# Nginx
docker pull nginx:1.25.4
  

Objectives

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

Prerequisites

Behind the scene


docker run hello-world
  
  • 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

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. Check for the file.
    
    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.


# :(
npm run dev
  

Image with PHP

Start with ubuntu:noble and create custom image named server-php:latest with PHP and Apache.

Do not try this!

How to install and configure PHP.


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

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 ...

Custom HTTP server

  • Apache HTTP Serve
  • 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/local/apache2/htdocs/" httpd:2.4.57
    # Bash
    docker run -it --rm -d -p 8080:80 -v ${PWD}:/usr/local/apache2/htdocs/ httpd:2.4.57
        
  • 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. Is shell specific (PowerShell).
    
    docker kill $(docker ps -q)
          

Dockerfile

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:

Dockerfile

More commands ...

Assignment: Vite

./practical-03/vite/Dockerfile

Create a Dockerfile for a container hosting the web application from practical-03. Follow the steps to get to the reasonable Dockerfile.
Use nginx:1.25.4 container to host the application. Beware of potential pitfalls!

You may add additional files to practical-03.

You can build and run the Dockerfile using following commands.


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

Building the application

  • Start with node:21.7.2-slim.
    Do not use node or node:latest!
  • Select working directory, mind the Linux file system conventions.
  • Set VITE_INSTANCE_NAME to "Docker" as a default value.
  • 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?

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}
    

Hosting the application

  • Create new stage using nginx:1.25.4.
  • Copy the distribution directory to /usr/share/nginx/html.
  • Add metadata. This does not expose the port automatically!
    
    EXPOSE 80
            

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/2023-24/api/;
  }
}
    

We want the backend to be set using ENV variable! This is Nginx specific.

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 as 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 SERVER_URL=http://server/api/
  

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


docker run -p 8080:80 -e SERVER_URL="https://webik.ms.mff.cuni.cz/~skoda/2023-24/api/" nswi153/frontend
  

Warning: This URL does not provide full implementation of the API! Use your own implementation instead.

Extra content ...

Content beyond this slide is not mandatory part of the seminar. Yet it may help you with better use and understanding of Docker.

Docker run command

List of selected arguments:

  • -it - with console
  • --rm - remove container when finished
  • --volume - map a volume / file system
  • --port - map a port

There are single letter alternatives!

Additional Docker commands

  • ENTRYPOINT <command> - Array (exec ~ no substitution!) and string (shell) form. Use for "executable" containers. Rest CMD from the base image.
  • CMD <command> - Array (exec or default parameters to ENTRYPOINT) and string (shell) form. Provide defaults for container execution.
  • 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.