В предыдущих статье познакомились с Deployment, Service и Ingress. Теперь наши приложения могут работать в кластере и принимать запросы пользователей. Однако появляется ещё одна важная задача. Практически любому приложению необходимы настройки:
адреса базы данных;
порта подключения;
режима работы (
developmentилиproduction);адреса внешних сервисов;
паролей;
API-ключей;
токенов доступа.
Кроме того, приложению часто требуется хранить пользовательские данные, журналы или файлы. Если поместить всё это внутрь Docker-образа, сопровождать приложение станет сложно, а иногда и небезопасно.
В этой статье разберём, как Kubernetes хранит конфигурацию, работает с секретными данными и обеспечивает постоянное хранение информации.
Почему конфигурацию нельзя хранить в Docker-образе?
Представим, что разработчик собрал Docker-образ приложения. Внутри него находятся:
программный код;
библиотеки;
настройки подключения к базе данных;
пароль администратора.
На первый взгляд это удобно — всё находится в одном месте. Но на практике такой подход создаёт множество проблем.
Каждое изменение требует пересборки образа
Предположим, база данных переехала на другой сервер. Если адрес хранится внутри образа, придётся:
изменить файл конфигурации;
заново собрать Docker-образ;
загрузить его в Registry;
обновить приложение в Kubernetes.
Хотя изменился всего один параметр.
Один образ — разные окружения
Обычно приложение запускается как минимум в трёх средах:
Development;
Testing;
Production.
Во всех них настройки отличаются. Например:
Development DB_HOST=dev-db Testing DB_HOST=test-db Production DB_HOST=prod-db
Если конфигурация находится внутри образа, придётся собирать отдельный Docker-образ для каждой среды. Это противоречит одной из основных идей контейнеризации:
Один Docker-образ должен запускаться в любом окружении без изменений.
Хранение паролей
Если пароль записан внутри Docker-образа или опубликован в репозитории Git, любой человек, имеющий доступ к образу или коду, сможет его увидеть. Поэтому конфиденциальные данные необходимо хранить отдельно.
Именно для решения этих задач Kubernetes предоставляет специальные объекты: ConfigMap и Secret.
ConfigMap
ConfigMap — это объект Kubernetes, предназначенный для хранения обычной конфигурации приложения. Главная идея очень проста:
Код приложения и его настройки должны храниться отдельно.
Например, создадим ConfigMap:
apiVersion: v1 kind: ConfigMap metadata: name: app-config data: A PP_ENV: production DB_HOST: postgres DB_PORT: "5432" LOG_LEVEL: info
В разделе data находятся пары «ключ — значение». Такая конфигурация может содержать:
адреса сервисов;
номера портов;
режим работы;
пути к каталогам;
настройки логирования;
любые другие параметры, не являющиеся секретными.
После создания ConfigMap эти данные можно использовать в любом Pod.
Передача ConfigMap через переменные окружения
Самый распространённый способ использования ConfigMap — передача значений в контейнер как переменных окружения. Например:
env: - name: DB_HOST valueFrom: configMapKeyRef: name: app-config key: DB_HOST - name: APP_ENV valueFrom: configMapKeyRef: name: app-config key: APP_ENV
После запуска контейнера приложение увидит обычные переменные окружения. К примеру:
DB_HOST=postgres APP_ENV=production
Монтирование ConfigMap как файлов
Не все программы умеют читать настройки из переменных окружения. Например, многие серверы ожидают конфигурационные файлы. В этом случае Kubernetes может подключить ConfigMap как обычную директорию.
Схематично это выглядит так:
ConfigMap ▼ /etc/config │ ├── app.conf ├── nginx.conf └── settings.ini
Для приложения такие файлы выглядят как обычные конфигурационные файлы на диске. Этот подход часто используют для:
Nginx;
Prometheus;
Grafana;
PostgreSQL;
различных Java-приложений.
Благодаря этому можно менять конфигурацию отдельно от Docker-образа и не пересобираться при каждом изменении настроек.
Secret
Рассмотрим другую ситуацию. Приложению необходимо подключиться к базе данных. Для этого нужны:
имя пользователя;
пароль;
токен;
API-ключ.
Хранить такую информацию в ConfigMap не рекомендуется. Для этого существует объект Secret. Secret предназначен для хранения конфиденциальных данных:
паролей;
токенов;
SSH-ключей;
TLS-сертификатов;
API-ключей;
других чувствительных данных.
Пример Secret:
apiVersion: v1 kind: Secret metadata: name: db-secret type: Opaque stringData: username: admin password: Passpass
Как и ConfigMap, Secret можно передавать приложению:
через переменные окружения;
как файл;
через подключённый том (Volume).
Для самого приложения способ использования практически не отличается.
Почему Base64 — не шифрование
Здесь важно разобраться с одним распространённым заблуждением. Многие считают, что Kubernetes автоматически шифрует Secret. Это не совсем так. По умолчанию значения Secret сохраняются в Kubernetes в виде строки, закодированной с помощью Base64.
Пример:
password ▼ cGFzc3dvcmQ=
Base64 — это способ кодирования данных, а не их защиты. Любой человек может легко выполнить обратное преобразование и получить исходное значение. Поэтому Base64 не обеспечивает безопасность. Для полноценной защиты используют:
шифрование данных в хранилище
etcd;HashiCorp Vault;
AWS Secrets Manager;
Azure Key Vault;
Google Secret Manager.
Тем не менее Secret остаётся стандартным механизмом хранения конфиденциальной информации в Kubernetes и позволяет отделить секреты от обычной конфигурации приложения.
Что дальше?
Теперь приложение умеет получать настройки, безопасно работать с конфиденциальными данными и использовать подключаемые тома.
Однако для баз данных и других сервисов этого ещё недостаточно. Необходимо обеспечить сохранность информации даже после удаления Pod, переноса приложения на другой узел или обновления кластера.
В статье про Docker мы познакомились с Volumes и выяснили, что контейнеры могут хранить данные не только внутри своей файловой системы. Однако обычные тома (Volumes) имеют важное ограничение: их жизненный цикл обычно связан с Pod. Если Pod будет полностью удалён, данные могут быть потеряны.
Для баз данных, файловых хранилищ и других приложений, работающих с постоянными данными, этого недостаточно.
Именно для этого в Kubernetes существуют объекты PersistentVolume (PV), PersistentVolumeClaim (PVC) и StorageClass.
Почему простого Volume недостаточно?
Представим, что внутри Pod работает PostgreSQL. Пользователь добавляет тысячи записей в базу данных. Затем происходит обновление приложения.
Если база данных использовала только файловую систему контейнера или временный Volume, все данные будут потеряны. Очевидно, что для производственных приложений это неприемлемо. Нужно хранилище, которое продолжит существовать независимо от Pod.
Что такое PersistentVolume (PV)?
PersistentVolume (PV) — это постоянный том хранения данных, которым управляет Kubernetes. Если обычный Volume принадлежит конкретному Pod, то PersistentVolume существует независимо от приложений. Можно представить его как отдельный виртуальный диск.
Kubernetes Cluster PersistentVolume ▼ Независимое хранилище
Даже если Pod будет удалён, сам PersistentVolume продолжит существовать. PV может использовать разные системы хранения:
локальный диск сервера;
Amazon EBS;
Google Persistent Disk;
Azure Disk;
Ceph;
другие поддерживаемые хранилища.
Именно поэтому Kubernetes может работать практически с любой современной системой хранения данных.
Что содержит PersistentVolume?
Каждый PV описывает характеристики хранилища.
размер диска;
способ доступа;
используемую систему хранения;
политику удаления данных и т.д.
Пример:
apiVersion: v1 kind: PersistentVolume metadata: name: postgres-pv spec: capacity: storage: 10Gi accessModes: - ReadWriteOnce
На практике описание PV обычно содержит больше параметров, но начинающему DevOps достаточно понимать его назначение.
Почему приложение не использует PV напрямую?
Возникает логичный вопрос:
Если PersistentVolume уже существует, почему Pod просто не подключается к нему?
Причина в том, что Kubernetes разделяет инфраструктуру и приложения. Созданием постоянных томов обычно занимается администратор кластера или облачная платформа. Разработчику же важно лишь сообщить:
Мне нужен диск определённого размера.
Для этого используется PersistentVolumeClaim.
Что такое PersistentVolumeClaim (PVC)?
PersistentVolumeClaim (PVC) — это запрос на получение постоянного хранилища.
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi
PVC сообщает Kubernetes:
«Мне нужен диск объёмом 10 ГиБ.»
После этого Kubernetes автоматически найдёт подходящий PersistentVolume и свяжет их между собой.
Как взаимодействуют PV и PVC?
Схема выглядит следующим образом:
Deployment ▼ Pod ▼ PersistentVolumeClaim ▼ PersistentVolume ▼ Физическое хранилище
Pod работает только с PVC. Он не знает, где физически находятся данные. Такое разделение делает приложение независимым от конкретной системы хранения.
StorageClass
До сих пор мы предполагали, что PersistentVolume уже существует. Но в современных облачных платформах создавать диски вручную обычно не требуется. Поскольку есть StorageClass.
StorageClass описывает, как Kubernetes должен создавать новые тома хранения.
Например:
SSD-диски;
HDD-диски;
высокопроизводительные NVMe-тома;
сетевые файловые системы.
Когда создаётся PVC, Kubernetes может автоматически создать новый PersistentVolume нужного типа. Этот процесс называется динамическим выделением хранилища (Dynamic Provisioning).
Как это работает?
Предположим, приложение запрашивает: 20 GB SSD. Далее происходит следующая последовательность:
PVC ▼ StorageClass ▼ Создание нового PersistentVolume ▼ Подключение к Pod
Инженеру не нужно заранее создавать диски вручную. StorageClass делает это автоматически.
Access Modes
При создании PV и PVC необходимо указать режим доступа (Access Mode). Наиболее распространённые варианты:
Режим |
Описание |
|---|---|
ReadWriteOnce (RWO) |
Том можно подключить для чтения и записи только к одному узлу одновременно. Наиболее распространённый вариант для баз данных. |
ReadOnlyMany (ROX) |
Несколько узлов могут одновременно читать данные, но запись запрещена. |
ReadWriteMany (RWX) |
Несколько узлов могут одновременно читать и записывать данные. Используется сетевыми файловыми системами. |
Выбор режима зависит от возможностей используемой системы хранения.
Полный путь данных
После создания Deployment происходит следующая цепочка:
Deployment ▼ Pod ▼ PersistentVolumeClaim ▼ StorageClass ▼ PersistentVolume ▼ Физическое хранилище
Если Pod будет удалён или перенесён на другой Worker Node, Kubernetes снова подключит тот же PersistentVolume через PVC. Для приложения данные останутся доступными. Именно благодаря этому базы данных и файловые сервисы могут безопасно работать в Kubernetes.
Практика Kubernetes: первое приложение, kubectl и Minikube
До этого момента мы постепенно изучали архитектуру Kubernetes и знакомились с его основными объектами: Pod, Deployment, ReplicaSet, Service, Ingress, ConfigMap, Secret, PersistentVolume и PersistentVolumeClaim. Теперь пришло время перейти от теории к практике.
Мы установим локальный Kubernetes-кластер с помощью Minikube, познакомимся с утилитой kubectl, развернём первое приложение, опубликуем его внутри кластера и научимся искать ошибки так, как это делают DevOps-инженеры в повседневной работе.
В результате вы поймёте полный путь приложения — от YAML-манифеста до работающего сервиса.
Что такое kubectl?
kubectl (читается как куб-контрол) — это официальная консольная утилита для управления Kubernetes-кластером. Практически вся работа DevOps-инженера с Kubernetes начинается с этой утилиты.
С помощью kubectl можно:
создавать ресурсы Kubernetes;
обновлять приложения;
просматривать состояние кластера;
получать журналы (логи);
подключаться к контейнерам;
удалять ресурсы;
диагностировать проблемы.
Как kubectl взаимодействует с Kubernetes?
Важно понимать, что kubectl не управляет контейнерами напрямую. После выполнения команды утилита отправляет запрос в Kubernetes API Server, который входит в состав Control Plane. Дальнейшую работу выполняют компоненты Kubernetes.
Схема взаимодействия выглядит следующим образом:
kubectl ▼ API Server ▼ Control Plane ▼ Worker Nodes
Поэтому kubectl можно использовать для управления как локальным кластером, так и удалёнными кластерами в облаке.
Что такое kubeconfig?
Откуда kubectl знает, к какому кластеру подключаться? Для этого используется файл kubeconfig. Он содержит:
адрес Kubernetes API Server;
данные для аутентификации;
список доступных кластеров;
список пользователей;
текущий контекст подключения.
По умолчанию файл располагается по адресу:
~/.kube/config
Если вы работаете сразу с несколькими кластерами (например, локальным, тестовым и производственным), kubeconfig позволит быстро переключаться между ними.
Что такое Context?
Context (контекст) — это сохранённый набор параметров подключения. Он определяет:
какой кластер использовать;
под каким пользователем работать;
какой Namespace использовать по умолчанию.
Посмотреть текущий контекст можно командой:
kubectl config current-context
Получить список всех контекстов:
kubectl config get-contexts
Переключиться между ними:
kubectl config use-context my-cluster
Использование контекстов особенно важно при работе сразу с несколькими окружениями, чтобы случайно не выполнить команды в производственном кластере вместо тестового.
Что такое Namespace?
По умолчанию все ресурсы создаются в пространстве имён default.Однако в одном Kubernetes-кластере могут одновременно работать десятки или даже сотни проектов.Чтобы разделить их между собой, используются Namespaces.
Например:
production development monitoring logging default
Посмотреть список Namespace:
kubectl get namespaces
Работать с конкретным Namespace можно через параметр:
kubectl get pods -n production
Использование Namespace позволяет изолировать приложения и упрощает управление крупными кластерами.
Почему Minikube?
Для изучения Kubernetes не обязательно арендовать сервер или разворачивать полноценный кластер из нескольких машин. Minikube — это инструмент, который запускает локальный Kubernetes-кластер на одном компьютере. Он подходит для обучения, экспериментов и разработки.
Преимущества Minikube:
бесплатный;
поддерживает Linux, Windows и macOS;
включает все основные компоненты Kubernetes;
позволяет быстро создавать и удалять локальные кластеры.
Для большинства начинающих DevOps-инженеров Minikube — лучший способ познакомиться с Kubernetes без дополнительных затрат.
Установка Minikube
Перед установкой убедитесь, что на компьютере уже установлены:
Docker (или другой поддерживаемый драйвер виртуализации);
kubectl.
Есть 20GB свободного места
Переходите по Ссылке и выбираете удобный способ. После установки Minikube запустите локальный кластер:
minikube start
Проверить его состояние можно командой:
kubectl get nodes
Если всё прошло успешно, вы увидите единственный Worker Node со статусом Ready.
Разворачиваем первое приложение
Создадим Deployment с веб-сервером Nginx.
kubectl create deployment nginx --image=nginx
Kubernetes автоматически:
создаст Deployment;
создаст ReplicaSet;
запустит Pod;
загрузит Docker-образ Nginx.
Проверим результат:
kubectl get deployments kubectl get pods
Если Pod находится в состоянии Running, приложение успешно запущено.
Публикуем приложение
Сейчас Nginx работает внутри кластера и недоступен извне. Создадим Service:
kubectl expose deployment nginx --port=80 --type=NodePort
Minikube умеет автоматически открывать опубликованный сервис в браузере:
minikube service nginx
После выполнения команды откроется страница приветствия Nginx. Поздравляем — вы развернули своё первое приложение в Kubernetes.
Команды kubectl
Посмотреть все Pod:
kubectl get pods
Посмотреть Deployment:
kubectl get deployments
Посмотреть Service:
kubectl get services
Получить подробную информацию о Pod:
kubectl describe pod <pod-name>
Посмотреть журналы контейнера:
kubectl logs <pod-name>
Подключиться к контейнеру:
kubectl exec -it <pod-name> -- sh
Применить YAML-манифест:
kubectl apply -f deployment.yaml
Удалить ресурс:
kubectl delete -f deployment.yaml
Запоминать все команды сразу не нужно — со временем они станут привычными инструментами в ежедневной работе.
Диагностика проблем
Даже опытные DevOps-инженеры регулярно сталкиваются с ошибками. Важно не только уметь запускать приложения, но и быстро находить причины неисправностей. Наиболее полезные команды для диагностики:
Просмотреть события Pod:
kubectl describe pod <pod-name>
Посмотреть журналы приложения:
kubectl logs <pod-name>
Подключиться внутрь контейнера:
kubectl exec -it <pod-name> -- sh
Посмотреть события кластера:
kubectl get events
В большинстве случаев эти команды помогают определить причину проблемы.
Полный путь приложения
Сейчас можно увидеть всю цепочку доставки приложения в Kubernetes:
Dockerfile │ docker build │ Container Registry │ kubectl apply │ Deployment │ ReplicaSet │ Pod │ Service │ Ingress │ Пользователь
Этот процесс лежит в основе развёртывания большинства современных приложений.
Ресурсы для изучения
Kubernetes Basics
Небольшой официальный интерактивный курс, который помогает быстро познакомиться с основными объектами Kubernetes.
LabEx
Много практических задач по Kubernetes, с готовыми облачными окружениями. Также есть материал для подготовки к сертификациям Certified Kubernetes Administrator (CKA), Certified Kubernetes Application Developer (CKAD), Certified Kubernetes Security Specialist (CKS).
Killercoda Kubernetes Scenarios
Бесплатная платформа с интерактивными лабораторными работами. Позволяет выполнять практические задания по Kubernetes прямо в браузере без установки собственного кластера.
Minikube Documentation
Документация по установке и использованию Minikube.
Заключение
Теперь вы познакомились с базовыми терминами и инструментами, которые используются в ежедневной работе DevOps-инженера: попробовали поработать с kubectl, развернули первое приложение в локальном кластере и увидели полный цикл его запуска.
На этом знакомство с основами Kubernetes можно считать завершённым. В следующей статье перейдем к Terraform.