Docker и Kubernetes для Go-разработчика
Зачем знать: в 2026 практически весь Go-код в продакшене работает в контейнерах, чаще всего в Kubernetes. Middle 1 разработчик обязан уметь собирать оптимальный multi-stage образ (5-30 МБ вместо 800 МБ), писать корректные probes, понимать requests/limits, ConfigMap/Secret, Service/Deployment. Без этого нельзя ни задеплоить, ни задебажить production-инцидент.
Содержание
Заголовок раздела «Содержание»- Базовая концепция
- Как в Go (с примерами)
- Gotchas
- Best practices в production
- Вопросы на собесе
- Practice
- Источники
1. Базовая концепция
Заголовок раздела «1. Базовая концепция»Что такое Docker
Заголовок раздела «Что такое Docker»Docker — это инструмент для упаковки приложения в контейнер: изолированный процесс, использующий ядро хоста, но видящий своё дерево файлов, сеть, PID-пространство. Образ контейнера состоит из слоёв (immutable layers) — каждое изменение в Dockerfile создаёт новый слой.
Чем контейнер отличается от VM
Заголовок раздела «Чем контейнер отличается от VM»| Container | VM | |
|---|---|---|
| Ядро | общее (хост) | собственное |
| Старт | 100ms | 30-60s |
| Размер | 5MB-1GB | 1-10GB |
| Изоляция | namespaces+cgroups | hardware-level |
| Overhead | ~0% | 5-15% |
Образ vs контейнер
Заголовок раздела «Образ vs контейнер»- Образ (image) — immutable шаблон с layers.
- Контейнер (container) — запущенный инстанс образа.
- Реестр (registry) — хранилище образов (Docker Hub, ghcr.io, ECR, Yandex Container Registry).
Что такое Kubernetes
Заголовок раздела «Что такое Kubernetes»K8s — orchestrator для контейнеров. Управляет:
- Запуском (
Pod= 1+ контейнеров). - Масштабированием (
Deployment→ReplicaSet→Pods). - Сетью (
Service,Ingress). - Конфигурацией (
ConfigMap,Secret). - Хранилищем (
PersistentVolume). - Здоровьем (
probes). - Авто-восстановлением (если pod упал — запускается заново).
Архитектура K8s
Заголовок раздела «Архитектура K8s»[Control Plane: kube-apiserver, etcd, controller-manager, scheduler] ↓ (kubectl, controllers)[Worker Nodes: kubelet, kube-proxy, container runtime (containerd/CRI-O)] ↓[Pods → Containers]В 2026 Docker как runtime в K8s не используется (dockershim удалён в 1.24, апрель 2022). Сейчас — containerd или CRI-O.
Основные объекты
Заголовок раздела «Основные объекты»- Pod — минимальная единица, 1+ контейнера, общая сеть и volumes.
- Deployment — управляет ReplicaSet, rolling update.
- StatefulSet — для stateful (БД, кэши с stable IDs).
- DaemonSet — один pod на каждом node (мониторинг агенты).
- Job/CronJob — разовые/периодические задачи.
- Service — стабильный endpoint (ClusterIP, NodePort, LoadBalancer).
- Ingress — HTTP-роутинг снаружи кластера.
- ConfigMap — конфигурация (env, файлы).
- Secret — sensitive данные (base64-encoded, не encrypted by default).
- Namespace — изоляция логическая.
2. Как в Go (с примерами)
Заголовок раздела «2. Как в Go (с примерами)»2.1 Простейший Dockerfile (BAD)
Заголовок раздела «2.1 Простейший Dockerfile (BAD)»FROM golang:1.22WORKDIR /appCOPY . .RUN go build -o server .CMD ["./server"]Размер: ~800МБ (golang base + ваш бинарник).
2.2 Multi-stage build (GOOD)
Заголовок раздела «2.2 Multi-stage build (GOOD)»# Build stageFROM golang:1.22 AS builderWORKDIR /app
# Кешируем зависимости отдельным слоемCOPY go.mod go.sum ./RUN go mod download
# Копируем исходники и собираемCOPY . .RUN CGO_ENABLED=0 GOOS=linux go build \ -trimpath \ -ldflags='-s -w -X main.Version=$(git describe --tags)' \ -o /app/bin .
# Runtime stage — distrolessFROM gcr.io/distroless/static-debian12:nonrootCOPY --from=builder /app/bin /app/binUSER nonroot:nonrootEXPOSE 8080ENTRYPOINT ["/app/bin"]Размер: ~5-15МБ.
Пояснения
Заголовок раздела «Пояснения»CGO_ENABLED=0— статическая линковка, не нужны glibc/musl в образе.-trimpath— убирает абсолютные пути из binary (для воспроизводимых сборок, безопасности).-ldflags='-s -w':-s— убирает symbol table.-w— убирает DWARF debug info.- Уменьшает binary на 20-30%, но затрудняет debug.
-X main.Version=...— инжектит версию в переменную при сборке.gcr.io/distroless/static-debian12:nonroot— минимальный runtime (только CA certs, tzdata, glibc-stub), без shell, под non-root пользователем.
2.3 Базовые образы — выбор
Заголовок раздела «2.3 Базовые образы — выбор»| Образ | Размер | Shell | SSL certs | CGO | Когда |
|---|---|---|---|---|---|
scratch | 0 | нет | нет (надо COPY) | нет | минимизация, нет ca-certs |
gcr.io/distroless/static | ~2МБ | нет | да | нет | рекомендуется |
gcr.io/distroless/base | ~20МБ | нет | да | да (glibc) | если нужен CGO |
alpine | ~5МБ | да | yes (apk add) | musl (CGO с проблемами) | если нужен shell для debug |
debian:slim | ~80МБ | да | да | да | максимальная совместимость |
ubuntu | ~80МБ | да | да | да | привычка |
Дефолт в 2026: distroless static с nonroot.
2.4 .dockerignore
Заголовок раздела «2.4 .dockerignore».git/.github/*.mdnode_modules/*.testcoverage.outtmp/.idea/.vscode/Без этого COPY . . тащит .git (десятки МБ) и invalidates cache.
2.5 docker-compose для локалки
Заголовок раздела «2.5 docker-compose для локалки»services: api: build: . ports: - "8080:8080" env_file: - .env.local depends_on: db: condition: service_healthy redis: condition: service_started
db: image: postgres:16-alpine environment: POSTGRES_USER: app POSTGRES_PASSWORD: app POSTGRES_DB: app ports: - "5432:5432" volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U app"] interval: 5s timeout: 3s retries: 5
redis: image: redis:7-alpine ports: - "6379:6379"
volumes: pgdata:Запуск: docker compose up -d. В 2026 — docker compose (без дефиса), плагин compose v2.
2.6 Kubernetes Deployment
Заголовок раздела «2.6 Kubernetes Deployment»apiVersion: apps/v1kind: Deploymentmetadata: name: payments namespace: prod labels: app: paymentsspec: replicas: 3 selector: matchLabels: app: payments strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 template: metadata: labels: app: payments version: v1.2.3 spec: containers: - name: app image: ghcr.io/myorg/payments:v1.2.3 ports: - containerPort: 8080 name: http - containerPort: 9090 name: metrics env: - name: ENV value: production - name: GOMEMLIMIT valueFrom: resourceFieldRef: resource: limits.memory divisor: 1Mi - name: GOMAXPROCS valueFrom: resourceFieldRef: resource: limits.cpu envFrom: - configMapRef: name: payments-config - secretRef: name: payments-secrets resources: requests: cpu: "200m" memory: "256Mi" limits: cpu: "1000m" memory: "512Mi" startupProbe: httpGet: path: /healthz port: http failureThreshold: 30 periodSeconds: 2 readinessProbe: httpGet: path: /ready port: http periodSeconds: 5 livenessProbe: httpGet: path: /healthz port: http periodSeconds: 10 failureThreshold: 3 securityContext: runAsNonRoot: true runAsUser: 65532 readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: ["ALL"] terminationGracePeriodSeconds: 302.7 Service
Заголовок раздела «2.7 Service»apiVersion: v1kind: Servicemetadata: name: payments namespace: prodspec: type: ClusterIP selector: app: payments ports: - name: http port: 80 targetPort: http2.8 ConfigMap + Secret
Заголовок раздела «2.8 ConfigMap + Secret»apiVersion: v1kind: ConfigMapmetadata: name: payments-configdata: LOG_LEVEL: "info" DB_HOST: "postgres.prod.svc.cluster.local" DB_PORT: "5432"
---apiVersion: v1kind: Secretmetadata: name: payments-secretstype: OpaquestringData: # удобнее чем data (base64) DB_PASSWORD: "super-secret" STRIPE_API_KEY: "sk_live_..."2.9 Probes — корректная реализация
Заголовок раздела «2.9 Probes — корректная реализация»package main
import ( "context" "net/http" "sync/atomic" "time")
var ( ready atomic.Bool healthy atomic.Bool)
func main() { healthy.Store(true)
mux := http.NewServeMux()
// Liveness — процесс жив (deadlock detection) mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { if !healthy.Load() { w.WriteHeader(http.StatusServiceUnavailable) return } w.WriteHeader(http.StatusOK) })
// Readiness — готов принимать трафик (зависимости подключены) mux.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) { if !ready.Load() { w.WriteHeader(http.StatusServiceUnavailable) return } // Проверка зависимостей ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second) defer cancel() if err := db.PingContext(ctx); err != nil { http.Error(w, "db unreachable", http.StatusServiceUnavailable) return } w.WriteHeader(http.StatusOK) })
server := &http.Server{Addr: ":8080", Handler: mux}
// Готовы после запуска и инициализации go func() { // миграции, кэш, прогрев initApp() ready.Store(true) }()
// Graceful shutdown go handleShutdown(server)
server.ListenAndServe()}
func handleShutdown(srv *http.Server) { sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT) <-sig
ready.Store(false) // K8s перестанет слать трафик time.Sleep(5 * time.Second) // ждать удаления endpoints
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second) defer cancel() srv.Shutdown(ctx)}Различия probes
Заголовок раздела «Различия probes»- Startup probe (с 1.16) — пока success, остальные probes не запускаются. Для долгих стартов (миграции, прогрев).
- Readiness — если fail, pod исключается из Service. Не убивает pod.
- Liveness — если fail, kubelet перезапускает контейнер. Используйте для detection deadlock.
2.10 Resources: requests vs limits
Заголовок раздела «2.10 Resources: requests vs limits»- requests — гарантированные ресурсы. Scheduler выбирает node с достаточными ресурсами.
- limits — верхняя граница. CPU throttling, memory — OOM kill при превышении.
200m= 0.2 CPU = 200 millicores.- При limits CPU контейнер throttled.
512Mi= 512 мебибайт = 512 × 2²⁰ байт.- При превышении limit — OOMKilled.
2.11 GOMEMLIMIT для Go (1.19+)
Заголовок раздела «2.11 GOMEMLIMIT для Go (1.19+)»Go рантайм не знает про cgroup limits → может OOM не запустив GC. Решение:
env: - name: GOMEMLIMIT valueFrom: resourceFieldRef: resource: limits.memory divisor: 1MiGOMEMLIMIT=460MiB (≈ 90% от 512Mi limit) заставит Go запускать GC агрессивнее при подходе к лимиту.
В 2026 есть автоматические библиотеки: github.com/KimMachineGun/automemlimit (читает cgroup и выставляет GOMEMLIMIT).
2.12 GOMAXPROCS в K8s
Заголовок раздела «2.12 GOMAXPROCS в K8s»Go по умолчанию использует runtime.NumCPU() = количество ядер node. Но контейнер с cpu.limits=500m имеет только 0.5 CPU.
import _ "go.uber.org/automaxprocs"Эта библиотека читает cgroup и выставляет GOMAXPROCS правильно. Обязательна для каждого Go-сервиса в K8s.
2.13 HPA (HorizontalPodAutoscaler)
Заголовок раздела «2.13 HPA (HorizontalPodAutoscaler)»apiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata: name: paymentsspec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: payments minReplicas: 3 maxReplicas: 20 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Pods pods: metric: name: http_requests_per_second target: type: AverageValue averageValue: "100"2.14 Helm — пакетный менеджер для K8s
Заголовок раздела «2.14 Helm — пакетный менеджер для K8s»helm install payments ./chart \ --namespace prod \ --values values.prod.yamlСтруктура chart:
chart/├── Chart.yaml├── values.yaml└── templates/ ├── deployment.yaml ├── service.yaml └── configmap.yamlTemplates используют Go templates ({{ .Values.replicas }}).
2.15 kubectl шпаргалка для debug
Заголовок раздела «2.15 kubectl шпаргалка для debug»kubectl get pods -n prod # список подовkubectl describe pod payments-xxx -n prod # детали (события)kubectl logs payments-xxx -n prod # логиkubectl logs payments-xxx -n prod --previous # логи прошлого crashkubectl logs -f -l app=payments -n prod # follow всех pods с labelkubectl exec -it payments-xxx -n prod -- sh # шелл (но distroless без sh!)kubectl port-forward payments-xxx 8080 -n prod # проброс порта на локалкуkubectl get events -n prod --sort-by=.lastTimestampkubectl top pods -n prod # CPU/memorykubectl rollout status deploy/payments -n prodkubectl rollout undo deploy/payments -n prod # откат2.16 Service discovery
Заголовок раздела «2.16 Service discovery»В кластере DNS работает по схеме <service>.<namespace>.svc.cluster.local:
db, _ := sql.Open("postgres", "postgres://postgres.prod.svc.cluster.local:5432/app")Или короче: postgres.prod или просто postgres (если в той же namespace).
2.17 Image security
Заголовок раздела «2.17 Image security»trivy image ghcr.io/myorg/payments:v1.2.3Сканирует на CVEs. В CI/CD обязательно. Также:
- Подписи образов (cosign).
- Pin digest (
@sha256:...) вместо tag. - SBOM (Software Bill of Materials).
2.18 Ingress
Заголовок раздела «2.18 Ingress»apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: payments annotations: nginx.ingress.kubernetes.io/rewrite-target: /spec: ingressClassName: nginx tls: - hosts: ["api.example.com"] secretName: api-tls rules: - host: api.example.com http: paths: - path: /payments pathType: Prefix backend: service: name: payments port: number: 80Или Gateway API (новый стандарт, 1.30+):
kind: HTTPRoute# ...2.19 Operators (briefly)
Заголовок раздела «2.19 Operators (briefly)»CRD + controller — расширение K8s для управления сложными системами (Postgres operator, Redis operator). Для middle 1 — знать что есть, использовать готовые (CloudNativePG, Strimzi для Kafka).
3. Gotchas
Заголовок раздела «3. Gotchas»3.1 COPY . . раньше go mod
Заголовок раздела «3.1 COPY . . раньше go mod»COPY . . # BADRUN go mod download # invalidates cache при любом измененииvs
COPY go.mod go.sum ./RUN go mod download # этот слой кешируетсяCOPY . . # invalidates только при изменении кода3.2 distroless без shell — нельзя exec sh
Заголовок раздела «3.2 distroless без shell — нельзя exec sh»kubectl exec -it pod -- sh # exec format errorРешения: debug ephemeral container (kubectl debug), или использовать :debug tag (gcr.io/distroless/static:debug — есть busybox).
3.3 Без CGO_ENABLED=0
Заголовок раздела «3.3 Без CGO_ENABLED=0»RUN go build -o /app/bin .# Binary с glibc → FROM scratch не работаетРешение: CGO_ENABLED=0 или используйте distroless/base (с glibc).
3.4 SIGTERM игнор → 30s ждёт kubectl
Заголовок раздела «3.4 SIGTERM игнор → 30s ждёт kubectl»K8s шлёт SIGTERM, ждёт terminationGracePeriodSeconds (default 30s), потом SIGKILL. Если ваш Go-код не реагирует на SIGTERM → задержки rolling update.
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)defer cancel()3.5 Readiness up = трафик идёт. Ошибка
Заголовок раздела «3.5 Readiness up = трафик идёт. Ошибка»Если зависимость (Redis) недоступна, readiness должен fail → K8s уберёт pod из Service.
3.6 Liveness слишком агрессивный
Заголовок раздела «3.6 Liveness слишком агрессивный»livenessProbe: httpGet: { path: /healthz } periodSeconds: 1 failureThreshold: 1При микро-пике CPU pod рестартует. Делайте failureThreshold: 3-5, periodSeconds: 10.
3.7 Liveness == Readiness
Заголовок раздела «3.7 Liveness == Readiness»livenessProbe: httpGet: { path: /ready } # проверяет БДЕсли БД упала на 30 секунд — все поды рестартуют → каскадная катастрофа. Liveness проверяет только себя, не зависимости.
3.8 OOMKilled из-за GC
Заголовок раздела «3.8 OOMKilled из-за GC»Если limits.memory = 512Mi и GOMEMLIMIT не выставлен — Go может OOM не успев запустить GC. Симптом: pod рестартует с OOMKilled, при этом heap < limit.
3.9 ConfigMap не обновляется без рестарта
Заголовок раздела «3.9 ConfigMap не обновляется без рестарта»Pod читает env в момент старта. Изменили ConfigMap → надо kubectl rollout restart deployment/....
Volume-mounted ConfigMap обновляется (через ~minute), но приложение должно watch файл.
3.10 Secret в env переменных
Заголовок раздела «3.10 Secret в env переменных»type: Opaque Secret — это base64, не encryption. Кто имеет доступ к namespace, видит секрет. Решения: KMS, sealed-secrets, External Secrets Operator (Vault, AWS Secrets Manager).
3.11 latest tag
Заголовок раздела «3.11 latest tag»image: myapp:latestНикогда. Pod рестартует, scheduler выберет другую ноду, образ может быть другим. Всегда:
image: myapp:v1.2.3# илиimage: myapp@sha256:abc...3.12 hostPath для логов
Заголовок раздела «3.12 hostPath для логов»volumes: - hostPath: path: /var/log/myappPod может переехать на другую node → данные исчезнут. Используйте PVC или stdout.
3.13 GOMAXPROCS из NumCPU
Заголовок раздела «3.13 GOMAXPROCS из NumCPU»Без automaxprocs go-сервис с cpu.limits=500m получит GOMAXPROCS=N (количество ядер node, например 64). Огромные scheduler overhead, contention.
3.14 imagePullPolicy
Заголовок раздела «3.14 imagePullPolicy»IfNotPresent(default для tagged) — берёт локальный, если есть.Always(default для:latest) — всегда pull.
При CI/CD с одинаковыми тэгами нужно Always (или менять tag на каждую сборку).
3.15 Pod не стартует — Pending
Заголовок раздела «3.15 Pod не стартует — Pending»kubectl describe pod:
Insufficient cpu/memory— у node нет ресурсов.node(s) didn't match Pod's node affinity— taints/affinity не дают.ImagePullBackOff— нет доступа к registry, неверный тэг.CreateContainerConfigError— Secret/ConfigMap отсутствует.
3.16 CrashLoopBackOff
Заголовок раздела «3.16 CrashLoopBackOff»Контейнер рестартует с экспоненциальной задержкой (10s, 20s, 40s … 5min). kubectl logs --previous — логи прошлой попытки.
3.17 Cluster DNS
Заголовок раздела «3.17 Cluster DNS»kubectl exec nslookup postgres.prod — если не резолвится, DNS pods (coredns) проблемы. Не специфично для Go.
3.18 PodDisruptionBudget
Заголовок раздела «3.18 PodDisruptionBudget»apiVersion: policy/v1kind: PodDisruptionBudgetmetadata: { name: payments }spec: minAvailable: 2 selector: { matchLabels: { app: payments } }Защита: при drain node нельзя убить больше, чем разрешено. Без него — кластерный upgrade может одновременно убить все pods вашего сервиса.
4. Best practices в production
Заголовок раздела «4. Best practices в production»4.1 Дисциплина в Dockerfile
Заголовок раздела «4.1 Дисциплина в Dockerfile»- Multi-stage build, distroless static, nonroot user, read-only root fs.
.dockerignoreобязателен.- Версии base images pinned (
golang:1.22.3неgolang:1.22).
4.2 Воспроизводимые сборки
Заголовок раздела «4.2 Воспроизводимые сборки»RUN CGO_ENABLED=0 go build -trimpath -ldflags='-s -w -buildid=' -o /app/bin .-buildid= + -trimpath → бинарь bit-for-bit reproducible. Полезно для supply chain security.
4.3 SBOM и подписи
Заголовок раздела «4.3 SBOM и подписи»syft my-image:tag -o spdx-json > sbom.jsoncosign sign --key cosign.key my-image:tagВ 2026 это стандарт для compliance (SLSA level 2+).
4.4 Минимум привилегий
Заголовок раздела «4.4 Минимум привилегий»securityContext: runAsNonRoot: true runAsUser: 65532 # nonroot user (distroless) readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: ["ALL"] seccompProfile: type: RuntimeDefault4.5 Resources
Заголовок раздела «4.5 Resources»- Всегда указывайте requests и limits.
- Requests = средняя нагрузка (для scheduler).
- Limits = max (для предотвращения noisy neighbour).
- Memory: limits = requests (нет swap, нельзя превысить).
- CPU: limits > requests (burstable).
4.6 GOMEMLIMIT + automaxprocs
Заголовок раздела «4.6 GOMEMLIMIT + automaxprocs»import _ "go.uber.org/automaxprocs"// в init или через env: GOMEMLIMITБез этих двух Go-сервис в K8s ведёт себя неправильно.
4.7 Graceful shutdown
Заголовок раздела «4.7 Graceful shutdown»ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)defer cancel()
go func() { srv.ListenAndServe()}()
<-ctx.Done()// 1. ready=false (K8s исключит из Service)ready.Store(false)time.Sleep(5 * time.Second) // ждать endpoints удаления
// 2. Shutdown servershutdownCtx, c := context.WithTimeout(context.Background(), 25*time.Second)defer c()srv.Shutdown(shutdownCtx)terminationGracePeriodSeconds: 30 → 5s sleep + 25s shutdown.
4.8 PodAntiAffinity
Заголовок раздела «4.8 PodAntiAffinity»affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: topologyKey: kubernetes.io/hostname labelSelector: matchLabels: { app: payments }Не запускать все реплики на одной node — отказ node не убивает сервис.
4.9 PodDisruptionBudget — обязательно для prod
Заголовок раздела «4.9 PodDisruptionBudget — обязательно для prod»См. 3.18.
4.10 NetworkPolicy
Заголовок раздела «4.10 NetworkPolicy»apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: { name: payments-egress }spec: podSelector: { matchLabels: { app: payments } } policyTypes: [Egress] egress: - to: - podSelector: { matchLabels: { app: postgres } } ports: - protocol: TCP port: 5432Zero-trust: сервис ходит только к нужным.
4.11 Логи в stdout
Заголовок раздела «4.11 Логи в stdout»Не файлы внутри контейнера. fluent-bit/vector подберёт.
4.12 Probes — раздельные endpoints
Заголовок раздела «4.12 Probes — раздельные endpoints»/healthz — только базовый ping. /ready — проверка зависимостей. /startup — для долгого инициализации.
4.13 Один процесс — один контейнер
Заголовок раздела «4.13 Один процесс — один контейнер»Не запускайте supervisor + N процессов в одном контейнере. Sidecar-pattern: отдельные контейнеры в pod (например, log shipper).
4.14 Versioned image tags
Заголовок раздела «4.14 Versioned image tags»v1.2.3 или git SHA. Никаких latest, dev, prod.
4.15 Helm/Kustomize для production
Заголовок раздела «4.15 Helm/Kustomize для production»Не плодите YAML вручную. Используйте Helm chart или Kustomize overlay для разных env (dev/staging/prod).
4.16 GitOps
Заголовок раздела «4.16 GitOps»ArgoCD / Flux: K8s манифесты в git, контроллер применяет автоматически. Откат = git revert.
4.17 Multi-arch images
Заголовок раздела «4.17 Multi-arch images»docker buildx build --platform linux/amd64,linux/arm64 -t myimage:v1.2.3 --push .В 2026 ARM64 (Graviton, Apple Silicon) — норма. Сборка через buildx.
4.18 Migrations через initContainer или Job
Заголовок раздела «4.18 Migrations через initContainer или Job»initContainers: - name: migrate image: migrate/migrate:v4 command: ["migrate", "-path", "/migrations", "-database", "$(DB_URL)", "up"]Не делайте миграции в main — race при rolling update.
4.19 Health endpoint не должен быть тяжёлым
Заголовок раздела «4.19 Health endpoint не должен быть тяжёлым»Probe вызывается каждые N секунд. Если /ready идёт в БД с тяжёлым query → DDoS себя.
4.20 Vertical Pod Autoscaler
Заголовок раздела «4.20 Vertical Pod Autoscaler»VPA автоматически подстраивает requests/limits на основе исторических данных. Используется в больших кластерах для оптимизации utilization.
5. Вопросы на собесе
Заголовок раздела «5. Вопросы на собесе»-
Что такое multi-stage build и зачем? Несколько
FROMв Dockerfile. Build stage компилирует, runtime stage только копирует binary. Итог: маленький image без build tools. -
Чем distroless лучше alpine? Нет shell (меньше surface для атак), меньше CVE, чёткое разделение build/runtime. Alpine использует musl (CGO иногда ломается).
-
Зачем
CGO_ENABLED=0? Статическая линковка. Бинарь не зависит от glibc/musl → можно класть в scratch. -
Что делает
-ldflags='-s -w'?-sубирает symbol table,-wубирает DWARF debug. Уменьшает бинарь на 20-30%, затрудняет debug. -
Чем readiness отличается от liveness? Liveness fail → перезапуск контейнера (deadlock detection). Readiness fail → исключение из Service (трафик не идёт).
-
Что такое startup probe? Для долгого старта. Пока success, остальные probes не активны.
-
Зачем GOMEMLIMIT? Go runtime не знает про cgroup memory limit → может OOM не успев GC. GOMEMLIMIT даёт ориентир GC.
-
Зачем automaxprocs? Без неё GOMAXPROCS = NumCPU (ядер на node, не лимит контейнера) → лишний overhead.
-
Чем requests от limits? Requests — гарантия для scheduler. Limits — верхняя граница. CPU limits → throttle, memory → OOMKilled.
-
Что такое Deployment vs StatefulSet? Deployment — для stateless (HTTP API). StatefulSet — для stateful (БД), stable network identity, ordered rolling update.
-
Чем ConfigMap от Secret? Семантика. Secret base64-encoded (не encrypted!), но обычно RBAC ограничивает доступ.
-
Что такое sidecar pattern? Дополнительный контейнер в Pod, дополняющий основной (например, envoy, log shipper).
-
Зачем pod anti-affinity? Распределить реплики по разным nodes/zones, чтобы отказ одного не положил весь сервис.
-
Как сервис A находит сервис B в K8s? DNS:
<service>.<namespace>.svc.cluster.local. ClusterIP service балансирует трафик между endpoints (pods). -
Что такое PodDisruptionBudget? Защита: при drain node нельзя выключить больше N pods из selector. Гарантирует минимум available.
-
Чем containerd отличается от Docker? containerd — low-level runtime (CRI). Docker — высокоуровневый продукт сверху. K8s в 2026 использует containerd напрямую (Docker shim удалён).
-
Что делает kubectl port-forward? Создаёт туннель
localhost:<port> → pod:<port>через apiserver. Для debug. -
Как graceful shutdown в Go?
signal.NotifyContext(SIGTERM), ready=false (для K8s),server.Shutdown(ctx). terminationGracePeriodSeconds = sleep + shutdown timeout. -
Что такое rolling update? Постепенная замена pods (новая версия).
maxSurge= сколько extra можно запустить.maxUnavailable= сколько может отсутствовать. -
Чем Ingress отличается от Service? Service — L4 балансировщик внутри кластера (или LoadBalancer наружу). Ingress — L7 HTTP-роутинг (path-based, host-based), нужен ingress controller (nginx, traefik).
-
Что такое HPA? HorizontalPodAutoscaler. Меняет количество реплик по метрикам (CPU, custom metrics из Prometheus).
-
Чем
kubectl logs --previousотличается от обычного?--previousпоказывает логи предыдущего инстанса контейнера (упавшего). Без него — только текущего. -
Что такое CRI? Container Runtime Interface — стандарт API между kubelet и container runtime (containerd, CRI-O).
-
Зачем nonroot user в контейнере? Defense in depth. Если злоумышленник эксплуатирует уязвимость, нет root → ограничены capabilities.
-
Что такое NetworkPolicy? L3/L4 firewall в K8s. Описывает, кто к кому может ходить. Реализуется CNI (Calico, Cilium).
-
Чем
Alwaysотличается отIfNotPresentдля imagePullPolicy?Always— pull при каждом старте.IfNotPresent— только если нет локально. Для:latestdefault —Always. -
Что такое Helm chart? Шаблонизированный набор K8s манифестов с values.yaml.
helm install/helm upgrade. -
Чем PVC отличается от Volume? Volume — общий термин. PVC = PersistentVolumeClaim, запрос на persistent storage, который сохраняется между рестартами pod.
-
Что такое kubeconfig? Файл
~/.kube/configс cluster, user, context. kubectl использует для подключения к кластеру. -
Зачем Operators? Encode operational knowledge: автоматический backup, scaling stateful systems (Postgres, Redis, Kafka). CRD + controller.
6. Practice
Заголовок раздела «6. Practice»Упражнение 1: Multi-stage Dockerfile
Заголовок раздела «Упражнение 1: Multi-stage Dockerfile»Напишите Dockerfile для Go-сервиса, используя:
- multi-stage build,
- distroless static,
- nonroot user,
- pinned base image versions. Получите образ < 20МБ.
Упражнение 2: docker-compose
Заголовок раздела «Упражнение 2: docker-compose»Сервис + Postgres + Redis. Сервис ждёт healthcheck Postgres перед стартом.
Упражнение 3: Probes
Заголовок раздела «Упражнение 3: Probes»Реализуйте HTTP endpoints /healthz, /ready, /startup. ready должен проверять DB ping.
Упражнение 4: Graceful shutdown
Заголовок раздела «Упражнение 4: Graceful shutdown»Обработайте SIGTERM: ready=false, sleep 5s, server.Shutdown(25s).
Упражнение 5: K8s манифест
Заголовок раздела «Упражнение 5: K8s манифест»Напишите Deployment + Service + ConfigMap + Secret для вашего сервиса. С resources, probes, securityContext.
Упражнение 6: GOMEMLIMIT/automaxprocs
Заголовок раздела «Упражнение 6: GOMEMLIMIT/automaxprocs»В сервис добавьте automaxprocs. Проверьте, что GOMAXPROCS корректен при cpu.limits=500m. Выставьте GOMEMLIMIT через downward API.
Упражнение 7: Helm chart
Заголовок раздела «Упражнение 7: Helm chart»Сделайте Helm chart для вашего сервиса. Параметризуйте replicas, image tag, resources.
Упражнение 8: HPA
Заголовок раздела «Упражнение 8: HPA»Настройте HPA: scale 3-10 pods по CPU > 70% и метрике http_requests_per_second > 100.
Упражнение 9: Debug в distroless
Заголовок раздела «Упражнение 9: Debug в distroless»Контейнер на distroless упал. Объясните, как посмотреть процесс/файлы без shell. (Ответ: kubectl debug --image=busybox).
Упражнение 10: Multi-arch build
Заголовок раздела «Упражнение 10: Multi-arch build»Соберите образ для amd64 и arm64 через docker buildx. Запустите на ARM-машине (apple m).
7. Источники
Заголовок раздела «7. Источники»- Docker docs — https://docs.docker.com/.
- Kubernetes docs — https://kubernetes.io/docs/.
- Distroless images — https://github.com/GoogleContainerTools/distroless.
- “Kubernetes Up & Running” by Kelsey Hightower et al. (O’Reilly, 3rd edition, 2024).
- “Kubernetes Patterns” by Bilgin Ibryam, Roland Huß (O’Reilly, 2nd edition, 2023).
- Go in K8s — automaxprocs, GOMEMLIMIT articles — https://www.uber.com/blog/how-we-saved-70k-cores-across-30-mission-critical-services/.
- CNCF: 12-Factor App in containers — https://12factor.net/.
- OWASP Docker Security Cheat Sheet — https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html.
- SLSA framework — https://slsa.dev/ (supply chain security).
- Kubernetes Production Best Practices — https://learnk8s.io/production-best-practices.