Найти в Дзене
Linux | Network | DevOps

Пошаговый разбор: деплой веб-приложения в Kubernetes

В предыдущих статьях мы рассмотрели настройку сети и создание
виртуального сервера Ubuntu Linux в Yandex Cloud с помощью Terraform и
деплой Angular+Java веб-приложения на этом сервере с помощью Ansible. В
этой статье мы научимся разворачивать JavaScript+Go веб-приложение в
кластере Kubernetes. Интересно? Тогда прошу под кат. Итак, наша задача - развернуть JavaScript+Go веб-приложение (особенности приложения описаны ниже) в кластере Kubernetes. Предварительные условия: Kubernetes
(k8s) — это платформа-оркестратор для запуска контейнеризованных
приложений в кластере (cluster) серверов. Она позволяет описать желаемое
состояние приложения (количество копий, конфигурации, тома (volumes) и
т.д.), а система сама обеспечивает достижение и поддержание этого
состояния: запускает контейнеры, перезапускает их при сбое,
масштабирует, балансирует трафик и т.п. Допустим, у нас имеется
приложение, которое состоит из нескольких частей - фронтенд, бэкенд,
база данных, вспомогательные сервис
Оглавление

В предыдущих статьях мы рассмотрели настройку сети и создание
виртуального сервера
Ubuntu Linux в Yandex Cloud с помощью Terraform и
деплой Angular+Java веб-приложения на этом сервере с помощью Ansible. В
этой статье мы научимся разворачивать JavaScript+Go веб-приложение в
кластере Kubernetes.

Интересно? Тогда прошу под кат.

Итак, наша задача - развернуть JavaScript+Go веб-приложение (особенности приложения описаны ниже) в кластере Kubernetes.

Предварительные условия:

  • на вашей машине должен быть установлен Kubernetes (инструкция для Linux)
  • в вашем распоряжении должен быть кластер Kubernetes с Ingress (я буду использовать Yandex Managed Service for Kubernetes)
  • для работы с Yandex Cloud требуются
    yc CLI OAuth-токен (yc config set token <OAuth-токен>)
  • по умолчанию приложение будет доступно по внешнему IP Ingress, но это
    неудобно и некрасиво, поэтому мы "прикрутим" свой домен (купить домен
    можно, например,
    здесь)

Пара слов о Kubernetes

Kubernetes
(k8s) — это платформа-оркестратор для запуска контейнеризованных
приложений в кластере (cluster) серверов. Она позволяет описать желаемое
состояние приложения (количество копий, конфигурации, тома (volumes) и
т.д.), а система сама обеспечивает достижение и поддержание этого
состояния: запускает контейнеры, перезапускает их при сбое,
масштабирует, балансирует трафик и т.п.

Допустим, у нас имеется
приложение, которое состоит из нескольких частей - фронтенд, бэкенд,
база данных, вспомогательные сервисы. Каждая часть упакована в контейнер
(например, Docker). Необходимо, чтобы эти контейнеры автоматически
запускались, перезапускались при сбое, масштабировались под нагрузкой и
могли обновляться без простоя.

K8s берет это на себя:

  • оркестрация контейнеров — распределяет их по серверам (узлам - nodes) в кластере
  • масштабирование — увеличивает или уменьшает количество копий контейнера в зависимости от нагрузки
  • автовосстановление — перезапускает упавшие контейнеры
  • сетевое взаимодействие — обеспечивает контейнерам доступ друг к другу и к внешнему миру
  • развертывания без простоя — обновляет версии приложения постепенно (rolling update)

Архитектура

Кластер разделен на контрольную плоскость (control plane/master) и воркеры (worker nodes).

Control plane

  • API Server — точка входа в кластер (REST API). Вся работа клиентов и компонентов выполняется через него
  • etcd — распределенное хранилище "ключ-значение", где k8s хранит свое состояние (конфигурации, объектный статус)
  • kube-scheduler (планировщик) — решает, на каких узлах запускать новые поды (pods)
  • kube-controller-manager
    — набор контроллеров (для узлов, реплик (replication), конечных точек
    (endpoints) и др.), которые следят за желаемым состоянием и приводят
    кластер в соответствие с ним

Worker nodes

  • kubelet — агент на каждом узле; получает от API Server манифесты подов и запускает контейнеры через runtime
  • container runtime (среда выполнения контейнеров, например, containerd, CRI-O) — собственно запускает контейнеры
  • kube-proxy — реализует сетевой доступ к сервисам (может проксировать или использовать iptables/iptables-replacement)
  • CNI-плагины (Calico, Flannel, WeaveNet и др.) обеспечивают сетевой уровень между подами

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

  • Container (контейнер) — как в Docker: приложение + зависимости
  • Pod
    (под) — минимальная единица в k8s: один или несколько контейнеров,
    запущенных вместе и разделяющих сеть и тома. Обычно в Pod — 1 основной
    контейнер + опциональные вспомогательные контейнеры (sidecars). Поды
    эфемерны
  • ReplicaSet (набор реплик) — гарантирует заданное число копий подов
  • Deployment
    (деплой, развертывание) — декларация обновлений/управления ReplicaSet
    (постепенные обновления, откаты). Наиболее часто используемый
    абстрактный объект для сервисов без состояния (stateless)
  • StatefulSet
    (набор состояний) — для приложений с состоянием (stateful) (базы
    данных, очереди) — сохраняет порядок запуска, стабильные идентификаторы,
    привязку томов
  • DaemonSet (набор демонов) — запускает копию пода на каждом (или выбранных) узле — удобно для логирования/мониторинга
  • Job/CronJob — разовые (Job) или периодические (CronJob) задачи
  • Service
    (сервис) — абстракция доступа к подам (виртуальный IP + балансировка).
    Типы: ClusterIP (по умолчанию, внутри кластера), NodePort, LoadBalancer
  • Ingress — правила маршрутизации (HTTP) и точка входа в кластер; требует Ingress Controller (nginx-ingress, Traefik и др.)
  • ConfigMap/Secret
    — конфигурация и секреты, доступные в подах. Secret хранит данные более
    защищенно (базовая защита — base64; в проде требуются дополнительные
    меры, например, Vault)
  • Volume, PersistentVolume (PV),
    PersistentVolumeClaim (PVC), StorageClass — абстракции хранения; PVC
    запрашивает PV, StorageClass описывает политики динамического
    обеспечения
  • Namespace (пространство имен) — виртуальное разделение ресурсов (multi-tenant, окружения)
  • CRD
    (Custom Resource Definition)/Operator — расширение API k8s для
    определения собственных типов и автоматизации сложных приложений

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

  • Мы пишем манифесты (в формате YAML) — декларативно описываем Deployment, Service и т.д.
  • kubectl apply отправляет эти манифесты (объекты) в API Server
  • контроллеры и планировщик решают, где и как создать поды; kubelet запускает контейнеры через runtime
  • если под падает, контроллер перезапускает/заменяет его в соответствии с установленной политикой
  • при обновлении Deployment происходит rolling update, состояние можно откатить

Сеть

  • В k8s действует модель "каждому поду — свой IP, поды общаются напрямую". Это требует CNI-плагин
  • Service скрывает набор подов и предоставляет стабильную конечную точку
  • Ingress управляет внешним HTTP(S)-трафиком на основе установленных правил
  • NetworkPolicy контролирует, кто с кем может общаться (фильтрация на уровне пода)

Хранилище данных

  • Поды по умолчанию эпизодичны (их состояние исчезает вместе с ними) — для постоянных данных используются PersistentVolumes
  • StorageClass позволяет динамически выделять тома (например, облачные диски в GCE/AWS)
  • для сервисов с состоянием (БД) используют StatefulSet + PVC

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

  • Requests/Limits
    (запросы/лимиты) — указывают минимальные/максимальные ресурсы (ЦП,
    память) для контейнера; влияют на планирование и QoS
  • Horizontal Pod Autoscaler (HPA) — масштабирует количество подов по метрикам
  • Vertical Pod Autoscaler (VPA) — корректирует запросы/лимиты
  • Cluster Autoscaler — добавляет/удаляет узлы в облаке под нужды кластера

Безопасность

  • RBAC (Role-Based Access Control) — управление доступом к API
  • NetworkPolicy — сетевые ограничения между подами
  • Secrets — управление конфиденциальной информацией
  • Pod
    Security — политики безопасности подов (раньше была PodSecurityPolicy, в
    новых версиях была заменена на Pod Security Admission; также есть
    инструменты вроде Gatekeeper/OPA)
  • runtime - запуск контейнеров с непривилегированными пользователями, ограничение capabilities, seccomp, SELinux/AppArmor и т.д.

Логирование, метрики, трассировка

  • Отчеты (логи): обычно собирают агенты (fluentd/fluent-bit, Logstash) и хранят в Elasticsearch/Loki
  • метрики: Prometheus + Grafana — фактически стандарт мониторинга
  • трассировка: Jaeger, OpenTelemetry для распределенной трассировки
  • ключевые компоненты: метрики пода/узла, система оповещений (alerting), панели мониторинга (dashboards)

Инструменты

  • kubectl — CLI для управления кластером
  • kubeadm, kops, kubespray — инструменты установки кластера
  • minikube, kind — локальные кластеры для разработки
  • Helm — менеджер пакетов (мы поговорим о нем в следующей статье)
  • Operators (операторы) — автоматизация управления сложными приложениями (CRD + контроллер)
  • Managed Kubernetes: GKE (Google), EKS (AWS), AKS (Azure), Yandex Cloud — облачные сервисы, упрощающие управление control plane.

Пример минимального Deployment + Service

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-deploy
spec:
replicas: 3
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: nginx:stable
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"

---
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: hello-svc
spec:
type: ClusterIP
selector:
app: hello
ports:
- port: 80
targetPort: 80

Мы подробно разберем большинство этих полей в дальнейшем.

Материалы для дополнительного изучения:

Пара слов о приложении для деплоя

Наше приложение состоит из фронтенда на Vue.js и бэкенда на Go. Для простоты мы не будем поднимать БД, сервисы будут взаимодействовать напрямую, без сохранения состояния.
Рассмотрим Dockerfile сервисов.

Dockerfile бэкенда:

FROM golang:1.21 as builder

WORKDIR /app

# Устанавливаем необходимые зависимости
COPY go.mod go.sum ./
RUN go mod download

# Копируем исходный код приложения
COPY . .

# Собираем приложение для Linux
# Используем CGO_ENABLED=0 для создания статически связанного бинарного файла,
# что позволяет запускать его в контейнере без библиотек C
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main ./cmd/api

# Загружаем сертификат Yandex Internal Root CA
RUN curl https://storage.yandexcloud.net/cloud-certs/CA.pem -o YandexInternalRootCA.crt

FROM alpine:latest

# Устанавливаем ca-certificates для работы с HTTPS
RUN apk --no-cache add ca-certificates

WORKDIR /root

# Копируем сертификат из builder
COPY --from=builder /app/YandexInternalRootCA.crt /usr/local/share/ca-certificates/YandexInternalRootCA.crt

# Обновляем список сертификатов
RUN update-ca-certificates

# Копируем собранное приложение из builder
COPY --from=builder /app/main .

EXPOSE 8081

# Запускаем приложение
CMD ["./main"]

Dockerfile для фронтенда:

FROM node:16.20.0-alpine3.18 as builder

WORKDIR /app

# Устанавливаем необходимые зависимости
COPY package*.json ./
RUN npm install

# Копируем исходный код приложения
COPY . .

# Собираем приложение
RUN npm run build

FROM nginx:1.25-alpine AS production

# Копируем собранное приложение из builder
COPY --from=builder /app/dist /usr/share/nginx/html

# Копируем конфигурацию Nginx
COPY default.conf /etc/nginx/conf.d/default.conf

EXPOSE 8080

Для раздачи статики используется Nginx. Его конфигурация (default.conf) выглядит так:

server {
listen 80;

location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}

# Проксирование запросов к серверу
location /api/ {
# my-store-backend - название контейнера бэкенда
proxy_pass http://my-store-backend:8081/;
}
}

Для сборки и запуска сервисов используется Docker Compose. docker-compose.yaml выглядит так:

version: '3.8'

services:
# Сервис бэкенда
backend:
# Образ
image: ${CI_REGISTRY_IMAGE}/my-store-backend:${VERSION}
# Название контейнера
container_name: my-store-backend
restart: always
# Сеть
networks:
- my-store
# Проверка состояния
healthcheck:
test: wget -qO- http://localhost:8081/health
interval: 10s
timeout: 5s
start_period: 10s
retries: 5

# Сервис фронтенда
frontend:
# Образ
image: ${CI_REGISTRY_IMAGE}/my-store-frontend:${VERSION}
# Название контейнера
container_name: my-store-frontend
restart: always
# Порты
ports:
- '80:80'
# Сеть
networks:
- my-store

networks:
my-store:

Сборка и запуск сервисов выполняются в Gitlab CI с помощью Docker Compose на удаленном сервере. Образы фронтенда и бэкенда хранятся в Gitlab
Registry. Например, стадии релиза (отправки образа в Gitlab Registry) и
деплоя бэкенда в gitlab-ci.yaml выглядят следующим образом:

# Стадия релиза
release-backend:
stage: release
image:
name: gcr.io/go-containerregistry/crane:debug
entrypoint: ['']
cache: []
before_script:
- crane auth login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- crane tag $CI_REGISTRY_IMAGE/my-store-backend:$CI_COMMIT_SHA $VERSION

# Стадия деплоя (запускается вручную при необходимости)
deploy-backend:
stage: deploy
image: docker:24.0.7-alpine3.19
before_script:
- apk add docker-cli-compose openssh-client bash
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY"| tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 600 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
- docker context create remote --description "remote ssh" --docker "host=ssh://${DEV_USER}@${DEV_HOST}"
script:
- echo "VERSION=${VERSION}" >> deploy.env
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker --context remote compose --env-file deploy.env up backend -d --pull "always" --force-recreate
environment:
name: review/$CI_COMMIT_REF_SLUG
url: http://${DEV_HOST}:8080
auto_stop_in: 1h
when: manual

Настройка Kubernetes

Для работы с кластером k8s Yandex Cloud необходимо выполнить команду yc managed-kubernetes cluster get-credentials <название кластера> --external. Это добавит конфигурацию k8s в $HOME/.kube/config. Не забудьте изменить дефолтный каталог с помощью команды yc config set folder-id <идентификатор каталога>.

Кратко обсудим структуру объектов k8s, требующихся для деплоя нашего
приложения. Каждому сервису требуются объекты Deployment и Service.
Кроме этого, сервисам требуется доступ к Gitlab Registry. Для этого мы
создадим объект Secrets в сервисе бэкенда.

Забавы ради мы также настроим для бэкенда Vertical Pod Autoscaler.

Что касается фронтенда, то ему также потребуются объекты Configmap с
конфигурацией Nginx, Ingress для балансировки нагрузки и обеспечения
доступа к приложению извне по домену, а также ClusterIssuer для работы с
сертификатами.

Создаем основную директорию и в ней 2 поддиректории:

mkdir kubernetes
cd kubernetes

mkdir frontend backend

Объекты бэкенда

Работаем в директории backend.

Начнем с Deployment:

deployment.yaml

---
apiVersion: apps/v1
kind: Deployment
metadata:
# Название деплоя
name: backend
# Метки
labels:
app: backend
spec:
# Количество реплик (копий) приложения
replicas: 1
# Лимит историй релизов.
# Это позволяет откатиться к предыдущим версиям приложения
# в случае необходимости
revisionHistoryLimit: 12
# Стратегия обновления приложения
strategy:
# Поэтапное обновление, без остановки приложения
type: RollingUpdate
rollingUpdate:
# Максимальное количество недоступных реплик во время обновления
maxUnavailable: 0
# Максимальное количество новых реплик, которые могут быть созданы во время обновления
maxSurge: 20%
# Селектор для выбора подов, к которым будет применяться этот деплой
selector:
matchLabels:
app: backend
# Шаблон для создания подов
template:
metadata:
labels:
app: backend
spec:
containers:
- name: backend
# Образ приложения
image: gitlab.my-services.ru:5050/my-store/my-store-backend:1.0.2213512
imagePullPolicy: IfNotPresent
# Ресурсы, которые будут выделены для контейнера
resources:
requests:
cpu: '0.5'
memory: '256Mi'
limits:
cpu: '1'
memory: '512Mi'
# Порты
ports:
- name: backend
containerPort: 8081
# Проверка состояния приложения
livenessProbe:
httpGet:
path: /health
port: 8081
initialDelaySeconds: 15
periodSeconds: 30
timeoutSeconds: 2
failureThreshold: 6
# Секреты для доступа к Gitlab Registry
imagePullSecrets:
- name: docker-config-secret

Теперь Service:

service.yaml

---
apiVersion: v1
kind: Service
metadata:
name: backend
labels:
app: backend
spec:
# Дефолтный тип сервиса - сервис доступен только внутри кластера
type: ClusterIP
ports:
- protocol: TCP
# Порт, на котором сервис будет доступен внутри кластера
port: 8081
# Порт, на который будет направляться трафик
# в контейнере приложения.
# В данном случае это порт, на котором запускается бэкенд
targetPort: 8081
# Селектор для выбора подов, к которым будет направляться трафик
selector:
app: backend

Теперь Secrets:

secrets.yaml

---
apiVersion: v1
kind: Secret
metadata:
name: docker-config-secret
data:
.dockerconfigjson: <dockerconfigjson>
type: kubernetes.io/dockerconfigjson

О том, как сформировать dockerconfigjson, можно почитать здесь. Вкратце:

docker login
cat ~/.docker/config.json | base64 --encode

И, наконец, Vertical Pod Autoscaler:

vpa.yaml

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: backend-vpa
spec:
# Цель VPA - Deployment с именем "backend"
targetRef:
apiVersion: apps/v1
kind: Deployment
name: backend
# Политика обновления
updatePolicy:
# Режим обновления - "Off" означает, что VPA не будет автоматически изменять ресурсы
# контейнеров, но будет предоставлять рекомендации
# для ручного применения.
# Это позволяет избежать неожиданных изменений в работе приложения.
# "Auto" означает автоматическое применение изменений
updateMode: 'Off'
resourcePolicy:
# Политики ресурсов для контейнеров в Deployment
containerPolicies:
# Применяем политику ко всем контейнерам
- containerName: '*'
# Ресурсы, которые будут контролироваться VPA
controlledResources: ['cpu', 'memory']

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

VPA анализирует нагрузку на приложение и рекомендует оптимальные значения ресурсов. Это помогает избежать недостатка или перерасхода ресурсов.

k8s предоставляет также Horizontal Pod Autoscaler (HPA) для горизонтального масштабирования - увеличения количества реплик приложения.

Объекты фронтенда

Работаем в директории frontend.

Начнем с Deployment:

deployment.yaml

---
apiVersion: apps/v1
kind: Deployment
metadata:
# Название деплоя
name: frontend
# Метки
labels:
app: frontend
spec:
# Количество реплик (копий) приложения
replicas: 1
# Лимит историй релизов.
# Это позволяет откатиться к предыдущим версиям приложения
# в случае необходимости
revisionHistoryLimit: 12
# Селектор для выбора подов, к которым будет применяться этот деплой
selector:
matchLabels:
app: frontend
# Шаблон для создания подов
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
# Образ приложения
image: gitlab.my-services.ru:5050/my-store/my-store-frontend:1.0.2213511
imagePullPolicy: IfNotPresent
# Ресурсы, которые будут выделены для контейнера
resources:
requests:
cpu: '0.5'
memory: '256Mi'
limits:
cpu: '1'
memory: '512Mi'
# Порты
ports:
- containerPort: 8080
# Какие тома и куда (внутри контейнера) монтировать
volumeMounts:
- name: nginx-config-volume
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
volumes:
- name: nginx-config-volume
# Ссылка на ConfigMap, который содержит конфигурацию Nginx
configMap:
name: frontend-nginx-config
# Секреты для доступа к Gitlab Registry.
# Secrets из бэкенда
imagePullSecrets:
- name: docker-config-secret

Теперь Service:

service.yaml

---
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
# По умолчанию сервис имеет тип ClusterIP - доступен только внутри кластера
ports:
- protocol: TCP
# Порт, на котором сервис будет доступен внутри кластера
port: 80
# Порт, на который будет направляться трафик
# в контейнере приложения
targetPort: 8080
# Селектор для выбора подов, к которым будет направляться трафик
selector:
app: frontend

Теперь ConfigMap с настройками Nginx (аналогично рассмотренному ранее default.conf, кроме названия контейнера бэкенда):

configmap.yaml

---
apiVersion: v1
kind: ConfigMap
metadata:
name: frontend-nginx-config
data:
default.conf: |
server {
listen 8080;

location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}

location /api/ {
proxy_pass http://backend:8081/;
}
}

Теперь Ingress:

ingress.yaml

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: frontend
annotations:
# Какой ClusterIssuer использовать для автоматической выдачи TLS-сертификата через cert-manager
cert-manager.io/cluster-issuer: 'http01-clusterissuer'
spec:
# Использовать контроллер ingress-nginx
ingressClassName: 'nginx'
# Настройка HTTPS
tls:
# Домены, для которых нужен сертификат
- hosts:
# Заменить на свой
- 'mysuperduper.host'
# Имя секрета, где будет храниться TLS-сертификат.
# Заменить на свой
secretName: mysuperduper.host-tls
# Правила маршрутизации трафика
rules:
# Домен, по которому будет доступно приложение.
# Заменить на свой
- host: 'mysuperduper.host'
http:
# Список путей и сервисов
paths:
# Все запросы к корню сайта
- path: /
pathType: Prefix
backend:
service:
# Трафик перенаправляется на сервис "frontend"
name: frontend
port:
number: 80

Ingress управляет входящим HTTP(S)-трафиком и маршрутизирует его к сервисам внутри кластера. Обратите внимание,
что он не устанавливается вместе с кластером k8s, его нужно
устанавливать отдельно либо через графический интерфейс облака, либо с
помощью Terraform.

Что касается домена, то в настройках его DNS
необходимо указать запись типа A со значением внешнего IP Ingress.
Получить этот адрес можно с помощью команды kubectl get svc -n <namespace> | grep ingress (столбец EXTERNAL_IP).

И, наконец, ClusterIssuer:

cert-manager.yaml

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: http01-clusterissuer
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
# Заменить на свой
email: myname@mail.com
privateKeySecretRef:
name: http01-clusterissuer-key
solvers:
- http01:
ingress:
class: nginx

Итого

Финальная структура проекта:

kubernetes
| backend
| deployment.yaml
| secrets.yaml
| service.yaml
| vpa.yml
| frontend
| cert-manager.yaml
| configmap.yml
| deployment.yaml
| ingress.yaml
| service.yaml

Команды:

cd kubernetes/
# Заменяем <dockerconfigjson> в backend/secrets.yaml

# Запуск
kubectl apply -R -f .
# Удаление
kubectl delete -R -f .

Мы рассмотрели далеко не все возможности, предоставляемые Kubernetes, но думаю вы получили неплохое представление о том, что и как позволяет
делать этот замечательный инструмент. Наряду с другими популярными
решениями для автоматизации ИТ-процессов (Ansible, Terraform, Docker и
т.д.), Kubernetes на сегодняшний день является важной частью арсенала
DevOps-инженера.