kapitola 2

Docker: od instalace k vlastnímu image

Co je Docker Engine, proč na Linux VPS nepotřebujeme Docker Desktop a jak se image mění na běžící container.

Docker Desktop vs. Docker Engine

Windows

Windows ↓ Docker Desktop ↓ WSL2 / Linux ↓ Docker Engine ↓ containers

Linux VPS

Debian Linux ↓ dockerd ↓ containerd ↓ runc / kernel ↓ containers

Na VPS není potřeba Docker Desktop, protože Linuxové prostředí už je samotný hostitelský systém.

Docker stack

docker CLI ↓ dockerd (Docker Engine daemon) ↓ containerd ↓ runc ↓ Linux kernel ↓ běžící container

Runtime je vrstva, která skutečně realizuje běh containeru: připraví izolaci, filesystem, cgroups/namespaces a spustí proces.

Instalace Dockeru z oficiálního APT repozitáře

sudo install -m 0755 -d /etc/apt/keyrings

sudo curl -fsSL https://download.docker.com/linux/debian/gpg \
  -o /etc/apt/keyrings/docker.asc

sudo chmod a+r /etc/apt/keyrings/docker.asc

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update

sudo apt install docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin

TLS certifikáty chrání HTTPS spojení k repozitáři; GPG signing key zase umožňuje APT ověřit, že metadata/balíčky skutečně pocházejí od Dockeru a nebyly změněny.

Docker daemon jako systemd služba

systemctl status docker

Typický výstup ukáže:

Loaded: loaded (...; enabled)
Active: active (running)
Main PID: ... (dockerd)

CLI komunikuje s daemonem přes Unix socket:

/var/run/docker.sock

Oprávnění typicky:

srw-rw---- 1 root docker ... /var/run/docker.sock

Proto běžný uživatel bez členství ve skupině docker dostane permission denied. Používali jsme tedy sudo docker ....

Členství ve skupině docker je prakticky root-equivalent oprávnění. Není to „nevinná“ skupina.

Image vs. container

Image

Neměnná šablona/layered artefakt. Vzniká buildem a může být základem mnoha containerů.

Container

Konkrétní běžící nebo zastavená instance image. Má svůj proces, síť a writable vrstvu.

sudo docker run hello-world
sudo docker images
sudo docker ps
sudo docker ps -a

docker run nejprve hledá image lokálně. Když není, stáhne ho z výchozího registry (Docker Hub), vytvoří container a spustí jeho hlavní proces.

Port mapping

sudo docker run --name docker-nginx -p 8080:80 -d nginx

-p HOST_PORT:CONTAINER_PORT.

VPS :8080 ↓ Docker ↓ container :80

Výchozí publikace znamená typicky všechna rozhraní:

0.0.0.0:8080 → container:80

Pro backend za reverse proxy jsme port schovali jen na localhost:

sudo docker run --name docker-nginx \
  -p 127.0.0.1:8080:80 \
  -d nginx
internet → 185.x.x.x:8080 ✗ VPS → 127.0.0.1:8080 ✓

Vlastní Dockerfile a build

FROM nginx:latest

COPY index.html /usr/share/nginx/html/index.html
sudo docker build -t docker-static:v1 .

Tečka je build context. Docker má při buildu k dispozici soubory z aktuálního adresáře.

Proto jsme přidali:

# .dockerignore
.git

.dockerignore říká Dockeru, co nemá posílat do build contextu. Není to totéž co .gitignore.

Build image – pozor na poslední tečku

sudo docker build -t docker-tahak:v1 .

Příkaz se skládá z několika částí:

Část Význam
sudo Docker socket zatím používáme s root oprávněním.
docker build Sestav nový Docker image.
-t docker-tahak:v1 Dej image jméno docker-tahak a tag v1.
. Build context je aktuální adresář.
Pokud poslední . zapomeneme, Docker neví, odkud má vzít Dockerfile a ostatní soubory.

Typická chyba:

docker buildx build requires 1 argument

Docker tím říká, že chybí PATH, URL nebo jiný build context.

Co přesně znamená build context?

Máme například:

docker-vps-tahak/
├── Dockerfile
├── README.md
└── site/
    ├── index.html
    ├── docker.html
    └── assets/
        └── style.css

A Dockerfile:

FROM nginx:latest

COPY site/ /usr/share/nginx/html/

Díky tečce na konci docker build ... . dostane Docker přístup k adresáři site/ a může jej při buildu zkopírovat.

Spuštění containeru: jméno containeru není jméno image

sudo docker run \
  --name docker-tahak \
  --restart unless-stopped \
  -p 127.0.0.1:8081:80 \
  -d \
  docker-tahak:v1

Tady jsou dvě různá jména, která se snadno pletou:

--name docker-tahak
        ↑
        jméno nového CONTAINERU


docker-tahak:v1
        ↑
        IMAGE, ze kterého container vytvoříme

Když omylem napíšeme:

sudo docker run --name docker-tahak ... nginx

vznikne sice container jménem docker-tahak, ale bude vytvořen z image nginx:latest. Náš vlastní web v něm nebude.

Port mapping

-p 127.0.0.1:8081:80

Čte se zleva doprava:

VPS: 127.0.0.1:8081 │ │ Docker port mapping ▼ container: port 80 │ ▼ nginx uvnitř containeru

Port 8081 tedy není port nginxu uvnitř containeru. Nginx v containeru pořád poslouchá na portu 80.

8081 je pouze port na hostitelském VPS, který Docker překládá na port 80 containeru.

Proč nefunguje veřejná IP na portu 8081?

http://PUBLIC_VPS_IP:8081

Protože jsme port navázali pouze na:

127.0.0.1:8081

127.0.0.1 je loopback – spojení dostupné pouze uvnitř samotného VPS.

z internetu: PUBLIC_IP:8081 ✗ z VPS: 127.0.0.1:8081 ✓

To je záměr. Veřejnost má komunikovat se systémovým nginxem, nikoli přímo s backend containerem.

Docker Compose

Compose je deklarativní popis celého prostředí. Hodí se ve chvíli, kdy máme více služeb: například web + databázi + Redis.

services:
  web:
    image: moje-app
    ports:
      - "8080:8000"

  db:
    image: postgres
docker compose up -d

Dockerfile odpovídá hlavně na „jak sestavit image?“. Compose odpovídá na „jak mají spolu běžet jednotlivé služby?“.