Sem 7

Docker Basics and Dockerfiles

Lab decks 1 and 2 plus the Docker cheat sheet: installing Docker, image and container commands, the container lifecycle, Dockerfile instructions, port mapping, and ENTRYPOINT versus CMD.

Why Docker in a blockchain courseSearch on YouTube

The syllabus lists Docker under "Base technologies" in the Introduction unit. Blockchain nodes, wallets and dev tools are usually shipped as containers. The lab uses Docker to run tools without installing them on the host.

Docker packages and runs an application in a loosely isolated environment called a container. The isolation lets you run many containers at once on one host. A container holds everything the app needs, so it does not depend on what the host has installed. Everyone who runs the same container gets the same behaviour.

TermMeaning
ImageA lightweight, standalone, executable package: code, runtime, system tools, libraries and settings. Read-only template.
ContainerA runtime instance of an image. It runs the same on any infrastructure.
RegistryA store of images. Docker Hub (hub.docker.com) is the default.
DockerfileA text file of instructions that builds an image.

An image is a class; a container is an object made from it. You can create many containers from one image.

Installing DockerSearch on YouTube

Install Docker Desktop from the Docker docs ("Install Docker Desktop on Mac" or "on Windows"). On Windows it runs on WSL 2. Check both after install:

docker --version     # e.g. Docker version 29.6.2, build dfc4efb
wsl --version        # Windows only: WSL version, kernel version, Windows version

Image commandsSearch on YouTube

docker pullSearch on YouTube

Syntax: docker pull [OPTIONS] NAME[:TAG|@DIGEST]. It downloads an image from a registry (usually Docker Hub) to the local machine.

docker pull alpine
docker pull ubuntu:24.04
docker pull nginx:latest
  • It downloads an image, not a container.
  • If the image already exists locally, only missing or updated layers are downloaded.
  • The default tag is latest if no tag is given (output: "Using default tag: latest").
  • Images are stored in Docker's local image cache.
  • The output shows the layer IDs, a Digest: sha256:... and the full name docker.io/library/alpine:latest.

Best practice: pin a version tag (for example ubuntu:24.04) instead of relying on latest. Pull only from trusted registries.

docker imagesSearch on YouTube

Syntax: docker images [OPTIONS] [REPOSITORY[:TAG]]. Both forms list local images:

docker images
docker image ls

Columns: IMAGE, ID, DISK USAGE, CONTENT SIZE, EXTRA. For example alpine:latest is about 8.42 MB.

Container lifecycleSearch on YouTube

Listing containersSearch on YouTube

docker ps        # running containers only
docker ps -a     # all containers (running + stopped); same as --all
docker ps -q     # container IDs only
docker ps -n 5   # last 5 created containers

Columns: CONTAINER ID, IMAGE, COMMAND, CREATED, STATUS, PORTS, NAMES.

Creating a containerSearch on YouTube

docker create makes a container from an image but does not start it. Its STATUS is Created.

docker create --name cont1 alpine
docker create --name myalpine alpine
docker create --name web nginx
docker create --name test ubuntu sleep infinity

Starting a containerSearch on YouTube

docker start cont1
docker ps -a     # STATUS: Exited (0) 10 seconds ago

cont1 starts and exits at once. Its default command is /bin/sh, which has no terminal attached, so it ends with exit code 0. A later docker exec cont1 pwd fails with "container ... is not running".

A container lives only as long as its main process (PID 1) is alive. When PID 1 exits, the container stops.

Keeping a container aliveSearch on YouTube

Give it a main process that never ends:

docker run --name cont3 -d alpine sleep infinity
docker ps -a     # cont3: COMMAND "sleep infinity", STATUS Up 14 seconds

docker run = create + start in one step. -d runs it detached (in the background).

Running commands inside: docker execSearch on YouTube

docker exec cont3 pwd          # prints /
docker exec cont3 ls           # bin dev etc home lib media mnt opt proc root ...
docker exec -t cont3 ls        # -t allocates a TTY, so output is in columns and coloured
docker exec -it cont3 /bin/sh  # interactive shell inside the container
FlagMeaning
-iInteractive: keep STDIN open
-tAllocate a pseudo-terminal (TTY)
-itBoth: you get a usable shell prompt (/ #)

Inside the Alpine shell, /bin holds BusyBox applets (ls, ping, sh, wget and so on).

Stopping and removingSearch on YouTube

docker stop cont3      # graceful stop; status becomes Exited
docker rm cont3        # fails if running: "stop the container before removing or force remove"
docker rm -f cont3     # force remove a running container
docker rm e67c         # a unique prefix of the container ID works
docker rm cont1

Removing imagesSearch on YouTube

docker rmi alpine ubuntu   # untags and deletes each layer (sha256 lines)
docker images              # list is now empty
docker image prune         # remove all unused images

Pull, Create, Start, Exec, Stop, Remove: "Please Check Some Extra Stuff Regularly". run = create + start.

Dockerfile basicsSearch on YouTube

A Dockerfile lists the steps that build an image. Each instruction makes a layer.

FROM ubuntu
RUN apt-get update && apt-get install -y
CMD ["echo", "Welcome to Ubuntu Docker container!"]
docker build -t mycont1 .

-t tags (names) the image. The final . is the build context: the folder whose files are sent to the builder. By default Docker looks for a file named Dockerfile in that folder.

InstructionPurpose
FROMBase image to start from
RUNRun a command at build time; result is saved as a layer
CMDDefault command (or default arguments) when the container starts
COPYCopy files from the build context into the image
WORKDIRSet the working directory for later instructions
ENVSet environment variables
LABELAdd metadata
EXPOSEDocument the port the app listens on
ENTRYPOINTFixed executable the container runs

Custom Dockerfile namesSearch on YouTube

Use -f to build from a file with another name, such as customalpine.txt:

FROM alpine
RUN apk update
RUN apk add --no-cache bash
CMD ["echo", "welcome to Custom Alpine"]
docker build -f customalpine.txt -t mycont2 .

apk is Alpine's package manager. --no-cache skips storing the package index, which keeps the image small.

docker historySearch on YouTube

docker history <image> shows the layers of an image, newest first, with the instruction that created each one and its size.

docker history ubuntu
docker history mycont1
docker history alpine
docker history mycont2

For mycont2, the top three layers are the ones the custom file added (CMD 0 B, apk add 2.15 MB, apk update 3.05 MB). Below them sit the base Alpine layers (ADD alpine-minirootfs ... 8.42MB). Metadata instructions like CMD add 0 B layers.

Containerising a simple appSearch on YouTube

The lab builds a single-page HTML site served by nginx.

# Dockerfile2
FROM nginx:alpine

WORKDIR /usr/share/nginx/html

COPY index.html .

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
docker build -f Dockerfile2 -t mysite .
docker run --name mysite mysite

daemon off; keeps nginx in the foreground, so nginx stays PID 1 and the container keeps running.

Port mappingSearch on YouTube

The container runs and docker ps shows PORTS 80/tcp. The host browser still shows "localhost refused to connect".

EXPOSE is for information only. It does not map a host port to the container port. You must map ports yourself with -p.

docker run -d -p 8080:80 --name mysite1 mysite

-p <host_port>:<container_port>. Now docker ps shows 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp, and http://localhost:8080 loads the site.

More Dockerfile instructionsSearch on YouTube

WORKDIRSearch on YouTube

Sets the working directory inside the image for the following instructions. It creates the folder if missing.

FROM ubuntu:24.04
WORKDIR /app
COPY . .
RUN ls

COPY . . copies the build context into /app.

ENVSearch on YouTube

Sets environment variables inside the image and container. Syntax ENV <KEY>=<VALUE>.

FROM ubuntu:24.04
ENV APP_ENV=production
ENV PORT=8080
WORKDIR /app

ENV suits configuration, not secrets. Database passwords, API keys and tokens stay visible in the image (for example in docker history and docker inspect).

LABELSearch on YouTube

Adds metadata to an image to identify, organise and manage it. Syntax LABEL key="value".

LABEL app="payment-service" \
      version="2.1.0" \
      team="backend" \
      environment="production"

ENTRYPOINTSearch on YouTube

Defines the main executable the container is designed to run. Syntax ENTRYPOINT ["executable", "argument"].

FROM ubuntu:24.04
RUN apt-get update && apt-get install -y nmap
ENTRYPOINT ["nmap"]

Arguments given to docker run are appended to the entrypoint:

You typeContainer runs
docker run nmap-container 192.168.1.10nmap 192.168.1.10
docker run nmap-container -sV 192.168.1.10nmap -sV 192.168.1.10

ENTRYPOINT fixes the executable. The user supplies the arguments at run time.

ENTRYPOINT vs CMDSearch on YouTube

Use them together: ENTRYPOINT is the fixed program, CMD gives default arguments that the user can override.

FROM python:3.12
WORKDIR /app
COPY . .
ENTRYPOINT ["python"]
CMD ["app.py"]
You typeContainer runs
docker run myapppython app.py (default)
docker run myapp test.pypython test.py (CMD overridden, for debugging)
ENTRYPOINTCMD
RoleFixed executableDefault command or default args
Run-time argsAppended to itReplace it
OverrideOnly with --entrypointAny args after the image name

Exec form vs shell formSearch on YouTube

ENTRYPOINT python app.py         # shell form
CMD python app.py                # shell form
ENTRYPOINT ["python", "app.py"]  # exec form (JSON array)

Shell form makes Docker run /bin/sh -c "python app.py". The process tree is:

PID 1
└── /bin/sh -c python app.py
    └── python app.py

Your Python app is not PID 1. The shell is. Signals such as SIGTERM from docker stop go to the shell, not to your app. Exec form runs the app directly as PID 1:

PID 1
└── python app.py

Prefer the exec (JSON array) form for ENTRYPOINT and CMD. The app becomes PID 1 and receives stop signals.

Docker CLI cheat sheetSearch on YouTube

TaskCommand
Start the daemondocker -d
Help (also --help on any subcommand)docker --help
System-wide infodocker info
Build imagedocker build -t <image_name> .
Build without cachedocker build -t <image_name> . --no-cache
List imagesdocker images
Delete imagedocker rmi <image_name>
Remove unused imagesdocker image prune
Log in to Docker Hubdocker login -u <username>
Push imagedocker push <username>/<image_name>
Search Hubdocker search <image_name>
Pull imagedocker pull <image_name>
Run with a namedocker run --name <container_name> <image_name>
Run and publish portdocker run -p <host_port>:<container_port> <image_name>
Run in backgrounddocker run -d <image_name>
Start or stopdocker start or docker stop <container_name> (or ID)
Remove stopped containerdocker rm <container_name>
Shell in running containerdocker exec -it <container_name> sh
Follow logsdocker logs -f <container_name>
Inspect containerdocker inspect <container_name> (or ID)
Running containersdocker ps
All containersdocker ps --all
Resource usagedocker container stats

Useful links from the sheet: docs.docker.com/desktop (install), github.com/docker/awesome-compose (example projects), docs.docker.com (docs).

What to do and check in the lab:

  1. Run docker --version (and wsl --version on Windows).
  2. Pull alpine, list it with docker images.
  3. docker create --name cont1 alpine, start it, and see it exit with Exited (0). Explain why with PID 1.
  4. docker run --name cont3 -d alpine sleep infinity; confirm Up in docker ps -a; run docker exec -it cont3 /bin/sh.
  5. Stop, remove (try rm on a running container, then rm -f), and rmi the images.
  6. Write a Dockerfile, build it with -t, then a second one with -f customalpine.txt. Compare layers with docker history.
  7. Build the nginx site image. Show that EXPOSE alone gives "refused to connect" and -p 8080:80 fixes it.

Quick self-testSearch on YouTube

What is the difference between a Docker image and a container?

An image is a read-only package with code, runtime, libraries and settings. A container is a running instance of an image. Many containers can be made from one image.

Which tag does docker pull nginx use, and why should you avoid relying on it?

latest. It can change over time, so builds are not reproducible. Pin a version such as ubuntu:24.04.

What is the difference between docker create, docker start and docker run?

create makes a container but does not start it (status Created). start starts an existing container. run does both in one step.

Viva: you start an Alpine container and it immediately shows Exited (0). Why? How do you keep it running?

A container lives only as long as its PID 1. Alpine's default /bin/sh has no terminal and exits at once. Give it a long-running main process, such as docker run -d alpine sleep infinity.

What do the -i and -t flags of docker exec do?

-i keeps STDIN open (interactive). -t allocates a pseudo-terminal. Together (-it) they give a usable shell, as in docker exec -it cont3 /bin/sh.

Viva: your nginx Dockerfile has EXPOSE 80 but the browser cannot reach localhost. Why?

EXPOSE only documents the port. It does not publish it. Run with -p 8080:80 to map host port 8080 to container port 80.

How do you build an image from a file not named Dockerfile?

Use -f: docker build -f customalpine.txt -t mycont2 .. The trailing . is the build context.

What does docker history show?

The layers of an image, newest first, with the instruction that created each layer and its size. Metadata instructions such as CMD show 0 B.

Viva: difference between ENTRYPOINT and CMD?

ENTRYPOINT sets the fixed executable; run-time arguments are appended to it. CMD sets default arguments or a default command; run-time arguments replace it. With ENTRYPOINT ["python"] and CMD ["app.py"], docker run myapp test.py runs python test.py.

Why prefer ENTRYPOINT ["python", "app.py"] over ENTRYPOINT python app.py?

Shell form runs /bin/sh -c "python app.py", so the shell is PID 1 and the app is a child that may not get stop signals. Exec form makes the app PID 1.

Why should secrets not go in ENV?

ENV values are stored in the image metadata. Anyone with the image can read them, for example with docker inspect or docker history.

  • An image is a read-only package; a container is its running instance; Docker Hub is the default registry.
  • Lifecycle: pull, create, start, exec, stop, rm, rmi; run = create + start, -d = background.
  • A container lives only while its PID 1 runs; sleep infinity keeps a test container alive.
  • Dockerfile: FROM, RUN, COPY, WORKDIR, ENV, LABEL, EXPOSE, ENTRYPOINT, CMD; build with docker build -t name ., use -f for other file names.
  • docker history shows the layers each instruction made.
  • EXPOSE documents a port; -p host:container publishes it.
  • ENTRYPOINT is the fixed executable; CMD gives overridable defaults. Use exec form so the app is PID 1.
  • Do not put secrets in ENV; pin image tags; pull only from trusted registries.