Skip to content
Marcel Schmalzl edited this page Aug 29, 2024 · 1 revision

Docker

  1. Choose base image
  2. docker init
  3. Edit created files by previous step
  4. docker build
  5. docker run

Useful commands

Init docker files and templates

docker init

Generates templates to use docker for several use-cases in an interactive way.

Pull docker image

Before using a docker image it has to be pulled from the docker hub. E.g.:

docker image pull mcr.microsoft.com/powershell
# or with specified tag:
docker image pull mcr.microsoft.com/powershell:latest

Inspect docker image

$ docker images    
REPOSITORY                     TAG       IMAGE ID       CREATED       SIZE
mcr.microsoft.com/powershell   latest    3914ce801a11   6 weeks ago   296MB
$ docker image inspect 3914ce801a11
# displays a long JSON with all the information about the image

Shows something like:

  • Architecture
  • OS type/flavour
  • Size
  • Tags
  • Users
  • ...

Run interactive shell in docker container

Basic use

docker run -it mcr.microsoft.com/powershell

# or (with own image):
# Runs the image and opens a bash; useful to manually execute a failing step (comment this out in your Dockerfile beforehand):
docker run -it myImage /bin/bash

Run as admin

docker run -it --user root myImage /bin/bash
# or for Windows Server Core:
docker exec --user ContainerAdministrator -it myImage cmd
# or for WindowsNanoServer
docker exec --user ContainerUser -it myImage cmd

Build docker image

docker build --tag someTag .

# or to show the full output (will create a lot of output in your terminal):
docker build --progress=plain .
  • Use --no-cache to start a clean build

Dockerfile

Dockerfile reference guide: https://docs.docker.com/engine/reference/builder/

Basic example

# Base image (use fqdn or create an alias to shorten fqdn)
# Only use one frome except when you want to use multiple build stages
# 
# If reproducability is important, consider using a versioned tag (e.g., alpine:3.17.2, ...)
FROM mcr.microsoft.com/powershell:latest as base

# Create a non-privileged user that the app will run under
# See https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#user
ARG UID=10001
RUN adduser \
    --disabled-password \
    --gecos "" \
    --home "/nonexistent" \
    --shell "/sbin/nologin" \
    --no-create-home \
    --uid "${UID}" \
    appuser

# Add env variable to disable interactive dialogues (for dpkg tools like apt)
# OPTIONAL: Use, if you get interactive prompts during `apt-get install` etc.
ARG DEBIAN_FRONTEND=noninteractive

# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a cache mount to /root/.cache/pip to speed up subsequent builds.
# Leverage a bind mount to requirements.txt to avoid having to copy them into into this layer.
# Append `:ro` to the file path to mount as read-only
# 
# For MacOS the mode must be inserted to set the permissions properly: https://github.com/moby/buildkit/issues/1463#issuecomment-620892736
RUN --mount=type=cache,target=/root/.cache/pip \
    --mount=type=bind,source=requirements.txt,target=requirements.txt \
    apt-get update && apt-get install -y \
    python3 \
    python3-pip \
    calibre \
    ghostscript && \
    python3 -m pip install -r requirements.txt

# Switch to the non-privileged user to run the application (all subsequent commands cannot be executed as root)
USER appuser

# Copy data to image (if a directory does not exist it will be created)
COPY my_script.py /app/
# COPY and set file owner
COPY --chown=appuser utils/helper_script.ps1 /app/utils/

# Set working directory; will create the directory if not existing
WORKDIR app/Books
# What the container should run when it is started.
ENTRYPOINT [ "python3", "my_script.py", "someArg" ]

Some comments

ARG vs. ENV

  • ARG only visible during build time of the image
  • ENV visible during build of the image and run of the application (within the container)

Single vs. multiple RUN

https://stackoverflow.com/questions/39223249/multiple-run-vs-single-chained-run-in-dockerfile-which-is-better

Docker best practices

Layering

Once a layer changes, all downstream layers have to be recreated as well. Make sure you choose your layers wisely to reduce build-time.

Check your layers and commands producing it

docker image history --no-trunc myImage

Multi-stage builds

  • Separate build-time dependencies
  • Reduce overall image size (by mainly excluding intermedidate steps)

Each FROM in the Dockerfile introduces a new stage. Only the final stage constitues the resulting image.

Copy files from stages

Use image name in COPY command:

FROM ubuntu:latest AS buildStage1
# do something
RUN yarn run build

FROM nginx:alpine
COPY --from=buildStage1 /app/build /opt/myapp

Misc

Docker ignore

vim .dockerignore

Like .gitignore; omit files that you want to exclude from your docker image (e.g. temporary folders should be omitted but you still want to add a COPY . . or so to your Dockerfile).

Gotchas

Mac

qemu: uncaught target signal 6 (Aborted) - core dumped

When you try running something in a container (in this case it was a PowerShell/pwsh script):

An error has occurred that was not properly handled. Additional information is shown below. The PowerShell process will exit.
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
   at lambda_method7(Closure)
qemu: uncaught target signal 6 (Aborted) - core dumped
Aborted

This can happen if you run a container on a Mac with the Apple silicon (M1).

Install softwareupdate --install-rosetta and rebuild the container.

Docker commands

  • View processes (-a = all): sudo docker ps -a
  • Run docker container deamonised (docker container would run in the background completely detached from your current shell): sudo docker run -d <image_name>
  • Run image with a different ENTRYPOINT command in interactive mode with /bin/bash: sudo docker run --entrypoint /bin/bash -i -t <image_name>
    • When there is no ENTRYPOINT defined in the Dockerfile it will only accept arguments in interactive mode; if this is the case and you want to start with an interactive shell you must pass the --entrypoint argument
  • Run image in interactive mode with /bin/bash mounting the host directory /opt/123/ to the container directory /opt/456/: sudo docker run -i -t -v /opt/123/:/opt/456/ <image_name> /bin/bash
    • Append :ro to the file path to mount as read-only
  • Set environment variables: ...-e FOO=foo -e BAR=bar...
Clone this wiki locally