MinIO Operator — это аддон для Kubernetes, который упрощает развертывание и управление S3-совместимыми хранилищами на базе MinIO. Он позволяет создавать распределенные кластеры хранения через CRD, управлять пользователями, бакетами и конфигурацией через Kubernetes-манифесты.
Установка в ПУ
Для работы аддона необходимо, чтобы в кластере был установлен аддон CSI-driver.
Для установки MinIO Operator, в панели управления кластером, перейдите во вкладку «Дополнения». Найдите «MinIO Operator» в списке и нажмите на него.

В открывшемся окне можно при необходимости изменить параметры установки. Описание параметров доступно в официальной документации. Воспользоваться аддоном можно не меняя настроек по умолчанию.
Для установки аддона нажмите на кнопку «Установить».

Дождитесь завершения установки. Для проверки, что установка завершена корректно, выполните команду:
kubectl get all -n operator
У запущенных подов должен быть статус Running.
Пример использования
В качестве примера развернем кластер MinIO из одного узла. Данные будут храниться на сетевом диске, а доступ к кластеру будет организован через Nginx Ingress с настроенным HTTPS.
В продакшн-среде рекомендуется использовать не менее 4 узлов для обеспечения отказоустойчивость.
Предварительные требования
В кластере должны быть установлены аддоны:
- Nginx Ingress
 - CSI-driver
 - cert-manager
 - MinIO Operator
 
Все аддоны установлены с параметрами по умолчанию.
Для доступа к консоли и S3 API мы будем использовать два поддомена:
- 
minio-console.timeweb-example.cloud— для доступа к панели управления; - 
s3.timeweb-example.cloud— для доступа к S3 API. 
Перейдем к созданию кластера. Сначала опишем создание неймспейса в файле namespace.yaml:
apiVersion: v1
kind: Namespace
metadata:
  name: minio-tenant
Теперь опишем тенант в файле tenant.yaml:
apiVersion: minio.min.io/v2
kind: Tenant
metadata:
  name: myminio
  namespace: minio-tenant
spec:
  configuration:
    name: storage-configuration
  users:
    - name: storage-user
  image: quay.io/minio/minio:RELEASE.2025-04-08T15-41-24Z
  requestAutoCert: false
  mountPath: /export
  podManagementPolicy: Parallel
  pools:
    - name: pool-0
      servers: 1
      volumesPerServer: 1
      volumeClaimTemplate:
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 2Gi
          storageClassName: nvme.network-drives.csi.timeweb.cloud
Здесь:
pools — определяет пул серверов (узлов) для MinIO:
- 
servers: 1— количество серверов в пуле. - 
volumesPerServer: 1— количество томов, подключаемых к каждому серверу; - 
volumeClaimTemplate— шаблон для создания PVC. Указываем: - 
accessModes: ReadWriteOnce— том может быть смонтирован только одним подом на одном узле; - 
storage: 2Gi— размер тома; - 
storageClassName: nvme.network-drives.csi.timeweb.cloud— класс хранилища, соответствующий NVMe-дискам, предоставляемым CSI-драйвером. 
Обязательный параметр spec.configuration.name, в котором мы передаем имя секрета. Параметр используется для создания root-пользователя.
В секции spec.users указывается список пользователей, для каждого из которых должен быть создан секрет с access и secret key. Имя секрета передается в поле name.
Опишем секрет storage-configuration.yaml:
apiVersion: v1
kind: Secret
metadata:
  name: storage-configuration
  namespace: minio-tenant
type: Opaque
stringData:
  config.env: |-
    export MINIO_ROOT_USER="minio"
    export MINIO_ROOT_PASSWORD="minio123"
    export MINIO_BROWSER="on"
И storage-user.yaml:
apiVersion: v1
kind: Secret
metadata:
  name: storage-user
  namespace: minio-tenant
type: Opaque
data:
  CONSOLE_ACCESS_KEY: Y29uc29sZQ==         # console
  CONSOLE_SECRET_KEY: Y29uc29sZTEyMw==     # console123
Обратите внимание, что CONSOLE_ACCESS_KEY и CONSOLE_SECRET_KEY передаются закодированные в base64. Например:
echo -n "console" | base64        # Y29uc29sZQ==
echo -n "console123" | base64     # Y29uc29sZTEyMw==
Применим все манифесты:
kubectl apply -f namespace.yaml
kubectl apply -f storage-configuration.yaml
kubectl apply -f storage-user.yaml
kubectl apply -f tenant.yaml
Проверьте, что все манифесты успешно применились и все сервисы корректно запустились:
kubectl get all -n minio-tenant
Вы должны будете увидеть примерно такой вывод:
NAME                   READY   STATUS    RESTARTS   AGE
pod/myminio-pool-0-0   2/2     Running   0          113m
NAME                      TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service/minio             ClusterIP   10.97.190.5   <none>        80/TCP     113m
service/myminio-console   ClusterIP   10.96.97.1    <none>        9090/TCP   113m
service/myminio-hl        ClusterIP   None          <none>        9000/TCP   113m
NAME                              READY   AGE
statefulset.apps/myminio-pool-0   1/1     113m
Теперь перейдем к настройке Nginx Ingress. Создадим файл ingress.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myminio-ingress
  namespace: minio-tenant
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  rules:
    - host: minio-console.timeweb-example.cloud
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myminio-console
                port:
                  number: 9090
    - host: s3.timeweb-example.cloud
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: minio
                port:
                  number: 80
  tls:
    - hosts:
        - minio-console.timeweb-example.cloud
      secretName: minio-console-tls
    - hosts:
        - s3.timeweb-example.cloud
      secretName: s3-tls
Не забудьте заменить домены s3.timeweb-example.cloud и minio-console.timeweb-example.cloud.
Для выпуска сертификатов создадим cluster-issuer.yaml:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: example@timeweb.cloud
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
      - http01:
          ingress:
            class: nginx
Поменяйте example@timeweb.cloud на вашу почту — она будет использоваться Let's Encrypt.
Опишем балансировщик нагрузки в файле lb.yaml:
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
spec:
  type: LoadBalancer
  selector:
    app.kubernetes.io/name: ingress-nginx
  ports:
    - name: http
      port: 80
      targetPort: 80
    - name: https
      port: 443
      targetPort: 443
Примените все манифесты:
kubectl apply -f ingress.yaml
kubectl apply -f cluster-issuer.yaml
kubectl apply -f lb.yaml
Дождитесь завершения создания балансировщика нагрузки в панели управления и укажите для доменов в качестве A-записи IP-адрес балансировщика.
Проверьте, что ingress запущен, а сертификаты выпущены:
kubectl get ingress -n minio-tenant
kubectl get certificates -n minio-tenant
Теперь можно перейти к панели управления MinIO. Перейдите по домену MinIO-консоли, в нашем примере — это minio-console.timeweb-example.cloud. Вы увидите страницу авторизации.

Для авторизации используйте данные, указанные в секретах storage-user.yaml или storage-configuration.yaml, например, логин console и пароль console123.
Перейдите в раздел «Buckets», чтобы создать бакет.

Перейдите в раздел «Access Keys», чтобы создать данные для подключения — например, с помощью S3cmd. Простой конфиг для подключения будет выглядеть так:
[default]
access_key = [Access Key]
secret_key = [Secret Key]
host_base = s3.timeweb-example.cloud
host_bucket = s3.timeweb-example.cloud
use_https = True