Kubernetes и k3s
Kubernetes управляет желаемым состоянием контейнерных приложений. Мы описываем, что должно работать, а controllers постоянно сравнивают это с фактическим состоянием и пытаются устранить расхождение.
k3s — дистрибутив Kubernetes, упакованный в один основной binary с необходимыми компонентами. Он удобен для небольших кластеров, лабораторных окружений и edge-сценариев, но сохраняет основные объекты и API Kubernetes.
Архитектура k3s
Узлы делятся на два типа:
Server
k3s server запускает control plane и datastore:
- Kubernetes API server;
- scheduler;
- controller manager;
- cloud controller manager;
- datastore;
- supervisor k3s.
Server node также запускает agent-компоненты и по умолчанию может размещать workload.
Agent
k3s agent подключает worker node к server:
- kubelet;
- container runtime;
- CNI;
- kube-proxy или его выбранную замену;
- локальный load balancer для соединения с server nodes.
Для одного server по умолчанию подходит встроенная SQLite. Для HA используются нечётное число server nodes со встроенным etcd — обычно не меньше трёх — либо поддерживаемое внешнее хранилище.
Базовые объекты
Pod
Минимальная единица размещения. Контейнеры одного pod разделяют сеть и могут разделять volumes. Pod обычно не создают вручную: им управляет Deployment, StatefulSet, DaemonSet или Job.
Deployment
Описывает stateless workload и стратегию обновления. Deployment управляет ReplicaSet, а ReplicaSet — pods.
Deployment
└── ReplicaSet
├── Pod
├── Pod
└── PodService
Даёт стабильную виртуальную точку доступа к группе pods. Выбирает их по labels. Если selector не совпал, Service существует, но endpoints будут пустыми.
ConfigMap и Secret
Передают конфигурацию. Secret — не «зашифрованный файл по умолчанию», а отдельный Kubernetes-объект с контролем доступа; значение в manifest обычно лишь base64-encoded. Защита зависит от RBAC, шифрования at rest и способа доставки.
Ingress
Описывает HTTP-маршрутизацию извне. Сам ресурс работает только при наличии ingress controller.
Доступ к кластеру
Kubeconfig содержит:
- clusters — адрес API и CA;
- users — credentials или способ их получить;
- contexts — пару cluster/user и namespace;
- current context.
kubectl config get-contexts
kubectl config current-context
kubectl config use-context <context>Перед любой изменяющей командой проверяю context и namespace.
Admin kubeconfig k3s
Файл
/etc/rancher/k3s/k3s.yamlдаёт праваsystem:adminи неограниченный доступ к кластеру. Его нельзя коммитить, пересылать как обычный текст или использовать там, где достаточно отдельной service account с RBAC.
Встроенные команды:
sudo k3s kubectl get nodes
sudo k3s crictl ps
sudo k3s ctr containers list
sudo journalctl -u k3sk3s kubectl по умолчанию умеет читать admin kubeconfig k3s. k3s crictl помогает смотреть container runtime через CRI, когда Kubernetes API показывает недостаточно.
Как читаю состояние pod
kubectl get pod <pod> -n <namespace> -o wide
kubectl describe pod <pod> -n <namespace>
kubectl logs <pod> -n <namespace> -c <container>
kubectl logs <pod> -n <namespace> -c <container> --previousПоле STATUS — краткое представление. Причину ищу в:
status.containerStatuses;stateиlastState;reasonи exit code;- conditions;
- events;
- логах текущего или предыдущего контейнера.
Частые состояния
| Состояние | Что проверяю |
|---|---|
Pending | scheduler events, requests, affinity, taints, PVC |
ImagePullBackOff | имя/tag, registry, credentials, platform |
CrashLoopBackOff | exit code, current/previous logs, command, config |
OOMKilled | memory limit, heap, native memory, нагрузку |
Running, но не Ready | readiness probe и зависимости |
Terminating | finalizers, preStop, grace period, volume detach |
Probes
- startup probe даёт медленно запускающемуся приложению время подняться;
- readiness probe отвечает, можно ли направлять трафик;
- liveness probe отвечает, нужно ли перезапустить контейнер.
Плохая liveness probe может превратить временную медленную зависимость в бесконечные рестарты. Readiness не лечит приложение — она исключает pod из endpoints.
Service не видит pod
Проверяю цепочку:
kubectl get service <service> -n <namespace> -o yaml
kubectl get endpoints <service> -n <namespace>
kubectl get pods -n <namespace> --show-labels
kubectl get endpointslice -n <namespace>Ключевые вопросы:
- selector совпадает с labels?
- pod Ready?
targetPortсоответствует реальному порту контейнера?- запрос идёт в правильный namespace?
- NetworkPolicy разрешает трафик?
Rollout
kubectl rollout status deployment/<name> -n <namespace>
kubectl rollout history deployment/<name> -n <namespace>
kubectl get rs -n <namespace>Во время обновления смотрю не только на число pods, но и на их revision, image digest и readiness. Один и тот же tag может скрывать разные образы, поэтому для воспроизводимости лучше immutable tag или digest.
Ресурсы Java-приложения
В контейнере сопоставляю:
- Kubernetes memory limit;
- максимальный heap;
- metaspace;
- direct buffers;
- thread stacks;
- память native-библиотек и JVM.
-Xmx не равен всей памяти процесса. Если heap занимает почти весь limit, контейнер может получить OOM kill без Java OutOfMemoryError.
CPU limit влияет на throttling. Медленный ответ не всегда означает нехватку потоков приложения: процесс мог получать меньше CPU, чем ожидается.
Порядок безопасной диагностики
kubectl config current-context
kubectl get nodes
kubectl get pods -A
kubectl describe pod <pod> -n <namespace>
kubectl get events -n <namespace> --sort-by=.metadata.creationTimestamp
kubectl logs <pod> -n <namespace> --previous
kubectl get deployment <name> -n <namespace> -o yamlСначала использую read-only команды. delete pod, rollout restart, scale и edit меняют состояние и могут уничтожить следы причины.
Вопросы для собеседования
Чем Deployment отличается от StatefulSet?
Deployment подходит для взаимозаменяемых replicas. StatefulSet даёт pod стабильную идентичность, порядок создания/удаления и шаблоны отдельных persistent volumes.
Почему приложение доступно через port-forward, но не через Service?
Port-forward обращается к выбранному pod напрямую. У Service могут быть неверные selectors, пустые endpoints, неправильный targetPort или сетевые ограничения.
Что произойдёт при падении pod?
Контейнер внутри pod может перезапустить kubelet. Если pod исчез, controller создаст замену в соответствии с желаемым числом replicas. Новый pod не обязан получить прежнее имя, IP или node.
В чём разница request и limit?
Request участвует в scheduling и задаёт гарантируемую основу. Limit ограничивает потребление: CPU обычно throttled, превышение memory limit может завершить контейнер.
Почему kubectl logs пуст после рестарта?
Команда читает текущий контейнер. Для непосредственно предыдущего экземпляра нужен --previous; более старые логи должны находиться в централизованном хранилище.