789 lines
31 KiB
Markdown
789 lines
31 KiB
Markdown
# MinIO в Kubernetes: объектное хранилище 1 ТБ с веб-интерфейсом
|
||
|
||
**Кластер:** Rocky Linux 9 · Kubernetes 1.33 · Flannel CNI
|
||
**Текущая топология:** k8s-manager-01 (10.203.0.92) · k8s-master-01 (10.203.0.97) · k8s-worker-01 (10.203.0.96)
|
||
**Дата исследования:** 2026-05-28
|
||
**Проверено на кластере:** 2026-05-28 (kubectl + Longhorn API)
|
||
|
||
---
|
||
|
||
## Содержание
|
||
|
||
1. [Обзор и назначение](#1-обзор-и-назначение)
|
||
2. [Режимы развёртывания](#2-режимы-развёртывания)
|
||
3. [Планирование хранилища 1 ТБ](#3-планирование-хранилища-1-тб)
|
||
4. [Helm-развёртывание (standalone)](#4-helm-развёртывание-standalone)
|
||
5. [MinIO Console: веб-интерфейс](#5-minio-console-веб-интерфейс)
|
||
6. [Интеграция с Traefik](#6-интеграция-с-traefik)
|
||
7. [Безопасность: пользователи и политики](#7-безопасность-пользователи-и-политики)
|
||
8. [Интеграция с Loki (S3 backend)](#8-интеграция-с-loki-s3-backend)
|
||
9. [Ansible-роль и плейбук](#9-ansible-роль-и-плейбук)
|
||
10. [Масштабирование до distributed-режима](#10-масштабирование-до-distributed-режима)
|
||
11. [Итоги и рекомендации](#11-итоги-и-рекомендации)
|
||
|
||
---
|
||
|
||
## 1. Обзор и назначение
|
||
|
||
MinIO — S3-совместимое объектное хранилище. Работает в Kubernetes как StatefulSet или Deployment, предоставляет:
|
||
|
||
- **S3 API** (порт 9000) — совместим с любым клиентом AWS SDK, boto3, mc, s3cmd
|
||
- **MinIO Console** (порт 9001) — встроенный веб-интерфейс: браузер объектов, управление пользователями, мониторинг
|
||
|
||
### Сценарии использования в текущем кластере
|
||
|
||
| Сценарий | Описание |
|
||
|---|---|
|
||
| Loki S3 backend | Хранилище chunks/index при переходе на distributed Loki |
|
||
| Бэкапы баз данных | S3-target для pg_dump, mysqldump, Velero |
|
||
| Артефакты CI/CD | GitLab Runner cache, артефакты сборки |
|
||
| Файловое хранилище | Статика, медиафайлы приложений |
|
||
| Резервные копии etcd | Автоматические снэпшоты control plane |
|
||
|
||
---
|
||
|
||
## 2. Режимы развёртывания
|
||
|
||
### Текущее состояние кластера (проверено)
|
||
|
||
```
|
||
Longhorn-диски на k8s-worker-01:
|
||
longhorn-disk1 /mnt/longhorn-disk1 ~4.09 TiB (свободно ~4.06 TiB) allowScheduling=true
|
||
longhorn-disk2 /mnt/longhorn-disk2 ~4.09 TiB (свободно ~4.06 TiB) allowScheduling=true
|
||
|
||
k8s-master-01: диск из Longhorn удалён — нода без дисков, в планировании не участвует ✓
|
||
|
||
StorageClass longhorn (default):
|
||
numberOfReplicas: 1 ✓
|
||
allowVolumeExpansion: true
|
||
createDefaultDiskLabeledNodes: true
|
||
|
||
Traefik hostPort (занято): 10001 (k8s-dashboard), 10002 (longhorn-ui)
|
||
Traefik hostPort (свободно): 10003+ → MinIO: 10005 (API), 10006 (Console)
|
||
```
|
||
|
||
### Standalone (текущий кластер — 1 worker)
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ k8s-worker-01 (10.203.0.96) │
|
||
│ │
|
||
│ [MinIO Pod — StatefulSet 1 replica] │
|
||
│ ├── порт 9000 (S3 API) │
|
||
│ └── порт 9001 (Console UI) │
|
||
│ ↓ │
|
||
│ [PVC 1 TiB → StorageClass longhorn-minio] │
|
||
│ [Longhorn → longhorn-disk1 (/mnt/longhorn-disk1, 4 TiB)] │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
↓ ClusterIP
|
||
┌─────────────────────┐
|
||
│ Traefik │
|
||
│ 10005 → API :9000 │
|
||
│ 10006 → UI :9001 │
|
||
└─────────────────────┘
|
||
```
|
||
|
||
**Ограничения standalone:** нет erasure coding. Потеря диска = потеря данных. Приемлемо при наличии Longhorn-снэпшотов по расписанию.
|
||
|
||
### SNMD — Single Node Multi-Drive (будущее)
|
||
|
||
Worker уже имеет два диска (~4 TiB каждый). MinIO поддерживает SNMD начиная с 4 дисков. При добавлении ещё двух дисков к worker-ноде можно перейти на SNMD без новых нод:
|
||
|
||
```
|
||
MinIO SNMD, 4 диска по ~1 TiB (из доступных 4 TiB на каждом диске):
|
||
- usable storage ≈ 2 TiB (EC:2 — паритет 50%)
|
||
- допустимая потеря: 2 из 4 дисков
|
||
```
|
||
|
||
### Distributed (4+ нод)
|
||
|
||
При добавлении worker-нод: 1 под MinIO на ноду, PVC через Longhorn на каждой. Требует минимум 4 пода (4 ноды) для полноценного erasure coding.
|
||
|
||
**Для текущего кластера** (1 worker, 2 диска) — только standalone.
|
||
|
||
---
|
||
|
||
## 3. Планирование хранилища 1 ТБ
|
||
|
||
### Реальная конфигурация дисков (проверено)
|
||
|
||
На `k8s-worker-01` уже настроены два Longhorn-диска:
|
||
|
||
| Диск | Путь | Всего | Свободно | Статус |
|
||
|---|---|---|---|---|
|
||
| `longhorn-disk1` | `/mnt/longhorn-disk1` | ~4.09 TiB | ~4.06 TiB | Ready, Schedulable |
|
||
| `longhorn-disk2` | `/mnt/longhorn-disk2` | ~4.09 TiB | ~4.06 TiB | Ready, Schedulable |
|
||
|
||
Оба диска полностью свободны. PVC 1 TiB займёт ~25% одного диска, оставляя ~3 TiB в резерве на том же диске.
|
||
|
||
### Выделенный StorageClass для MinIO
|
||
|
||
Дефолтный `longhorn` (numberOfReplicas: 1) технически подойдёт, но для MinIO рекомендуется отдельный StorageClass по двум причинам:
|
||
|
||
- **reclaimPolicy: Retain** — при случайном удалении namespace или PVC данные на диске не уничтожаются (дефолтный Longhorn использует `Delete`)
|
||
- **diskSelector** — позволяет зафиксировать MinIO на `longhorn-disk1`, оставив `longhorn-disk2` для других workload (Loki, бэкапы и т.д.)
|
||
|
||
```yaml
|
||
# применить через роль minio/tasks/storageclass.yml
|
||
apiVersion: storage.k8s.io/v1
|
||
kind: StorageClass
|
||
metadata:
|
||
name: longhorn-minio
|
||
provisioner: driver.longhorn.io
|
||
reclaimPolicy: Retain # данные остаются при удалении PVC
|
||
allowVolumeExpansion: true
|
||
parameters:
|
||
numberOfReplicas: "1"
|
||
dataLocality: "best-effort"
|
||
diskSelector: "minio" # тег только на longhorn-disk1
|
||
fsType: "ext4"
|
||
dataEngine: "v1"
|
||
```
|
||
|
||
### Disk-тег для изоляции MinIO на disk1 (опционально)
|
||
|
||
Если нужно зафиксировать MinIO именно на `longhorn-disk1` (рекомендуется при нескольких workload):
|
||
|
||
```bash
|
||
kubectl -n longhorn-system patch node.longhorn.io k8s-worker-01 --type=json -p='[
|
||
{"op":"add","path":"/spec/disks/longhorn-disk1/tags","value":["minio"]}
|
||
]'
|
||
```
|
||
|
||
Без тега Longhorn выберет любой из двух дисков (disk1 или disk2) — это тоже корректно, просто менее предсказуемо.
|
||
|
||
### Репликация на уровне Longhorn
|
||
|
||
Сейчас: `numberOfReplicas: 1` — без репликации. При добавлении второго worker с Longhorn-дисками поднять до `2` — Longhorn начнёт реплицировать PVC MinIO между нодами без остановки пода.
|
||
|
||
> **Важно:** репликация Longhorn защищает от потери ноды, но **не заменяет** MinIO distributed mode. Longhorn реплицирует блочное устройство целиком, erasure coding MinIO работает на уровне объектов.
|
||
|
||
### Расчёт полезной ёмкости
|
||
|
||
Физический диск (`longhorn-disk1`) — ~4.09 TiB. MinIO получает PVC 1 TiB — остаток диска доступен для других PVC.
|
||
|
||
| Параметр | Значение |
|
||
|---|---|
|
||
| Физический диск | ~4.09 TiB (`longhorn-disk1`) |
|
||
| PVC для MinIO | 1 TiB |
|
||
| Overhead Longhorn + ext4 | ~2% |
|
||
| Overhead MinIO (метаданные) | ~1% |
|
||
| **Доступно для объектов** | **~990 Gi** |
|
||
| Рекомендуемый порог заполнения | 80% → ~790 Gi |
|
||
| Остаток на диске для других PVC | ~3.09 TiB |
|
||
|
||
MinIO по умолчанию отказывается принимать данные при заполнении > 95% (настраивается через `MINIO_STORAGE_CLASS_STANDARD`).
|
||
|
||
---
|
||
|
||
## 4. Helm-развёртывание (standalone)
|
||
|
||
### Helm chart
|
||
|
||
Используется официальный chart `minio/minio` (не bitnami — он добавляет лишние зависимости).
|
||
|
||
```bash
|
||
helm repo add minio https://charts.min.io/
|
||
helm repo update
|
||
```
|
||
|
||
### `minio-values.yml`
|
||
|
||
```yaml
|
||
# roles/minio/files/minio-values.yml
|
||
|
||
## Режим: standalone
|
||
mode: standalone
|
||
|
||
## Образ
|
||
image:
|
||
repository: quay.io/minio/minio
|
||
tag: RELEASE.2025-04-22T22-12-26Z # фиксированная версия
|
||
pullPolicy: IfNotPresent
|
||
|
||
## Корневые учётные данные (переопределяются через Secret)
|
||
existingSecret: minio-root-credentials
|
||
|
||
## Хранилище
|
||
persistence:
|
||
enabled: true
|
||
storageClass: longhorn-minio # выделенный SC: numberOfReplicas=1, только worker-диски
|
||
accessMode: ReadWriteOnce
|
||
size: 1Ti
|
||
|
||
## Ресурсы пода
|
||
resources:
|
||
requests:
|
||
memory: 512Mi
|
||
cpu: 250m
|
||
limits:
|
||
memory: 2Gi
|
||
cpu: 1000m
|
||
|
||
## Привязка к worker-ноде (не запускать на control plane)
|
||
nodeSelector:
|
||
node-role.kubernetes.io/control-plane: "" # исключить
|
||
affinity:
|
||
nodeAffinity:
|
||
requiredDuringSchedulingIgnoredDuringExecution:
|
||
nodeSelectorTerms:
|
||
- matchExpressions:
|
||
- key: node-role.kubernetes.io/control-plane
|
||
operator: DoesNotExist
|
||
|
||
## Сервисы
|
||
service:
|
||
type: ClusterIP
|
||
port: 9000
|
||
|
||
consoleService:
|
||
type: ClusterIP
|
||
port: 9001
|
||
|
||
## Buckets создаются автоматически при старте
|
||
buckets:
|
||
- name: loki-chunks
|
||
policy: none
|
||
purge: false
|
||
- name: backups
|
||
policy: none
|
||
purge: false
|
||
- name: artifacts
|
||
policy: none
|
||
purge: false
|
||
|
||
## Пользователи (пароли — через Secret, задаются ниже)
|
||
users:
|
||
- accessKey: loki
|
||
existingSecret: minio-user-loki
|
||
existingSecretKey: secretKey
|
||
policy: readwrite
|
||
- accessKey: backup
|
||
existingSecret: minio-user-backup
|
||
existingSecretKey: secretKey
|
||
policy: readwrite
|
||
|
||
## Политики для бакетов
|
||
policies:
|
||
- name: loki-policy
|
||
statements:
|
||
- resources:
|
||
- "arn:aws:s3:::loki-chunks"
|
||
- "arn:aws:s3:::loki-chunks/*"
|
||
actions:
|
||
- "s3:GetObject"
|
||
- "s3:PutObject"
|
||
- "s3:DeleteObject"
|
||
- "s3:ListBucket"
|
||
|
||
## Метрики (если Prometheus есть)
|
||
metrics:
|
||
serviceMonitor:
|
||
enabled: false # включить когда появится Prometheus
|
||
|
||
## MinIO Console — веб-интерфейс
|
||
consoleIngress:
|
||
enabled: false # используем Traefik напрямую через ClusterIP
|
||
|
||
## Окружение MinIO
|
||
environment:
|
||
MINIO_BROWSER_REDIRECT_URL: "http://10.203.0.96:10006" # URL Console через Traefik
|
||
MINIO_STORAGE_CLASS_STANDARD: "EC:0" # standalone: без erasure coding
|
||
MINIO_UPDATE: "off" # отключить автообновление
|
||
```
|
||
|
||
### Secret с корневыми учётными данными
|
||
|
||
```bash
|
||
kubectl create secret generic minio-root-credentials \
|
||
--from-literal=rootUser=minioadmin \
|
||
--from-literal=rootPassword='СИЛЬНЫЙ_ПАРОЛЬ_МИНИМУМ_8_СИМВОЛОВ' \
|
||
-n minio
|
||
```
|
||
|
||
### Установка
|
||
|
||
```bash
|
||
kubectl create namespace minio
|
||
|
||
helm upgrade --install minio minio/minio \
|
||
--namespace minio \
|
||
--version 5.4.0 \
|
||
--values roles/minio/files/minio-values.yml \
|
||
--wait
|
||
```
|
||
|
||
### Проверка
|
||
|
||
```bash
|
||
# Статус пода
|
||
kubectl get pod -n minio
|
||
|
||
# Логи
|
||
kubectl logs -n minio -l app=minio
|
||
|
||
# Проброс порта для быстрой проверки
|
||
kubectl port-forward -n minio svc/minio 9000:9000 &
|
||
kubectl port-forward -n minio svc/minio-console 9001:9001 &
|
||
|
||
# Проверка S3 API через mc (MinIO Client)
|
||
mc alias set local http://localhost:9000 minioadmin ПАРОЛЬ
|
||
mc ls local
|
||
mc admin info local
|
||
```
|
||
|
||
---
|
||
|
||
## 5. MinIO Console: веб-интерфейс
|
||
|
||
MinIO Console — встроенный React-интерфейс, запускается в том же поде на порту 9001. Отдельная установка не нужна начиная с MinIO RELEASE.2021-07-08.
|
||
|
||
### Возможности Console
|
||
|
||
| Раздел | Функции |
|
||
|---|---|
|
||
| Object Browser | Навигация по бакетам, загрузка/скачивание файлов, просмотр метаданных |
|
||
| Buckets | Создание бакетов, версионирование, lifecycle policies, replication |
|
||
| Identity → Users | Создание пользователей, назначение политик |
|
||
| Identity → Groups | Группировка пользователей |
|
||
| Identity → Policies | Редактор IAM-политик (JSON) |
|
||
| Monitoring | Дашборд загрузки, IOPS, throughput в реальном времени |
|
||
| Logs | Потоковый просмотр логов MinIO в браузере |
|
||
| Audit | Журнал операций (включается через `MINIO_AUDIT_WEBHOOK_*`) |
|
||
|
||
### Настройка адреса Console
|
||
|
||
MinIO Console проверяет `MINIO_BROWSER_REDIRECT_URL` при формировании redirect после логина. Без правильного значения Console вернёт 401 или redirect на неверный URL.
|
||
|
||
```yaml
|
||
# В minio-values.yml — уже задано выше
|
||
environment:
|
||
MINIO_BROWSER_REDIRECT_URL: "http://10.203.0.96:10006"
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Интеграция с Traefik
|
||
|
||
Два порта в `traefik_port_map`:
|
||
- `10005` — MinIO S3 API (для клиентов, boto3, mc)
|
||
- `10006` — MinIO Console (веб-интерфейс)
|
||
|
||
### Добавить в `inventory/prod/group_vars/traefik.yml`
|
||
|
||
```yaml
|
||
traefik_port_map:
|
||
# ... существующие записи ...
|
||
|
||
- name: minio-api
|
||
description: "MinIO S3 API"
|
||
port: 10005
|
||
backend:
|
||
namespace: minio
|
||
service: minio
|
||
port: 9000
|
||
scheme: http
|
||
basicauth:
|
||
enabled: false # MinIO использует собственную аутентификацию (AWS Signature v4)
|
||
|
||
- name: minio-console
|
||
description: "MinIO Console (Web UI)"
|
||
port: 10006
|
||
backend:
|
||
namespace: minio
|
||
service: minio-console
|
||
port: 9001
|
||
scheme: http
|
||
basicauth:
|
||
enabled: false # MinIO Console имеет собственный логин
|
||
```
|
||
|
||
> **Важно:** BasicAuth от Traefik + MinIO Console не совместимы — браузер не может пройти двойную аутентификацию. MinIO Console защищён своим логином, этого достаточно.
|
||
|
||
После добавления запустить `setup_traefik.yml`.
|
||
|
||
### Адреса после развёртывания
|
||
|
||
| Сервис | URL | Назначение |
|
||
|---|---|---|
|
||
| MinIO S3 API | `http://10.203.0.96:10005` | Внешний доступ клиентов |
|
||
| MinIO Console | `http://10.203.0.96:10006` | Веб-интерфейс администратора |
|
||
| MinIO API (внутри кластера) | `http://minio.minio.svc.cluster.local:9000` | Для подов кластера |
|
||
| MinIO Console (внутри кластера) | `http://minio-console.minio.svc.cluster.local:9001` | — |
|
||
|
||
### Настройка mc (MinIO Client) для внешнего доступа
|
||
|
||
```bash
|
||
# На manager-ноде или локально
|
||
mc alias set prod http://10.203.0.96:10005 minioadmin ПАРОЛЬ
|
||
|
||
# Проверка
|
||
mc ls prod
|
||
mc admin info prod
|
||
mc du prod/loki-chunks
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Безопасность: пользователи и политики
|
||
|
||
### Принцип минимальных привилегий
|
||
|
||
Не используйте root-учётные данные в приложениях. Для каждого сервиса — отдельный пользователь с ограниченной политикой.
|
||
|
||
### Создание пользователей через mc
|
||
|
||
```bash
|
||
# Пользователь для Loki (только свой бакет)
|
||
mc admin user add prod loki ПАРОЛЬ_LOKI
|
||
|
||
mc admin policy create prod loki-policy /dev/stdin <<'EOF'
|
||
{
|
||
"Version": "2012-10-17",
|
||
"Statement": [
|
||
{
|
||
"Effect": "Allow",
|
||
"Action": ["s3:*"],
|
||
"Resource": [
|
||
"arn:aws:s3:::loki-chunks",
|
||
"arn:aws:s3:::loki-chunks/*"
|
||
]
|
||
}
|
||
]
|
||
}
|
||
EOF
|
||
|
||
mc admin policy attach prod loki-policy --user loki
|
||
|
||
# Пользователь для бэкапов (только запись в backups)
|
||
mc admin user add prod backup ПАРОЛЬ_BACKUP
|
||
|
||
mc admin policy create prod backup-policy /dev/stdin <<'EOF'
|
||
{
|
||
"Version": "2012-10-17",
|
||
"Statement": [
|
||
{
|
||
"Effect": "Allow",
|
||
"Action": ["s3:PutObject", "s3:GetObject", "s3:ListBucket"],
|
||
"Resource": [
|
||
"arn:aws:s3:::backups",
|
||
"arn:aws:s3:::backups/*"
|
||
]
|
||
}
|
||
]
|
||
}
|
||
EOF
|
||
|
||
mc admin policy attach prod backup-policy --user backup
|
||
```
|
||
|
||
### Lifecycle policy: автоудаление старых объектов
|
||
|
||
```bash
|
||
# Удалять объекты в backups старше 30 дней
|
||
mc ilm rule add --expire-days 30 prod/backups
|
||
|
||
# Удалять незавершённые multipart uploads старше 7 дней
|
||
mc ilm rule add --expire-days 7 --noncurrent-expire-days 7 prod/backups
|
||
```
|
||
|
||
### Версионирование бакетов (защита от случайного удаления)
|
||
|
||
```bash
|
||
# Включить версионирование
|
||
mc version enable prod/backups
|
||
|
||
# Посмотреть все версии объекта
|
||
mc ls --versions prod/backups/db-dump.sql.gz
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Интеграция с Loki (S3 backend)
|
||
|
||
При переходе Loki на distributed-режим (см. [PLG исследование](../logging/plg.md)) MinIO становится S3 backend для хранения chunks и index.
|
||
|
||
### Loki values для S3 backend
|
||
|
||
```yaml
|
||
# roles/logging/templates/loki-values.yml.j2 (distributed режим)
|
||
loki:
|
||
storage:
|
||
type: s3
|
||
s3:
|
||
endpoint: http://minio.minio.svc.cluster.local:9000
|
||
region: us-east-1 # MinIO игнорирует region, но поле обязательно
|
||
bucketnames: loki-chunks
|
||
access_key_id: loki
|
||
secret_access_key: "{{ loki_minio_password }}"
|
||
insecure: true # http (не https)
|
||
s3forcepathstyle: true # обязательно для MinIO
|
||
|
||
schemaConfig:
|
||
configs:
|
||
- from: "2024-01-01"
|
||
store: tsdb
|
||
object_store: s3 # ← было filesystem
|
||
schema: v13
|
||
index:
|
||
prefix: loki_index_
|
||
period: 24h
|
||
```
|
||
|
||
### Secret для Loki в namespace monitoring
|
||
|
||
```bash
|
||
kubectl create secret generic loki-minio-secret \
|
||
--from-literal=AWS_ACCESS_KEY_ID=loki \
|
||
--from-literal=AWS_SECRET_ACCESS_KEY='ПАРОЛЬ_LOKI' \
|
||
-n monitoring
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Ansible-роль и плейбук
|
||
|
||
### Структура роли `roles/minio/`
|
||
|
||
```
|
||
roles/minio/
|
||
├── defaults/main.yml
|
||
├── tasks/
|
||
│ ├── main.yml
|
||
│ ├── namespace.yml — создать namespace minio
|
||
│ ├── secrets.yml — создать Secrets из vault/переменных
|
||
│ ├── helm.yml — helm upgrade --install minio
|
||
│ └── mc.yml — настройка mc alias, пользователей, политик, lifecycle
|
||
├── files/
|
||
│ └── minio-values.yml
|
||
└── templates/
|
||
└── minio-values.yml.j2
|
||
```
|
||
|
||
### `defaults/main.yml`
|
||
|
||
```yaml
|
||
minio_namespace: minio
|
||
minio_chart_version: "5.4.0" # minio/minio chart
|
||
minio_image_tag: "RELEASE.2025-04-22T22-12-26Z"
|
||
|
||
minio_storage_class: longhorn-minio # выделенный SC, не дефолтный longhorn (numberOfReplicas=3)
|
||
minio_storage_size: 1Ti
|
||
|
||
minio_console_url: "http://10.203.0.96:10006"
|
||
minio_api_external_url: "http://10.203.0.96:10005"
|
||
|
||
# Имена Secrets (значения создаются вручную через kubectl)
|
||
minio_root_secret: minio-root-credentials
|
||
|
||
minio_buckets:
|
||
- loki-chunks
|
||
- backups
|
||
- artifacts
|
||
|
||
minio_resources_requests_memory: "512Mi"
|
||
minio_resources_limits_memory: "2Gi"
|
||
```
|
||
|
||
### `tasks/helm.yml`
|
||
|
||
```yaml
|
||
- name: MinIO | Add Helm repo
|
||
kubernetes.core.helm_repository:
|
||
name: minio
|
||
repo_url: https://charts.min.io/
|
||
|
||
- name: MinIO | Deploy via Helm
|
||
kubernetes.core.helm:
|
||
name: minio
|
||
chart_ref: minio/minio
|
||
chart_version: "{{ minio_chart_version }}"
|
||
release_namespace: "{{ minio_namespace }}"
|
||
create_namespace: true
|
||
values: "{{ lookup('template', 'minio-values.yml.j2') | from_yaml }}"
|
||
wait: true
|
||
wait_condition:
|
||
type: Ready
|
||
status: "True"
|
||
timeout: "10m0s"
|
||
```
|
||
|
||
### `playbooks/setup_minio.yml`
|
||
|
||
```yaml
|
||
---
|
||
- name: Deploy MinIO object storage
|
||
hosts: manager_nodes
|
||
become: false
|
||
roles:
|
||
- minio
|
||
```
|
||
|
||
### `.gitlab-ci.yml` — новый job
|
||
|
||
```yaml
|
||
setup/minio:
|
||
stage: setup
|
||
script:
|
||
- ansible-playbook -i inventory/prod playbooks/setup_minio.yml
|
||
rules:
|
||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||
when: manual
|
||
resource_group: production
|
||
```
|
||
|
||
### Переменные GitLab CI/CD (добавить)
|
||
|
||
| Переменная | Описание |
|
||
|---|---|
|
||
| `MINIO_ROOT_PASSWORD` | Пароль root-пользователя MinIO |
|
||
| `MINIO_LOKI_PASSWORD` | Пароль пользователя loki |
|
||
| `MINIO_BACKUP_PASSWORD` | Пароль пользователя backup |
|
||
|
||
---
|
||
|
||
## 10. Масштабирование
|
||
|
||
### Этапы роста: Longhorn и MinIO вместе
|
||
|
||
Реальное текущее состояние (проверено): worker имеет 2 диска по ~4 TiB, оба пустые. Это даёт несколько путей масштабирования без добавления нод:
|
||
|
||
| Этап | Кластер | Longhorn | MinIO |
|
||
|---|---|---|---|
|
||
| **Сейчас** | 1 worker, 2 диска по 4 TiB | `longhorn` SC: `numberOfReplicas: 1`, master без дисков ✓ | standalone, PVC 1 TiB на disk1 |
|
||
| +2 диска к worker | 1 worker, 4 диска | 4 PVC по 1 TiB, `numberOfReplicas: 1` | **SNMD**: 4 drives, EC:2, ~2 TiB usable |
|
||
| +1 worker с дисками | 2 workers | Поднять `numberOfReplicas: 2` в `longhorn-minio` | standalone + Longhorn-репликация между нодами |
|
||
| +3 workers (4 total) | 4 workers | `numberOfReplicas: 2` | **Distributed**: 4 пода, EC:2 |
|
||
|
||
### Шаг 1: добавление второго worker (без пересоздания MinIO)
|
||
|
||
После добавления `k8s-worker-02` в кластер:
|
||
|
||
```bash
|
||
# Убедиться что Longhorn видит новую ноду и диск
|
||
kubectl get nodes -o wide
|
||
kubectl get nodes.longhorn.io -n longhorn-system
|
||
|
||
# Обновить numberOfReplicas у StorageClass (или через Longhorn UI)
|
||
kubectl edit storageclass longhorn
|
||
# numberOfReplicas: "1" → "2"
|
||
|
||
# Обновить replicas у существующего PVC MinIO
|
||
kubectl -n longhorn-system edit volume <minio-pvc-volume-name>
|
||
# spec.numberOfReplicas: 1 → 2
|
||
# Longhorn начнёт ребалансировку в фоне, MinIO продолжает работать
|
||
```
|
||
|
||
### Шаг 2: SNMD — 4 диска на одной ноде (реалистичный следующий шаг)
|
||
|
||
Worker уже имеет 2 диска (~4 TiB каждый). При добавлении ещё 2 дисков можно перейти на SNMD **без новых нод**:
|
||
|
||
```yaml
|
||
# minio-values.yml — SNMD режим (4 диска на k8s-worker-01)
|
||
mode: distributed # в MinIO SNMD тоже использует mode: distributed
|
||
replicas: 1 # 1 нода
|
||
drivesPerNode: 4 # 4 PVC на под
|
||
|
||
persistence:
|
||
storageClass: longhorn-minio
|
||
size: 1Ti # 4 × 1 TiB = 4 TiB raw → ~2 TiB usable (EC:2)
|
||
```
|
||
|
||
При SNMD Longhorn создаёт 4 отдельных PVC (по одному на каждый логический диск MinIO). `numberOfReplicas: 1` в StorageClass — MinIO EC уже обеспечивает отказоустойчивость.
|
||
|
||
### Шаг 3: distributed (4+ workers)
|
||
|
||
```yaml
|
||
# minio-values.yml при 4 worker-нодах
|
||
mode: distributed
|
||
replicas: 4 # 4 пода на 4 нодах
|
||
drivesPerNode: 1 # 1 Longhorn PVC на под
|
||
|
||
persistence:
|
||
storageClass: longhorn-minio
|
||
size: 1Ti # 4 × 1 TiB raw → ~2 TiB usable (EC:2)
|
||
|
||
affinity:
|
||
podAntiAffinity:
|
||
requiredDuringSchedulingIgnoredDuringExecution:
|
||
- labelSelector:
|
||
matchLabels:
|
||
app: minio
|
||
topologyKey: kubernetes.io/hostname
|
||
```
|
||
|
||
В distributed-режиме Longhorn предоставляет отдельный PVC каждому поду MinIO. Репликацию Longhorn для distributed оставить на `numberOfReplicas: 1` — MinIO EC уже обеспечивает отказоустойчивость.
|
||
|
||
### Миграция standalone → distributed
|
||
|
||
MinIO **не поддерживает** in-place миграцию. Процедура:
|
||
|
||
```
|
||
1. Запустить distributed MinIO рядом (namespace minio-dist)
|
||
2. mc mirror minio-standalone minio-distributed --preserve --watch
|
||
3. Дождаться синхронизации всех объектов (mc du для сверки объёмов)
|
||
4. Переключить Traefik (изменить service в IngressRoute) на новый сервис
|
||
5. Обновить endpoint в configs Loki, бэкапов и других клиентов
|
||
6. Подождать 24-48 часов, убедиться что клиенты работают корректно
|
||
7. Удалить standalone namespace и PVC
|
||
```
|
||
|
||
### Расширение объёма без миграции (только standalone)
|
||
|
||
Longhorn поддерживает расширение PVC онлайн — без остановки MinIO:
|
||
|
||
```bash
|
||
kubectl patch pvc minio -n minio \
|
||
-p '{"spec":{"resources":{"requests":{"storage":"2Ti"}}}}'
|
||
|
||
# Longhorn расширит том; MinIO увидит новое пространство автоматически
|
||
mc admin info prod # проверить новый объём в разделе capacity
|
||
```
|
||
|
||
---
|
||
|
||
## 11. Итоги и рекомендации
|
||
|
||
### Почему Longhorn — правильный выбор для этого кластера
|
||
|
||
Longhorn уже является стандартом хранилища в кластере. Использование его для MinIO даёт:
|
||
|
||
- **Единая точка управления** — диски, снэпшоты, репликация управляются через Longhorn UI, без отдельной операционной нагрузки
|
||
- **Онлайн-расширение** — PVC MinIO расширяется без остановки пода (`kubectl patch pvc`)
|
||
- **Путь к отказоустойчивости** — при добавлении второго worker достаточно поднять `numberOfReplicas: 2`, MinIO продолжает работать без изменений
|
||
- **Снэпшоты как бэкап** — Longhorn умеет делать снэпшоты PVC по расписанию; для MinIO standalone это основной механизм защиты данных до перехода на distributed
|
||
|
||
### Порядок развёртывания
|
||
|
||
```
|
||
1. Применить StorageClass longhorn-minio (см. раздел 3)
|
||
(опц.) Longhorn UI → k8s-worker-01 → добавить тег "minio" на longhorn-disk1 для изоляции
|
||
2. kubectl create namespace minio
|
||
3. Создать Secret minio-root-credentials вручную
|
||
4. Запустить setup_minio.yml (Helm install)
|
||
5. Добавить minio-api и minio-console в traefik_port_map (порты 10005, 10006 — свободны)
|
||
6. Запустить setup_traefik.yml
|
||
7. Проверить Console: http://10.203.0.96:10006
|
||
8. Создать пользователей через mc (loki, backup)
|
||
9. Обновить loki-values.yml если Loki уже работает
|
||
```
|
||
|
||
### Итоговые адреса
|
||
|
||
| Сервис | URL |
|
||
|---|---|
|
||
| MinIO Console (UI) | `http://10.203.0.96:10006` |
|
||
| MinIO S3 API | `http://10.203.0.96:10005` |
|
||
| MinIO API (внутри кластера) | `http://minio.minio.svc.cluster.local:9000` |
|
||
|
||
### Следующие шаги
|
||
|
||
- [ ] Применить StorageClass `longhorn-minio` (раздел 3)
|
||
- [ ] (опц.) Longhorn UI: добавить тег `minio` на `longhorn-disk1` для изоляции диска
|
||
- [ ] Создать Secret `minio-root-credentials` вручную
|
||
- [ ] Написать роль `roles/minio/` по структуре из раздела 9
|
||
- [ ] Добавить job `setup/minio` в `.gitlab-ci.yml`
|
||
- [ ] Добавить `minio-api` и `minio-console` в `traefik_port_map` (порты 10005, 10006)
|
||
- [ ] Добавить переменные `MINIO_ROOT_PASSWORD`, `MINIO_LOKI_PASSWORD` в GitLab CI/CD Variables
|
||
- [ ] Добавить firewalld-правило в `roles/minio/tasks/firewall.yml` (порты 9000, 9001 в `trusted` zone для flannel.1/cni0)
|