📦

Virtualization & Containers Intermediate

From hypervisors to Kubernetes: virtual machines, Docker containers and orchestration explained.

16 lessons 48 quiz questions
Lessons & quizzes Certificate

📚 Lessons & quizzes

Each lesson ends with its own short quiz. Answer them as you go — score 90% across all lessons to earn your certificate.

1 Why virtualization: consolidation, isolation, efficiency

Virtualization lets a single physical computer run many independent virtual computers at once. Before it was common, organisations often ran one application per physical server, which left most hardware idle — a typical server might use only 10–15% of its CPU.

Virtualization solves this through consolidation (packing many workloads onto one machine to raise utilisation), isolation (each virtual machine is walled off, so a crash or compromise in one does not directly affect others), and efficiency (workloads can be created, copied, moved and destroyed in software instead of buying and racking new hardware).

2 Hypervisors: type-1 (bare-metal) vs type-2 (hosted)

A hypervisor (also called a virtual machine monitor) is the software layer that creates and runs virtual machines, sharing the real hardware among them. There are two kinds.

A type-1 or bare-metal hypervisor runs directly on the physical hardware with no host operating system beneath it. Examples include VMware ESXi, Microsoft Hy-V and the open-source KVM/Xen. Because it sits closest to the hardware, it offers the best performance and is used in data centres.

A type-2 or hosted hypervisor runs as an ordinary application on top of a normal operating system (Windows, macOS, Linux). Examples include VirtualBox and VMware Workstation. It is convenient for desktops and testing but adds the overhead of the host OS.

3 Anatomy of a virtual machine

A virtual machine (VM) is a complete computer emulated in software. The hypervisor presents each VM with virtual hardware: one or more virtual CPUs (vCPUs) mapped onto real CPU cores, an amount of virtual RAM carved out of host memory, one or more virtual disks stored as files on the host (formats such as .vmdk, .vhdx or .qcow2), and virtual network cards.

On top of this virtual hardware the VM runs its own full guest operating system — kernel, drivers, system libraries and applications — completely separate from the host. Because each VM carries an entire OS, it behaves exactly like a standalone machine and can run an OS different from the host.

4 Snapshots and live migration

Because a VM is just software and files, the hypervisor can perform powerful operations. A snapshot captures the exact state of a VM at a moment in time — its disk contents and, optionally, its running memory. You can take a snapshot before a risky upgrade and roll back instantly if something breaks. Snapshots are not full backups, though: they usually depend on the original disk.

Live migration moves a running VM from one physical host to another with little or no downtime. The hypervisor copies the VM’s memory pages to the destination while it keeps running, then transfers the last changes and switches over. This enables hardware maintenance and load balancing without stopping services. VMware calls this vMotion.

5 VMs vs containers: shared kernel, weight, boot time

A container is a lighter form of isolation than a VM. The crucial difference: containers share the host’s operating-system kernel, while each VM ships its own full guest OS and kernel.

Because a container does not carry an entire OS, it is much smaller (megabytes versus gigabytes), starts in milliseconds to seconds rather than the tens of seconds a VM needs to boot, and uses far less memory. This lets you pack many more containers than VMs onto the same hardware. The trade-off is isolation strength: VMs are more strongly separated by the hypervisor, whereas containers share a kernel and therefore have a larger shared attack surface.

6 What a container really is: namespaces and cgroups

A container is not magic — it is an ordinary process on the host, made to look and feel like it has its own machine using two Linux kernel features.

Namespaces provide isolation of what a process can see. Separate namespaces exist for process IDs (PID), network interfaces, mount points (filesystem), hostnames (UTS), users and inter-process communication. Inside its namespaces a container sees its own process tree, its own network stack and its own root filesystem.

Control groups (cgroups) provide limits on what a process can use — how much CPU, memory, disk and network I/O a container is allowed to consume. Together, namespaces (isolation) and cgroups (resource limits) turn a normal process into a container.

7 Container images and layers

A running container starts from a container image: a read-only template bundling an application together with everything it needs to run — libraries, dependencies, a minimal filesystem and metadata describing how to start it. An image is built once and can be run anywhere a compatible container runtime exists, which is why containers are so portable.

Images are built in layers. Each instruction that changes the filesystem adds a new read-only layer stacked on top of the previous ones, using a union/overlay filesystem. Layers are cached and shared: if two images both start from the same base layer, that layer is stored only once, saving space and speeding up builds and downloads. When a container runs, the runtime adds a thin writable layer on top; changes made by the container go there and are lost when the container is removed (unless stored in a volume).

8 Docker basics: image vs container, run and build

Docker is the most popular toolset for building and running containers. The single most important distinction is image vs container: an image is the static, read-only template; a container is a running (or stopped) instance created from that image. The relationship mirrors a program file versus a running process — one image can spawn many containers.

docker run creates and starts a container from an image, pulling the image first if it is not present locally. docker build creates a new image from a Dockerfile. Other everyday commands include docker ps (list running containers), docker images (list local images), docker stop and docker rm.

# Pull and run an Nginx web server, mapping host port 8080 to container port 80
docker run -d -p 8080:80 --name web nginx:latest

# See it running
docker ps

# Build an image from a Dockerfile in the current directory
docker build -t myapp:1.0 .

# Stop and remove the container
docker stop web
docker rm web

9 The Dockerfile

A Dockerfile is a plain-text recipe describing, step by step, how to build an image. Each instruction usually creates a new layer. Common instructions: FROM chooses a base image; WORKDIR sets the working directory; COPY brings files into the image; RUN executes a command at build time (for example installing dependencies); EXPOSE documents a port; CMD (or ENTRYPOINT) defines what runs when the container starts.

A good practice is to copy dependency files and install dependencies before copying the rest of the source, so Docker can reuse the cached dependency layer when only the application code changes.

# Start from a small official base image
FROM node:20-alpine

# Set the working directory inside the image
WORKDIR /app

# Copy dependency manifests first to leverage layer caching
COPY package*.json ./
RUN npm ci --omit=dev

# Now copy the rest of the application source
COPY . .

# Document the port the app listens on
EXPOSE 3000

# Command run when a container starts
CMD ["node", "server.js"]

10 Registries and Docker Hub

A registry is a server that stores and distributes container images, much like a package repository for libraries. You push images to a registry to share them and pull images from it to run them. Docker Hub is the default public registry; it hosts official images (curated base images such as nginx, postgres and python) as well as images published by individuals and organisations.

An image is identified by repository:tag, for example nginx:1.27. The latest tag is just a default name, not a guarantee of the newest version, so pinning a specific tag is safer for reproducible builds. Other registries include GitHub Container Registry, Amazon ECR, Google Artifact Registry and private self-hosted registries.

# Log in, tag your image for a registry account, and push it
docker login
docker tag myapp:1.0 myuser/myapp:1.0
docker push myuser/myapp:1.0

# Pull an official image by repository:tag
docker pull postgres:16

11 Container networking and volumes (persistence)

By default Docker gives containers a private bridge network so they can talk to each other and reach the outside world, while the host stays isolated. To make a service reachable from outside, you publish a port with -p host:container. Containers on a user-defined network can also reach each other by container name via Docker’s built-in DNS.

Container filesystems are ephemeral: anything written inside a container is lost when it is removed. For data that must survive — databases, uploads, logs — you use volumes. A named volume is managed by Docker and stored outside the container’s writable layer; a bind mount maps a host directory into the container. Both keep data alive across container restarts and replacements.

# Create a named volume and attach it to a database container
docker volume create dbdata
docker run -d --name db -v dbdata:/var/lib/postgresql/data postgres:16

# Bind-mount a host directory into a container (host path : container path)
docker run -d -v /srv/site:/usr/share/nginx/html -p 8080:80 nginx

12 docker-compose for multi-container apps

Real applications are rarely a single container — a web app might need a web server, an API, a database and a cache. Docker Compose lets you define all these services, plus their networks and volumes, in one declarative compose.yaml file, then start the whole stack with a single command.

Each service names an image (or a build context), its ports, environment variables, volumes and dependencies. docker compose up creates a shared network and starts everything; services reach each other by service name. docker compose down tears it all down. This makes a multi-container app reproducible and easy to share, but Compose runs on a single host — it is not a cluster scheduler.

services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"
    depends_on:
      - api
  api:
    build: ./api
    environment:
      - DATABASE_URL=postgres://db:5432/app
  db:
    image: postgres:16
    volumes:
      - dbdata:/var/lib/postgresql/data
volumes:
  dbdata:

13 Why orchestration is needed

Running a few containers by hand is easy. Running hundreds across many machines, keeping them healthy day and night, is not. Orchestration is the automation that manages containers at scale across a cluster of hosts.

An orchestrator answers questions that quickly overwhelm manual operations: Which machine should each container run on? (scheduling and bin-packing). What happens when a container or a whole node dies? (self-healing — restart or reschedule it). How do we run more copies under load and fewer when idle? (scaling). How do clients find a moving set of containers? (service discovery and load balancing). How do we update without downtime? (rolling updates and rollbacks). Doing all of this reliably by hand is impractical, which is why platforms like Kubernetes exist.

14 Kubernetes core objects: pod, deployment, service, node, control plane

Kubernetes (often abbreviated K8s) is the dominant container orchestrator. A few core objects make up its model.

A pod is the smallest deployable unit in Kubernetes — one or a few tightly coupled containers that share a network address and storage. A deployment declares the desired state for a set of identical pods (which image, how many replicas) and manages updates. A service gives a stable network name and IP that load-balances traffic across a changing set of pods, so clients are unaffected when pods come and go.

A node is a worker machine (VM or physical) that runs pods. The control plane is the brain: the API server (entry point), scheduler (places pods on nodes), controller manager (drives actual state toward desired state) and etcd (the cluster’s key-value datastore).

15 Scaling and self-healing in Kubernetes

Kubernetes is declarative: you tell it the desired state and its controllers continuously work to make reality match. This drives both scaling and self-healing.

Scaling: a deployment has a replica count. Raise it and Kubernetes starts more pods; lower it and it stops some. The Horizontal Pod Autoscaler can change that count automatically based on metrics such as CPU usage, adding pods under load and removing them when idle.

Self-healing: if a pod crashes or a node fails, the controller notices that the actual number of healthy pods is below the desired number and creates replacements, rescheduling them onto healthy nodes. Liveness probes let Kubernetes detect a hung container and restart it, and readiness probes keep traffic away from pods that are not yet ready.

# Scale a deployment to 5 replicas
kubectl scale deployment web --replicas=5

# Autoscale between 2 and 10 pods targeting 70% CPU
kubectl autoscale deployment web --min=2 --max=10 --cpu-percent=70

# Watch pods being recreated if one is deleted
kubectl get pods -w

16 Container security and best practices

Containers are convenient but need care. Some widely accepted best practices:

Use small base images. Minimal images (such as Alpine or distroless) contain fewer packages, which means a smaller attack surface, faster pulls and fewer vulnerabilities to patch.

Do not run as root. By default a container process may run as root, and a breakout would then be root on a shared kernel. Create and switch to an unprivileged user (the USER instruction) and drop unneeded Linux capabilities.

Scan images for vulnerabilities. Tools like Trivy, Grype or built-in registry scanners inspect image layers for known CVEs so you can patch before deploying. Other essentials: pin specific image tags or digests rather than latest, never bake secrets into images, and keep base images updated.

# In a Dockerfile: create and switch to a non-root user
RUN adduser -D appuser
USER appuser

# Scan an image for known vulnerabilities with Trivy
trivy image myapp:1.0

🎓 Certificate of Completion

🔒 Complete every lesson quiz above with 90%+ to unlock your downloadable certificate.