CoursesKubernetes fundamentalsContainers in a nutshell

Containers in a nutshell

The thing Kubernetes actually runs.

Beginner8 min · lesson 2 of 24
In plain terms
A container is a lunchbox for a program: the app plus everything it needs, sealed so it runs the same on your laptop and in the cloud. Kubernetes is what arranges thousands of these lunchboxes.

Kubernetes runs containers. So the first thing you need is a clear picture of what one of those actually is. Start with the object the technology borrowed its name from. A shipping container is a big steel box. You pack goods inside, seal the doors, and every crane, ship and truck moves that box around without knowing or caring what is in it. A software container copies that trick. It is a sealed box holding your app plus everything the app needs in order to run.

What is inside the box

An app on its own is rarely enough to run. It wants a particular version of a programming language, a handful of code libraries, and a few configuration settings. Miss one of those on a new machine and the app falls over. A container packs the lot together: your code, the runtime (the engine that actually executes your code), the libraries, and the settings. Those supporting pieces have a name. They are the app's dependencies, the other software it leans on. All of it gets sealed into one unit. Because the container carries its own dependencies with it, it behaves the same on your laptop, on a teammate's machine, and on a server in the cloud. That is the end of "but it works on my machine".

You may be wondering how that differs from a virtual machine, which also bundles software into one package. A virtual machine is a separate house. It brings a whole copy of an operating system along with it, foundations and plumbing and all. A container is an apartment in a building. Its own locked front door, its own rooms, but the foundation underneath is shared with every other apartment in the block. That shared foundation is the host machine's operating system kernel (the core part of the operating system that talks to the hardware). Sharing it keeps containers small and quick. They start in seconds, and one machine can run many of them at once.

Image versus container

Two words get swapped by mistake constantly, so pin them down now. An image is the template. You build it once and it freezes: the app and all its dependencies packed together, read-only. A container is a running copy of that image. Recipe and cake. One recipe, baked as often as you like, gives you a kitchen full of cakes. One image, started as often as you like, gives you a cluster full of containers. The commands below use Docker, a popular tool for building and running containers on a single computer. Start by looking at an image sitting on disk.

terminal
$ docker pull nginx:1.27
1.27: Pulling from library/nginx
a2318d6c47ec: Pull complete
5b5f5c8f3d0a: Pull complete
8f1e9c2d4b7a: Pull complete
Digest: sha256:9c3f1e0d7a2b4c6e8f0a1b2c3d4e5f60718293a4b5c6d7e8f9a0b1c2d3e4f5a6
Status: Downloaded newer image for nginx:1.27
docker.io/library/nginx:1.27
$ docker images nginx
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx 1.27 e0e5f7a1c9d2 4 weeks ago 192MB

That image is the frozen template. Nothing is running yet. Now start a container from it. The command below runs it in the background and hands the container a name, so it is easy to find again.

terminal
$ docker run -d --name web nginx:1.27
9f3c2a1b7e4d5c6f8a0b1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9f3c2a1b7e4d nginx:1.27 "/docker-entrypoint.…" 8 seconds ago Up 6 seconds 80/tcp web

One image, one running container. Start ten more from the same image and all ten come out identical. That is container work done by hand: one tool, one machine, you typing. Kubernetes does the same job across a whole fleet of machines (each machine in the fleet is called a node), and it does the typing for you. You do not run that command a thousand times. You hand Kubernetes a short text file describing what you want, and it fetches the images and starts the containers wherever there is room.

How Kubernetes runs a container

One detail before the file. Kubernetes will not run a bare container for you. It wraps your container in a Pod, the smallest thing Kubernetes knows how to run. For now, read "Pod" as "a thin wrapper around one container"; a later lesson opens it up properly. You describe the Pod in a YAML file. YAML (Yet Another Markup Language) is a plain-text format for writing settings as name-and-value pairs, where indentation shows what belongs to what. Here is a complete one you can save and use.

pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80

Four things to pick out. The apiVersion and kind lines tell Kubernetes what sort of object this is, here a Pod. The metadata block hands it a name, web, so you can refer to it later. The spec block is the wish itself: run one container called web, built from the nginx:1.27 image, and expect it to listen on port 80. That is the entire file. Send it to the cluster (the group of machines Kubernetes manages on your behalf) using kubectl, the command-line tool that talks to a cluster, then check whether it came up.

terminal
$ kubectl apply -f pod.yaml
pod/web created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
web 1/1 Running 0 9s

READY 1/1 means the single container in the Pod is up and healthy. STATUS Running means Kubernetes found the image, pulled it down onto a machine, and started it. You wrote down what you wanted, and the cluster went and made it true. Everything bigger in this course, Deployments, Services, scaling, is built on that one move: describe a container, let Kubernetes run it.

When the Pod refuses to start

That was the happy path. Real clusters make you read failures too, and the most common first stumble is a mistyped image name. Type the tag as nginx:1.72 when you meant nginx:1.27, and the Pod never comes up.

terminal
$ kubectl apply -f pod.yaml
pod/web created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
web 0/1 ImagePullBackOff 0 30s

READY 0/1 means the container never started, and STATUS tells you why. Kubernetes tried to pull the image, could not, and is now waiting longer and longer between retries. For the detail, ask the Pod to describe itself. The Events section at the bottom is where you look whenever something is stuck.

terminal
$ kubectl describe pod web
Name: web
Namespace: default
Status: Pending
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 46s default-scheduler Successfully assigned default/web to worker-1
Normal Pulling 17s (x3 over 45s) kubelet Pulling image "nginx:1.72"
Warning Failed 16s (x3 over 44s) kubelet Failed to pull image "nginx:1.72": not found
Warning Failed 16s (x3 over 44s) kubelet Error: ErrImagePull
Normal BackOff 3s (x4 over 43s) kubelet Back-off pulling image "nginx:1.72"

Read it from the top. The scheduler placed the Pod on worker-1 without trouble. Then the kubelet (the agent running on every node, the thing that pulls images and starts containers) went looking for nginx:1.72, failed, and kept failing. No such tag exists. Correct it to nginx:1.27 and apply the file again. kubectl describe is your first move any time a Pod will not start.

Smaller images start faster

Here is a problem that hides from you, because on your laptop it never shows up. You build an image carrying a full operating system plus a compiler and build tools, and it comes out at 1.2 GB (gigabytes). Your laptop already has that image downloaded, so the container starts instantly. In a cluster, a node that has never seen the image has to fetch all 1.2 GB before anything runs, which can take minutes. It lands at the worst possible moment: scaling up under a traffic spike, or when a node dies and gets replaced.

terminal
$ docker images myapp
REPOSITORY TAG IMAGE ID CREATED SIZE
myapp full a1b2c3d4e5f6 2 hours ago 1.24GB
myapp slim f6e5d4c3b2a1 2 hours ago 82MB

The fix is a slimmer starting point. Many projects publish -slim or -alpine variants of their base image, and distroless images (images that ship your app and its runtime and nothing else, no package manager, no shell) go further still. Smaller image, faster pull, quicker scaling. The honest trade-off: those images drop the shell and the usual debugging tools, so when a container misbehaves you have far less to poke at from the inside. While you are learning, a full image is fine. In production, small wins.

A container image is a frozen filesystem plus one default process, and that is the whole of it. Kubernetes did not invent containers and does not change what they are. It schedules them, restarts them, and wires them together. So the image habits you already have carry over untouched: pin digests, run as a non-root user, keep the thing small. The cluster has no taste. Hand it a dangerous image and it will run that image happily, on every node you own.

One consequence of that catches people out. When a Pod dies and Kubernetes starts a replacement, you get a brand new container built from the same image, not your old process brought back to life. Whatever the previous container wrote into /tmp went with it. That is why "it worked until the Pod restarted" nearly always turns out to be state living on the container filesystem, or a volume nobody configured.

Try this

Create a Pod, then read back the image and container status Kubernetes recorded for it. Then do the same with an image that cannot be pulled, and compare what you get.

terminal
$ kubectl run web --image=nginx:1.27 --restart=Never
pod/web created
$ kubectl get pod web -o jsonpath='{.status.containerStatuses[0].imageID}{"\n"}{.status.phase}{"\n"}'
docker.io/library/nginx@sha256:abcdef0123456789...
Running
$ kubectl delete pod web
pod "web" deleted
$ kubectl run bad --image=nginx:does-not-exist-xyz --restart=Never
pod/bad created
$ kubectl get pod bad
NAME READY STATUS RESTARTS AGE
bad 0/1 ImagePullBackOff 0 12s
$ kubectl delete pod bad
pod "bad" deleted

Takeaway

Containers are running instances of images, and Pods are how Kubernetes places and restarts those instances. Pin your image versions, check the digest the cluster actually pulled, expect an empty filesystem every time a Pod comes back, and read ImagePullBackOff as a wrong name or a missing registry entry rather than a mysterious cluster fault.

Kubernetes does not build images
A first-day surprise for most people: Kubernetes cannot build a container image. It only pulls images that already exist in a registry (an online store for images) and runs them. Building happens somewhere else first, with Docker or a build pipeline, and then you push the result to the registry. Point a Pod at an image nobody ever pushed and you land in exactly the same ImagePullBackOff. Kubernetes went looking, and there was nothing there.
From image to running container
1buildpack app + deps into an image2pushstore the image in a registry3kubectl applysend your Pod file4pulla node fetches the image5runcontainer starts inside a Pod
You do the first two steps once. Kubernetes repeats the last three every time it places a container, on whichever node has room.
Quick check
01An image and a container: what is the actual difference between them?
Incorrect — They are not. One is the frozen template, the other is a running copy of it.
Correct — One image can start many containers, the way one recipe bakes many cakes.
Incorrect — That is the wrong way round. The image is what sits stored; the container is what runs.
Incorrect — Size is not what separates them. Template versus running instance is.
02The lesson gives one main reason a container is smaller and quicker to start than a virtual machine. Which one?
Incorrect — No. Compression is not the difference. What each one has to carry in order to run is.
Correct — Sharing the host kernel keeps containers small and lets them start in seconds, the way an apartment uses the building's foundation instead of pouring its own.
Incorrect — No. The small size comes from the shared kernel, not from any limit on what a container may hold.
Incorrect — No. A container bundles all of its dependencies. The speed comes from sharing the operating-system kernel, not from going without.
03Your image weighs 1.2 GB because it bundles a whole operating system plus build tools. It starts instantly on your laptop, but when the cluster scales onto a fresh node the new pod takes minutes to appear. What is going on, and what fixes it?
Incorrect — No. The wait is the download, not memory. The lesson ties slow starts on new nodes to image size.
Incorrect — No. Kubernetes never builds images, it only pulls them, so there is no rebuild to turn off.
Incorrect — No. The pod does come up, only slowly. A wrong tag would leave it stuck in ImagePullBackOff and never reach Running.
Correct — Your laptop already has the image cached, but a fresh node fetches every byte of it. A -slim, -alpine or distroless base pulls and scales much faster.

Related