Как использовать S3-хранилище в Kubernetes: руководство с примерами

c-lt-3oxlntcmmrj-dnt_kfqskk.png


S3 — не самое быстрое хранилище и подходит не для всех задач. Но это отличный вариант, если нужно организовать надежное и масштабируемое пространство для данных большого объема. По этой причине S3 часто используют в качестве хранилища для приложений, развернутых в Kubernetes.

Привет, Хабр! Меня зовут Филипп, я DevOps-инженер в Selectel. В своей обзорной статье хочу разобрать, какие есть решения для работы с объектным хранилищем из K8s. Соберу тестовый стенд из кластера Managed Kubernetes и покажу, как с установить и настроить нужные компоненты через Terraform. Начинаем!

Используйте навигацию, если не хотите читать текст полностью:

→ Знакомство с CSI: что это и когда использовать
→ Подготовка инфраструктуры
→ Обзор сtrox/csi-s3
→ Обзор yandex-cloud/k8s-csi-s3
→ Выводы и заключение

Знакомство с CSI: что это и когда использовать


Container Storage Interface (CSI) — это стандартный интерфейс, который позволяет Kubernetes взаимодействовать с различными системами хранения данных. Он унифицирует доступ к хранилищам данных и упрощает интеграцию различных систем хранения в кластере. Тем самым — обеспечивает гибкость и масштабируемость.


CSI полезен для администраторов и разработчиков, которые стремятся автоматизировать и упростить управление хранилищами данных в облачных и локальных средах. Это особенно актуально для крупных проектов с высокими требованиями к масштабируемости и надежности, где необходимо использовать разнообразные системы хранения без создания уникальных решений. CSI позволяет легко добавлять или менять хранилища данных по мере развития инфраструктуры.

Кроме того, CSI-драйверы развиваются независимо от ядра Kubernetes и работают в пользовательском пространстве. Это значит, что они не требуют изменений в коде K8s для добавления новых функций или исправлений. Такой подход снижает риски безопасности, так как уязвимости в CSI-драйверах напрямую не влияют напрямую на Kubernetes. А еще это позволяет быстро реагировать на изменения в требованиях к хранению данных, развивать и обновлять драйверы отдельно от основного кода K8s.

Из чего состоит CSI


CSI можно разделить на компоненты, которые разрабатывают Kubernetes и сторонние поставщики хранилищ. Подробнее архитектура описана в другой статье.

ueqlzuv0acs5vb6ekzwumrb3cis.png

Компоненты от Kubernetes

  • Driver registrar — регистрирует CSI-драйверы в kubelet.
  • External provisioner — обрабатывает запросы на создание и удаление томов.
  • External attacher — управляет монтированием и размонтированием томов.


Компоненты от сторонних поставщиков

  • CSI Identity — проверяет и возвращает информацию о плагине.
  • CSI Controller — управляет жизненным циклом томов.
  • CSI Node — управляет монтированием и размонтированием томов на узлах.


Но как работает CSI на практике? Подготовим инфраструктуру и протестируем подключение к S3 из Kubernetes.

tgxossbniikzubbzrfjobss2hyq.png

Подготовка инфраструктуры


До настройки и использования CSI необходимо подготовить инфраструктуру, которая будет включать кластер Kubernetes и S3-хранилище. В качестве основного IaC-инструмента я выбрал Terraform. Именно с помощью него буду создавать инфраструктуру и устанавливать Helm-чарты в кластер.


Первым делом нужно создать сервисного пользователя в панели управления Selectel. И установить роли «Администратор аккаунта» и «Администратор пользователей». О том, как это сделать, мы подробно рассказали в документации. Этот этап нужен для инициализации Terraform-провайдера Selectel, в который далее передадим логин и пароль.

Чтобы не писать самостоятельно большие портянки с кодом Terraform, воспользуемся кодом из публичного репозитория GitHub. Клонируем его и вносим некоторые коррективы. Далее пошагово опишу, что нужно будет сделать.

  1. Клонируйте репозиторий к себе и перейдите в созданную папку:
#: git clone git@github.com:selectel/selectel-infra-examples.git && cd selectel-infra-examples


  1. В main.tf закомментируйте все модули и ресурсы, кроме project-with-user, mks и s3-credentials.
  2. Если нужно, измените имя проекта, пользователя, регион и требуемую конфигурацию для системной группы нод.
  3. В качестве версии Managed Kubernetes укажите доступную версию. По завершении должен получиться главный файл main.tf следущего вида:
# Создаем проект с пользователем
module "project-with-user" {
  source          = "./modules/os_project_with_user"
  os_project_name = "csi_test_project"      # Имя проекта
  os_username     = "csi_test_project_user" # Имя для нового проектного сервисного пользователя
}
# Создаем S3-ключ для пользователя
module "s3-creds" {
  source           = "./modules/s3/s3-credentials"
  os_user_id       = module.project-with-user.user_id
  os_project_id    = module.project-with-user.project_id
  credentials_name = "csi-s3-creds"
  # Важный блок, без него данный модуль пойдет вперед создания
  # проекта и пользователя и упадет с ошибкой!
  depends_on = [
    module.project-with-user
  ]
}
# Создаем MKs
module "mks" {
  source = "./modules/mks/k8s-cluster-standalone"
  cluster_name = "csi-test-cluster" # Имя кластера
  kube_version = "1.28.9"           # Версия кластера
  os_availability_zone = "ru-9a" # Регион и зона
  os_region            = "ru-9"  # Регион
  os_project_id        = module.project-with-user.project_id
  # Для теста оставьте как есть
  nodegroups     = 1
  ng_nodes_count = [2]
  ng_cpus        = [4]
  ng_ram_mb      = [8192]
  ng_volume_gb   = [100]
  ng_volume_type = ["fast"]
  ng_labels      = [{ "role" : "system" }]
  # Для того чтобы отключить gpu группы нод,
  # необходимо передать gpu_nodegroups = 0
  gpu_nodegroups     = 0
  gpu_ng_nodes_count = [0]
  gpu_ng_volume_gb   = [100]
  gpu_ng_volume_type = ["fast"]
  gpu_ng_labels      = [{ "role" : "gpu" }]
  gpu_ng_flavor      = ["3031"]
  nat_subnet_cidr   = "10.222.0.0/16"
  enable_autorepair = false
  network_id        = ""
  # Важный блок, без него данный модуль пойдет вперед создания
  # проекта и пользователя и упадет с ошибкой!
  depends_on = [
    module.project-with-user
  ]
}


  1. Немного модифицируем код Terraform, чтобы мы могли устанавливать компоненты (по сути, Helm-чарты), используя Terraform-провайдер Helm. Добавим в файл providers.tf инициализацию helm-tf-провайдера:
provider "helm" {
  kubernetes {
    host                   = module.mks.kube_config.server
    client_certificate     = base64decode(module.mks.kube_config.client_cert)
    client_key             = base64decode(module.mks.kube_config.client_key)
    cluster_ca_certificate = base64decode(module.mks.kube_config.cluster_ca_cert)
  }
}


На мой взгляд это очень удобно для тестирования, ведь мы можем описывать все в одном месте в коде Terraform.

  1. Далее проверим конфигурацию — для этого выполним пару Terraform-команд. Инициализируем конфигурацию и проверим, какие изменения Terraform планирует внести в инфраструктуру:
#: terraform init
#: terraform plan


В конце вывода команды terraform plan вы должны увидеть, что будет создано n ресурсов:

...
Plan:  to add, 0 to change, 0 to destroy.


  1. Теперь применим конфигурацию и подтвердим изменения:
#: terraform apply


В результате Terraform развернет все необходимое для тестового стенда: проект, сервисного пользователя, S3-контейнер, S3-ключ, кластер и ноды MKs и сеть для MKs.

Для выполнения команды kubectl и, например, доступа к кластеру через Lens, понадобится kube_config файл, который можно получать через панель управления. По готовности можно перейти дальше и начать уже знакомиться с CSI-драйверами для S3. Как и отмечал ранее, рекомендую устанавливать чарты с помощью terraform provider helm — так вы получите описание проекта в одном месте. Но никто не запрещает устанавливать Helm-чарты через сам Helm.

Обзор сtrox/csi-s3


Ctrox/csi-s3 — это CSI-драйвер, который позволяет использовать S3-совместимые хранилища в кластерах Kubernetes. Основная идея заключается в обеспечении возможности монтирования S3-контейнеров как файловых систем внутри подов Kubernetes. Это делает хранилища доступными для приложений внутри кластера — без внесения изменений в код.


GitHub-репозиторий →

Неофициальный Helm-чарт →

Особенности


1. Инструмент поддерживает различные S3-совместимые хранилища.

2. Ctrox/csi-s3 позволяет монтировать S3-контейнеры в виде файловой системы. Это хорошо, если нужно обеспечить прямой доступ к данным в хранилище с подов в Kubernetes, например, для обработки или анализа данных в реальном времени. Без предварительного скачивания и локального хранения этих данных.

3. Поддерживает «маунтеры»: rclone, s3fs, goofys и s3backer. Вы можете выбрать наиболее подходящий инструмент.

  • rclone — универсальный инструмент для синхронизации и монтирования различных облачных хранилищ.
  • s3fs — позволяет монтировать S3-хранилища как файловую систему, используя FUSE.
  • goofys — оптимизирован для высокопроизводительного чтения данных из S3. Далее будем тестировать этот вариант.
  • s3backer (experimental*) — позволяет использовать S3 как блочное устройство.


4. Ctrox/csi-s3 редко обновляется. Крайние апдейты были около двух лет назад.

5. Нет официального Helm-чарта, но можно использовать компонент от Cloudve.

Установка


1. Добавим ресурс helm_release и опишем переменную values. Достаточно в main.tf вписать следующий код Terraform:

resource "helm_release" "ctrox-csi-s3" {
  name             = "ctrox-csi-s3"
  repository       = ""
  chart            = "ctrox-csi-s3"
  version          = "0.1.0"
  atomic           = true
  wait             = true
  wait_for_jobs    = true
  namespace        = "ctrox-csi-s3"
  create_namespace = true
  values = [
    <<-EOT
        attacher:
        image:
          repository: "quay.io/k8scsi/csi-attacher"
          pullPolicy: IfNotPresent
          tag: v3.1.0 # По умолчанию ставится v2.2.0
      secret:
           create: true
           name: "csi-s3-secret" # Имя секрета
           accessKey: ${module.s3-creds.s3_credentials_access_key}
           secretKey: ${module.s3-creds.s3_credentials_secret_key}
           endpoint: "https://s3.ru-1.storage.selcloud.ru"
      storageClass:
        mounter: goofys # Выбираем маунтер из доступных rclone, s3fs, goofys.
    EOT
  ]
  depends_on = [
    module.mks
  ]
}


Важно: данные для подключения к S3 можно получить из вывода модуля s3-creds. Но помните, что они хранятся в стейте и никак не защищены.

2. Далее применим код Terraform и дождемся, пока все запустится:

#: terraform apply
kubectl --namespace ctrox-csi-s3 get pods -w -o wide


3. Также понадобится подправить cluster-role с именем external-attacher-runner и добавить в него api-group. Выполним команду kubectl:

kubectl patch clusterrole external-attacher-runner --type='json' -p='[{"op": "add", "path": "/rules/-", "value": {"apiGroups": ["storage.k8s.io"], "resources": ["volumeattachments/status"], "verbs": ["patch"]}}]'


Тестирование


Для тестирования будем использовать утилиту fio в поде. К контейнеру с ней смонтируем PVC по пути /mnt.

Вот пример создания PVC и пода с запуском fio:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: csi-s3-pvc
  namespace: default
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: csi-s3
---
apiVersion: v1
kind: Pod
metadata:
  name: csi-s3-test
  namespace: default
spec:
  containers:
    - name: csi-s3-test-container
      image: xridge/fio
      command: ["/bin/sh"]
      args: ["-c", "fio --name=write_test --filename=/mnt/test-file --size=5G --time_based --runtime=60 --rw=write --bs=4k --ioengine=sync --group_reporting && fio --name=read_test --filename=/mnt/test-file --size=5G --time_based --runtime=60 --rw=read --bs=4k --ioengine=sync --group_reporting"]
      volumeMounts:
        - mountPath: /mnt
          name: storage
  volumes:
    - name: storage
      persistentVolumeClaim:
        claimName: csi-s3-pvc
        readOnly: false


Результаты тестирования можно посмотреть в логах пода:

kubectl logs csi-s3-test


Результаты


Преобразуем вывод команды fio в таблицу, в которой укажем следующие параметры.

  • Пропускная способность (BW, Bandwidth) — скорость передачи данных, измеряемая в MiB/s (мебибайтах в секунду) или MB/s (мегабайтах в секунду). Указывает, сколько данных можно передать за определенный промежуток времени.
  • Средняя задержка (clat) — среднее время задержки (latency) для выполнения операций ввода-вывода, измеряемое в микросекундах (мкс) или наносекундах (нс). Чем ниже значение, тем быстрее система обрабатывает запросы.
  • Пиковая задержка (max clat) — максимальное время задержки, зарегистрированное во время теста. Пиковое значение показывает наихудший случай времени ожидания для операции ввода-вывода.
  • BS-size (размер блока) — размер блока данных, используемый в тесте ввода-вывода. Например, 4k (килобайты) или 2M (мегабайты). Различные размеры блоков могут влиять на производительность системы в зависимости от конфигурации и типов рабочих нагрузок.

Первое тестирование ctrox/csi-s3 с маунтером Goofys показало хорошие результаты для чтения. CSI достигла скорости 355 MiB/s (372 MB/s) и 10,24 мкс avg clat. Однако запись оказалась медленнее: 44,1 MiB/s (46,3 MB/s) и 87,78 мкс avg clat.

Обзор yandex-cloud/k8s-csi-s3


Это решение от Яндекс, которое является форком ctrox/csi-s3. В него добавлена поддержка маунтера GeeseFS, основанного на goofys.


GitHub-репозиторий →

Официальный Helm-чарт →

Особенности


GeeseFS — высокопроизводительный файловый монтировщик через FUSE для S3-совместимых хранилищ. И все особенности завязаны на нем.

  • Производительность. GeeseGS быстрее традиционных монтировщиков вроде s3fs. Все это — благодаря оптимизированному коду, который минимизирует количество операций чтения и записи. Подробнее о тестах производительности читайте в репозитории на GitHub.
  • Частичное изменение и дозапись объектов. Вы можете хранить в контейнере данные, например логи, в виде единого файла, периодически дописывая информацию. Также упрощается работа с большими файлами.


Остальные особенности хорошо описаны в документации.

Установка


Важно: перед установкой необходимо удалить все, что связано с ctrox.

1. Клонируем репозиторий и при описании helm_release укажем локальное расположение Helm-чарта:

#: git clone https://github.com/yandex-cloud/k8s-csi-s3.git yandex-csi-s3
resource "helm_release" "yandex-csi-s3" {
  name             = "yandex-csi-s3"
  chart            = "./yandex-csi-s3/deploy/helm/csi-s3"
  atomic           = true
  wait             = true
  wait_for_jobs    = true
  namespace        = "yandex-csi-s3"
  create_namespace = true
  values = [
    <<-EOT
      secret:
        accessKey: "${module.s3-creds.s3_credentials_access_key}"
        secretKey: "${module.s3-creds.s3_credentials_secret_key}"
        endpoint: "https://s3.ru-1.storage.selcloud.ru"
        region: "ru-1"
    EOT
  ]
  depends_on = [
    module.mks
  ]
}


2. Применим код Terraform и проверим, что поды успешно запустились:

kubectl --namespace ctrox-csi-s3 get pods -w -o wide


Если все поды успешно запустились, можем переходить к тестированию.

Тестирование


Выполним тест с запуском пода c fio. Применим манифест:

Код запуска пода с fio ↓
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: csi-s3-pvc
  namespace: default
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: csi-s3
---
apiVersion: v1
kind: Pod
metadata:
  name: csi-s3-test
  namespace: default
spec:
  containers:
    - name: csi-s3-test-container
      image: xridge/fio
      command: ["/bin/sh"]
      args: ["-c", "fio --name=write_test --filename=/mnt/test-file --size=5G --time_based --runtime=60 --rw=write --bs=4k --ioengine=sync --group_reporting && fio --name=read_test --filename=/mnt/test-file --size=5G --time_based --runtime=60 --rw=read --bs=4k --ioengine=sync --group_reporting"]
      volumeMounts:
        - mountPath: /mnt
          name: storage
  volumes:
    - name: storage
      persistentVolumeClaim:
        claimName: csi-s3-pvc
        readOnly: false


Вытащим из логов контейнера необходимые значения и зафиксируем их в таблице:

kubectl logs csi-s3-test

GeeseFs показал себя лучше в чтении, где скорость достигла 446 MiB/s (468 MB/s) и 8,04 мкс avg clat. Однако запись оказалась в 2 раза медленней: 24,7 MiB/s (25,9 MB/s) и 157,36 мкс avg clat.

Выводы и заключение


Давайте объединим результаты операций чтения/записи маунтерами Goofys и GeeseFS и сделаем общие выводы.

Общий анализ


Пропускная способность (BW)

  • Чтение. GeeseFS показал лучшую пропускную способность по сравнению с Goofys. Показатель GeeseFS достиг 446 MiB/s (468 MB/s), в то время как для Goofys он же составляет 355 MiB/s (372 MB/s).
  • Запись. Снова GeeseFS показал лучшие результаты с пропускной способностью: 24,7 MiB/s (25.9 MB/s) против 44,1 MiB/s (46,3 MB/s). Это указывает на лучшую оптимизацию чтения у GeeseFS.


Средняя задержка (clat)

  • Чтение. GeeseFS показал более низкую среднюю задержку (8,04 мкс) по сравнению с Goofys (10,24 мкс), что свидетельствует о более быстрой обработке запросов чтения.
  • Запись. Goofys показал лучшую производительность с задержкой 87,78 мкс против 157,36 мкс у GeeseFS. Это может быть связано с оптимизацией записи у Goofys.


Пиковая задержка (max clat):

демонстрировал более стабильную производительность с пиковым значением 16,91 мс против 300,555 мс у Goofys.Запись. В этом случае GeeseFS показал лучшие результаты. Пиковым значение составило 8,982 мс по сравнению с 121,541 мс у Goofys.

Заключение


Благодаря CSI-реализации в kubernetes и маунтеров мы можем легко использовать S3-контейнер для различных целей. Например, для хранения резервных копий, больших данных или любых других артефактов, с которым работает ваше приложение.

Что думаете насчет рассмотренных инструментов? Может, использовали их на практике? Поделитесь своим мнением в комментариях!

Возможно, эти тексты тоже вас заинтересуют:

→ Как тестировать UX-сценарии в сложных продуктах? Метод «домашки» для пользователей
→ СХД в реестре Минпромторга, новые AMD Ryzen и другое железо июня
→ Всего два месяца — и новый релиз: Linux 6.10 уже готов

© Habrahabr.ru