Session 3 · Images, containers and registries#

The big idea#

An image is a read-only template: files plus the instructions to start a program. A container is a running (or stopped) instance made from an image. You can start many containers from one image, and each gets its own private, writable layer.

Everyday example: a bakery. The recipe and dough is the image: you can’t eat it, and it never changes. The cookies you bake are containers. Bake ten cookies from one recipe; eating or breaking one doesn’t change the recipe or the other cookies. The registry is the recipe shop where bakeries buy and share recipes.

One image, many containers An image is a read-only template. Each container made from it gets its own writable layer, and changes there never touch the image. Image alpine (read-only) Container dm-a writable layer Container dm-b writable layer Container dm-c writable layer docker run delete the container and its writable layer goes too
An image is a read-only template. Each container made from it gets its own writable layer, and changes there never touch the image.

One image, several containers#

docker rm -f dm-a dm-b >/dev/null 2>&1
echo '--- one image, two containers'
docker create --name dm-a alpine echo "container A" >/dev/null
docker create --name dm-b alpine echo "container B" >/dev/null
docker ps -a --filter name=dm- --format '{{.Names}}  {{.Image}}  {{.Status}}' | sort
echo '--- start them'
docker start -a dm-a
docker start -a dm-b
echo '--- changes live in the container, not in the image'
docker run --name dm-a2 alpine sh -c 'echo secret > /note.txt && cat /note.txt'
docker run --rm alpine cat /note.txt 2>&1 | sed 's/^/new container: /'
echo '--- the layers an image is built from'
docker history alpine:3.20 --format '{{.CreatedBy}}  {{.Size}}'
docker rm -f dm-a dm-b dm-a2 >/dev/null
--- one image, two containers
dm-a  alpine  Created
dm-b  alpine  Created
--- start them
container A
container B
--- changes live in the container, not in the image
secret
new container: cat: can't open '/note.txt': No such file or directory
--- the layers an image is built from
CMD ["/bin/sh"]  0B
ADD alpine-minirootfs-3.20.10-x86_64.tar.gz …  7.81MB

What to take from it:

  • docker create makes two containers (dm-a, dm-b) from the same image. They are Created, not running.

  • docker start -a runs each one and shows its output.

  • A file written in one container (/note.txt) is gone in a brand-new container from the same image. Changes belong to the container’s writable layer, not the image.

  • docker history lists the layers: a tiny Alpine root filesystem (7.81MB) plus a start command (CMD ["/bin/sh"], 0 bytes). Images are stacks of layers, and identical layers are shared between images.

Where images come from#

Images reach your machine in two ways. You pull one that someone else published (docker pull nginx:alpine), or you build your own from a Dockerfile (Session 7). Either way it is stored locally as layers. When you docker run a name you don’t have, Docker pulls it for you first. That is why the first run of a new image is slow and the second is instant.

A tag such as 3.20 or alpine is just a label pointing at one image. The same image can have several tags, and a tag can later be moved to a newer image. If you need something exact, you can name an image by its digest (alpine@sha256:...), which never changes.

Useful commands for this session#

Command

What it does

docker pull nginx:alpine

Download an image from the registry

docker images

List local images

docker ps / docker ps -a

Running containers / all containers

docker rm NAME

Delete a container

docker rmi IMAGE

Delete an image (no container may use it)

docker history IMAGE

Show an image’s layers

Common mistakes#

  • Mixing up image and container. Deleting a container does not delete the image, and an image can’t “run”; you run a container made from it.

  • Saving data inside a container. It disappears with the container (Session 8 fixes this).

  • Piling up stopped containers. docker ps -a shows them all. Use --rm for throwaway runs, or docker container prune to clear stopped ones.

  • Trying to remove an image that a container still uses. Remove the container first.

  • Thinking docker pull updates running containers. It only fetches the new image. Recreate the container to use it.

Hands-on exercises#

Exercise 1. Create two containers from alpine that each print their own name, run both, and remove them.

Solution
docker create --name one alpine echo "I am one"
docker create --name two alpine echo "I am two"
docker start -a one
docker start -a two
docker rm one two

Each start -a prints that container’s line. The image is shared; the containers are separate.

Exercise 2. Prove that a file created in one container is not visible in another.

Solution
docker run --name first alpine sh -c 'echo hi > /x.txt'
docker run --rm alpine cat /x.txt
docker rm first

The second command fails with cat: can't open '/x.txt': No such file or directory.

Exercise 3. How many layers does alpine:3.20 have, and which one is the largest?

Solution
docker history alpine:3.20 --format '{{.CreatedBy}}  {{.Size}}'

On this machine it shows two entries: the CMD line (0B) and the root filesystem ADD (7.81MB), which is the largest.

Practice#

Take the Session 3 quiz. The original bakery story is in the reference section.