# Comments start with '#'
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y --no-install-recommends wget \
&& rm -rf /var/lib/apt/lists/*
COPY data /myapp/dataoffice: vienna / linz
size: ~ 40 people
we do what we love..
custom software solution
cloud transformation
DevOps automation

|
Gepardec believes in learning by doing
The training is lab driven
Work together!
Ask questions at any time
1 days duration
Mostly exercises
Regular breaks
Fmailiarity with Bash or Powershell
Bash Cheat sheet http://bit.ly/2mTQr8l
You have been given an instance for use in the exercises
Ask the instructor for the credentials if you don’t have them already
By the end of this training, trainess will be able to
Asses the advantages of a containerized software development & deployment
Use container engine features necessary for running containerized applications
Ideal software should
be modular and flexible (DEVs)
be easy to migrate (DevOps)
be easy to scale, monitor and lifecycle (OPS)
mitigate vulnerabilities (Security)
and run cheap (business)
Containers can be removed and replaced with a minimum of impact on their neighbors, increasing developer choice and speed.
Containers have private system resources, so a compromise in one does not affect the rest.
Containers have private system resources, so a compromise in one does not affect the rest.
Containers save datacenter costs by running many more application instances than virtual machines can on the same physical hosts.
By the end of this module, trainees will be able to
Explain containers as host processes isolated with namespaces and control groups
Verify PID namespace and lifecycle behavior using Docker CLI commands
Use key Docker commands to inspect, run, stop, remove, and troubleshoot containers
Containers are processes sandboxed by
Kernel namespaces
Control groups
Root privilege and syscall restrictions (Linux)
DEFAULT
Process IDs
Network stacks
Inter-process communications
Mount points
Hostnames
OPTIONAL
User IDs
Control groups: limit memory & CPU
Root privilege management: allowlist Linux capabilities
System call management: allowlist available system calls (seccomp)
Linux Security Modules: mandatory filesystem access control
See the demo
Process isolation
in the exercise book
Work through
Running and inspecting a container
Interactive containers
Detached containers and logging
Starting, stopping, inspecting and deleting containers
In the exercise book.
STDOUT and STDERR for a container process
docker container logs <container_name>
docker container logs -f --tail 50 <container_name>
PID 1 receives stop signals sent by the runtime
Well-behaved applications should handle SIGTERM and exit cleanly
Forced termination (docker container kill) sends SIGKILL immediately
Single process constrained by kernel namespaces, control groups and other Linux technologies
Private & ephemeral filesystem and data
PID 1 lifecycle, logging, and signal handling directly affect operability
List of container commands: http://dockr.ly/2iLBV2I
Getting started with containers: http://dockr.ly/2gmxKWB
Start containers automatically: http://dockr.ly/2xB8sMl
Limit a container’s resources: http://dockr.ly/2wqN5Nn
Isolate containers with a user namespace: http://dockr.ly/2gmyKdf
Keep containers alive during daemon downtime: http://dockr.ly/2emLwb5
Intro to Windows Containers: https://dockr.ly/2CTYhYb
By the end of this module, trainees will be able to:
Explain image layers, copy-on-write, and the writable container layer
Build reproducible images with Dockerfiles / Containerfiles
Use cache-aware build patterns for faster iteration
Apply production-ready image practices for security and operability
Correctly tag, namespace, and publish images to registries
A container image is a packaged filesystem plus metadata
Built from a stack of immutable layers
Starts from a base image
Each build instruction can create a new layer
Three common methods:
Build from a Dockerfile / Containerfile (recommended)
Commit a running container (docker container commit) for quick experimentation only
Import a tarball as a base image (docker image import) for special cases
docker container commit stores current writable layer as a new image layer
Pro: quick for interactive exploration
Con: difficult to audit, reproduce, and automate
Guidance: avoid for production pipelines
Machine-readable build recipe
Documents image construction step-by-step
Enables CI/CD automation and repeatability
FROM defines the base image
RUN, COPY, ADD, ENV, and others define layers/metadata
# Comments start with '#'
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y --no-install-recommends wget \
&& rm -rf /var/lib/apt/lists/*
COPY data /myapp/dataSee the demo:
Creating images
in the exercise book.
Work through:
Interactive image creation
Creating images with Dockerfiles (1/2)
in the exercise book.
Cache reuse depends on:
The instruction itself
Build arguments and environment that affect the instruction
Files from the build context used by that instruction
The parent layer digest
Every container runs a PID 1 process
ENTRYPOINT defines the executable
CMD defines default arguments (or default command if no ENTRYPOINT)
Runtime command can override CMD
--entrypoint can override ENTRYPOINT
FROM alpine:3.22
RUN apk add --no-cache yq
ENTRYPOINT ["yq"]# Shell form (runs via /bin/sh -c)
CMD java -jar /app/app.jar
# Exec form (preferred for ENTRYPOINT/CMD in production)
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
CMD ["--server.port=8080"]Work through:
Creating images with Dockerfiles (2/2)
in the exercise book.
COPY is the default choice for local files.
COPY <src> <dest>Use ADD only when you explicitly need:
Auto-extracting local tar archives
Fetching from URL sources (if policy allows)
# when app.tar.gz contains index.js and config.json
# with
ADD app.tar.gz /app/
# result would be: /app/index.js and /app/config.json
# with
COPY app.tar.gz /app/
# result would be: /app/app.tar.gz.dockerignore is critical for fast and safe builds.
Benefits:
Reduces build context transfer size
Prevents accidental inclusion of secrets and local artifacts
Improves cache stability
Example:
.git
node_modules
*.log
.env
target
distBuild images that are:
Lightweight
Secure
Fast to build
Easy to operate and debug
Special empty base image
Not pulled from a registry
Useful for minimal final images
Common in multi-stage builds
FROM scratch
COPY hello /hello
ENTRYPOINT ["/hello"]Naive single-stage C build:
FROM alpine:3.20
RUN apk add --no-cache build-base
WORKDIR /app
COPY hello.c /app
RUN gcc -Wall hello.c -o /app/hello
CMD ["/app/hello"]Improved multi-stage build:
# Build stage
FROM alpine:3.20 AS build
RUN apk add --no-cache build-base
WORKDIR /src
COPY hello.c .
RUN gcc -Wall hello.c -o /out/hello
# Runtime stage
FROM alpine:3.20
COPY --from=build /out/hello /app/hello
ENTRYPOINT ["/app/hello"]Use named stages and optional build target selection.
FROM base-image AS base
# ...
FROM toolchain-image AS test
# ...
FROM runtime-image
COPY --from=test /out/app /app/app# Build final image (last stage)
docker image build -t myapp:1.0 .
# Build an intermediate stage by name
docker image build --target test -t myapp:test .Work through:
Multi-stage builds
in the exercise book.
Start from trusted, maintained base images
Prefer minimal runtime images
Run as non-root user (USER)
Avoid embedding secrets in image layers
Keep package lists/caches out of final image
Example:
FROM node:22-bookworm-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN useradd -r -u 10001 appuser && chown -R appuser:appuser /app
USER appuser
CMD ["node", "server.js"]HEALTHCHECK lets orchestrators detect unhealthy containers.
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD wget -qO- http://localhost:8080/health || exit 1Guidance:
Keep checks lightweight
Verify critical readiness path
Log to stdout/stderr for observability
Cache-aware development pattern:
FROM python:3.12-slim
WORKDIR /app
# Copying dependency manifests first avoids re-installing dependencies on every code change.
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]Rebuild from Dockerfile when dependencies or base image change
Avoid layering ad-hoc patch commands over outdated images
Prefer immutable references in CI (image@sha256:…) for reproducibility
Tags are mutable pointers (myapp:1.2.3, myapp:latest)
Digests are immutable content addresses (myapp@sha256:…)
Recommended: publish semantic version tags plus immutable digest references
# Tag examples
docker image build -t myorg/myapp:1.2.3 -t myorg/myapp:latest .
# Pull immutable image by digest
docker image pull myorg/myapp@sha256:abc123...FROM: base image
RUN: execute build-time commands
COPY: copy files into image
ADD: specialized copy behavior
ENTRYPOINT and CMD: default runtime behavior
USER: set runtime user
HEALTHCHECK: define container health probe
Images can be addressed as:
Root namespace (nginx, redis) on default registries
User/org namespace (myorg/myapp:1.2.3)
Fully qualified registry path (registry.example.com/myorg/myapp:1.2.3)
Modern teams often publish for multiple architectures.
# Example: build and push amd64 + arm64
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t registry.example.com/myorg/myapp:1.2.3 \
--push .Integrate image scanning before deployment.
Example tools:
Docker Scout
Trivy
Grype
Typical workflow:
Build image locally or in CI
Tag with namespace and version
Authenticate to registry
Push image
Deploy by version tag and/or digest
docker image tag myapp:1.2.3 registry.example.com/team/myapp:1.2.3
docker image push registry.example.com/team/myapp:1.2.3Work through:
Managing images
in the exercise book.
Use a maintained base image (pin digest when needed)
Keep build context small with .dockerignore
Use multi-stage builds to remove compilers/SDKs from runtime
Run as non-root with USER
Keep secrets out of layers (BuildKit secrets)
Add HEALTHCHECK when meaningful
Scan images in CI/CD
Tag consistently (version, git-sha, optional latest)
Container images are built from read-only layers plus metadata.
Dockerfiles provide reproducible, auditable image builds.
Layering and cache strategy strongly affect developer feedback cycle.
Security and operability practices are required for production images.
Tags are mutable references; digests are immutable content addresses.
Dockerfile reference: https://docs.docker.com/reference/dockerfile/
Build cache and optimization: https://docs.docker.com/build/cache/
Multi-stage builds: https://docs.docker.com/build/building/multi-stage/
Build secrets: https://docs.docker.com/build/building/secrets/
Best practices: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
Image scanning overview: https://docs.docker.com/scout/
By the end of this module, trainees will be able to
Define a volume and identify its primary use cases
Describe advantages and potential security risks of mounting volumes and host directories
Volumes provide a R/W path separate from the layered filesystem.
Mount data at container startup
Persist data when a container is deleted
Share data between containers
Speed up I/O by circumventing the union filesystem
Named: managed by Docker; filesystem independent; user-specified identifier
Anonymous: managed by Docker; filesystem independent; randomly-generated identifier
Host mounted: mount a specific path on the host; DIY management
See the demo
Basic Volume Usage
in the exercise book.
VOLUME instruction creates a mount point
Can specify arguments in a JSON array or string
Cannot map volumes to host directories
Volumes are initialized when the container is executed
FROM nginx:latest
...
# string example
VOLUME /myvolume
# string example with multiple volumes
VOLUME /www/website1 /www/website2
# JSON example
VOLUME ["myvol1", "myvol2"]
...Point of ingress to the host and other containers
Don’t mount things unnecessarily
Use the :ro flag whenever possible
Linux: in-memory tmpfs mounts available
Work through
Database Volumes
in the exercise book.
Volumes persist data beyond the container lifecycle
Volumes bypass the copy-on-write system (better for write-heavy containers)
How to use volumes: http://dockr.ly/2vRZBDG
Troubleshoot volume errors: http://dockr.ly/2vyjvbP
Docker volume reference: http://dockr.ly/2ewrlew
By the end of this module, trainees will be able to
Execute cleanup commands
Locate Docker system information
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 39 2 9.01 GB 7.269 GB (80%)
Containers 2 2 69.36 MB 0 Bdocker system prune
more limited…
docker image prune [--filter "foo=bar"]
docker container prune [--filter "foo=bar"]
docker volume prune [--filter "foo=bar"]
docker network prune [--filter "foo=bar"]
$ docker system info
Containers: 2
Running: 2
Paused: 0
Stopped: 0
Images: 105
Server Version: 17.03.0-ee
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Swarm: active
NodeID: ybmqksh6fm627armruq0e8id1
Is Manager: true
ClusterID: 2rbf1dv6t5ntro2fxbry6ikr3
Managers: 1
Nodes: 1
Orchestration:
Task History Retention Limit: 5
Raft:
Snapshot Interval: 10000
Number of Old Snapshots to Retain: 0
Heartbeat Tick: 1$ docker system events
2017-01-25T16:57:48.553596179-06:00 container create 30eb630790d44052f26c1081...
2017-01-25T16:57:48.556718161-06:00 container attach 30eb630790d44052f26c1081...
2017-01-25T16:57:48.698190608-06:00 network connect de1b2b40f522e69318847ada3...
2017-01-25T16:57:49.062631155-06:00 container start 30eb630790d44052f26c1081d...
2017-01-25T16:57:49.164526268-06:00 container die 30eb630790d44052f26c1081dbf...
2017-01-25T16:57:49.613422740-06:00 network disconnect de1b2b40f522e69318847a...
2017-01-25T16:57:49.815845051-06:00 container destroy 30eb630790d44052f26c108...Generate events with docker container run --rm alpine echo 'Hello world'
Work through
Cleaning up Docker resources
Inspecting commands
in the exercise book.
What is the origin of dangling container image layers?
What are potential pitfalls automating system cleanup, and how can we avoid them?
Questions?
System commands reference: http://dockr.ly/2eMR53i
Containers are isolated processes
Container images provide filesystem for containers
Volumes persist data
Goal: Build a docker image that runs a Java application
Find the fat jar hello-world-<version>-runner.jar in the zip you downloaded
Run the application with java –jar hello-world-<version>-runner.jar.
Try it out via http://localhost:8080/
Considerations:
What container image is suitable?
Do you need CMD, or ENTRYPOINT, or maybe both?
If you run two containers, what do need to take care of?
Sample Containerfile
FROM alpine/java
WORKDIR /data
EXPOSE 8080
COPY build/libs/hello-world-runner.jar hello-world-runner.jar
CMD ["-jar", "hello-world-runner.jar"]
ENTRYPOINT ["java"]Solution commands
docker build -t hello_world .
docker run -d -p 8080:8080 hello_world
By the end of this module, trainees will be able to
Describe Docker’s container network model and its security implications
Describe the basic technologies that underwrite single host networks
Understand how Docker manipulates a host’s firewall rules to control container traffic
Containers have no public IP address by default.
Can forward host port → container port
Mapping created manually or automatically.
Port mappings visible via
docker container ls or
docker container port
See the demo
Single host networks
in the exercise book.
Work through
Introduction to Container Networking
Container Port Mapping
in the exercise book.
Single host networks follow the container networking model:
Sandbox: Network namespaces
Endpoint: veth (Linux)
Network: bridge (Linux)
Containers resolve each other by DNS lookup when named and attached to custom networks
Docker software defined networks are firewalled from each other by default
Docker Reference: Designing Scalable, Portable Container Networks: https://dockr.ly/2q3O8jq
Network containers: http://dockr.ly/2x1BYgW
Docker container networking: http://dockr.ly/1QnT6y8
Understand container communication: http://dockr.ly/2iSrHO0
By the end of this module, trainees will be able to
Design scalable Docker services
Leverage Docker’s built-in service discovery mechanism
Write a compose file describing an application
Applications consisting of one or more containers across one or more nodes
Docker Compose facilitates multi-container design on a single node
Goal: declare and (re)configure many similar containers all at once
Goal: scale apps by adding containers seamlessly
A service defines the desired state of a group of identically configured containers
Docker provides transparent service discovery for services
Services are assigned a Virtual IP which spreads traffic out across the underlying container automatically.
See the demo
Docker Compose
in the exercise book.
Work through
Starting a Compose App
Scaling a Compose App
in the exercise book.
Docker Compose makes single node orchestration easy
Compose services make scaling applications easy
Bottleneck identification important
Syntactically: compose.yaml (or docker-compose.yml) + API
Docker Compose examples: http://dockr.ly/1FL2VQ6
Overview of Docker Compose CLI (docker compose): http://dockr.ly/2wtQlZT
Docker Compose file reference: http://dockr.ly/2iHUpeX
Docker Compose and Windows: http://bit.ly/2watrqk

Set up a SonarQube server that listens on port 9000
Connect it to a persistent database
Use PostgreSQL and persist its data on the host filesystem using volumes
Verify that the data is persistent (create user and delete the container)
Hint: use Docker Compose
version: "3"
services:
sonarqube:
image: sonarqube:lts-community
depends_on:
- sonar_db
environment:
SONAR_JDBC_URL: jdbc:postgresql://sonar_db:5432/sonar
SONAR_JDBC_USERNAME: sonar
SONAR_JDBC_PASSWORD: sonar
ports:
- "9001:9000"
volumes:
- sonarqube_conf:/opt/sonarqube/conf
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs
- sonarqube_temp:/opt/sonarqube/temp
sonar_db:
image: postgres:13
environment:
POSTGRES_USER: sonar
POSTGRES_PASSWORD: sonar
POSTGRES_DB: sonar
volumes:
- sonar_db:/var/lib/postgresql
- sonar_db_data:/var/lib/postgresql/data
volumes:
sonarqube_conf:
sonarqube_data:
sonarqube_extensions:
sonarqube_logs:
sonarqube_temp:
sonar_db:
sonar_db_data:By the end of this module, trainees will be able to
Explain what Kubernetes is and when to use it
Distinguish between Pods, Deployments, Services, and Secrets
Read and apply a basic Kubernetes manifest
Deploy and verify a simple application on Kubernetes
Docker Compose is excellent for multi-container apps on one host
Kubernetes is for orchestrating containers across a cluster of nodes
It adds scheduling, self-healing, service discovery, and rolling updates
A cluster consists of control-plane and worker nodes
The API server is the central interface
Desired state is defined in YAML manifests
Controllers continuously reconcile actual state to desired state
Smallest deployable unit in Kubernetes
Usually one main container per Pod
All containers in a Pod share network namespace and volumes
Manages a ReplicaSet of Pods
Defines image, replicas, and rollout strategy
Supports rolling updates and easy rollbacks
Recreates Pods automatically if they fail
Stable virtual endpoint for a dynamic Pod set
Selects Pods by labels
Common types:
ClusterIP for internal traffic
NodePort for quick external access
LoadBalancer for cloud-managed ingress
Stores sensitive values (passwords, tokens, keys)
Inject into Pods via environment variables or mounted files
Should not be hard-coded in images or plain manifests
See the demo
Kubernetes basics
in the instructor demo book.
Please take our feedback survey
Get in touch: office@gepardec.com
#WECKDENGEPARDENINDIR