Demystifying Containers and Container Images

Dan Čermák

CC BY 4.0

Follow Along

dcermak.github.io/container-images

who -u

Dan Čermák

Open Source Advocate @SUSE
i3 SIG, Package maintainer
Developer Tools, Testing and Documentation, Home Automation
https://dancermak.name
dcermak
@Defolos@mastodon.social
@defolos.bsky.social

Agenda

Software Delivery: The Real Problem

  • Dev environment != Production environment
  • Deploy ⇒ 3h downtime
  • VM?
  • Tarball?

Part 1: Bundle Everything

rsync -avz --exclude=/app/ / /app/
tar -czf app.tar.gz /app

and deploy:

tar -xzf app.tar.gz -C /opt/
chroot /opt/app/ /usr/local/bin/.bin

Success?

Part 2: Process Isolation

  • Isolate access to system resources
  • Limit system resource usage

Linux Namespaces

provide kernel-level resource isolation

Available Namespaces:

  • user
  • mnt
  • pid
  • net
  • ipc
  • uts (unix time sharing)
  • cgroup
  • time

Part 3: Resource Limits - cgroups

Part 4: Syscall filtering

Part 5: Capabilities

A scratch workspace - UnionFS

Create one yourself!

mount -t overlay overlay \
      -o lowerdir=lower,\
         upperdir=upper,workdir=/work/ \
           merged

Create a Container

The Manual Approach Doesn't Scale

Have Missing
folder_supervised filesystem isolation build_circle standardized container build process
safety_divider process isolation share simple sharing & distribution
memory resource limits terminal convenient launch & setup tool
security security hardening

Introducing: Docker

  • Standardized build process → Dockerfile
  • Easy sharing/distribution → Docker Registry
  • Simple interface → docker CLI

Container Image Build

FROM registry.opensuse.org/opensuse/tumbleweed
RUN zypper -n in python3
COPY . /src/
RUN pip install .
RUN make test

and we need some CoW

Dockerfile

FROM registry.opensuse.org/opensuse/tumbleweed
COPY ./project/ /src/
ENV USER="geeko"
RUN zypper -n in openssh-clients; \
    ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N ""; \
    zypper -n rm --clean-deps openssh-clients; \
    zypper -n clean; rm -rf /var/log/lastlog;
VOLUME ["/src/data"]
WORKDIR /src/
EXPOSE 22
RUN useradd $USER
USER $USER
CMD ["echo hello"]
ENTRYPOINT ["/bin/bash", "-ce"]

Docker Registry

docker pull registry.opensuse.org/opensuse/leap
docker pull registry.opensuse.org/opensuse/leap:16.0
docker pull registry.opensuse.org/opensuse/leap:16.0@sha256:cd9aac11608afabc96a10074619cf2a65ccf60ff4b72d09d4e4e125af409035d

Entrypoint

Networking

Security

  • container potentially as privileged as the user running it
  • container breakout attacks exist
  • SELinux/Apparmor are your friends

Podman

Actually Docker

Podman

Rootless Containers

man 7 user_namespaces

The child process created by clone(2) with the CLONE_NEWUSER flag
starts out with a complete set of capabilities in the new user
namespace.
  • users remapped
  • rootless networking runs in userspace

Best Practices

RUN zypper -n in python3-pip; \
    pip install . ; \
    zypper -n rm --clean-deps gcc; zypper -n clean; \
    rm -rf {/target,}/var/log/{alternatives.log,lastlog,tallylog,zypper.log,zypp/history,YaST2}
$ podman run -e POSTGRES_PORT=1234 \
             -e POSTGRES_USER=pg \
                 my-app
$ podman run my-app bash
#

or:

$ podman run my-app
#

Volumes are your friend:

VOLUME ["/var/db/"]
# /var/db/ is now erased after each step!

use the exec-form:

ENTRYPOINT ["/usr/bin/my-app", "-param", "value"]

When to use Containers

  • Single-process applications
  • "Works on my machine" problems
  • Cloud/OS independent deployment
  • Reproducible environments

When NOT to Use Containers

  • High-performance I/O applications
  • Legacy multi-process applications
  • Desktop GUI applications

Container Orchestration

docker-compose

services:
  app:
    build: .
    ports:
      - "8080:8080"
    volumes:
      - .:/src
    depends_on:
      db:
        condition: service_healthy
  db:
    image: registry.opensuse.org/opensuse/mariadb
    environment:
      - MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=1
docker compose up

Quadlet / podman generate systemd

[Unit]
Description=TW container

[Container]
Image=registry.opensuse.org/opensuse/tumbleweed

# volume and network defined below in other configs
Volume=test.volume:/data
Network=test.network

Exec=sleep infinity

[Service]
Restart=always
TimeoutStartSec=900

[Install]
# Start by default on boot
WantedBy=multi-user.target default.target

Kubernetes

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-application
  labels:
    app: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: web-container
        image: nginx:latest
        ports:
        - containerPort: 80
        resources:
          limits:
            cpu: "0.5"
            memory: "512Mi"
          requests:
            cpu: "0.2"
            memory: "256Mi"
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5

Questions?

dcermak.github.io/container-images