Kubernetes для Go-разработчика: runtime, probes, operators
Зачем знать: Middle 2 Go-разработчик в 2026 году пишет сервисы, которые ВСЕГДА запускаются в Kubernetes. Без знания специфики runtime в k8s (CFS throttling, GOMEMLIMIT, probes) ты будешь писать код, который “почему-то падает в проде”. Middle 2 должен уметь не просто задеплоить приложение, но и понять, как написать kubernetes-native Go-сервис: оптимизировать GOMAXPROCS под CPU limits, настроить probes, писать operators через kubebuilder.
Содержание
Заголовок раздела «Содержание»- Концепция: ресурсная модель и runtime
- Production-практики: probes, sidecars, operators
- Gotchas: CFS, OOM, restart loops
- Real cases: Uber automaxprocs, Istio, cert-manager
- Вопросы для собеседования
- Practice
- Источники
1. Концепция: ресурсная модель и runtime
Заголовок раздела «1. Концепция: ресурсная модель и runtime»1.1 Pod, Deployment, Service (повтор)
Заголовок раздела «1.1 Pod, Deployment, Service (повтор)»Pod — минимальная единица в k8s. Один или несколько контейнеров делят network/IPC namespace.
Deployment — managed pod replicas с rolling update.
replicas: N— желаемое количество.strategy: RollingUpdateс maxSurge / maxUnavailable.revisionHistoryLimit— сколько ReplicaSet’ов хранить для rollback.
Service — стабильная network identity для pods.
- ClusterIP (internal), NodePort, LoadBalancer, ExternalName.
- Selector → labels на pods.
[Deployment] manages → [ReplicaSet] manages → [Pod 1], [Pod 2], [Pod 3][Service] selects pods by label → load balances traffic1.2 ConfigMap и Secret
Заголовок раздела «1.2 ConfigMap и Secret»ConfigMap — non-sensitive configuration. Secret — sensitive (passwords, API keys).
Два способа использования:
- Mount as volume — файлы в
/etc/config/. Автоматическое обновление при изменении ConfigMap (через ~30 секунд). - Environment variables —
env.valueFrom.configMapKeyRef. НЕ обновляется без рестарта pod’a.
# Volume mount — preferredspec: containers: - name: app volumeMounts: - name: config mountPath: /etc/config readOnly: true volumes: - name: config configMap: name: app-configSecret на disk — base64-encoded, не encrypted. Для real encryption:
- Sealed Secrets (Bitnami) — encrypted Secret в Git.
- External Secrets Operator — sync из Vault/AWS Secrets Manager.
- SOPS — encrypted YAML.
- CSI Secrets Store — mount из Vault напрямую.
1.3 Resource requests vs limits
Заголовок раздела «1.3 Resource requests vs limits»Requests — гарантированные ресурсы. Scheduler использует для placement. Limits — максимум. При превышении — throttling (CPU) или OOMKill (memory).
resources: requests: cpu: 100m # 0.1 CPU memory: 128Mi limits: cpu: 500m # 0.5 CPU memory: 256MiQoS классы:
- Guaranteed: requests == limits (для всех контейнеров и ресурсов).
- Burstable: есть requests, limits больше или отсутствуют.
- BestEffort: нет requests, нет limits. Первые на kill при memory pressure.
Production recommendation: всегда Guaranteed для критичных сервисов. Burstable — для batch jobs.
1.4 CPU limits trap для Go
Заголовок раздела «1.4 CPU limits trap для Go»Проблема: Go runtime по умолчанию читает runtime.NumCPU() из /proc/cpuinfo или nproc. В k8s это возвращает количество CPU на узле, не лимит pod’a.
Node: 32 CPUPod CPU limit: 2 CPU
Go runtime думает: GOMAXPROCS = 32Реально доступно: 2 CPU (через CFS quota)Что происходит:
- Go scheduler запускает 32 OS thread’a.
- Каждый ожидает CPU time.
- CFS даёт суммарно 2 CPU секунд за период.
- Threads throttle’ятся: 30 из 32 ждут.
- Latency tail растёт катастрофически: при contention thread может ждать 100ms+.
Доказательство в Linux:
/sys/fs/cgroup/cpu/cpu.cfs_quota_us # 200000 (200ms)/sys/fs/cgroup/cpu/cpu.cfs_period_us # 100000 (100ms)→ pod может использовать 2 CPU1.5 Решение: automaxprocs
Заголовок раздела «1.5 Решение: automaxprocs»Uber automaxprocs (https://github.com/uber-go/automaxprocs):
import _ "go.uber.org/automaxprocs"Auto-detect CPU limit из cgroup и устанавливает GOMAXPROCS правильно.
В Go 1.25+ появилось exp.GOMAXPROCS auto-detection, но stable not yet. Используй automaxprocs.
1.6 Memory limits и Go GC
Заголовок раздела «1.6 Memory limits и Go GC»Проблема: Go GC по умолчанию работает по GOGC (% increase trigger). Не знает про k8s memory limit.
Container memory limit: 512 MBGo RSS: 400 MBNext GC trigger: at 800 MB (GOGC=100, doubling)→ OOMKilled before GC runsРешение: GOMEMLIMIT (Go 1.19+).
GOMEMLIMIT=460MiB # 90% от 512MiGo GC будет триггериться, чтобы remain ниже этого лимита. Поможет избежать OOMKill.
Рекомендация: 90% от k8s memory limit.
В Go 1.21+ можно автоматически:
import "github.com/KimMachineGun/automemlimit"// устанавливает GOMEMLIMIT из cgroup limit1.7 Probes: deep
Заголовок раздела «1.7 Probes: deep»K8s имеет три типа probes:
Liveness probe — “Is the container alive?”.
- Failure → restart container (с backoff).
- Используй для detected deadlock, infinite loop.
- НЕ для downstream check (если DB down — не рестартить, ничего не поможет).
Readiness probe — “Can the container serve traffic?”.
- Failure → remove from Service endpoints (no restart).
- Используй для downstream dependency check, warm-up, draining.
- При rolling update новый pod не получает traffic пока readiness не OK.
Startup probe — “Has the container started?”.
- Failure → restart (как liveness).
- Используется для slow start (databases, JVM apps).
- Когда startup проходит — liveness/readiness берут управление.
livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 3 timeoutSeconds: 2
readinessProbe: httpGet: path: /ready port: 8080 periodSeconds: 5 failureThreshold: 2
startupProbe: httpGet: path: /healthz port: 8080 failureThreshold: 30 periodSeconds: 10 # 30 * 10s = 5 минут max startup1.8 Init containers
Заголовок раздела «1.8 Init containers»Init containers запускаются перед основным, sequentially. Каждый должен success’нуть.
Use cases:
- Wait for dependency:
until nc -z postgres 5432; do sleep 1; done. - Schema migration: запустить
migrate up. - Download data, secrets.
spec: initContainers: - name: wait-for-db image: busybox command: ['sh', '-c', 'until nc -z postgres 5432; do sleep 1; done'] - name: migrate image: my-app:latest command: ['/app/migrate', 'up'] containers: - name: app image: my-app:latest1.9 Sidecars
Заголовок раздела «1.9 Sidecars»Sidecar — вспомогательный контейнер в том же pod’е.
Примеры:
- Logging agent: Fluent Bit/Vector читает логи из shared volume.
- Proxy: Envoy/Linkerd-proxy для service mesh.
- Vault agent: получает secrets и пишет в volume.
В k8s 1.29+ есть native sidecar containers — sidecars стартуют до основного, останавливаются после.
spec: initContainers: - name: envoy-proxy restartPolicy: Always # makes it a sidecar image: envoy:latest1.10 PodDisruptionBudget (PDB)
Заголовок раздела «1.10 PodDisruptionBudget (PDB)»PDB гарантирует minimum replicas во время voluntary disruptions (node drain, deployment update).
apiVersion: policy/v1kind: PodDisruptionBudgetmetadata: name: api-pdbspec: minAvailable: 2 # или maxUnavailable: 1 selector: matchLabels: app: apiВажно: PDB не работает для involuntary (node crash). Только для cluster admin operations.
1.11 Affinity / Anti-affinity
Заголовок раздела «1.11 Affinity / Anti-affinity»Spread pods across nodes/zones:
affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: [api] topologyKey: kubernetes.io/hostnameЭто spread по nodes. Заменив topologyKey на topology.kubernetes.io/zone — по AZ.
Topology Spread Constraints (k8s 1.18+) — better API:
spec: topologySpreadConstraints: - maxSkew: 1 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: app: api1.12 Network policies
Заголовок раздела «1.12 Network policies»NetworkPolicy — firewall между pods.
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: api-policyspec: podSelector: matchLabels: app: api policyTypes: [Ingress, Egress] ingress: - from: - podSelector: matchLabels: app: frontend ports: - port: 8080 egress: - to: - podSelector: matchLabels: app: db ports: - port: 5432Требует CNI с NetworkPolicy support: Calico, Cilium (не Flannel).
1.13 Service types
Заголовок раздела «1.13 Service types»- ClusterIP: internal, default.
- NodePort: открыт на каждом node, port 30000-32767. Старый стиль.
- LoadBalancer: cloud LB (ELB, NLB, GCLB). Стоит денег.
- ExternalName: CNAME alias для external service.
В production обычно: ClusterIP + Ingress controller.
1.14 Ingress
Заголовок раздела «1.14 Ingress»Ingress — HTTP/HTTPS routing на L7.
Популярные controllers:
- NGINX Ingress — самый популярный, simple.
- Traefik — auto-discovery, dashboard.
- Envoy-based (Contour, Emissary) — high-performance.
- HAProxy Ingress.
Gateway API — новый стандарт (replacement для Ingress), стандарт в k8s 1.29+. Лучше абстракция (HTTPRoute, GRPCRoute).
1.15 StatefulSets
Заголовок раздела «1.15 StatefulSets»StatefulSet для stateful apps (DBs, Redis, Kafka).
Особенности vs Deployment:
- Stable network identity:
pod-0,pod-1,pod-2. - Persistent volume per pod (через volumeClaimTemplates).
- Ordered scaling: pod-1 не стартует пока pod-0 не Ready.
1.16 DaemonSets
Заголовок раздела «1.16 DaemonSets»DaemonSet — один pod на каждом node. Use cases:
- Log collector (Fluent Bit, Vector).
- Monitoring agent (node-exporter, Datadog agent).
- CNI / storage plugins.
1.17 Jobs / CronJobs
Заголовок раздела «1.17 Jobs / CronJobs»Job — one-time task, runs to completion. CronJob — Job по schedule.
apiVersion: batch/v1kind: CronJobmetadata: name: cleanupspec: schedule: "0 2 * * *" # 2am daily jobTemplate: spec: template: spec: containers: - name: cleanup image: my-app:latest command: ['/app', 'cleanup'] restartPolicy: OnFailure1.18 Operators
Заголовок раздела «1.18 Operators»Operator — application-specific controller, расширяющий k8s API.
Custom Resource Definition (CRD) — новый тип ресурсов:
apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata: name: postgresclusters.postgres.example.comspec: group: postgres.example.com versions: [...] names: kind: PostgresCluster plural: postgresclustersController — process, watching CRD instances и reconciling state.
Reconcile loop:
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { // 1. Get current desired state (CRD spec) var cluster postgresv1.PostgresCluster if err := r.Get(ctx, req.NamespacedName, &cluster); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) }
// 2. Get current actual state (StatefulSet, ConfigMap, Service) // 3. Diff // 4. Apply changes to reach desired
return ctrl.Result{RequeueAfter: 30 * time.Second}, nil}Tools:
- kubebuilder — official scaffold for operator-sdk.
- operator-sdk — Red Hat, ops k8s.
- controller-runtime — library underneath both.
Real operators:
- cert-manager — TLS certs (Let’s Encrypt).
- postgres-operator (Zalando, Crunchy).
- redis-operator.
- prometheus-operator — управляет Prometheus/Alertmanager.
- argo-cd — GitOps.
1.19 Multi-cluster
Заголовок раздела «1.19 Multi-cluster»- kubefed — deprecated.
- Karmada — CNCF, multi-cluster scheduling.
- ArgoCD ApplicationSet — deploy в multiple clusters from one config.
- Cluster API — manage clusters declaratively.
1.20 Service mesh
Заголовок раздела «1.20 Service mesh»Istio — самый функциональный, sidecar-based.
- mTLS automatic между services.
- Traffic policies (canary, mirror, retry).
- Observability (metrics, traces).
- Ambient mode (Istio 1.18+, 2023) — без sidecars, использует ztunnel + waypoint proxies.
Linkerd — minimalistic, fast, простой.
- Rust data plane (linkerd2-proxy).
- Без CRDs zoo Istio.
Cilium + Hubble — eBPF-based, без sidecars вообще.
2. Production-практики
Заголовок раздела «2. Production-практики»2.1 Production-ready Go в k8s checklist
Заголовок раздела «2.1 Production-ready Go в k8s checklist»// 1. CPU/memory limits awareimport ( _ "go.uber.org/automaxprocs" _ "github.com/KimMachineGun/automemlimit")
// 2. Graceful shutdownfunc main() { srv := &http.Server{Addr: ":8080"}
go func() { if err := srv.ListenAndServe(); err != http.ErrServerClosed { log.Fatal(err) } }()
stop := make(chan os.Signal, 1) signal.Notify(stop, syscall.SIGTERM, syscall.SIGINT) <-stop
// K8s sends SIGTERM, gives 30s before SIGKILL ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second) defer cancel() srv.Shutdown(ctx)}
// 3. Probes endpointsmux.HandleFunc("/healthz", liveness)mux.HandleFunc("/ready", readiness)mux.HandleFunc("/metrics", promhttp.Handler())2.2 Resource sizing
Заголовок раздела «2.2 Resource sizing»CPU:
- request: 50-200m для I/O-heavy сервисов.
- limit: 500m-2 для CPU-heavy.
- Используй HPA + metrics.
Memory:
- Измерь baseline через pprof.
- request: baseline + 20%.
- limit: baseline + 50%.
- GOMEMLIMIT = 90% от limit.
2.3 Pre-stop hook for graceful
Заголовок раздела «2.3 Pre-stop hook for graceful»lifecycle: preStop: exec: command: ["/bin/sh", "-c", "sleep 5"]При termination: 1) pod marked Terminating, удалён из Service endpoints. 2) preStop вызывается. 3) SIGTERM. 4) Через terminationGracePeriodSeconds — SIGKILL.
Sleep 5 даёт time, чтобы load balancer обновил endpoints до того, как pod начнёт refuse’ить connections.
2.4 Logs collection
Заголовок раздела «2.4 Logs collection»Best practice — structured JSON logs to stdout. K8s/container runtime собирает их в /var/log/containers/. DaemonSet (Fluent Bit / Vector) пересылает в Loki/Elasticsearch.
НЕ писать в file внутри container’а — теряется при crash.
2.5 Operator design pattern
Заголовок раздела «2.5 Operator design pattern»// kubebuilder generated controllertype PostgresClusterReconciler struct { client.Client Scheme *runtime.Scheme}
func (r *PostgresClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := log.FromContext(ctx)
var cluster postgresv1.PostgresCluster if err := r.Get(ctx, req.NamespacedName, &cluster); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) }
// Reconcile StatefulSet sts := &appsv1.StatefulSet{} op, err := controllerutil.CreateOrUpdate(ctx, r.Client, sts, func() error { sts.Name = cluster.Name sts.Namespace = cluster.Namespace sts.Spec.Replicas = &cluster.Spec.Replicas // ... configure return controllerutil.SetControllerReference(&cluster, sts, r.Scheme) }) if err != nil { return ctrl.Result{}, err } log.Info("StatefulSet reconciled", "operation", op)
// Update status cluster.Status.ReadyReplicas = sts.Status.ReadyReplicas if err := r.Status().Update(ctx, &cluster); err != nil { return ctrl.Result{}, err }
return ctrl.Result{RequeueAfter: 30 * time.Second}, nil}
func (r *PostgresClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&postgresv1.PostgresCluster{}). Owns(&appsv1.StatefulSet{}). Complete(r)}Key principles:
- Reconcile должен быть idempotent.
- Owner references для cascade deletion.
- Status subresource для current state.
- Predicates / Watches для tracking related resources.
2.6 Webhook (validating / mutating)
Заголовок раздела «2.6 Webhook (validating / mutating)»Operators могут exposing admission webhooks:
- Validating webhook — reject invalid CRD.
- Mutating webhook — modify CRD on create (add defaults, sidecar injection).
Istio использует mutating webhook для auto-inject sidecar в namespace.
2.7 Production-ready Dockerfile для Go
Заголовок раздела «2.7 Production-ready Dockerfile для Go»FROM golang:1.22-alpine AS buildWORKDIR /srcCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN CGO_ENABLED=0 GOOS=linux go build \ -trimpath -ldflags="-s -w -buildid=" \ -o /out/app ./cmd/server
FROM gcr.io/distroless/static-debian12:nonrootCOPY --from=build /out/app /appUSER nonroot:nonrootEXPOSE 8080ENTRYPOINT ["/app"]Best practices:
- Multi-stage build (small final image).
distrolessилиscratch(smaller, less CVE).nonrootuser.-trimpathдля reproducibility.-s -wдля smaller binary.
2.8 HorizontalPodAutoscaler (см. файл 35)
Заголовок раздела «2.8 HorizontalPodAutoscaler (см. файл 35)»2.9 Distroless и SECCOMP
Заголовок раздела «2.9 Distroless и SECCOMP»securityContext: runAsNonRoot: true runAsUser: 65532 readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: ["ALL"] seccompProfile: type: RuntimeDefault2.10 Resource budgeting
Заголовок раздела «2.10 Resource budgeting»# Namespace ResourceQuotaapiVersion: v1kind: ResourceQuotametadata: name: team-quota namespace: team-aspec: hard: requests.cpu: "10" requests.memory: 20Gi limits.cpu: "20" limits.memory: 40Gi persistentvolumeclaims: "10"3. Gotchas
Заголовок раздела «3. Gotchas»3.1 ⚠️ CPU throttling без CFS bandwidth
Заголовок раздела «3.1 ⚠️ CPU throttling без CFS bandwidth»Если node не использует CFS — limits не enforced. Большинство managed k8s (EKS, GKE) включают CFS по умолчанию.
В k8s 1.27+ есть --cpu-cfs-quota=false опция kubelet — иногда отключают для уменьшения throttling latency tail. Но это уберёт limits enforcement.
3.2 ⚠️ OOMKill из-за page cache
Заголовок раздела «3.2 ⚠️ OOMKill из-за page cache»Контейнер использует page cache (для file I/O). Page cache считается в memory limit. Если читаешь большие файлы — OOMKill, хотя heap размер маленький.
Решение: O_DIRECT для bypass cache (rare) или мониторь working set (container_memory_working_set_bytes).
3.3 ⚠️ Liveness probe restart loop
Заголовок раздела «3.3 ⚠️ Liveness probe restart loop»Если liveness слишком aggressive (period 1s, failure 1) → spurious restarts.
Правильно:
initialDelaySeconds: 30(или используй startup probe).failureThreshold: 3+.periodSeconds: 10+.
3.4 ⚠️ Readiness probe и graceful shutdown
Заголовок раздела «3.4 ⚠️ Readiness probe и graceful shutdown»При SIGTERM:
- Pod marked Terminating.
- К-let stops sending traffic — но через endpoints update, не мгновенно.
- Сервер должен немедленно стать unready (readiness fail), но продолжать serve existing.
var ready atomic.Boolready.Store(true)
// /ready handlermux.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) { if !ready.Load() { w.WriteHeader(http.StatusServiceUnavailable) return } w.WriteHeader(http.StatusOK)})
// On SIGTERM<-stopready.Store(false)time.Sleep(5 * time.Second) // let LB noticesrv.Shutdown(...)3.5 ⚠️ Init container запускается при каждом restart
Заголовок раздела «3.5 ⚠️ Init container запускается при каждом restart»Если pod рестартует, init containers запускаются заново. Migration job в init — каждый restart мигрирует. Решение: idempotent migrations (Flyway, golang-migrate).
3.6 ⚠️ Sidecar shutdown order
Заголовок раздела «3.6 ⚠️ Sidecar shutdown order»До k8s 1.29: sidecars (e.g., Envoy proxy) могут shutdown до основного контейнера → app не может connect downstream → errors during shutdown.
Решение: native sidecars (1.29+) или pre-stop hook + delay.
3.7 ⚠️ ConfigMap volume update не атомарный
Заголовок раздела «3.7 ⚠️ ConfigMap volume update не атомарный»Когда ConfigMap update — k8s обновляет файл, но Go SDK может прочитать частично. Используй fsnotify + проверь полноту перед apply.
3.8 ⚠️ Secret в env vars
Заголовок раздела «3.8 ⚠️ Secret в env vars»Если Secret в env var:
- Видим через
kubectl describe pod. - Видим через
/proc/PID/environдля других контейнеров на node. - Не auto-updates.
Лучше: volume mount с appropriate permissions.
3.9 ⚠️ K8s service discovery delay
Заголовок раздела «3.9 ⚠️ K8s service discovery delay»Когда new pod ready → endpoint controller обновляет Endpoints → kube-proxy обновляет iptables на каждом node. Этот processes занимает несколько секунд. Запросы могут идти в pod, который ещё starting.
3.10 ⚠️ Headless service для stateful
Заголовок раздела «3.10 ⚠️ Headless service для stateful»Для StatefulSet используй headless service (clusterIP: None). Это даёт DNS A records на каждый pod: pod-0.svc.namespace.svc.cluster.local.
3.11 ⚠️ Topology hints (zone-aware routing)
Заголовок раздела «3.11 ⚠️ Topology hints (zone-aware routing)»В multi-AZ кластере без topology hints traffic от pod в zone-a может идти в pod в zone-c — cross-AZ network cost. Используй service.kubernetes.io/topology-mode: Auto или TrafficDistribution policy.
3.12 ⚠️ Operator stuck reconciliation
Заголовок раздела «3.12 ⚠️ Operator stuck reconciliation»Если Reconcile возвращает error в loop — exponential backoff. Логи покажут “reconciliation failed”. Recovery: фикс controller code, restart.
3.13 ⚠️ Watch cache resync
Заголовок раздела «3.13 ⚠️ Watch cache resync»В operator watch’и периодически делают full resync (через resyncPeriod). При больших количествах CRD instances это спайки CPU. Tune resync period.
4. Real cases
Заголовок раздела «4. Real cases»4.1 Uber automaxprocs (origin story)
Заголовок раздела «4.1 Uber automaxprocs (origin story)»Uber обнаружили, что в k8s P99 latency их Go-сервисов спайкается. Анализ показал CFS throttling. Они построили automaxprocs:
- Читает
/sys/fs/cgroup/cpu/cpu.cfs_quota_usиcpu.cfs_period_us. - Устанавливает GOMAXPROCS = floor(quota/period).
- Result: P99 latency дропнулся в 2-3 раза.
4.2 Istio Ambient (2023)
Заголовок раздела «4.2 Istio Ambient (2023)»Istio в 2023 анонсировал Ambient Mode — отказ от sidecars:
- ztunnel (L4) на каждом node для mTLS.
- Waypoint proxies (L7) опционально per namespace.
- Меньше memory overhead (без sidecar per pod).
- Easier upgrade.
4.3 cert-manager
Заголовок раздела «4.3 cert-manager»cert-manager — самый популярный operator. Управляет TLS certs от Let’s Encrypt:
- CRD:
Certificate,Issuer,ClusterIssuer. - ACME protocol для проверки domain ownership.
- Auto-renew.
4.4 PostgreSQL operators
Заголовок раздела «4.4 PostgreSQL operators»Zalando postgres-operator:
- High availability через Patroni.
- Automated failover.
- Backup в S3 через WAL-E.
Crunchy postgres-operator:
- Commercial focus, enterprise.
Используются банками, телекомами.
4.5 ArgoCD GitOps
Заголовок раздела «4.5 ArgoCD GitOps»ArgoCD — GitOps controller:
- Watch Git repo с k8s manifests.
- Reconcile cluster state to match Git.
- UI, RBAC, multi-tenancy.
- Используется почти везде в 2026.
4.6 prometheus-operator
Заголовок раздела «4.6 prometheus-operator»prometheus-operator (CoreOS, теперь Red Hat) управляет:
- Prometheus instances.
- AlertManager.
- ServiceMonitor / PodMonitor (declarative scrape config).
В каждом серьёзном кластере.
4.7 K8s 1.29+ native sidecars
Заголовок раздела «4.7 K8s 1.29+ native sidecars»Native sidecar containers (1.29 alpha, 1.30 beta):
- Sidecars запускаются до main, останавливаются после.
- Решает order shutdown problem.
- Используется Istio, Linkerd, Vault Agent.
5. Вопросы для собеседования
Заголовок раздела «5. Вопросы для собеседования»Q1: Что произойдёт, если в Go-сервисе в k8s не настроить GOMAXPROCS под CPU limit? A: Go запустит OS threads равные NumCPU (всего узла). При CPU limit (например 2 CPU) — CFS throttling: threads ждут CPU time, latency tail растёт катастрофически. Решение — automaxprocs.
Q2: Что такое GOMEMLIMIT и зачем? A: Soft memory limit для Go GC (1.19+). Если RSS приближается к лимиту, GC триггерится агрессивнее. Предотвращает OOMKill. Стандарт — 90% от k8s memory limit.
Q3: Liveness vs readiness probe? A: Liveness — “alive?”, failure = restart container. Readiness — “can serve?”, failure = remove from Service endpoints (no restart). Liveness для deadlock detection, readiness для warm-up/downstream check.
Q4: Когда нужен startup probe? A: Для slow startup (Java apps, large in-memory init). Failure → restart (как liveness). Пока startup не OK, liveness/readiness не вызываются. Позволяет иметь “медленный старт + быстрый health check после”.
Q5: Что такое PodDisruptionBudget?
A: Гарантирует minimum доступных pods во время voluntary disruptions (node drain, cluster upgrade). minAvailable: 2 или maxUnavailable: 1. Не помогает при involuntary (node crash).
Q6: Чем StatefulSet отличается от Deployment? A: StatefulSet даёт stable network identity (pod-0, pod-1), persistent volume per pod, ordered scaling. Для DBs (Postgres, Cassandra), брокеров (Kafka, Redis), где нужна стабильная identity.
Q7: Что такое CRD и controller? A: CRD — Custom Resource Definition, расширяет k8s API новым типом ресурсов. Controller — process, который watches CRD и reconciles state (создаёт нужные StatefulSet, Services, etc).
Q8: Зачем нужен kubebuilder? A: Scaffold tool для operators в Go. Генерирует boilerplate: types, controller skeleton, RBAC, deployment manifests. Использует controller-runtime библиотеку.
Q9: Что такое reconcile loop? A: Основной цикл controller’а. Сравнивает desired state (CRD spec) с actual state (existing resources), применяет изменения для convergence. Должен быть idempotent — может вызываться многократно.
Q10: ConfigMap mount as volume vs env var? A: Volume — файлы в /etc/config/, auto-update при изменении ConfigMap. Env var — фиксирован при старте, нужен restart pod’a для refresh. Volume — preferred для config files.
Q11: Что такое sidecar pattern? A: Вспомогательный контейнер в том же pod’е (delит network/IPC). Use cases: log collector, proxy (Envoy для mesh), Vault agent. В k8s 1.29+ native sidecars с правильным lifecycle.
Q12: Resource requests vs limits — что важнее? A: Requests — scheduler decision (где placement), гарантия. Limits — enforcement (throttling/OOMKill). Для production critical — requests == limits (Guaranteed QoS).
Q13: Что такое graceful shutdown в k8s? A: При SIGTERM (от kubelet) pod должен: 1) Стать unready (readiness fail), 2) Дать LB обновить endpoints (sleep 5s в preStop), 3) Завершить in-flight requests, 4) Закрыть. До terminationGracePeriodSeconds — иначе SIGKILL.
Q14: Какие популярные операторы знаешь? A: cert-manager (TLS), prometheus-operator (мониторинг), postgres-operator (Zalando/Crunchy), ArgoCD (GitOps), Istio Operator, KEDA (autoscaling).
Q15: Что такое service mesh и зачем? A: Layer between services для traffic management, observability, security. Istio/Linkerd. Преимущества: mTLS without code changes, retries/timeouts policies, traces. Минусы: complexity, sidecar overhead.
Q16: Что такое Istio Ambient Mode? A: Istio 1.18+ — без sidecars per pod. Используется ztunnel (L4) на каждом node для mTLS + опциональные waypoint proxies (L7). Меньше memory overhead.
Q17: Чем NetworkPolicy отличается от security group? A: NetworkPolicy — k8s native firewall между pods, по labels. Требует CNI с поддержкой (Calico, Cilium). Security group — cloud-level, по IP/CIDR. Можно использовать вместе.
Q18: Что такое affinity/anti-affinity? A: Правила, где можно/нельзя placement pod’a. PodAntiAffinity = “не размещай рядом с подобными” (для HA через spread). Topology Spread Constraints — современный API для этого.
Q19: Headless service зачем?
A: clusterIP: None. DNS возвращает все pod IPs (а не single VIP). Для StatefulSets (pod-0.svc.namespace) и client-side load balancing.
Q20: Что такое Ingress vs Gateway API? A: Ingress — старый API для HTTP/HTTPS routing. Gateway API — новый (k8s 1.29 GA), лучше абстракция, разделение role’ей (cluster admin vs developer), HTTPRoute/GRPCRoute, поддерживается NGINX, Istio, Contour.
Q21: Resource quota — зачем? A: Limit ресурсов на namespace (suma requests/limits). Прдотвращает один team взять всё. Используется в multi-tenant clusters.
Q22: Что такое CronJob? Какие проблемы?
A: Scheduled Job. Проблемы: clock drift между nodes (job может запуститься в other zone), missed run (если cluster был down), overlapping (если предыдущая run ещё не завершилась — настрой concurrencyPolicy: Forbid).
Q23: Distroless vs alpine для Go? A: Distroless: меньший (~5MB), меньше CVE, нет shell (security). Alpine: ~5MB, но musl libc (CGO issues). Для статичных Go binary — distroless лучший выбор.
Q24: Что такое webhook (admission)? A: K8s API server вызывает webhook на create/update. Validating — accept/reject. Mutating — modify object (add sidecar, defaults). Используется Istio (inject envoy), cert-manager.
Q25: Как diagnose OOMKill?
A: 1) kubectl describe pod — “Last State: Terminated, Reason: OOMKilled”. 2) kubectl logs --previous. 3) Метрики container_memory_working_set_bytes vs container_spec_memory_limit_bytes. 4) pprof heap profile если есть. 5) Решения: increase limit, GOMEMLIMIT, fix leak.
6. Practice
Заголовок раздела «6. Practice»-
Deploy Go service в k8s с правильной конфигурацией: requests/limits, automaxprocs, GOMEMLIMIT, probes, graceful shutdown.
-
CPU throttling эксперимент: deploy без automaxprocs, измерь P99 latency. Включи automaxprocs, сравни.
-
Liveness probe pitfall: сделай deliberately bad liveness (1s, 1 failure), посмотри restart loop. Исправь.
-
Build operator с kubebuilder: создай простой operator (например, “EchoCluster” CRD который deploy echo deployment с N replicas).
-
PodDisruptionBudget: создай Deployment 3 replicas + PDB minAvailable=2. Сделай
kubectl drain node— увидь, что вторая pod не убивается. -
NetworkPolicy lab: deploy 3 микросервиса, ограничь traffic NetworkPolicy. Используй Calico/Cilium.
-
Istio sidecar: deploy app в Istio mesh, проверь mTLS через kiali. Сравни latency без mesh.
-
GitOps с ArgoCD: настрой ArgoCD на свой Git репо, выкатывай через PR в main.
7. Источники
Заголовок раздела «7. Источники»- Kubernetes docs: https://kubernetes.io/docs/ — официальная документация.
- Kubebuilder book: https://book.kubebuilder.io/ — building operators in Go.
- uber-go/automaxprocs: https://github.com/uber-go/automaxprocs.
- KimMachineGun/automemlimit: https://github.com/KimMachineGun/automemlimit.
- CNCF operator white paper: https://github.com/cncf/tag-app-delivery/blob/main/operator-wg/whitepaper/Operator-WhitePaper_v1-0.md.
- “Kubernetes Patterns” — Bilgin Ibryam, O’Reilly.
- “Programming Kubernetes” — Michael Hausenblas, O’Reilly.
- Istio documentation: https://istio.io/latest/docs/.
- Gateway API: https://gateway-api.sigs.k8s.io/.
- cert-manager docs: https://cert-manager.io/docs/.