Kubernetes основы

Kubernetes (K8s) — платформа оркестрации контейнеров, которая автоматизирует их развёртывание, масштабирование, сетевое взаимодействие и восстановление после сбоев.

В Kubernetes обычно описывают желаемое состояние системы в YAML-манифестах. Контроллеры кластера сравнивают его с фактическим состоянием и стараются устранить различия.

Пример минимального Pod:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  containers:
    - name: nginx
      image: nginx:1.27
      ports:
        - containerPort: 80

Создание объекта:

kubectl apply -f pod.yaml

Проверка:

kubectl get pods
kubectl describe pod nginx

Содержание


Основные понятия Kubernetes

Kubernetes управляет приложениями через объекты API. Каждый объект описывает часть состояния кластера: контейнеры, сетевой доступ, конфигурацию, секреты или правила масштабирования.

Понятие Назначение
Cluster Совокупность control plane и рабочих узлов
Node Сервер или виртуальная машина, на которой запускаются Pod
Pod Минимальная единица запуска одного или нескольких контейнеров
Controller Компонент, приводящий объект к желаемому состоянию
Deployment Управляет обновлением и количеством экземпляров приложения
Service Предоставляет стабильную сетевую точку доступа к группе Pod
Namespace Логически разделяет объекты внутри кластера
ConfigMap Хранит несекретную конфигурацию
Secret Хранит конфиденциальные значения

Kubernetes использует декларативный подход. Вместо последовательности команд запуска описывается конечное состояние:

spec:
  replicas: 3

Это означает, что система должна поддерживать три экземпляра приложения. Если один Pod завершится, контроллер создаст новый.

Типичный цикл работы:

  1. Пользователь отправляет манифест через kubectl.
  2. API server проверяет запрос и сохраняет состояние.
  3. Контроллер обнаруживает новый или изменённый объект.
  4. Scheduler выбирает подходящий узел для Pod.
  5. Kubelet на узле запускает контейнеры.
  6. Контроллеры продолжают проверять соответствие фактического состояния желаемому.

Архитектура Kubernetes

Кластер Kubernetes состоит из control plane и одного или нескольких worker nodes.

Пользователь / CI/CD
        |
        v
    API Server
        |
  +-----+--------------------+
  | Control Plane            |
  | etcd                     |
  | Scheduler                |
  | Controller Manager       |
  +--------------------------+
        |
        v
  +--------------------------+
  | Worker Node              |
  | kubelet                  |
  | container runtime        |
  | kube-proxy / CNI         |
  | Pods                     |
  +--------------------------+

Control plane

Control plane управляет состоянием всего кластера. Его компоненты принимают запросы, хранят конфигурацию, планируют Pod и запускают циклы согласования.

kube-apiserver

API server — центральная точка взаимодействия с Kubernetes API. Через него работают kubectl, контроллеры, операторы и внешние системы.

Основные задачи:

Команда:

kubectl get pods

не обращается к узлам напрямую. Она отправляет запрос API server.

etcd

etcd — распределённое key-value-хранилище, в котором сохраняется состояние Kubernetes.

В нём находятся сведения об объектах кластера, включая:

Резервное копирование etcd критично для самостоятельного кластера. Потеря etcd означает потерю сохранённого состояния control plane.

kube-scheduler

Scheduler выбирает worker node для Pod, у которого ещё не назначен узел.

При выборе учитываются:

Scheduler выбирает узел, но не запускает контейнеры самостоятельно.

kube-controller-manager

Controller Manager запускает встроенные контроллеры Kubernetes.

Примеры:

Контроллер выполняет цикл согласования:

желаемое состояние -> сравнение -> действие -> новое фактическое состояние

Например, если Deployment требует три реплики, а работает только две, соответствующий контроллер инициирует создание ещё одного Pod.

cloud-controller-manager

В облачной среде Cloud Controller Manager интегрирует Kubernetes с API облачного провайдера.

В зависимости от платформы он может участвовать в:

В локальном кластере этот компонент может отсутствовать.

Worker node

Worker node — сервер или виртуальная машина, на которой работают прикладные Pod.

На узле обычно находятся:

kubelet

Kubelet — агент Kubernetes на каждом worker node.

Он:

Container runtime

Container runtime непосредственно управляет контейнерами. Kubernetes взаимодействует с ним через CRI — Container Runtime Interface.

Распространённые реализации:

Kubernetes управляет контейнерами через CRI, поэтому команды Docker не являются основным интерфейсом управления объектами кластера.

Сеть кластера

Сетевую связность Pod обычно обеспечивает CNI-плагин. Конкретная реализация зависит от дистрибутива и конфигурации кластера.

Базовая модель Kubernetes предполагает:


Манифесты Kubernetes

Объекты Kubernetes обычно описываются в YAML.

Базовая структура:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: demo
  labels:
    app: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27

Основные поля:

Поле Назначение
apiVersion Версия API объекта
kind Тип объекта
metadata Имя, namespace, labels, annotations и другие метаданные
spec Желаемое состояние объекта
status Фактическое состояние, которое заполняет Kubernetes

status обычно не записывают в исходный манифест. Это поле обновляется компонентами кластера.

Просмотр структуры ресурса:

kubectl explain deployment
kubectl explain deployment.spec
kubectl explain deployment.spec.template.spec.containers

Императивный и декларативный подходы

Императивная команда сразу создаёт или изменяет ресурс:

kubectl create deployment web --image=nginx:1.27
kubectl scale deployment web --replicas=3

Декларативный подход использует файлы:

kubectl apply -f deployment.yaml

Для инфраструктуры и CI/CD обычно предпочтителен декларативный подход, потому что манифесты можно хранить в Git, проверять и повторно применять.

Проверка без сохранения

Клиентская проверка и вывод результата:

kubectl apply --dry-run=client -f deployment.yaml

Создание шаблона YAML:

kubectl create deployment web \
  --image=nginx:1.27 \
  --dry-run=client \
  -o yaml

Серверная проверка использует API текущего кластера:

kubectl apply --dry-run=server -f deployment.yaml

Labels, selectors и annotations

Labels

Labels — пары ключ-значение для классификации и выбора объектов.

metadata:
  labels:
    app: shop
    component: backend
    environment: production

Поиск по label:

kubectl get pods -l app=shop
kubectl get pods -l 'environment in (staging,production)'

Labels используются Service, Deployment и другими объектами для выбора связанных ресурсов.

Selectors

Selector выбирает объекты по labels.

Пример селектора Deployment:

selector:
  matchLabels:
    app: web

Labels шаблона Pod должны соответствовать селектору:

template:
  metadata:
    labels:
      app: web

Если Service выбирает app: web, трафик будет направляться на подходящие готовые Pod.

Annotations

Annotations содержат дополнительные метаданные, которые не используются для обычной выборки объектов.

metadata:
  annotations:
    description: "Публичный веб-сервис"
    owner: "platform-team"

В annotations могут храниться:


Pod

Pod — минимальная единица, которую Kubernetes планирует на узел и запускает.

Pod содержит один или несколько контейнеров, которые совместно используют:

Обычно один Pod содержит один основной контейнер приложения. Дополнительный sidecar-контейнер добавляется, если он тесно связан с основным процессом.

Простой Pod

apiVersion: v1
kind: Pod
metadata:
  name: web
  labels:
    app: web
spec:
  containers:
    - name: nginx
      image: nginx:1.27
      ports:
        - name: http
          containerPort: 80

Создание и проверка:

kubectl apply -f pod.yaml
kubectl get pod web -o wide
kubectl describe pod web

Удаление:

kubectl delete pod web

Если Pod был создан напрямую, после удаления он не восстановится. Для долгоживущих приложений обычно используют Deployment.

Несколько контейнеров в Pod

apiVersion: v1
kind: Pod
metadata:
  name: app-with-sidecar
spec:
  containers:
    - name: app
      image: nginx:1.27
      volumeMounts:
        - name: shared
          mountPath: /usr/share/nginx/html
    - name: content-writer
      image: busybox:1.36
      command:
        - sh
        - -c
        - while true; do date > /data/index.html; sleep 10; done
      volumeMounts:
        - name: shared
          mountPath: /data
  volumes:
    - name: shared
      emptyDir: {}

Контейнеры общаются через localhost, поскольку находятся в одном сетевом namespace.

Команды и аргументы контейнера

containers:
  - name: worker
    image: busybox:1.36
    command: ["sh", "-c"]
    args:
      - while true; do echo working; sleep 30; done

В Kubernetes:

Переменные окружения

containers:
  - name: app
    image: example/app:1.0
    env:
      - name: APP_ENV
        value: production
      - name: HTTP_PORT
        value: "8080"

Числовые и логические значения переменных окружения рекомендуется заключать в кавычки, потому что значение env.value является строкой.

Запросы и ограничения ресурсов

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 256Mi

100m CPU означает 0,1 ядра.

Probes

Проверки помогают Kubernetes определять состояние контейнера.

livenessProbe:
  httpGet:
    path: /health/live
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /health/ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5

startupProbe:
  httpGet:
    path: /health/startup
    port: 8080
  failureThreshold: 30
  periodSeconds: 2
Проверка Назначение
startupProbe Проверяет завершение запуска медленного приложения
readinessProbe Определяет, можно ли направлять трафик в Pod
livenessProbe Определяет, нужно ли перезапустить контейнер

Ошибка readiness probe убирает Pod из готовых endpoints Service, но не обязательно перезапускает контейнер.

Жизненный цикл Pod

Часто встречаются фазы:

Фаза Значение
Pending Pod принят, но контейнеры ещё не готовы к запуску
Running Pod назначен узлу, хотя отдельные контейнеры ещё могут быть не готовы
Succeeded Все контейнеры успешно завершились и не перезапускаются
Failed Хотя бы один контейнер завершился с ошибкой и не будет перезапущен
Unknown Состояние Pod не удалось получить

Фаза Pod и причина ожидания контейнера — разные вещи. Например, в выводе может встречаться CrashLoopBackOff или ImagePullBackOff; подробности следует смотреть через kubectl describe и kubectl logs.


ReplicaSet

ReplicaSet поддерживает заданное количество одинаковых Pod.

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - containerPort: 80

Если один из трёх Pod исчезнет, ReplicaSet создаст замену.

Проверка:

kubectl get replicasets
kubectl get rs
kubectl describe rs web

На практике ReplicaSet редко создают напрямую. Обычно им управляет Deployment, который дополнительно предоставляет стратегию обновления и историю ревизий.

Связь объектов:

Deployment
    |
    +-- ReplicaSet текущей версии
    |       +-- Pod
    |       +-- Pod
    |       +-- Pod
    |
    +-- ReplicaSet предыдущей версии

При обновлении Deployment создаётся новый ReplicaSet. Старый обычно сохраняется с нулевым числом реплик для возможности отката.


Deployment

Deployment управляет ReplicaSet и предназначен для развёртывания stateless-приложений.

Он поддерживает:

Манифест Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  labels:
    app: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - name: http
              containerPort: 80
          resources:
            requests:
              cpu: 100m
              memory: 64Mi
            limits:
              cpu: 500m
              memory: 256Mi
          readinessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 3
            periodSeconds: 5

Применение:

kubectl apply -f deployment.yaml

Проверка:

kubectl get deployments
kubectl get rs
kubectl get pods -l app=web
kubectl rollout status deployment/web

Обновление образа

Декларативный вариант — изменить image в YAML и применить файл:

kubectl apply -f deployment.yaml

Императивный вариант:

kubectl set image deployment/web nginx=nginx:1.28

Имя nginx перед знаком = должно совпадать с именем контейнера в Deployment.

История и откат

kubectl rollout history deployment/web
kubectl rollout history deployment/web --revision=2
kubectl rollout undo deployment/web
kubectl rollout undo deployment/web --to-revision=2

Для осмысленной истории изменений можно записать описание:

kubectl annotate deployment web \
  kubernetes.io/change-cause="Update nginx to 1.28"

Масштабирование

kubectl scale deployment web --replicas=5

Если реплики управляются другим механизмом, например Horizontal Pod Autoscaler, ручное значение впоследствии может быть изменено этим контроллером.

Стратегии обновления

RollingUpdate

Значение по умолчанию. Новые Pod постепенно заменяют старые.

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxUnavailable: 1
    maxSurge: 1

Recreate

Старые Pod удаляются до создания новых:

strategy:
  type: Recreate

Подходит только тогда, когда одновременная работа старой и новой версий недопустима и приемлем перерыв в доступности.

Deployment не гарантирует готовность приложения сам по себе

Наличие запущенного процесса ещё не означает, что приложение готово принимать трафик. Для корректного rollout важно настроить readinessProbe.

Если новые Pod не становятся Ready, обновление может остановиться, а старые реплики продолжат обслуживать запросы в пределах стратегии.


Service

Pod являются временными: их IP-адреса могут изменяться после пересоздания. Service предоставляет стабильные DNS-имя и виртуальный IP для доступа к группе Pod.

Service выбирает Pod по labels:

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
    - name: http
      port: 80
      targetPort: 8080

Здесь:

Если selector Service не совпадает с labels Pod, Service будет существовать, но не получит готовых endpoints.

Проверка:

kubectl get services
kubectl get svc
kubectl describe service web
kubectl get endpointslices -l kubernetes.io/service-name=web

DNS Service

Внутри того же namespace Service обычно доступен по короткому имени:

http://web

Полное DNS-имя имеет вид:

web.demo.svc.cluster.local

где:

Основные типы Service

Тип Назначение
ClusterIP Доступ только через виртуальный IP внутри кластера
NodePort Открывает одинаковый порт на узлах кластера
LoadBalancer Запрашивает внешний балансировщик у поддерживаемой инфраструктуры
ExternalName Возвращает DNS CNAME на внешнее имя

В этой справке подробно рассматриваются ClusterIP и NodePort.


ClusterIP

ClusterIP — стандартный тип Service. Он предоставляет внутренний виртуальный IP, доступный в сети кластера.

apiVersion: v1
kind: Service
metadata:
  name: backend
  namespace: demo
spec:
  type: ClusterIP
  selector:
    app: backend
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 8080

Схема:

Pod-клиент
    |
    v
backend:80 / ClusterIP
    |
    +--> backend Pod:8080
    +--> backend Pod:8080
    +--> backend Pod:8080

Создание и проверка:

kubectl apply -f service.yaml
kubectl get svc -n demo
kubectl describe svc backend -n demo

Проверка из временного Pod:

kubectl run curl \
  --image=curlimages/curl \
  --restart=Never \
  -it --rm \
  -- curl http://backend.demo.svc.cluster.local

Headless Service

Если указать:

spec:
  clusterIP: None

Service становится headless и не получает обычный виртуальный IP. DNS может возвращать адреса отдельных Pod. Такой режим часто используется системами, которым нужна прямая адресация реплик.


NodePort

NodePort открывает порт на каждом узле кластера и перенаправляет трафик в Service.

apiVersion: v1
kind: Service
metadata:
  name: web-nodeport
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30080

Доступ:

http://<IP-узла>:30080

По умолчанию NodePort обычно выделяется из диапазона 30000–32767. Конкретный диапазон может быть изменён в настройках API server.

Если nodePort не задан, Kubernetes выбирает свободный порт автоматически:

ports:
  - port: 80
    targetPort: 80

Просмотр назначенного порта:

kubectl get svc web-nodeport

NodePort часто используют:

Для production-доступа обычно дополнительно используют LoadBalancer или Ingress/Gateway API, TLS и контролируемую внешнюю точку входа.

Важно учитывать firewall и Security Groups инфраструктуры: открытый Kubernetes Service не гарантирует, что порт разрешён на сетевом уровне облака или узла.


ConfigMap

ConfigMap хранит несекретную конфигурацию отдельно от образа контейнера.

Подходящие данные:

Не следует хранить в ConfigMap пароли, токены и закрытые ключи.

ConfigMap с отдельными значениями

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: demo
data:
  APP_ENV: production
  LOG_LEVEL: info
  HTTP_PORT: "8080"

Создание:

kubectl apply -f configmap.yaml

Просмотр:

kubectl get configmap app-config -n demo
kubectl describe configmap app-config -n demo
kubectl get configmap app-config -n demo -o yaml

Передача одного значения в переменную окружения

env:
  - name: LOG_LEVEL
    valueFrom:
      configMapKeyRef:
        name: app-config
        key: LOG_LEVEL

Передача всех значений

envFrom:
  - configMapRef:
      name: app-config

После этого ключи ConfigMap становятся переменными окружения контейнера.

ConfigMap как файл

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  default.conf: |
    server {
      listen 80;
      location /health {
        access_log off;
        return 200 "ok\n";
      }
      location / {
        root /usr/share/nginx/html;
      }
    }

Подключение:

volumes:
  - name: nginx-config
    configMap:
      name: nginx-config

containers:
  - name: nginx
    image: nginx:1.27
    volumeMounts:
      - name: nginx-config
        mountPath: /etc/nginx/conf.d
        readOnly: true

Каждый ключ становится именем файла, а значение — его содержимым.

Создание ConfigMap командой

Из литералов:

kubectl create configmap app-config \
  --from-literal=APP_ENV=production \
  --from-literal=LOG_LEVEL=info

Из файла:

kubectl create configmap nginx-config \
  --from-file=default.conf

Создание YAML без отправки в кластер:

kubectl create configmap app-config \
  --from-literal=APP_ENV=production \
  --dry-run=client \
  -o yaml

Обновление ConfigMap

При использовании ConfigMap через переменные окружения уже запущенный контейнер не получает новое значение автоматически. Обычно требуется перезапуск Pod:

kubectl rollout restart deployment/app -n demo

При подключении ConfigMap как volume изменения могут появиться в файлах не мгновенно. Приложение при этом должно уметь перечитывать конфигурацию. Монтирование отдельного ключа через subPath обычно не получает автоматические обновления.


Secret

Secret предназначен для конфиденциальных данных:

Пример:

apiVersion: v1
kind: Secret
metadata:
  name: database-credentials
  namespace: demo
type: Opaque
stringData:
  username: app_user
  password: change-me

Поле stringData принимает обычные строки. API server преобразует их в содержимое поля data.

В data значения записываются в Base64:

apiVersion: v1
kind: Secret
metadata:
  name: database-credentials
type: Opaque
data:
  username: YXBwX3VzZXI=
  password: Y2hhbmdlLW1l

Base64 — это кодирование, а не шифрование. Любой, кто получил значение, может его декодировать:

echo 'YXBwX3VzZXI=' | base64 --decode

Поэтому Secret нельзя считать безопасным только из-за Base64.

Создание Secret командой

kubectl create secret generic database-credentials \
  --from-literal=username=app_user \
  --from-literal=password='strong-password'

Из файлов:

kubectl create secret generic ssh-key \
  --from-file=id_rsa=./id_rsa

Secret в переменной окружения

env:
  - name: DB_USER
    valueFrom:
      secretKeyRef:
        name: database-credentials
        key: username
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: database-credentials
        key: password

Все ключи Secret:

envFrom:
  - secretRef:
      name: database-credentials

Secret как volume

volumes:
  - name: database-credentials
    secret:
      secretName: database-credentials

containers:
  - name: app
    image: example/app:1.0
    volumeMounts:
      - name: database-credentials
        mountPath: /var/run/secrets/database
        readOnly: true

В контейнере появятся файлы:

/var/run/secrets/database/username
/var/run/secrets/database/password

Просмотр значения

kubectl get secret database-credentials \
  -n demo \
  -o jsonpath='{.data.username}' | base64 --decode

Вывод секрета возможен только при наличии соответствующих прав RBAC.

Безопасная работа с Secret

Рекомендуется:

Для GitOps применяют решения с зашифрованными манифестами или синхронизацией из внешнего хранилища. Обычный YAML с stringData не должен содержать production-пароли в репозитории.

Типы Secret

Тип Назначение
Opaque Произвольные данные
kubernetes.io/tls TLS-сертификат и закрытый ключ
kubernetes.io/dockerconfigjson Доступ к container registry
kubernetes.io/basic-auth Имя пользователя и пароль
kubernetes.io/ssh-auth Закрытый SSH-ключ

TLS Secret:

kubectl create secret tls web-tls \
  --cert=tls.crt \
  --key=tls.key

Registry Secret:

kubectl create secret docker-registry registry-credentials \
  --docker-server=registry.example.com \
  --docker-username=user \
  --docker-password='password'

Подключение registry Secret:

spec:
  imagePullSecrets:
    - name: registry-credentials

Namespaces

Namespace логически разделяет объекты внутри одного кластера.

Namespaces помогают:

Просмотр:

kubectl get namespaces
kubectl get ns

Часто встречаются системные namespaces:

Namespace Назначение
default Пространство по умолчанию
kube-system Компоненты и дополнения Kubernetes
kube-public Ресурсы, которые могут быть доступны всем пользователям
kube-node-lease Lease-объекты узлов для heartbeat

Создание namespace

apiVersion: v1
kind: Namespace
metadata:
  name: demo
kubectl apply -f namespace.yaml

Или командой:

kubectl create namespace demo

Работа с namespace

kubectl get pods -n demo
kubectl apply -f deployment.yaml -n demo
kubectl get all -n demo

Флаг -A показывает объекты во всех namespaces:

kubectl get pods -A

Установка namespace по умолчанию для текущего context:

kubectl config set-context --current --namespace=demo

Проверка:

kubectl config view --minify --output 'jsonpath={..namespace}'

Namespace в манифесте

metadata:
  name: web
  namespace: demo

Если namespace указан и в YAML, и через -n, значения должны быть согласованы.

Область имён объектов

Имена namespaced-объектов должны быть уникальны только внутри namespace. Например, Service с именем api может существовать одновременно в dev и prod.

Некоторые ресурсы являются кластерными и не принадлежат namespace. Например:

Проверить область ресурса:

kubectl api-resources

В колонке NAMESPACED указано, относится ли тип объекта к namespace.

Удаление namespace

kubectl delete namespace demo

Удаление namespace запускает удаление всех namespaced-объектов внутри него. Эту команду следует использовать осторожно.

Namespace не является полной границей безопасности

Сам по себе namespace не изолирует сеть и не запрещает доступ пользователям. Для изоляции дополнительно используют:


kubectl — основные команды

kubectl — клиент командной строки для Kubernetes API.

Общий формат:

kubectl <действие> <тип-ресурса> <имя> [флаги]

Примеры:

kubectl get pods
kubectl describe deployment web
kubectl delete service web

Контексты и конфигурация

Информация о конфигурации:

kubectl config view

Список контекстов:

kubectl config get-contexts

Текущий контекст:

kubectl config current-context

Переключение:

kubectl config use-context development

Проверка доступности API:

kubectl cluster-info

Перед изменениями важно убедиться, что выбран правильный кластер и namespace.

Просмотр ресурсов

kubectl get pods
kubectl get deployments
kubectl get services
kubectl get configmaps
kubectl get secrets
kubectl get nodes

Сокращения:

kubectl get po
kubectl get deploy
kubectl get rs
kubectl get svc
kubectl get cm
kubectl get ns

Расширенная информация:

kubectl get pods -o wide

Наблюдение за изменениями:

kubectl get pods --watch
kubectl get pods -w

По label:

kubectl get pods -l app=web

Из определённого namespace:

kubectl get pods -n demo

Из всех namespaces:

kubectl get pods -A

Форматы вывода

YAML:

kubectl get deployment web -o yaml

JSON:

kubectl get pod web -o json

Имя ресурса:

kubectl get pods -o name

JSONPath:

kubectl get pods \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.phase}{"\n"}{end}'

Custom columns:

kubectl get pods \
  -o custom-columns='NAME:.metadata.name,IMAGE:.spec.containers[*].image,PHASE:.status.phase'

Создание и изменение

Применить один файл:

kubectl apply -f deployment.yaml

Применить каталог:

kubectl apply -f k8s/

Создать ресурс без декларативного управления:

kubectl create -f namespace.yaml

Редактировать объект:

kubectl edit deployment web

Изменить число реплик:

kubectl scale deployment web --replicas=4

Перезапустить Deployment:

kubectl rollout restart deployment/web

Добавить label:

kubectl label pod web environment=dev

Добавить или изменить annotation:

kubectl annotate deployment web owner=platform-team --overwrite

Удаление

По файлу:

kubectl delete -f deployment.yaml

По имени:

kubectl delete deployment web

По label:

kubectl delete pods -l app=web

Удаление Pod, которым управляет Deployment, обычно приводит к созданию нового Pod.

Описание объекта и события

kubectl describe pod web
kubectl describe deployment web
kubectl describe node worker-1

События:

kubectl get events --sort-by=.metadata.creationTimestamp

Для конкретного namespace:

kubectl get events -n demo --sort-by=.metadata.creationTimestamp

События имеют ограниченное время хранения, поэтому их следует проверять вскоре после возникновения проблемы.

Логи

Логи Pod с одним контейнером:

kubectl logs web

Для конкретного контейнера:

kubectl logs web -c nginx

Следить за логами:

kubectl logs -f web

Последние строки:

kubectl logs web --tail=100

Логи предыдущего экземпляра контейнера после перезапуска:

kubectl logs web --previous

Логи Pod Deployment по label:

kubectl logs -l app=web --all-containers=true --tail=100

Выполнение команды в контейнере

Интерактивная оболочка:

kubectl exec -it web -- sh

Если доступен Bash:

kubectl exec -it web -- bash

В Pod с несколькими контейнерами:

kubectl exec -it web -c nginx -- sh

Одиночная команда:

kubectl exec web -- printenv

Разделитель -- отделяет параметры kubectl от команды внутри контейнера.

Копирование файлов

Из локальной системы в Pod:

kubectl cp ./config.json demo/web:/tmp/config.json

Из Pod:

kubectl cp demo/web:/var/log/app.log ./app.log

Для работы kubectl cp в контейнере обычно требуется tar.

Port forwarding

Доступ к Pod:

kubectl port-forward pod/web 8080:80

Доступ через Service:

kubectl port-forward service/web 8080:80

После запуска:

http://localhost:8080

Port forwarding удобен для локальной диагностики, но не заменяет постоянную публикацию сервиса.

Rollout

kubectl rollout status deployment/web
kubectl rollout history deployment/web
kubectl rollout undo deployment/web
kubectl rollout restart deployment/web
kubectl rollout pause deployment/web
kubectl rollout resume deployment/web

Временный диагностический Pod

kubectl run debug \
  --image=busybox:1.36 \
  --restart=Never \
  -it --rm \
  -- sh

Проверка DNS:

nslookup web

HTTP-проверка с образом curl:

kubectl run curl \
  --image=curlimages/curl \
  --restart=Never \
  -it --rm \
  -- curl -v http://web

Справка API

kubectl api-resources
kubectl api-versions
kubectl explain pod
kubectl explain pod.spec.containers
kubectl explain service.spec.ports

Проверка прав

kubectl auth can-i create deployments -n demo
kubectl auth can-i delete pods -n production
kubectl auth can-i --list -n demo

Проверка от имени ServiceAccount при наличии прав на impersonation:

kubectl auth can-i get secrets \
  --as=system:serviceaccount:demo:app \
  -n demo

Метрики

Если в кластере установлен Metrics Server:

kubectl top nodes
kubectl top pods
kubectl top pods -n demo

Отсутствие данных в kubectl top не означает, что Pod не потребляет ресурсы: возможно, Metrics Server не установлен или недоступен.


Общий пример приложения

Пример включает:

Структура:

k8s-demo/
├── namespace.yaml
├── configmap.yaml
├── secret.yaml
├── deployment.yaml
├── service-clusterip.yaml
└── service-nodeport.yaml

namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: k8s-demo

configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: web-content
  namespace: k8s-demo
data:
  index.html: |
    <!doctype html>
    <html lang="ru">
      <head>
        <meta charset="utf-8">
        <title>Kubernetes Demo</title>
      </head>
      <body>
        <h1>Приложение работает в Kubernetes</h1>
      </body>
    </html>

secret.yaml

Учебный пример. Настоящие секреты не следует хранить в открытом виде в Git.

apiVersion: v1
kind: Secret
metadata:
  name: web-secret
  namespace: k8s-demo
type: Opaque
stringData:
  APP_TOKEN: replace-in-real-environment

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: k8s-demo
  labels:
    app: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - name: http
              containerPort: 80
          env:
            - name: APP_TOKEN
              valueFrom:
                secretKeyRef:
                  name: web-secret
                  key: APP_TOKEN
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              cpu: 250m
              memory: 128Mi
          readinessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 2
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 10
            periodSeconds: 10
          volumeMounts:
            - name: web-content
              mountPath: /usr/share/nginx/html
              readOnly: true
      volumes:
        - name: web-content
          configMap:
            name: web-content

service-clusterip.yaml

apiVersion: v1
kind: Service
metadata:
  name: web
  namespace: k8s-demo
spec:
  type: ClusterIP
  selector:
    app: web
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: http

targetPort: http ссылается на именованный порт контейнера:

ports:
  - name: http
    containerPort: 80

service-nodeport.yaml

apiVersion: v1
kind: Service
metadata:
  name: web-nodeport
  namespace: k8s-demo
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: http
      nodePort: 30080

Развёртывание

kubectl apply -f namespace.yaml
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service-clusterip.yaml
kubectl apply -f service-nodeport.yaml

Или применить весь каталог:

kubectl apply -f k8s-demo/

Если файлы применяются из одного каталога, namespace должен быть создан до namespaced-ресурсов. В автоматизации это можно делать отдельным шагом.

Проверка

kubectl get all -n k8s-demo
kubectl get configmap,secret -n k8s-demo
kubectl get pods -n k8s-demo -l app=web -o wide
kubectl rollout status deployment/web -n k8s-demo
kubectl get endpointslices -n k8s-demo \
  -l kubernetes.io/service-name=web

Доступ через port-forward:

kubectl port-forward service/web 8080:80 -n k8s-demo

После этого приложение доступно по адресу:

http://localhost:8080

В среде, где узлы доступны напрямую, NodePort может быть открыт по адресу:

http://<IP-узла>:30080

Обновление

Изменить образ:

kubectl set image deployment/web \
  nginx=nginx:1.28 \
  -n k8s-demo

Следить за обновлением:

kubectl rollout status deployment/web -n k8s-demo

Откатить:

kubectl rollout undo deployment/web -n k8s-demo

Масштабирование

kubectl scale deployment web \
  --replicas=4 \
  -n k8s-demo

Проверка:

kubectl get pods -n k8s-demo -l app=web

Удаление примера

Удаление всего namespace вместе с его объектами:

kubectl delete namespace k8s-demo

Диагностика

Pod находится в Pending

Проверить:

kubectl describe pod <pod> -n <namespace>
kubectl get events -n <namespace> --sort-by=.metadata.creationTimestamp
kubectl get nodes
kubectl describe node <node>

Возможные причины:

ImagePullBackOff или ErrImagePull

Проверить:

kubectl describe pod <pod> -n <namespace>

Возможные причины:

CrashLoopBackOff

Проверить текущие и предыдущие логи:

kubectl logs <pod> -n <namespace>
kubectl logs <pod> -n <namespace> --previous
kubectl describe pod <pod> -n <namespace>

Возможные причины:

Pod работает, но Service недоступен

Проверить Service и endpoints:

kubectl get svc <service> -n <namespace> -o yaml
kubectl get endpointslices -n <namespace> \
  -l kubernetes.io/service-name=<service>
kubectl get pods -n <namespace> --show-labels

Проверить:

Если приложение слушает только 127.0.0.1 внутри контейнера, другие Pod обычно не смогут обратиться к нему по IP Pod. Серверу часто требуется слушать 0.0.0.0.

Deployment не завершает rollout

kubectl rollout status deployment/<name> -n <namespace>
kubectl describe deployment <name> -n <namespace>
kubectl get rs,pods -n <namespace>
kubectl describe pod <new-pod> -n <namespace>

Частые причины:

Ошибка Forbidden

Проверить права:

kubectl auth can-i get pods -n <namespace>
kubectl auth can-i create deployments -n <namespace>

Forbidden означает, что API server распознал пользователя, но авторизация не разрешает действие.

Проверка конфигурации внутри Pod

Переменные окружения:

kubectl exec <pod> -n <namespace> -- printenv

Файлы:

kubectl exec <pod> -n <namespace> -- ls -la /path/to/config
kubectl exec <pod> -n <namespace> -- cat /path/to/config/file

Не выводите чувствительные данные Secret в общие терминальные логи или системы CI.


Рекомендации

Используйте Deployment вместо отдельных Pod

Прямой Pod удобен для обучения и диагностики, но не предоставляет полноценное управление обновлением и репликами. Для обычного stateless-приложения используйте Deployment.

Фиксируйте версии образов

Предпочтительно:

image: nginx:1.27.4

Менее предсказуемо:

image: nginx:latest

Неизменяемый digest обеспечивает ещё более точную фиксацию:

image: nginx@sha256:<digest>

Настраивайте requests и limits

Без requests Scheduler не получает точной информации о потребностях приложения. Слишком низкие limits могут вызывать throttling и OOMKilled, а слишком высокие requests — мешать размещению Pod.

Добавляйте readiness и liveness probes осознанно

Readiness должна проверять готовность обслуживать трафик. Liveness должна определять зависшее состояние, которое можно исправить перезапуском. Слишком агрессивная liveness probe может создать цикл перезапусков.

Отделяйте конфигурацию от образа

Используйте:

Не храните открытые секреты в Git

Base64 не защищает Secret. Используйте шифрование, внешние secret managers или специализированный GitOps-процесс.

Применяйте labels последовательно

Полезный набор:

metadata:
  labels:
    app.kubernetes.io/name: web
    app.kubernetes.io/instance: web-production
    app.kubernetes.io/component: frontend
    app.kubernetes.io/managed-by: kubectl

Согласованные labels упрощают поиск, мониторинг и автоматизацию.

Проверяйте контекст перед изменениями

kubectl config current-context
kubectl config view --minify

Особенно важно перед удалением, масштабированием или изменением production-ресурсов.

Храните манифесты в системе контроля версий

Это позволяет:

Проверяйте манифесты до применения

kubectl apply --dry-run=client -f k8s/
kubectl apply --dry-run=server -f k8s/
kubectl diff -f k8s/

kubectl diff показывает предполагаемые изменения и может возвращать ненулевой код, если различия найдены, что нужно учитывать в скриптах.

Помните об области видимости

Namespace помогает организовать ресурсы, но не заменяет сетевую и полномочную изоляцию. Используйте RBAC, NetworkPolicy и политики безопасности в соответствии с требованиями среды.


Краткая шпаргалка

# Кластер и контекст
kubectl cluster-info
kubectl config current-context
kubectl config get-contexts
kubectl config use-context <context>

# Ресурсы
kubectl get pods -A
kubectl get all -n <namespace>
kubectl get pod <pod> -o wide
kubectl describe pod <pod> -n <namespace>

# Применение и удаление
kubectl apply -f <file-or-directory>
kubectl diff -f <file-or-directory>
kubectl delete -f <file-or-directory>

# Логи и команды
kubectl logs <pod> -n <namespace>
kubectl logs <pod> --previous -n <namespace>
kubectl exec -it <pod> -n <namespace> -- sh

# Deployment
kubectl rollout status deployment/<name> -n <namespace>
kubectl rollout history deployment/<name> -n <namespace>
kubectl rollout undo deployment/<name> -n <namespace>
kubectl rollout restart deployment/<name> -n <namespace>
kubectl scale deployment <name> --replicas=3 -n <namespace>

# Сеть
kubectl get svc -n <namespace>
kubectl get endpointslices -n <namespace>
kubectl port-forward service/<name> 8080:80 -n <namespace>

# Конфигурация
kubectl get configmap <name> -o yaml -n <namespace>
kubectl get secret <name> -n <namespace>

# Диагностика
kubectl get events -n <namespace> --sort-by=.metadata.creationTimestamp
kubectl auth can-i <verb> <resource> -n <namespace>
kubectl top pods -n <namespace>