В предыдущих статье познакомились с Deployment, Service и Ingress. Теперь наши приложения могут работать в кластере и принимать запросы пользователей. Однако появляется ещё одна важная задача. Практически любому приложению необходимы настройки:

  • адреса базы данных;

  • порта подключения;

  • режима работы (development или production);

  • адреса внешних сервисов;

  • паролей;

  • API-ключей;

  • токенов доступа.

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

В этой статье разберём, как Kubernetes хранит конфигурацию, работает с секретными данными и обеспечивает постоянное хранение информации.


Почему конфигурацию нельзя хранить в Docker-образе?

Представим, что разработчик собрал Docker-образ приложения. Внутри него находятся:

  • программный код;

  • библиотеки;

  • настройки подключения к базе данных;

  • пароль администратора.

На первый взгляд это удобно — всё находится в одном месте. Но на практике такой подход создаёт множество проблем.

Каждое изменение требует пересборки образа

Предположим, база данных переехала на другой сервер. Если адрес хранится внутри образа, придётся:

  1. изменить файл конфигурации;

  2. заново собрать Docker-образ;

  3. загрузить его в Registry;

  4. обновить приложение в 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.

Комментарии (0)