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

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.

  1. Концепция: ресурсная модель и runtime
  2. Production-практики: probes, sidecars, operators
  3. Gotchas: CFS, OOM, restart loops
  4. Real cases: Uber automaxprocs, Istio, cert-manager
  5. Вопросы для собеседования
  6. Practice
  7. Источники

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 traffic

ConfigMap — non-sensitive configuration. Secret — sensitive (passwords, API keys).

Два способа использования:

  1. Mount as volume — файлы в /etc/config/. Автоматическое обновление при изменении ConfigMap (через ~30 секунд).
  2. Environment variablesenv.valueFrom.configMapKeyRef. НЕ обновляется без рестарта pod’a.
# Volume mount — preferred
spec:
containers:
- name: app
volumeMounts:
- name: config
mountPath: /etc/config
readOnly: true
volumes:
- name: config
configMap:
name: app-config

Secret на 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 напрямую.

Requests — гарантированные ресурсы. Scheduler использует для placement. Limits — максимум. При превышении — throttling (CPU) или OOMKill (memory).

resources:
requests:
cpu: 100m # 0.1 CPU
memory: 128Mi
limits:
cpu: 500m # 0.5 CPU
memory: 256Mi

QoS классы:

  • Guaranteed: requests == limits (для всех контейнеров и ресурсов).
  • Burstable: есть requests, limits больше или отсутствуют.
  • BestEffort: нет requests, нет limits. Первые на kill при memory pressure.

Production recommendation: всегда Guaranteed для критичных сервисов. Burstable — для batch jobs.

Проблема: Go runtime по умолчанию читает runtime.NumCPU() из /proc/cpuinfo или nproc. В k8s это возвращает количество CPU на узле, не лимит pod’a.

Node: 32 CPU
Pod CPU limit: 2 CPU
Go runtime думает: GOMAXPROCS = 32
Реально доступно: 2 CPU (через CFS quota)

Что происходит:

  1. Go scheduler запускает 32 OS thread’a.
  2. Каждый ожидает CPU time.
  3. CFS даёт суммарно 2 CPU секунд за период.
  4. Threads throttle’ятся: 30 из 32 ждут.
  5. 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 CPU

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.

Проблема: Go GC по умолчанию работает по GOGC (% increase trigger). Не знает про k8s memory limit.

Container memory limit: 512 MB
Go RSS: 400 MB
Next GC trigger: at 800 MB (GOGC=100, doubling)
→ OOMKilled before GC runs

Решение: GOMEMLIMIT (Go 1.19+).

Окно терминала
GOMEMLIMIT=460MiB # 90% от 512Mi

Go GC будет триггериться, чтобы remain ниже этого лимита. Поможет избежать OOMKill.

Рекомендация: 90% от k8s memory limit.

В Go 1.21+ можно автоматически:

import "github.com/KimMachineGun/automemlimit"
// устанавливает GOMEMLIMIT из cgroup limit

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 startup

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:latest

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:latest

PDB гарантирует minimum replicas во время voluntary disruptions (node drain, deployment update).

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
spec:
minAvailable: 2 # или maxUnavailable: 1
selector:
matchLabels:
app: api

Важно: PDB не работает для involuntary (node crash). Только для cluster admin operations.

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: api

NetworkPolicy — firewall между pods.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-policy
spec:
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).

  • ClusterIP: internal, default.
  • NodePort: открыт на каждом node, port 30000-32767. Старый стиль.
  • LoadBalancer: cloud LB (ELB, NLB, GCLB). Стоит денег.
  • ExternalName: CNAME alias для external service.

В production обычно: ClusterIP + Ingress controller.

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).

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.

DaemonSet — один pod на каждом node. Use cases:

  • Log collector (Fluent Bit, Vector).
  • Monitoring agent (node-exporter, Datadog agent).
  • CNI / storage plugins.

Job — one-time task, runs to completion. CronJob — Job по schedule.

apiVersion: batch/v1
kind: CronJob
metadata:
name: cleanup
spec:
schedule: "0 2 * * *" # 2am daily
jobTemplate:
spec:
template:
spec:
containers:
- name: cleanup
image: my-app:latest
command: ['/app', 'cleanup']
restartPolicy: OnFailure

Operator — application-specific controller, расширяющий k8s API.

Custom Resource Definition (CRD) — новый тип ресурсов:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: postgresclusters.postgres.example.com
spec:
group: postgres.example.com
versions: [...]
names:
kind: PostgresCluster
plural: postgresclusters

Controller — 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.
  • kubefed — deprecated.
  • Karmada — CNCF, multi-cluster scheduling.
  • ArgoCD ApplicationSet — deploy в multiple clusters from one config.
  • Cluster API — manage clusters declaratively.

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 вообще.


// 1. CPU/memory limits aware
import (
_ "go.uber.org/automaxprocs"
_ "github.com/KimMachineGun/automemlimit"
)
// 2. Graceful shutdown
func 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 endpoints
mux.HandleFunc("/healthz", liveness)
mux.HandleFunc("/ready", readiness)
mux.HandleFunc("/metrics", promhttp.Handler())

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

Best practice — structured JSON logs to stdout. K8s/container runtime собирает их в /var/log/containers/. DaemonSet (Fluent Bit / Vector) пересылает в Loki/Elasticsearch.

НЕ писать в file внутри container’а — теряется при crash.

// kubebuilder generated controller
type 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:

  1. Reconcile должен быть idempotent.
  2. Owner references для cascade deletion.
  3. Status subresource для current state.
  4. Predicates / Watches для tracking related resources.

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.

FROM golang:1.22-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build \
-trimpath -ldflags="-s -w -buildid=" \
-o /out/app ./cmd/server
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /out/app /app
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/app"]

Best practices:

  • Multi-stage build (small final image).
  • distroless или scratch (smaller, less CVE).
  • nonroot user.
  • -trimpath для reproducibility.
  • -s -w для smaller binary.
securityContext:
runAsNonRoot: true
runAsUser: 65532
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
# Namespace ResourceQuota
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-quota
namespace: team-a
spec:
hard:
requests.cpu: "10"
requests.memory: 20Gi
limits.cpu: "20"
limits.memory: 40Gi
persistentvolumeclaims: "10"

Если node не использует CFS — limits не enforced. Большинство managed k8s (EKS, GKE) включают CFS по умолчанию.

В k8s 1.27+ есть --cpu-cfs-quota=false опция kubelet — иногда отключают для уменьшения throttling latency tail. Но это уберёт limits enforcement.

Контейнер использует page cache (для file I/O). Page cache считается в memory limit. Если читаешь большие файлы — OOMKill, хотя heap размер маленький.

Решение: O_DIRECT для bypass cache (rare) или мониторь working set (container_memory_working_set_bytes).

Если liveness слишком aggressive (period 1s, failure 1) → spurious restarts.

Правильно:

  • initialDelaySeconds: 30 (или используй startup probe).
  • failureThreshold: 3+.
  • periodSeconds: 10+.

При SIGTERM:

  1. Pod marked Terminating.
  2. К-let stops sending traffic — но через endpoints update, не мгновенно.
  3. Сервер должен немедленно стать unready (readiness fail), но продолжать serve existing.
var ready atomic.Bool
ready.Store(true)
// /ready handler
mux.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) {
if !ready.Load() {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
w.WriteHeader(http.StatusOK)
})
// On SIGTERM
<-stop
ready.Store(false)
time.Sleep(5 * time.Second) // let LB notice
srv.Shutdown(...)

3.5 ⚠️ Init container запускается при каждом restart

Заголовок раздела «3.5 ⚠️ Init container запускается при каждом restart»

Если pod рестартует, init containers запускаются заново. Migration job в init — каждый restart мигрирует. Решение: idempotent migrations (Flyway, golang-migrate).

До k8s 1.29: sidecars (e.g., Envoy proxy) могут shutdown до основного контейнера → app не может connect downstream → errors during shutdown.

Решение: native sidecars (1.29+) или pre-stop hook + delay.

Когда ConfigMap update — k8s обновляет файл, но Go SDK может прочитать частично. Используй fsnotify + проверь полноту перед apply.

Если Secret в env var:

  • Видим через kubectl describe pod.
  • Видим через /proc/PID/environ для других контейнеров на node.
  • Не auto-updates.

Лучше: volume mount с appropriate permissions.

Когда new pod ready → endpoint controller обновляет Endpoints → kube-proxy обновляет iptables на каждом node. Этот processes занимает несколько секунд. Запросы могут идти в pod, который ещё starting.

Для StatefulSet используй headless service (clusterIP: None). Это даёт DNS A records на каждый pod: pod-0.svc.namespace.svc.cluster.local.

В multi-AZ кластере без topology hints traffic от pod в zone-a может идти в pod в zone-c — cross-AZ network cost. Используй service.kubernetes.io/topology-mode: Auto или TrafficDistribution policy.

Если Reconcile возвращает error в loop — exponential backoff. Логи покажут “reconciliation failed”. Recovery: фикс controller code, restart.

В operator watch’и периодически делают full resync (через resyncPeriod). При больших количествах CRD instances это спайки CPU. Tune resync period.


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 раза.

Istio в 2023 анонсировал Ambient Mode — отказ от sidecars:

  • ztunnel (L4) на каждом node для mTLS.
  • Waypoint proxies (L7) опционально per namespace.
  • Меньше memory overhead (без sidecar per pod).
  • Easier upgrade.

cert-manager — самый популярный operator. Управляет TLS certs от Let’s Encrypt:

  • CRD: Certificate, Issuer, ClusterIssuer.
  • ACME protocol для проверки domain ownership.
  • Auto-renew.

Zalando postgres-operator:

  • High availability через Patroni.
  • Automated failover.
  • Backup в S3 через WAL-E.

Crunchy postgres-operator:

  • Commercial focus, enterprise.

Используются банками, телекомами.

ArgoCD — GitOps controller:

  • Watch Git repo с k8s manifests.
  • Reconcile cluster state to match Git.
  • UI, RBAC, multi-tenancy.
  • Используется почти везде в 2026.

prometheus-operator (CoreOS, теперь Red Hat) управляет:

  • Prometheus instances.
  • AlertManager.
  • ServiceMonitor / PodMonitor (declarative scrape config).

В каждом серьёзном кластере.

Native sidecar containers (1.29 alpha, 1.30 beta):

  • Sidecars запускаются до main, останавливаются после.
  • Решает order shutdown problem.
  • Используется Istio, Linkerd, Vault Agent.

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.


  1. Deploy Go service в k8s с правильной конфигурацией: requests/limits, automaxprocs, GOMEMLIMIT, probes, graceful shutdown.

  2. CPU throttling эксперимент: deploy без automaxprocs, измерь P99 latency. Включи automaxprocs, сравни.

  3. Liveness probe pitfall: сделай deliberately bad liveness (1s, 1 failure), посмотри restart loop. Исправь.

  4. Build operator с kubebuilder: создай простой operator (например, “EchoCluster” CRD который deploy echo deployment с N replicas).

  5. PodDisruptionBudget: создай Deployment 3 replicas + PDB minAvailable=2. Сделай kubectl drain node — увидь, что вторая pod не убивается.

  6. NetworkPolicy lab: deploy 3 микросервиса, ограничь traffic NetworkPolicy. Используй Calico/Cilium.

  7. Istio sidecar: deploy app в Istio mesh, проверь mTLS через kiali. Сравни latency без mesh.

  8. GitOps с ArgoCD: настрой ArgoCD на свой Git репо, выкатывай через PR в main.


  1. Kubernetes docs: https://kubernetes.io/docs/ — официальная документация.
  2. Kubebuilder book: https://book.kubebuilder.io/ — building operators in Go.
  3. uber-go/automaxprocs: https://github.com/uber-go/automaxprocs.
  4. KimMachineGun/automemlimit: https://github.com/KimMachineGun/automemlimit.
  5. CNCF operator white paper: https://github.com/cncf/tag-app-delivery/blob/main/operator-wg/whitepaper/Operator-WhitePaper_v1-0.md.
  6. “Kubernetes Patterns” — Bilgin Ibryam, O’Reilly.
  7. “Programming Kubernetes” — Michael Hausenblas, O’Reilly.
  8. Istio documentation: https://istio.io/latest/docs/.
  9. Gateway API: https://gateway-api.sigs.k8s.io/.
  10. cert-manager docs: https://cert-manager.io/docs/.