Cloud i DevOps
Fundamenty chmury i DevOps dla pracującego inżyniera: Docker, CI/CD, Kubernetes, Infrastructure as Code, twelve-factor app, wdrożenia blue-green/canary oraz trzy filary observability.
- Multi-stage buildy Dockera i wpływ warstw na rozmiar oraz cache builda
- Rdzeniowe obiekty Kubernetes: Pod, Deployment, Service, ConfigMap/Secret i ich wzajemne role
- Deklaratywne Infrastructure as Code (Terraform): init/plan/apply, dryf stanu, przewaga nad ręcznym provisioningiem
- Dwanaście czynników aplikacji, zwłaszcza config w zmiennych środowiskowych i bezstanowe procesy
- Blue-green vs canary: mechanizm przełączania ruchu i pułapki migracji bazy danych
- Trzy filary observability (logi, metryki, trace'y) i korelacja przez trace_id
1 · Docker: obrazy, warstwy i multi-stage buildy
★ egzaminKontener nie jest lekką maszyną wirtualną: dzieli jądro systemu hosta i izoluje procesy przez namespaces oraz cgroups. Obraz Docker to niezmienny szablon zbudowany z warstw; kontener to jego uruchomiona instancja z własną, zapisywalną warstwą na wierzchu.
FROM node:22
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
CMD ["node", "dist/server.js"] # --- Etap budowania ---
FROM node:22 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# --- Etap uruchomieniowy ---
FROM node:22-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"] | Obraz bazowy | Rozmiar względem alternatyw | Kompromis |
|---|---|---|
| ubuntu:24.04 / debian:bookworm | Duży, pełny system pakietów | Wygodny do debugowania (shell, apt), ale duża powierzchnia ataku |
| node:22-slim / python:3.13-slim | Średni | Mniej pakietów niż pełny obraz, wciąż ma shell i podstawowe narzędzia |
| gcr.io/distroless/* | Mały, brak shella i menedżera pakietów | Najmniejsza powierzchnia ataku, trudniejszy debugging (brak sh do exec) |
- Sortuj instrukcje od najrzadziej do najczęściej zmienianych (zależności przed kodem źródłowym)
- Używaj .dockerignore, by nie kopiować node_modules/.git do kontekstu builda
- Pinuj wersję obrazu bazowego (node:22.14-slim, nie node:latest) dla powtarzalnych buildów
- Uruchamiaj proces jako użytkownik nie-root (USER) w finalnym etapie
- Łącz RUN apt-get update && apt-get install w jedną instrukcję, by nie zostawiać cache'u pakietów w osobnej warstwie
Build jednoetapowy
- Finalny obraz zawiera kompilator/dev-dependencies, których produkcja nie potrzebuje
- Większa powierzchnia ataku (więcej pakietów = więcej CVE)
- Wolniejszy pull i dłuższy cold start przez zbędny balast
Multi-stage build
- Etap builder zawiera narzędzia, ale nigdy nie trafia do finalnego obrazu
- Do warstwy runtime kopiowane są tylko gotowe artefakty (np. /dist)
- Mniejszy, szczuplejszy obraz gotowy na produkcję
2 · Pipeline CI/CD: etapy i przyczyny porażek
★ egzaminPipeline CI/CD to automatyzacja ścieżki od commitu do działającego kodu na środowisku. Celem jest wykryć problem jak najwcześniej (fail-fast) i sprawić, że wdrożenie jest nudną, powtarzalną czynnością, nie wydarzeniem wysokiego ryzyka.
- 1 Build: kompilacja/pakowanie kodu do artefaktu (obraz Docker, binarka, paczka)
- 2 Test: testy jednostkowe, integracyjne, czasem bezpieczeństwa (SAST, skan zależności)
- 3 Deploy: wypchnięcie artefaktu na środowisko (staging, potem produkcja), zwykle z bramką jakości między etapami
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
build-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- run: npm test
deploy:
needs: build-test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "deploy do produkcji po zielonym build-test" | Etap | Pyta o | Typowe narzędzie |
|---|---|---|
| Build | Czy kod się kompiluje/pakuje? | docker build, npm run build |
| Test | Czy zachowanie jest poprawne? | npm test, pytest, testy kontraktowe |
| Deploy | Czy artefakt działa na docelowym środowisku? | kubectl apply, terraform apply, helm upgrade |
- 1 Trigger Push lub pull request uruchamia pipeline automatycznie
- 2 Build Kod kompilowany/pakowany do artefaktu (np. obraz Docker z tagiem commit SHA)
- 3 Test Automatyczne testy blokują dalsze etapy przy pierwszym czerwonym wyniku
- 4 Deploy do staging Artefakt trafia na środowisko zbliżone do produkcji
- 5 Bramka (opcjonalna) Ręczna akceptacja lub automatyczne kryteria przed produkcją
- 6 Deploy do produkcji Ten sam, niezmieniony artefakt z wcześniejszych etapów trafia na produkcję
3 · Fundamentalne obiekty Kubernetes
★ egzaminKubernetes orkiestruje kontenery przez deklaratywne obiekty API: deklarujesz pożądany stan, a control plane (scheduler, kontrolery) nieustannie dąży do zgodności rzeczywistego stanu z zadeklarowanym (reconciliation loop).
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
labels:
app: api-server
spec:
replicas: 3
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api-server
image: registry.example.com/api-server:1.4.2
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: api-server-config
- secretRef:
name: api-server-secrets
---
apiVersion: v1
kind: Service
metadata:
name: api-server
spec:
type: ClusterIP
selector:
app: api-server
ports:
- port: 80
targetPort: 8080 apiVersion: v1
kind: ConfigMap
metadata:
name: api-server-config
data:
LOG_LEVEL: "info"
FEATURE_FLAG_NEW_CHECKOUT: "true"
---
apiVersion: v1
kind: Secret
metadata:
name: api-server-secrets
type: Opaque
stringData:
DATABASE_URL: postgres://app:changeme@db.internal:5432/app | Obiekt | Odpowiada za | Analogia |
|---|---|---|
| Pod | Uruchomienie kontenera(-ów) jako jednej jednostki | Proces systemowy |
| Deployment | Utrzymanie N replik Poda + rolling update/rollback | Systemd unit z auto-restartem i wersjonowaniem |
| Service | Stabilny adres do zmiennego zestawu Podów | Load balancer + wewnętrzny DNS |
| ConfigMap/Secret | Konfiguracja i dane wrażliwe poza obrazem | Plik .env zamontowany z zewnątrz |
4 · Infrastructure as Code: deklaratywne provisioning
★ egzaminInfrastructure as Code zamienia klikanie w konsoli chmurowej na kod: infrastrukturę opisuje się w plikach, wersjonuje w Git i stosuje przez narzędzie, które oblicza różnicę między stanem zadeklarowanym a rzeczywistym.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = "eu-central-1"
}
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t3.micro"
tags = {
Name = "web-server"
}
} - 1 terraform init Pobiera providery (np. hashicorp/aws) i inicjalizuje backend stanu
- 2 terraform plan Pokazuje różnicę między kodem a bieżącym stanem (co zostanie dodane/zmienione/usunięte)
- 3 terraform apply Wykonuje zmiany z planu i zapisuje nowy stan
- 4 terraform destroy Usuwa zasoby zarządzane przez ten kod, zgodnie z zapisanym stanem
- Każda zmiana infrastruktury przechodzi code review jak zmiana w aplikacji (pull request z terraform plan w opisie)
- Infrastruktura jest odtwarzalna 1:1 w innym regionie/koncie (disaster recovery, nowe środowisko)
- Historia zmian jest w Git, nie w pamięci osoby, która kiedyś kliknęła w konsoli
- Ten sam kod (moduł) da się sparametryzować i użyć dla dev/staging/prod
| Podejście | Przykład | Ryzyko |
|---|---|---|
| Ręczne klikanie w konsoli | AWS/GCP/Azure Console | Brak historii zmian, nieodtwarzalne 1:1, wiedza 'w głowie' jednej osoby |
| Imperatywne skrypty | aws-cli/gcloud w bash | Działa, ale trzeba samemu obsłużyć idempotencję i błędy częściowego wykonania |
| Deklaratywne IaC | Terraform, Pulumi | Idempotentne z założenia, ale wymaga zarządzania plikiem stanu |
5 · Dwanaście czynników aplikacji (Twelve-Factor App)
★ egzaminTwelve-Factor App (Heroku, 2011) to zestaw dwunastu praktyk budowania aplikacji SaaS łatwych do skalowania, przenoszenia między środowiskami i utrzymania bez ręcznej ingerencji. Mimo wieku, każdy punkt wprost mapuje się na dzisiejsze kontenery i Kubernetes.
- 1 I. Codebase - jedna baza kodu w kontroli wersji, wiele wdrożeń z niej wynikających
- 2 II. Dependencies - jawnie zadeklarowane zależności (package.json), nigdy 'zainstalowane ręcznie na serwerze'
- 3 III. Config - konfiguracja różniąca się między środowiskami trzymana w zmiennych środowiskowych, nie w kodzie
- 4 IV. Backing services - baza danych, kolejka, cache traktowane jako podłączane zasoby wymienne bez zmiany kodu
- 5 V. Build, release, run - ścisłe rozdzielenie etapu budowy, złożenia z configiem i uruchomienia
- 6 VI. Processes - procesy aplikacji bezstanowe, trwały stan żyje w backing service
- 7 VII. Port binding - aplikacja sama nasłuchuje na porcie, nie wymaga wstrzykniętego web serwera
- 8 VIII. Concurrency - skalowanie przez uruchamianie kolejnych instancji procesu (horyzontalnie), nie przez grubszy proces
- 9 IX. Disposability - szybki start i graceful shutdown, procesy jednorazowe do zabicia/odtworzenia w każdej chwili
- 10 X. Dev/prod parity - środowiska deweloperskie, staging i produkcja maksymalnie zbliżone
- 11 XI. Logs - logi jako strumień zdarzeń na stdout, nie zarządzane przez aplikację pliki
- 12 XII. Admin processes - zadania administracyjne (migracje, konsola) uruchamiane jako jednorazowe procesy w tym samym środowisku co aplikacja
// Factor III: Config - wartości środowiskowe, nie zahardkodowane w kodzie
const config = {
port: process.env.PORT ?? 3000,
databaseUrl: process.env.DATABASE_URL,
logLevel: process.env.LOG_LEVEL ?? 'info',
};
if (!config.databaseUrl) {
throw new Error('DATABASE_URL is required');
} | Czynnik | Skrót myślowy |
|---|---|
| I. Codebase | Jedno repo, wiele deployów |
| III. Config | Env vars, nie kod |
| VI. Processes | Bez stanu w pamięci |
| IX. Disposability | Można zabić w każdej chwili |
| XI. Logs | stdout, nie własny plik |
6 · Strategie wdrożeń: blue-green i canary
Blue-green i canary to dwie odpowiedzi na to samo pytanie: jak wdrożyć nową wersję bez przestoju i bez ryzykowania całego ruchu produkcyjnego na raz.
apiVersion: v1
kind: Service
metadata:
name: api-server
spec:
selector:
app: api-server
version: blue
ports:
- port: 80
targetPort: 8080 kubectl patch service api-server -p '{"spec":{"selector":{"version":"green"}}}' apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server-stable
spec:
replicas: 9
selector:
matchLabels:
app: api-server
track: stable
template:
metadata:
labels:
app: api-server
track: stable
spec:
containers:
- name: api-server
image: registry.example.com/api-server:1.4.2
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server-canary
spec:
replicas: 1
selector:
matchLabels:
app: api-server
track: canary
template:
metadata:
labels:
app: api-server
track: canary
spec:
containers:
- name: api-server
image: registry.example.com/api-server:1.5.0-rc1 | Blue-green | Canary | |
|---|---|---|
| Ryzyko na start | 0% ruchu na nowej wersji przed przełączeniem | Mały procent ruchu od razu na nowej wersji |
| Szybkość rollbacku | Natychmiastowa (przełącz selektor z powrotem) | Szybka, ale wymaga też cofnięcia proporcji ruchu |
| Koszt infrastruktury | 2x zasobów przez czas przejścia | Nieznacznie większy (dodatkowe repliki canary) |
| Wymagania | Load balancer/Service z łatwo przełączalnym targetem | Możliwość precyzyjnego podziału ruchu (mesh lub proporcja replik) |
- Health checki (readiness/liveness) - bez nich orkiestrator nie wie, czy nowa wersja jest gotowa na ruch
- Metryki błędów i latencji dostępne w czasie rzeczywistym jako podstawa decyzji o zwiększeniu ruchu canary lub rollbacku
- Automatyczny trigger rollbacku po przekroczeniu progu błędów, nie tylko ręczna obserwacja
- Kompatybilność wsteczna schematu bazy danych i kontraktów API między starą a nową wersją
7 · Observability: logi, metryki, trace'y
★ egzaminObservability to zdolność zadawania systemowi pytań, których nie przewidziałeś przy jego budowie, opierając się na trzech uzupełniających się źródłach sygnału: logach, metrykach i trace'ach.
{"timestamp":"2026-07-05T10:32:14Z","level":"error","service":"checkout-api","trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","message":"payment gateway timeout","order_id":"ord_88213"} # HELP http_requests_total Total number of HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="GET",status="200"} 182933
http_requests_total{method="GET",status="500"} 12
# HELP http_request_duration_seconds Histogram of request duration
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.1"} 15234
http_request_duration_seconds_bucket{le="0.5"} 18211
http_request_duration_seconds_bucket{le="+Inf"} 18220 from opentelemetry import trace
tracer = trace.get_tracer("checkout-service")
def process_order(order_id: str) -> None:
with tracer.start_as_current_span("process_order") as span:
span.set_attribute("order.id", order_id)
charge_payment(order_id) | Filar | Kardynalność/koszt | Typowe pytanie |
|---|---|---|
| Logi | Wysoki koszt przy dużym wolumenie, bogaty kontekst | Co dokładnie się stało w tym jednym żądaniu o 10:32? |
| Metryki | Niski koszt, niska kardynalność | Czy p99 latencji rośnie w ostatniej godzinie? |
| Trace'y | Średni koszt, często samplowane | Która usługa w łańcuchu wywołań spowalnia to żądanie? |
8 · Świadomość kosztów w chmurze
Chmura rozlicza za zarezerwowaną pojemność, nie za realne wykorzystanie: uruchomiona instancja, przypięty wolumen czy działający load balancer kosztują identycznie, czy obsługują milion żądań, czy zero.
- Nieodpięte wolumeny dyskowe (EBS/Persistent Disk) po usunięciu instancji, która ich używała
- Środowiska dev/staging działające 24/7, mimo że są używane tylko w godzinach pracy
- Przewymiarowane instancje wybrane 'na zapas' zamiast na podstawie realnego profilu obciążenia
- Zapomniane zasoby testowe/PoC, których nikt nie posprzątał po zakończeniu eksperymentu
- Nieużywane elastic/static IP przypisane, ale niepodłączone do żadnego zasobu
aws ec2 describe-volumes \
--filters Name=status,Values=available \
--query 'Volumes[].{ID:VolumeId,Size:Size,AZ:AvailabilityZone}' \
--output table resource "aws_autoscaling_schedule" "scale_down_nightly" {
scheduled_action_name = "scale-down-nightly"
autoscaling_group_name = aws_autoscaling_group.app.name
recurrence = "0 20 * * MON-FRI"
min_size = 0
max_size = 0
desired_capacity = 0
} | Model rozliczenia | Kiedy ma sens | Kompromis |
|---|---|---|
| On-demand | Nieprzewidywalne/krótkotrwałe obciążenie | Najwyższa cena jednostkowa, zero zobowiązań |
| Reserved/Savings Plan | Stabilne, przewidywalne obciążenie bazowe | Niższa cena za zobowiązanie 1-3 lata |
| Spot/Preemptible | Obciążenia odporne na przerwanie (batch, CI) | Znacznie taniej, ale instancja może zostać odebrana |
Sprawdź się - testowanie to nauka
24 pytań w losowej kolejności. Twoje wyniki zapisują się lokalnie.