Перейти к содержимому

Docker и Kubernetes для Go-разработчика

Зачем знать: в 2026 практически весь Go-код в продакшене работает в контейнерах, чаще всего в Kubernetes. Middle 1 разработчик обязан уметь собирать оптимальный multi-stage образ (5-30 МБ вместо 800 МБ), писать корректные probes, понимать requests/limits, ConfigMap/Secret, Service/Deployment. Без этого нельзя ни задеплоить, ни задебажить production-инцидент.

  1. Базовая концепция
  2. Как в Go (с примерами)
  3. Gotchas
  4. Best practices в production
  5. Вопросы на собесе
  6. Practice
  7. Источники

Docker — это инструмент для упаковки приложения в контейнер: изолированный процесс, использующий ядро хоста, но видящий своё дерево файлов, сеть, PID-пространство. Образ контейнера состоит из слоёв (immutable layers) — каждое изменение в Dockerfile создаёт новый слой.

ContainerVM
Ядрообщее (хост)собственное
Старт100ms30-60s
Размер5MB-1GB1-10GB
Изоляцияnamespaces+cgroupshardware-level
Overhead~0%5-15%
  • Образ (image) — immutable шаблон с layers.
  • Контейнер (container) — запущенный инстанс образа.
  • Реестр (registry) — хранилище образов (Docker Hub, ghcr.io, ECR, Yandex Container Registry).

K8s — orchestrator для контейнеров. Управляет:

  • Запуском (Pod = 1+ контейнеров).
  • Масштабированием (DeploymentReplicaSetPods).
  • Сетью (Service, Ingress).
  • Конфигурацией (ConfigMap, Secret).
  • Хранилищем (PersistentVolume).
  • Здоровьем (probes).
  • Авто-восстановлением (если pod упал — запускается заново).
[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 — изоляция логическая.

FROM golang:1.22
WORKDIR /app
COPY . .
RUN go build -o server .
CMD ["./server"]

Размер: ~800МБ (golang base + ваш бинарник).

# Build stage
FROM golang:1.22 AS builder
WORKDIR /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 — distroless
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /app/bin /app/bin
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/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 пользователем.
ОбразРазмерShellSSL certsCGOКогда
scratch0нетнет (надо 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.

.git/
.github/
*.md
node_modules/
*.test
coverage.out
tmp/
.idea/
.vscode/

Без этого COPY . . тащит .git (десятки МБ) и invalidates cache.

compose.yaml
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.

deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: payments
namespace: prod
labels:
app: payments
spec:
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: 30
apiVersion: v1
kind: Service
metadata:
name: payments
namespace: prod
spec:
type: ClusterIP
selector:
app: payments
ports:
- name: http
port: 80
targetPort: http
apiVersion: v1
kind: ConfigMap
metadata:
name: payments-config
data:
LOG_LEVEL: "info"
DB_HOST: "postgres.prod.svc.cluster.local"
DB_PORT: "5432"
---
apiVersion: v1
kind: Secret
metadata:
name: payments-secrets
type: Opaque
stringData: # удобнее чем data (base64)
DB_PASSWORD: "super-secret"
STRIPE_API_KEY: "sk_live_..."
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)
}
  • Startup probe (с 1.16) — пока success, остальные probes не запускаются. Для долгих стартов (миграции, прогрев).
  • Readiness — если fail, pod исключается из Service. Не убивает pod.
  • Liveness — если fail, kubelet перезапускает контейнер. Используйте для detection deadlock.
  • requests — гарантированные ресурсы. Scheduler выбирает node с достаточными ресурсами.
  • limits — верхняя граница. CPU throttling, memory — OOM kill при превышении.
  • 200m = 0.2 CPU = 200 millicores.
  • При limits CPU контейнер throttled.
  • 512Mi = 512 мебибайт = 512 × 2²⁰ байт.
  • При превышении limit — OOMKilled.

Go рантайм не знает про cgroup limits → может OOM не запустив GC. Решение:

env:
- name: GOMEMLIMIT
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: 1Mi

GOMEMLIMIT=460MiB (≈ 90% от 512Mi limit) заставит Go запускать GC агрессивнее при подходе к лимиту.

В 2026 есть автоматические библиотеки: github.com/KimMachineGun/automemlimit (читает cgroup и выставляет GOMEMLIMIT).

Go по умолчанию использует runtime.NumCPU() = количество ядер node. Но контейнер с cpu.limits=500m имеет только 0.5 CPU.

import _ "go.uber.org/automaxprocs"

Эта библиотека читает cgroup и выставляет GOMAXPROCS правильно. Обязательна для каждого Go-сервиса в K8s.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: payments
spec:
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"
Окно терминала
helm install payments ./chart \
--namespace prod \
--values values.prod.yaml

Структура chart:

chart/
├── Chart.yaml
├── values.yaml
└── templates/
├── deployment.yaml
├── service.yaml
└── configmap.yaml

Templates используют Go templates ({{ .Values.replicas }}).

Окно терминала
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 # логи прошлого crash
kubectl logs -f -l app=payments -n prod # follow всех pods с label
kubectl exec -it payments-xxx -n prod -- sh # шелл (но distroless без sh!)
kubectl port-forward payments-xxx 8080 -n prod # проброс порта на локалку
kubectl get events -n prod --sort-by=.lastTimestamp
kubectl top pods -n prod # CPU/memory
kubectl rollout status deploy/payments -n prod
kubectl rollout undo deploy/payments -n prod # откат

В кластере DNS работает по схеме <service>.<namespace>.svc.cluster.local:

db, _ := sql.Open("postgres", "postgres://postgres.prod.svc.cluster.local:5432/app")

Или короче: postgres.prod или просто postgres (если в той же namespace).

Окно терминала
trivy image ghcr.io/myorg/payments:v1.2.3

Сканирует на CVEs. В CI/CD обязательно. Также:

  • Подписи образов (cosign).
  • Pin digest (@sha256:...) вместо tag.
  • SBOM (Software Bill of Materials).
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
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
# ...

CRD + controller — расширение K8s для управления сложными системами (Postgres operator, Redis operator). Для middle 1 — знать что есть, использовать готовые (CloudNativePG, Strimzi для Kafka).


COPY . . # BAD
RUN go mod download # invalidates cache при любом изменении

vs

COPY go.mod go.sum ./
RUN go mod download # этот слой кешируется
COPY . . # invalidates только при изменении кода
Окно терминала
kubectl exec -it pod -- sh # exec format error

Решения: debug ephemeral container (kubectl debug), или использовать :debug tag (gcr.io/distroless/static:debug — есть busybox).

RUN go build -o /app/bin .
# Binary с glibc → FROM scratch не работает

Решение: CGO_ENABLED=0 или используйте distroless/base (с glibc).

K8s шлёт SIGTERM, ждёт terminationGracePeriodSeconds (default 30s), потом SIGKILL. Если ваш Go-код не реагирует на SIGTERM → задержки rolling update.

ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
defer cancel()

Если зависимость (Redis) недоступна, readiness должен fail → K8s уберёт pod из Service.

livenessProbe:
httpGet: { path: /healthz }
periodSeconds: 1
failureThreshold: 1

При микро-пике CPU pod рестартует. Делайте failureThreshold: 3-5, periodSeconds: 10.

livenessProbe:
httpGet: { path: /ready } # проверяет БД

Если БД упала на 30 секунд — все поды рестартуют → каскадная катастрофа. Liveness проверяет только себя, не зависимости.

Если limits.memory = 512Mi и GOMEMLIMIT не выставлен — Go может OOM не успев запустить GC. Симптом: pod рестартует с OOMKilled, при этом heap < limit.

Pod читает env в момент старта. Изменили ConfigMap → надо kubectl rollout restart deployment/....

Volume-mounted ConfigMap обновляется (через ~minute), но приложение должно watch файл.

type: Opaque Secret — это base64, не encryption. Кто имеет доступ к namespace, видит секрет. Решения: KMS, sealed-secrets, External Secrets Operator (Vault, AWS Secrets Manager).

image: myapp:latest

Никогда. Pod рестартует, scheduler выберет другую ноду, образ может быть другим. Всегда:

image: myapp:v1.2.3
# или
image: myapp@sha256:abc...
volumes:
- hostPath:
path: /var/log/myapp

Pod может переехать на другую node → данные исчезнут. Используйте PVC или stdout.

Без automaxprocs go-сервис с cpu.limits=500m получит GOMAXPROCS=N (количество ядер node, например 64). Огромные scheduler overhead, contention.

  • IfNotPresent (default для tagged) — берёт локальный, если есть.
  • Always (default для :latest) — всегда pull.

При CI/CD с одинаковыми тэгами нужно Always (или менять tag на каждую сборку).

kubectl describe pod:

  • Insufficient cpu/memory — у node нет ресурсов.
  • node(s) didn't match Pod's node affinity — taints/affinity не дают.
  • ImagePullBackOff — нет доступа к registry, неверный тэг.
  • CreateContainerConfigError — Secret/ConfigMap отсутствует.

Контейнер рестартует с экспоненциальной задержкой (10s, 20s, 40s … 5min). kubectl logs --previous — логи прошлой попытки.

kubectl exec nslookup postgres.prod — если не резолвится, DNS pods (coredns) проблемы. Не специфично для Go.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: payments }
spec:
minAvailable: 2
selector: { matchLabels: { app: payments } }

Защита: при drain node нельзя убить больше, чем разрешено. Без него — кластерный upgrade может одновременно убить все pods вашего сервиса.


  • Multi-stage build, distroless static, nonroot user, read-only root fs.
  • .dockerignore обязателен.
  • Версии base images pinned (golang:1.22.3 не golang:1.22).
RUN CGO_ENABLED=0 go build -trimpath -ldflags='-s -w -buildid=' -o /app/bin .

-buildid= + -trimpath → бинарь bit-for-bit reproducible. Полезно для supply chain security.

Окно терминала
syft my-image:tag -o spdx-json > sbom.json
cosign sign --key cosign.key my-image:tag

В 2026 это стандарт для compliance (SLSA level 2+).

securityContext:
runAsNonRoot: true
runAsUser: 65532 # nonroot user (distroless)
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
  • Всегда указывайте requests и limits.
  • Requests = средняя нагрузка (для scheduler).
  • Limits = max (для предотвращения noisy neighbour).
  • Memory: limits = requests (нет swap, нельзя превысить).
  • CPU: limits > requests (burstable).
import _ "go.uber.org/automaxprocs"
// в init или через env: GOMEMLIMIT

Без этих двух Go-сервис в K8s ведёт себя неправильно.

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 server
shutdownCtx, c := context.WithTimeout(context.Background(), 25*time.Second)
defer c()
srv.Shutdown(shutdownCtx)

terminationGracePeriodSeconds: 30 → 5s sleep + 25s shutdown.

affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels: { app: payments }

Не запускать все реплики на одной node — отказ node не убивает сервис.

См. 3.18.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: payments-egress }
spec:
podSelector: { matchLabels: { app: payments } }
policyTypes: [Egress]
egress:
- to:
- podSelector: { matchLabels: { app: postgres } }
ports:
- protocol: TCP
port: 5432

Zero-trust: сервис ходит только к нужным.

Не файлы внутри контейнера. fluent-bit/vector подберёт.

/healthz — только базовый ping. /ready — проверка зависимостей. /startup — для долгого инициализации.

Не запускайте supervisor + N процессов в одном контейнере. Sidecar-pattern: отдельные контейнеры в pod (например, log shipper).

v1.2.3 или git SHA. Никаких latest, dev, prod.

Не плодите YAML вручную. Используйте Helm chart или Kustomize overlay для разных env (dev/staging/prod).

ArgoCD / Flux: K8s манифесты в git, контроллер применяет автоматически. Откат = git revert.

Окно терминала
docker buildx build --platform linux/amd64,linux/arm64 -t myimage:v1.2.3 --push .

В 2026 ARM64 (Graviton, Apple Silicon) — норма. Сборка через buildx.

initContainers:
- name: migrate
image: migrate/migrate:v4
command: ["migrate", "-path", "/migrations", "-database", "$(DB_URL)", "up"]

Не делайте миграции в main — race при rolling update.

Probe вызывается каждые N секунд. Если /ready идёт в БД с тяжёлым query → DDoS себя.

VPA автоматически подстраивает requests/limits на основе исторических данных. Используется в больших кластерах для оптимизации utilization.


  1. Что такое multi-stage build и зачем? Несколько FROM в Dockerfile. Build stage компилирует, runtime stage только копирует binary. Итог: маленький image без build tools.

  2. Чем distroless лучше alpine? Нет shell (меньше surface для атак), меньше CVE, чёткое разделение build/runtime. Alpine использует musl (CGO иногда ломается).

  3. Зачем CGO_ENABLED=0? Статическая линковка. Бинарь не зависит от glibc/musl → можно класть в scratch.

  4. Что делает -ldflags='-s -w'? -s убирает symbol table, -w убирает DWARF debug. Уменьшает бинарь на 20-30%, затрудняет debug.

  5. Чем readiness отличается от liveness? Liveness fail → перезапуск контейнера (deadlock detection). Readiness fail → исключение из Service (трафик не идёт).

  6. Что такое startup probe? Для долгого старта. Пока success, остальные probes не активны.

  7. Зачем GOMEMLIMIT? Go runtime не знает про cgroup memory limit → может OOM не успев GC. GOMEMLIMIT даёт ориентир GC.

  8. Зачем automaxprocs? Без неё GOMAXPROCS = NumCPU (ядер на node, не лимит контейнера) → лишний overhead.

  9. Чем requests от limits? Requests — гарантия для scheduler. Limits — верхняя граница. CPU limits → throttle, memory → OOMKilled.

  10. Что такое Deployment vs StatefulSet? Deployment — для stateless (HTTP API). StatefulSet — для stateful (БД), stable network identity, ordered rolling update.

  11. Чем ConfigMap от Secret? Семантика. Secret base64-encoded (не encrypted!), но обычно RBAC ограничивает доступ.

  12. Что такое sidecar pattern? Дополнительный контейнер в Pod, дополняющий основной (например, envoy, log shipper).

  13. Зачем pod anti-affinity? Распределить реплики по разным nodes/zones, чтобы отказ одного не положил весь сервис.

  14. Как сервис A находит сервис B в K8s? DNS: <service>.<namespace>.svc.cluster.local. ClusterIP service балансирует трафик между endpoints (pods).

  15. Что такое PodDisruptionBudget? Защита: при drain node нельзя выключить больше N pods из selector. Гарантирует минимум available.

  16. Чем containerd отличается от Docker? containerd — low-level runtime (CRI). Docker — высокоуровневый продукт сверху. K8s в 2026 использует containerd напрямую (Docker shim удалён).

  17. Что делает kubectl port-forward? Создаёт туннель localhost:<port> → pod:<port> через apiserver. Для debug.

  18. Как graceful shutdown в Go? signal.NotifyContext(SIGTERM), ready=false (для K8s), server.Shutdown(ctx). terminationGracePeriodSeconds = sleep + shutdown timeout.

  19. Что такое rolling update? Постепенная замена pods (новая версия). maxSurge = сколько extra можно запустить. maxUnavailable = сколько может отсутствовать.

  20. Чем Ingress отличается от Service? Service — L4 балансировщик внутри кластера (или LoadBalancer наружу). Ingress — L7 HTTP-роутинг (path-based, host-based), нужен ingress controller (nginx, traefik).

  21. Что такое HPA? HorizontalPodAutoscaler. Меняет количество реплик по метрикам (CPU, custom metrics из Prometheus).

  22. Чем kubectl logs --previous отличается от обычного? --previous показывает логи предыдущего инстанса контейнера (упавшего). Без него — только текущего.

  23. Что такое CRI? Container Runtime Interface — стандарт API между kubelet и container runtime (containerd, CRI-O).

  24. Зачем nonroot user в контейнере? Defense in depth. Если злоумышленник эксплуатирует уязвимость, нет root → ограничены capabilities.

  25. Что такое NetworkPolicy? L3/L4 firewall в K8s. Описывает, кто к кому может ходить. Реализуется CNI (Calico, Cilium).

  26. Чем Always отличается от IfNotPresent для imagePullPolicy? Always — pull при каждом старте. IfNotPresent — только если нет локально. Для :latest default — Always.

  27. Что такое Helm chart? Шаблонизированный набор K8s манифестов с values.yaml. helm install / helm upgrade.

  28. Чем PVC отличается от Volume? Volume — общий термин. PVC = PersistentVolumeClaim, запрос на persistent storage, который сохраняется между рестартами pod.

  29. Что такое kubeconfig? Файл ~/.kube/config с cluster, user, context. kubectl использует для подключения к кластеру.

  30. Зачем Operators? Encode operational knowledge: автоматический backup, scaling stateful systems (Postgres, Redis, Kafka). CRD + controller.


Напишите Dockerfile для Go-сервиса, используя:

  • multi-stage build,
  • distroless static,
  • nonroot user,
  • pinned base image versions. Получите образ < 20МБ.

Сервис + Postgres + Redis. Сервис ждёт healthcheck Postgres перед стартом.

Реализуйте HTTP endpoints /healthz, /ready, /startup. ready должен проверять DB ping.

Обработайте SIGTERM: ready=false, sleep 5s, server.Shutdown(25s).

Напишите Deployment + Service + ConfigMap + Secret для вашего сервиса. С resources, probes, securityContext.

В сервис добавьте automaxprocs. Проверьте, что GOMAXPROCS корректен при cpu.limits=500m. Выставьте GOMEMLIMIT через downward API.

Сделайте Helm chart для вашего сервиса. Параметризуйте replicas, image tag, resources.

Настройте HPA: scale 3-10 pods по CPU > 70% и метрике http_requests_per_second > 100.

Контейнер на distroless упал. Объясните, как посмотреть процесс/файлы без shell. (Ответ: kubectl debug --image=busybox).

Соберите образ для amd64 и arm64 через docker buildx. Запустите на ARM-машине (apple m).


  1. Docker docshttps://docs.docker.com/.
  2. Kubernetes docshttps://kubernetes.io/docs/.
  3. Distroless imageshttps://github.com/GoogleContainerTools/distroless.
  4. “Kubernetes Up & Running” by Kelsey Hightower et al. (O’Reilly, 3rd edition, 2024).
  5. “Kubernetes Patterns” by Bilgin Ibryam, Roland Huß (O’Reilly, 2nd edition, 2023).
  6. Go in K8s — automaxprocs, GOMEMLIMIT articleshttps://www.uber.com/blog/how-we-saved-70k-cores-across-30-mission-critical-services/.
  7. CNCF: 12-Factor App in containershttps://12factor.net/.
  8. OWASP Docker Security Cheat Sheethttps://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html.
  9. SLSA frameworkhttps://slsa.dev/ (supply chain security).
  10. Kubernetes Production Best Practiceshttps://learnk8s.io/production-best-practices.