Тома в Kubernetes — это ключевой механизм для управления данными в контейнерах. Они позволяют сохранять данные между перезапусками контейнеров, делиться файлами между подами и подключать внешние хранилища. Сегодня мы подробно разберем, как работают Volumes в Kubernetes, и на практике создадим несколько их типов, включая emptyDir
, hostPath
и PersistentVolume
.
Kubernetes Volumes — это объект в кластере Kubernetes, предназначенный для управления данными в контейнерах. Контейнеры по своей природе являются эфемерными, то есть их состояние не сохраняется после перезапуска или удаления. Это создает проблему для приложений, которым требуется постоянное хранение данных, таких как базы данных, файлы конфигурации или кэш. Kubernetes Volumes решают данную проблему, предоставляя способ хранения данных, доступных для контейнеров в поде, и управления ими на уровне кластера.
Основная задача томов — обеспечить постоянное хранение данных, их доступность между перезапусками контейнеров и возможность совместного использования данных между контейнерами в одном поде. Тома также позволяют подключать внешние хранилища, такие как облачные диски, NFS или локальные директории, к контейнерам. Без использования механизма volumes данные, записанные в контейнер, будут потеряны при его перезапуске, так как файловая система контейнера является временной.
Тома в Kubernetes также упрощают управление конфигурацией. Например, с помощью томов можно монтировать файлы конфигурации, таких объектов, как ConfigMap или Secret, что позволяет динамически обновлять настройки приложения без необходимости пересборки образа контейнера.
cloud
ConfigMap
и Secret
для передачи конфиденциальных данных или настроек.В Kubernetes тома связываются с подами через указание спецификации в YAML или JSON манифестах. Они определяют, где и как данные будут доступны контейнерам.
Далее мы будем рассматривать практическую часть для которой нам понадобится кластер Kubernetes. Арендовать готовый кластер можно с помощью сервиса Kubernetes в облаке. Для работы с сервисами достаточно одной master-ноды и одной worker-ноды с минимальной конфигурацией.
После заказа кластера он будет готов к использованию в течение нескольких минут. kubeconfig
для подключения к кластеру будет сгенерирован и доступен в разделе «Дашборд».
Kubernetes поддерживает различные типы томов, каждый из которых подходит для определенных сценариев использования. Рассмотрим наиболее распространенные типы томов и их особенности на практике.
emptyDir
— это временный том в Kubernetes, который создается при запуске пода и существует только в течение его жизненного цикла. Данные в emptyDir
хранятся на узле кластера, где выполняется под, и удаляются при завершении работы пода (например, при его перезапуске или удалении). Этот тип тома подходит для временных данных, таких как кэш, промежуточные результаты обработки или для обмена данными между контейнерами внутри одного пода.
Пример конфигурации emptyDir
приведен ниже:
apiVersion: v1
kind: Namespace
metadata:
name: emptydir-volume
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: emptydir-volume
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.26.0
ports:
- containerPort: 80
volumeMounts:
- name: temp-storage
mountPath: /cache # Путь до директории внутри контейнера, куда монтируется emptyDir
volumes:
- name: temp-storage
emptyDir: {}
Мы выбрали образ веб-сервера Nginx, для которого emptyDir
используется как временное хранилище. Оно подходит для кэширования данных или хранения лог-файлов, которые не требуют долговременного сохранения.
Сохраняем конфигурацию выше в файл с именем nginx-emptydir.yaml
и применяем:
kubectl apply -f nginx-emptydir.yaml
Проверим что под был успешно запущен:
kubectl get pods -n emptydir-volume
Как можно увидеть на скриншоте выше, под был успешно запущен и его статус отображается как Running
.
Далее подключаемся к контейнеру при помощи команды:
kubectl exec -it nginx-deployment-b8789c9cb-9xnv4 -n emptydir-volume -- /bin/bash
Где nginx-deployment-b8789c9cb-9xnv4
это имя пода. Не забудьте заменить на нужное — у вас будет свое уникальное имя.
Внутри контейнера проверяем наличие директории /cache
:
ls -l /
Директория была успешно создана. Теперь попробуем создать файл, чтобы проверить, что возможна запись:
echo "test k8s emptydir" > /cache/new-test-file.txt
Проверяем наличие файла а также выводим его содержимое:
ls /cache && cat /cache/new-test-file.txt
Файл был успешно создан и запись успешно проходит.
Далее выходим из контейнера при помощи команды:
exit
Как мы уже подчеркивали ранее, том emptyDir
удаляется при удалении пода, но сохраняется при перезапуске контейнера внутри пода (если под не удаляется). Чтобы проверить это, сымитируем сбой, завершив основной процесс в контейнере при помощи команды:
kubectl exec nginx-deployment-b8789c9cb-9xnv4 -c nginx -n emptydir-volume -- sh -c "kill 1"
Далее снова подключаемся к контейнеру:
kubectl exec -it nginx-deployment-b8789c9cb-9xnv4 -n emptydir-volume -- /bin/bash
Где nginx-deployment-b8789c9cb-9xnv4
это имя пода. Не забудьте заменить на нужное — у вас будет свое уникальное имя. Чтобы узнать имя пода можно выполнить команду:
kubectl get po -n emptydir-volume
Проверяем что ранее созданная директория осталась на месте:
ls /
Также проверим, что ранее созданный файл new-test-file.txt
остался:
Файл testfile.txt
остался на месте, так как emptyDir
сохраняется на уровне пода.
hostPath
позволяет монтировать директорию или файл с узла кластера в контейнер. Этот тип тома полезен для доступа к системным файлам узла, например, логам или конфигурациям. Однако использование hostPath требует осторожности, так как оно привязывает под к конкретному узлу.
Рассмотрим работу hostPath
на практике. Для этого создадим два пода. В первом поде с именем writer
мы создадим файл demo.txt
, который будет храниться в директории /host
. В файл будет записана строка «Hello from writer». С помощью второго пода с именем reader
мы прочитаем файл demo.txt
, который был создан в первом поде. Опция DirectoryOrCreate
означает, что заданный путь для директории должен существовать либо, если он отсутствует, должна быть создана новая директория. Конфигурация подов приведена ниже:
apiVersion: v1
kind: Namespace
metadata:
name: hostpath-testing
---
apiVersion: v1
kind: Pod
metadata:
name: writer
namespace: hostpath-testing
spec:
containers:
- name: writer
image: busybox
command: [ "sh", "-c", "echo Hello from writer > /host/demo.txt && sleep 3600" ]
volumeMounts:
- name: host-volume
mountPath: /host
volumes:
- name: host-volume
hostPath:
path: /tmp/demo
type: DirectoryOrCreate
restartPolicy: Never
---
apiVersion: v1
kind: Pod
metadata:
name: reader
namespace: hostpath-testing
spec:
containers:
- name: reader
image: busybox
command: [ "sleep", "3600" ]
volumeMounts:
- name: host-volume
mountPath: /host
volumes:
- name: host-volume
hostPath:
path: /tmp/demo
type: Directory
restartPolicy: Never
Сохраняем конфигурацию выше в файл с именем hostpath-testing.yaml
и применяем:
kubectl apply -f hostpath-testing.yaml
Проверим что поды были успешно запущены:
kubectl get pods -n hostpath-testing
Как можно увидеть на скриншоте выше, поды были успешно запущены и их статусы отображается как Running
.
Далее подключаемся ко второму поду с именем reader и выводим содержимое файла demo.txt
:
kubectl exec reader -n hostpath-testing -- cat /host/demo.txt
Как можно заметить на скриншоте выше, содержимое файла было успешно выведено.
ConfigMap
— это объект, используемый для хранения неконфиденциальных данных в формате ключ-значение. Он предназначен для передачи конфигурационных параметров в поды, таких как настройки приложения, переменные окружения или конфигурационные файлы, без необходимости встраивать их в образ контейнера.
Рассмотрим использование ConfigMap
на практике. В конфигурации ниже задействован образ с веб-сервером Nginx, для которого будет использован файл nginx.conf
(основной конфигурационный файл Nginx). ConfigMap
используется как том (config-volume
), монтируя файл nginx.conf
в директорию /etc/nginx/conf.d
контейнера в режиме только для чтения:
apiVersion: v1
kind: Namespace
metadata:
name: nginx-configmap
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
namespace: nginx-configmap
data:
nginx.conf: |
events {}
http {
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html index.htm;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-app
namespace: nginx-configmap
spec:
replicas: 2
selector:
matchLabels:
app: nginx-app
template:
metadata:
labels:
app: nginx-app
spec:
containers:
- name: nginx
image: nginx:1.26.0
ports:
- containerPort: 80
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
volumes:
- name: config-volume
configMap:
name: nginx-config
Сохраняем конфигурацию выше в файл с именем nginx-configmap.yaml
и применяем:
kubectl apply -f nginx-configmap.yaml
Проверим что поды были успешно запущены:
kubectl get pods -n nginx-configmap
Как можно увидеть на скриншоте выше, поды были успешно запущены и их статусы отображается как Running
.
Далее подключаемся к контейнеру при помощи команды:
kubectl exec -it nginx-app-687d4c5ddf-2pfjz -n nginx-configmap -- /bin/bash
Где nginx-app-687d4c5ddf-2pfjz
это имя пода. Не забудьте заменить на нужное — у вас будет свое уникальное имя.
Внутри контейнера отобразим файл nginx.conf
для которого мы поменяли конфигурацию:
cat /etc/nginx/nginx.conf
Как можно увидеть на скриншоте выше, ранее проброшенная конфигурация была успешно применена.
Для приложений, требующих постоянного хранения, Kubernetes предоставляет механизмы PersistentVolume
(PV) и PersistentVolumeClaim
(PVC).
PersistentVolume
— это объект, представляющий ресурс хранения в кластере Kubernetes. Он может быть как физическим, так и логическим ресурсом, например, NFS, блоком в облачном хранилище или локальным диском.
PersistentVolumeClaim
— это запрос от пользователя или приложения на использование хранилища. PVC запрашивает определенный объем хранилища и режим доступа. Kubernetes связывает PVC с подходящим PV, если он доступен, или динамически создает PV при помощи StorageClass
.
В качестве ресурса для PV мы воспользуемся дополнением CSI S3 от хостинг-провайдера Timeweb Cloud.
CSI S3 — это плагин для Kubernetes, который позволяет монтировать S3-бакеты как тома хранения для контейнеров. Он поддерживает динамическое выделение бакетов и их монтирование через FUSE (например, с использованием GeeseFS
или rclone
). Более подробно с CSI S3 можно ознакомиться перейдя по ссылке.
Для начала необходимо установить плагин CSI S3. Для этого переходим в раздел «Дополнения» на странице управления кластером и находим плагин с именем «CSI S3»:
На странице плагина создаем новый бакет или используем существующий (если он был арендован ранее):
После этого нажимаем на кнопку «Установить» и дожидаемся установки плагина.
Для проверки установки выполним команду ниже:
kubectl get storageclass csi-s3 -o yaml
В ответ должна отобразиться информация о подключенном бакете:
После того мы создали объект типа PV, создадим PVC. Пример конфигурации приведен ниже:
apiVersion: v1
kind: Namespace
metadata:
name: test-pv-and-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: csi-s3-pvc
namespace: test-pv-and-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
storageClassName: csi-s3
Манифест выше определяет PersistentVolumeClaim
с именем csi-s3-pvc
и запрашивает 5 ГБ хранилища с режимом доступа ReadWriteMany
, который разрешает множественным подам читать и записывать данные одновременно используя класс хранения csi-s3
.
Сохраняем конфигурацию выше в файл с именем pvc-testing.yaml и применяем:
kubectl apply -f pvc-testing.yaml
Далее проверяем что объект PersistentVolumeClaim
был успешно создан:
kubectl get persistentvolumeclaim -n test-pv-and-pvc
Чтобы приложение могло использовать PVC, его необходимо подключить в манифесте Deployment. Пример ниже демонстрирует, как контейнер с образом Nginx использует подключенный PVC:
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-pvc-nginx-deployment
namespace: test-pv-and-pvc
spec:
replicas: 1
selector:
matchLabels:
app: nginx-app
template:
metadata:
labels:
app: nginx-app
spec:
containers:
- name: nginx-test-container
image: nginx:1.26.0
volumeMounts:
- name: s3-storage
mountPath: /usr/share/nginx/html
volumes:
- name: s3-storage
persistentVolumeClaim:
claimName: csi-s3-pvc
Контейнер монтирует том s3-storage по пути /usr/share/nginx/html
, который связан с PersistentVolumeClaim
под названием csi-s3-pvc
.
Сохраняем конфигурацию выше в файл с именем pvc-deployment-connect.yaml
и применяем:
kubectl apply -f pvc-deployment-connect.yaml
Проверяем что все объекты были успешно созданы и под запущен:
kubectl get all -n test-pv-and-pvc
Далее проверим что ранее примонтированный том доступен в контейнере. Для этого подключаемся к контейнеру при помощи команды:
kubectl exec -it test-pvc-nginx-deployment-5fd656dd6d-5r6sr -n test-pv-and-pvc -- /bin/bash
Где test-pvc-nginx-deployment-5fd656dd6d-5r6sr
это имя пода. Не забудьте поменять на свое. Имя пода можно узнать при помощи команды:
kubectl get po -n test-pv-and-pvc
Внутри контейнера выполняем команду:
df -h
Директория /usr/share/nginx/html
была успешно примонтирована.
Теперь возвращаемся в веб-интерфейс хранилище S3 в панели управления:
Далее переходим в раздел «Объекты»:
Открываем корневую директорию хранилища и нажимаем на кнопку «Загрузить файл»:
В качестве примера загрузим какой-нибудь файл. Например, картинку в формате SVG:
Подключаемся к контейнеру еще раз:
kubectl exec -it test-pvc-nginx-deployment-5fd656dd6d-5r6sr -n test-pv-and-pvc -- /bin/bash
Где test-pvc-nginx-deployment-5fd656dd6d-5r6sr
это имя пода. Не забудьте поменять на свое.
Переходим в директорию usr/share/nginx/html
:
cd usr/share/nginx/html
и проверяем наличие ранее загруженного файла:
Файл успешно отобразился.
Подготовили для вас выгодные тарифы на облачные серверы
ReadWriteOnce
, ReadOnlyMany
, ReadWriteMany
) в зависимости от потребностей приложения.hostPath
привязывает под к конкретному узлу, что может нарушить переносимость и отказоустойчивость.emptyDir
, который ограничен ресурсами узла.hostPath
убедитесь, что директория на узле доступна для записи.StorageClass
динамическое выделение PV невозможно, что приводит к ручному управлению хранилищем.emptyDir
использует диск узла, его переполнение может повлиять на стабильность кластера.reclaimPolicy
).Kubernetes Volumes
— это мощный инструмент для работы с данными в кластере, обеспечивающий гибкость и удобство управления конфигурацией. Знание типов томов, их принципов работы и лучших практик упрощает взаимодействие приложений с файлами. Чтобы обеспечить стабильность и высокую производительность системы, важно учитывать особенности каждого типа тома и избегать типичных ошибок.