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

Базовые абстракции в K8s

Презентация​

Ссылка на презентацию
Последняя редакция: 18.09.26

Теория​

Кластер: Node и Control Plane​

Кластер k8s состоит из узлов (Node) - отдельных физических или виртуальных машин, на которых реально работают контейнеры
Управляет всем этим Control Plane - набор компонентов, которые принимают решения о кластере (что где запустить, как реагировать на сбои) и хранят его состояние

Компоненты Control Plane​

  • etcd - база данных кластера, хранит всё его состояние
  • kube-api-server - API k8s, обрабатывает запросы от kubectl и остальных компонентов
  • scheduler - решает, на каком узле запустить под, исходя из доступных ресурсов
  • controller-manager - следит за состоянием сущностей и приводит его к желаемому

Компоненты Node​

  • kubelet - следит за контейнерами внутри подов на своём узле
  • kube-proxy - отвечает за сетевое взаимодействие пода с остальным кластером
  • CRI (Container Runtime Interface) - интерфейс, через который kubelet запускает контейнеры (Docker, containerd и т.д.)

Pod​

Pod - базовая единица развёртывания в k8s, может содержать один или несколько контейнеров
Обычно поды не создают напрямую - ими управляет Deployment (см. ниже)

Deployment и ReplicaSet​

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

При обновлении Deployment (например, новый образ) по умолчанию используется стратегия RollingUpdate - поды обновляются последовательно, не все разом
maxUnavailable - сколько подов максимум может быть недоступно одновременно во время обновления (чем меньше значение, тем плавнее обновление, но дольше)

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest

Service и его типы​

Поды эфемерны - пересоздаются с новым IP, поэтому напрямую по IP пода на них не ходят
Service даёт стабильную точку доступа к набору подов (по тем же лейблам, что и в Deployment)

Тип Service задаётся полем type:

  • ClusterIP (по умолчанию) - доступ только изнутри кластера
  • NodePort - доступ снаружи через отдельный порт на узле
  • LoadBalancer - внешний балансировщик нагрузки (нужны дополнительные сущности/облако)
  • ExternalName - обращение к внешнему ресурсу под именем Service
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80
targetPort: 80

Namespace​

Namespace - логическое разделение ресурсов внутри одного кластера
Используется, чтобы разграничить доступ и не путать сущности с одинаковыми именами в разных "пространствах"

Стандартные namespace, которые есть в любом кластере:

  • default
  • kube-public
  • kube-node-lease
  • kube-system

Создать свой namespace можно двумя способами - императивно (kubectl create namespace ..., см. пример ниже) или декларативно, манифестом:

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

Доступ к Service локально​

Через minikube:

minikube service nginx-service --url -n hello-world

Через kubectl (port-forward - работает для любого кластера, не только minikube):

kubectl port-forward service/nginx-service 5000:80

На Linux с драйвером docker оба способа работают одинаково просто
На macOS и Windows у Docker Desktop свои сетевые ограничения - minikube service --url и kubectl port-forward продолжают работать как обычно, а вот прямой поход браузером на <minikube ip>:<nodePort> может не сработать вообще (net::ERR_ADDRESS_UNREACHABLE), даже если манифест полностью корректен - подробнее и с разбором первопричины в 05-K8s-networking

Примеры​

Разворачиваем nginx в отдельном namespace, с 3 репликами и доступом снаружи:

kubectl create namespace hello-world
kubectl apply -f deployment.yaml -n hello-world
kubectl apply -f service.yaml -n hello-world
kubectl get pods -n hello-world
minikube service nginx-service --url -n hello-world

(манифесты deployment.yaml и service.yaml - из примеров в теории выше; kubectl create namespace тут для краткости - вместо него можно было применить namespace.yaml, как в теории)

Проверь себя​

1. Из чего состоит кластер k8s?

Из узлов (Node), на которых реально работают контейнеры, и Control Plane, который принимает решения о кластере и хранит его состояние

2. За что отвечает kube-api-server?

Обрабатывает запросы от kubectl и остальных компонентов кластера - это API k8s

3. Кто создаёт поды - Deployment напрямую или через что-то ещё?

Deployment создаёт ReplicaSet, а уже ReplicaSet следит, чтобы число запущенных подов совпадало с заданными репликами

4. Что делает maxUnavailable в стратегии RollingUpdate?

Задаёт, сколько подов максимум может быть недоступно одновременно во время обновления

5. Какой тип Service используется по умолчанию, если поле type не указано?

ClusterIP - доступ только изнутри кластера

6. Зачем нужен Namespace?

Логически разделяет ресурсы внутри одного кластера - разграничивает доступ и позволяет иметь сущности с одинаковыми именами в разных namespace

7. Чем minikube service --url отличается от kubectl port-forward?

minikube service --url работает только с minikube и сразу даёт URL
kubectl port-forward работает с любым кластером, но пробрасывает порт только пока сама команда запущена

Практика​

  1. Напиши манифест Deployment для nginx на 3 реплики со стратегией RollingUpdate
  2. Напиши манифест Service типа NodePort для этого Deployment
  3. Разверни оба манифеста в отдельном namespace: kubectl create namespace hello-world, затем kubectl apply -f ... -n hello-world
  4. Проверь, что все 3 пода запущены: kubectl get pods -n hello-world
  5. Получи URL сервиса: minikube service nginx-service --url -n hello-world
  6. Пробрось порт вручную через kubectl port-forward и сравни с предыдущим способом

Итог / чек-лист​

  • Кластер = Node (где работают контейнеры) + Control Plane (управляет кластером: etcd, kube-api-server, scheduler, controller-manager)
  • На каждом Node - kubelet (следит за контейнерами) и kube-proxy (сеть), контейнеры запускаются через CRI
  • Pod - базовая единица развёртывания, обычно создаётся не напрямую, а через Deployment
  • Deployment создаёт ReplicaSet, а тот держит число подов равным replicas; обновление по умолчанию - RollingUpdate с ограничением maxUnavailable
  • Service даёт стабильный доступ к подам по лейблам; типы - ClusterIP (по умолчанию), NodePort, LoadBalancer, ExternalName
  • Namespace - логическое разделение ресурсов кластера
  • Локальный доступ к Service - minikube service --url или kubectl port-forward

Дополнительно​