0:00–0:30
Lecture
0:30–1:50
Lab 6A
1:50–2:00
Debrief
0:00 – 0:30Lecture · 30 min
Why containers exist, how they work, and how they differ from VMs
Part 1 — The problem containers solve (7 min)
- Open with a scenario: "You've built a Python web application on your laptop. It works perfectly. You deploy it to a server and it crashes because the server has Python 3.9 and your app needs Python 3.12. The libraries are different versions. The paths are different." This is the classic "works on my machine" problem. Containers solve it by packaging the application with all its dependencies — the right Python version, the right libraries, the right config — into a single unit that runs identically anywhere Docker is installed.
- The alternative — VMs — also solve this, but at a cost. A VM includes an entire OS, a kernel, drivers, and all system services, just to run one application. A VM image might be 20GB; a container image for the same application might be 200MB. VMs take minutes to boot; containers start in seconds. A server running VMs might handle 10–20 VMs; the same server running containers might handle hundreds of containers.
Part 2 — How containers work (12 min)
- Containers use two Linux kernel features to create isolation without virtualising hardware:
Namespaces — each container gets its own isolated view of system resources. Key namespaces: pid (the container's process tree starts at PID 1, independent from the host), net (isolated network stack, interfaces, routing), mnt (isolated filesystem view), uts (isolated hostname), ipc (isolated inter-process communication). The container thinks it's alone on the system; the host sees it as a group of processes.
cgroups (control groups) — limit the resources a container can use: CPU time, memory, disk I/O, network bandwidth. Without cgroups, a misbehaving container could consume all available memory and starve other containers or the host.
- Key comparison — draw on the board:
VM: Hardware → Hypervisor → [Guest OS + App] × N. Each VM has a full OS.
Container: Hardware → Host OS/Kernel → Docker → [Container (App + Libs)] × N. All containers share the host kernel.
This is why a Docker container for Nginx is 50MB instead of 5GB — it's just Nginx and its libraries, not a whole OS.
- Images vs containers: An image is a read-only, layered filesystem snapshot — the template. Think of it like a class in programming. A container is a running instance of an image with a writable layer on top — the object. You can run many containers from one image. Stopping a container doesn't delete it. Deleting a container doesn't delete the image. The image layers are cached — if two images share a common base layer (both use Ubuntu 24.04 as their base), that layer is stored once on disk.
- Docker Hub — a public registry of container images. Official images (nginx, ubuntu, mysql, python) are maintained by the software vendors or Docker. Community images are contributed by individuals and organisations. You pull images from Docker Hub using
docker pull or implicitly when you run an image that isn't cached locally.
Part 3 — Docker architecture (5 min)
- Three components: Docker daemon (dockerd) — the background service that manages images, containers, networks, and volumes. Docker client (docker) — the CLI tool that sends commands to the daemon over a Unix socket (/var/run/docker.sock). Docker Hub — the remote registry. When you run
docker run nginx, the client sends the request to the daemon, which checks locally for the image, pulls it from Docker Hub if absent, creates a container, and starts it.
- Why
docker info shows storage and root: the Docker daemon stores all image layers, container data, and volumes in its root directory (usually /var/lib/docker). The storage driver (typically overlay2 on Ubuntu) manages the layered filesystem for images and containers.
Part 4 — Week 6 arc (6 min)
- Preview the week: Monday and Tuesday cover the Docker fundamentals. Wednesday adds docker-compose for managing multi-container stacks. Thursday is the capstone — the Nginx server from Week 4 becomes a reverse proxy in front of a Docker container. Students who have kept their VMs working arrive at Thursday with the DNS, Nginx, and VPN already in place — Thursday just connects the final piece.
0:30 – 1:50Lab 6A · 80 min
Lab 6A — Install Docker, run first containers, manage images, interactive and detached modes
Part 1 — Install Docker (10 min)
- Install from apt:
sudo apt install docker.io. Verify the installation: docker --version and sudo docker info. In the docker info output, find: Server Version, Storage Driver, Docker Root Dir, and the number of containers/images. Record these in the lab sheet.
- Add the student user to the docker group to avoid needing sudo for every command:
sudo usermod -aG docker student. Log out and back in for the group membership to take effect. Verify: docker ps without sudo — should work without permission error.
Part 2 — First containers (20 min)
- Run hello-world:
docker run hello-world. Read the output together — Docker explains exactly what it did: checked local cache, pulled from Hub, created a container, ran it, output the message. Ask: "Is this container still running?" — check with docker ps. Nothing. Check docker ps -a — it's there, exited. The container completed its job (print a message) and stopped.
- Explore images and containers:
docker images — list all locally pulled images (shows hello-world)
docker ps -a — all containers including stopped ones
Run hello-world again with a name: docker run --name first_container hello-world
Run it a third time: docker run hello-world — note Docker reuses the cached image (no download)
- Clean up: remove both containers, then remove the image:
docker rm first_container [auto-named-container-id]
docker rmi hello-world
Verify with docker ps -a and docker images — both empty.
Part 3 — Interactive containers (25 min)
- Pull and run busybox interactively:
docker run -it busybox. The -i flag keeps stdin open; -t allocates a pseudo-TTY. The shell prompt changes — you're inside the container. Run: hostname, ps aux, cat /etc/os-release. Note: very few processes, minimal OS, short hostname (the container ID prefix).
- Detach without stopping: press CTRL-P then CTRL-Q. You're back at S1's shell. Verify the container is still running:
docker ps — busybox shows as Up. Reattach: docker attach [container-id-or-name]. You're back inside.
- Stop and run in detached mode: exit the container (
exit). Run busybox detached: docker run -d -it --name busybox_bg busybox. The container starts in the background. Verify: docker ps — running. Exec into it: docker exec -it busybox_bg sh. This opens a new shell without attaching to the existing session — useful for debugging running containers without risk of accidentally stopping them.
- Stop and remove:
docker stop busybox_bg && docker rm busybox_bg && docker rmi busybox.
Part 4 — Doom in Docker (25 min)
- Search Docker Hub from the command line:
docker search doom. Find doom-in-docker. Pull and run it with port mapping:
docker run -d -p 8080:8080 --name doom linuxserver/webtop
(If doom-in-docker isn't available, use any web UI container — the principle is port mapping.)
Open a browser on Windows and navigate to http://[S1-external-IP]:8080. The application should appear in the browser.
- This demonstrates the power of containers: a complete application deployed in one command, accessible through a browser, running in complete isolation from the host OS. Stop and remove when done.
Lab 6A complete when: Docker installed and user added to docker group. hello-world pulled, run, and cleaned up. busybox run interactively, detached with CTRL-P+Q, reattached, and cleaned up. docker exec -it demonstrated. Port-mapped container running and accessible from browser.
1:50 – 2:00Debrief · 10 min
- Ask: "We installed Docker and ran containers. What didn't we install?" — we didn't install Nginx, or Doom, or any other application. Docker pulled the image and ran it. The application is inside the image. This is the portability win: no dependency management, no installation scripts, no "it works on my machine."
- Preview Tuesday: port mapping and persistent storage. Today's containers were ephemeral — any data written inside them is gone when they stop. Tomorrow we learn how to map container ports to host ports, and how to mount host directories into containers so data persists.
Learning outcomes — by end of Day 1, students can…
Explain containers vs VMsDescribe how namespaces and cgroups implement isolation, and why containers start faster and use fewer resources than VMs
Install Docker and verify itInstall docker.io, interpret docker info output, and add a user to the docker group
Run and manage containersPull images, run named and anonymous containers, list with docker ps, remove containers and images
Use interactive and detached modesRun with -it, detach with CTRL-P+Q, reattach with docker attach, exec into a running container with docker exec -it
Common issues and fixes
| Issue | Likely cause | Fix |
| docker ps returns "permission denied" | User not in docker group, or group membership not yet active | Run: sudo usermod -aG docker student. Log out completely and log back in. Verify: groups student — should include docker |
| docker run hello-world fails with "Unable to find image" | No internet access from S1, or Docker Hub unreachable | Test: curl https://hub.docker.com. Check NAT routing (ping 8.8.8.8 from S1). Verify /etc/docker/daemon.json doesn't have an incorrect registry mirror |
| CTRL-P + CTRL-Q not working to detach | Terminal emulator intercepting the key sequence | In Windows Terminal, ensure focus is on the SSH session. Alternatively, use docker exec -it in a second session and work from there instead of the attached session |