Technologie DevOps // Wykład 01

Kontenery Docker

Obrazy, kontenery, sieci oraz Docker Compose — od mechanizmu systemu operacyjnego do kompletnego środowiska aplikacji.

Problem 01
„U mnie
działa.”

Inna wersja środowiska, brak zależności, odmienna konfiguracja i ręczne kroki tworzą niepowtarzalne wdrożenia.

Model mentalny

Pakujemy proces razem z jego środowiskiem

Kod
+
Runtime
+
Biblioteki
=
Obraz
Krótka historia izolacji

Kontenery nie pojawiły się nagle

1979chroot ogranicza widok systemu plików procesu.
lata 2000.Jails, Zones i maszyny wirtualne popularyzują izolację usług.
2006–2008cgroups i namespaces stają się fundamentem kontenerów Linuksa.
2013Docker upraszcza budowanie, dystrybucję i uruchamianie obrazów.
2015OCI standaryzuje format obrazu i uruchamianie kontenerów.
Dlaczego branża tego potrzebowała

Płacimy także za niewykorzystane zasoby

Serwer fizyczny

Kupowany pod obciążenie szczytowe. Przez większość czasu część CPU i RAM pozostaje niewykorzystana.

Maszyna wirtualna

Lepsza konsolidacja, ale każda VM utrzymuje własny system operacyjny i rezerwuje zasoby.

Kontener

Wspólne jądro oznacza mniejszy narzut, większą gęstość usług i szybsze skalowanie.

Koszt to nie tylko zakup serwera: energia, chłodzenie, miejsce, licencje, utrzymanie i czas administratorów.

Izolacja

VM kontra kontener

Maszyna wirtualnaKontener
System gościaPełnyWspółdzielone jądro hosta
StartZwykle wolniejszyZwykle sekundy lub mniej
RozmiarGBMB–GB
Granica izolacjiHypervisorMechanizmy jądra
Ważne

Kontener to izolowany proces

  • ma własny widok procesów, sieci i systemu plików,
  • korzysta z jądra systemu gospodarza,
  • nie jest domyślnie granicą bezpieczeństwa równą VM,
  • po zatrzymaniu procesu głównego kontener kończy pracę.
Podstawowy przepływ

Definicja staje się procesem

Dockerfile
→ build →
Image
→ run →
Container
Pod maską

Dlaczego głównie Linux?

Docker wykorzystuje mechanizmy jądra Linuksa. Jądro tworzy izolację i limity — Docker dostarcza model obrazów, API i narzędzia.

namespaces
cgroups
capabilities
overlayfs
Jądro Linuksa / 01

Namespaces zmieniają widok świata

  • PID — procesy,
  • NET — interfejsy, routing i porty,
  • MNT — punkty montowania,
  • UTS — nazwa hosta,
  • USER — mapowanie użytkowników.
Jądro Linuksa / 02

Cgroups mierzą i ograniczają

CPU

udziały i limity

Pamięć

limit i reakcja na przekroczenie

I/O

kontrola dostępu do urządzeń

Jądro Linuksa / 03

Warstwy są współdzielone

Obraz to stos niezmiennych warstw. Dwa kontenery uruchomione z tego samego obrazu współdzielą warstwy obrazu, ale każdy otrzymuje własną cienką warstwę zapisu.

Zmiana pliku nie modyfikuje obrazu. Mechanizm copy-on-write kopiuje zmieniany plik do warstwy kontenera.

warstwa kontenera
zapis / copy-on-write
kod aplikacji
tylko odczyt
biblioteki i runtime
tylko odczyt, współdzielone
bazowy system plików
tylko odczyt, współdzielony
jądro hosta — poza obrazem
Windows

Są także kontenery Windows

Linux containers

Na Windows korzystają z jądra Linuksa dostarczanego przez WSL 2. To najczęstszy tryb pracy Docker Desktop.

eclipse-temurin:21-jre

Windows containers

Korzystają z jądra Windows i obrazów Windows Server. Wymagają zgodnego hosta Windows; nie uruchomimy ich natywnie na jądrze Linuksa.

mcr.microsoft.com/windows/nanoserver

Obraz musi pasować do rodziny jądra hosta. Docker Desktop pozwala przełączać tryb Linux/Windows containers.

Windows Subsystem for Linux

Czym jest WSL?

Środowiskiem umożliwiającym uruchamianie dystrybucji i narzędzi Linuksa na Windows. WSL 2 używa prawdziwego jądra Linuksa.

wsl --install
wsl --status
wsl --list --verbose
Architektura lokalna

Od terminala do kontenera

docker CLI
→
Docker Engine / WSL 2
→
Linux container

Kod przechowywany w systemie plików WSL zwykle działa wydajniej niż kod montowany z dysku Windows.

Image

Jak czytać nazwę obrazu?

docker.io/library/postgres:17

Registry

docker.io — serwer przechowujący obrazy. Pominięty oznacza domyślnie Docker Hub.

Repository

library/postgres — nazwa i przestrzeń obrazu. library oznacza Docker Official Image.

Tag

17 — wariant lub wersja. Brak tagu oznacza latest, a nie „najnowszy bezpieczny”.

postgres:17 · eclipse-temurin:21-jre · ghcr.io/acme/api:1.4.2

Warstwy i cache

Kolejność instrukcji ma znaczenie

Rzadko zmieniające się zależności umieszczamy przed często zmienianym kodem.

COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package
Dystrybucja

Skąd pobieramy obrazy?

Docker Hub

publiczne i oficjalne obrazy

GHCR

GitHub Container Registry

Prywatny registry

obrazy organizacji

registry/repository:tag · tag może się zmienić, digest wskazuje konkretną treść.

Dockerfile

Przepis na obraz

FROM eclipse-temurin:21-jre
WORKDIR /app
COPY target/app.jar app.jar
EXPOSE 8080
USER 10001
ENTRYPOINT ["java", "-jar", "app.jar"]
Wielostopniowe budowanie

Narzędzia budujące nie muszą trafić do wyniku

FROM maven:3-eclipse-temurin-21 AS build
WORKDIR /src
COPY . .
RUN mvn -q -DskipTests package

FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /src/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
Kontekst budowania

Docker widzi tylko przekazany katalog

# .dockerignore
target
.git
.idea
.env

Nie wysyłaj do silnika sekretów, repozytorium ani zbędnych artefaktów.

CLI

Zbuduj, oznacz, sprawdź

docker build -t space-api:1.0 .
docker image ls
docker image inspect space-api:1.0
docker history space-api:1.0
DEMO NA ŻYWOZbuduj obraz API, pokaż cache warstw, listę obrazów i docker history.
Container

Instancja obrazu w ruchu

Kontener ma własną konfigurację, sieć i warstwę zapisu. Usunięcie kontenera usuwa tę warstwę — dane trwałe wymagają wolumenu.

Uruchomienie

docker run łączy kilka operacji

docker run -d --name space-api \
  -p 8080:8080 \
  -e SPRING_PROFILES_ACTIVE=docker \
  space-api:1.0

-d tło · --name nazwa · -p port · -e zmienna

DEMO NA ŻYWOUruchom API, sprawdź ps, logi oraz endpoint HTTP.
Sieć

Port hosta ≠ port kontenera

localhost:8088
host
-p 8088:8080
container:8080
proces

EXPOSE dokumentuje port. Dopiero -p udostępnia go hostowi.

DEMO NA ŻYWOUruchom ten sam obraz z innym portem hosta i porównaj adresy.
Komunikacja

W jednej sieci używamy nazw

docker network create mission-net
docker run --network mission-net --name db mysql:8
docker run --network mission-net --name api space-api

API łączy się z db:3306, nie z localhost:3306.

DEMO NA ŻYWOPokaż błąd z localhost, potem wspólną sieć i rozwiązywanie nazwy db.
Dane trwałe

Named volume żyje niezależnie od kontenera

docker volume create mysql-data

docker run --name db \
  -v mysql-data:/var/lib/mysql \
  mysql:8
  • mysql-data — nazwa nadana przez nas,
  • /var/lib/mysql — miejsce danych wewnątrz kontenera,
  • Docker wybiera fizyczną lokalizację na hoście,
  • usunięcie kontenera nie usuwa named volume,
  • nowy kontener może podłączyć ten sam wolumen.

Named volume wybieramy dla danych aplikacji. Bind mount jest wygodniejszy, gdy człowiek ma edytować pliki z hosta, np. kod źródłowy.

Named volume / cykl życia

Kontener można wymienić bez utraty bazy

kontener DB v1
→ zapis →
mysql-data
← odczyt ←
kontener DB v2
docker rm -f db                 # dane zostają
docker volume ls               # volume nadal istnieje
docker run --name db-new \
  -v mysql-data:/var/lib/mysql mysql:8

docker volume rm mysql-data    # jawnie usuwa dane
DEMO NA ŻYWODodaj rekord, usuń kontener bazy, uruchom nowy z tym samym volume i pokaż, że rekord pozostał.
Ściąga

Najczęściej używane polecenia

docker ps -a
docker logs -f NAME
docker exec -it NAME sh
docker inspect NAME
docker stop NAME
docker rm NAME
docker image ls
docker volume ls
DEMO NA ŻYWOWykonaj kolejno ps, logs, exec i inspect na działającym API.
Gdy nie działa

Obserwuj, zanim zmienisz

  1. docker ps -a — czy proces nadal działa?
  2. docker logs — co zgłosiła aplikacja?
  3. docker inspect — jaka jest konfiguracja?
  4. docker exec — co widać od środka?
Docker Compose

Opis całego środowiska jako kod

Jeden plik deklaruje usługi, obrazy, sposób budowania, sieci, wolumeny, porty, zmienne i warunki gotowości.

frontend
→
api
→
database
Definicja

Anatomia compose.yaml

services:
  db:
    image: postgres:17
    environment:
      POSTGRES_DB: missions
      POSTGRES_PASSWORD: DB_PASSWORD_FROM_ENV
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:
System

Trzy usługi, jedna sieć projektu

frontend

publikuje port dla przeglądarki

api

wywołuje bazę po nazwie db

db

zapisuje dane w wolumenie

Konfiguracja

Nie wszystko powinno być w obrazie

  • ustawienia przekazujemy przy uruchomieniu,
  • sekretów nie commitujemy do repozytorium,
  • sieć domyślna daje DNS po nazwie usługi,
  • wolumen oddziela cykl życia danych od kontenera.
Gotowość

Uruchomiony proces nie zawsze jest gotowy

healthcheck:
  test: ["CMD-SHELL", "pg_isready -U app"]
  interval: 5s
  timeout: 3s
  retries: 10

depends_on:
  db:
    condition: service_healthy
CLI

Cykl życia środowiska

docker compose config
docker compose up --build -d
docker compose ps
docker compose logs -f api
docker compose down
docker compose down -v   # usuwa też dane!
DEMO NA ŻYWOUsuń ręcznie uruchomione elementy i odtwórz cały system jednym poleceniem Compose.
Bezpieczeństwo

Najczęstsze zagrożenia

  • niezaufane lub stare obrazy,
  • sekrety w warstwach obrazu,
  • proces działający jako root,
  • nadmiar capabilities.
  • montowanie socketu Dockera,
  • publiczne porty bazy,
  • brak limitów zasobów,
  • podatne zależności.
Checklista

Buduj mniejszą powierzchnię ataku

  • używaj minimalnych, oficjalnych obrazów i przypinaj wersje,
  • uruchamiaj proces jako użytkownik bez uprawnień root,
  • nie zapisuj sekretów w Dockerfile ani repozytorium,
  • skanuj obrazy i regularnie je przebudowuj,
  • publikuj tylko potrzebne porty.
Podsumowanie

Obraz opisuje. Kontener wykonuje. Compose łączy.

Dockerfile
→
Image
→
Container
→
System