Сборка Min.io dev кластера в контейнерах

Привет! В этой статье рассмотрим разворачивание dev кластера min.io в контейнерах (docker-compose) с tls, site-replication и заденем немного tiering. Также настроим мониторинг нашего кластера в prometheus+grafana.

Статья представляет собой пошаговое руководство для разворачивания кластерного minio в контейнерах от bitnami. Также рядом развернем однонодный minio. Объединим эти 2 minio с помощью site-replication. Создадим тестового пользователя, тестовый policy, тестовый бакет и попробуем с ним работать.

Подготовка

Начнем с клонирования репозитория https://github.com/yubazh/minio-compose

git clone git@github.com:yubazh/minio-compose.git

Для использования TLS нам необходимо сгенерировать сертификаты. Сертификаты можно сгенерировать с помощью скрипта generate_ssl_cert.ssh, который лежит в директории certsgen. Скрипт использует openssl. Перед запуском сертификата, необходимо поместить свой ip-адрес в файл certsgen/server-ext.cnf вместо адреса 192.168.0.199. Также стоит обратить внимание, что сертификат будет выписан на *.local. Доменные имена minio{1…4}.local будут использовать для взаимодействия нод minio между собой.

cat certsgen/server-ext.cnf       
subjectAltName=DNS:*.local,IP:0.0.0.0,IP:127.0.0.1,IP:192.168.0.199

Посмотрим на сам скрипт:

# удаляем существующие сертификаты
rm *.pem

# генерируем certificate authority с ключом 
# 1. Generate CA's private key and self-signed certificate
openssl req -x509 -newkey rsa:4096 -days 365 -nodes -keyout ca-key.pem -out ca-cert.pem -subj "/C=XX/ST=XXX/L=XXXX/O=XXXXX/OU=XXXXXX/CN=*.local/emailAddress=mail@gmail.com"

echo "CA's self-signed certificate"
openssl x509 -in ca-cert.pem -noout -text

# генерируем сертификат с ключом
# 2. Generate web server's private key and certificate signing request (CSR)
openssl req -newkey rsa:4096 -keyout server-key.pem -out server-req.pem -subj "/C=XX/ST=XXX/L=XXXX/O=XXXXX/OU=XXXXXX/CN=*.local/emailAddress=mail@gmail.com" -nodes

# 3. Use CA's private key to sign web server's CSR and get back the signed certificate
openssl x509 -req -in server-req.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 60 -extfile server-ext.cnf

echo "Server's signed certificate"
openssl x509 -in server-cert.pem -noout -text

echo "Command to verify Server and CA certificates"
openssl verify -CAfile ca-cert.pem server-cert.pem

# удаляем директорию certs, в которую мы положим вновь созданные сертификаты для minio
echo "Deleting certs dir"
rm -rf ../certs

# создаем заново директорию ../certs и помещаем туда необходимые сертификаты
echo "Creating certs dirs"
mkdir -p ../certs/CAs
cp ca-cert.pem ../certs/ca.crt
cp ca-cert.pem ../certs/CAs/ca.crt
cp server-cert.pem ../certs/public.crt
cp server-key.pem ../certs/private.key 
chmod +r ../certs/private.key

# удаляем существующие директории для хранения данных самого minio 
echo "Deleting minio dirs"
rm -rf ../minio*

# создаем новые директории для хранения данных minio 
echo "Creating minio dirs"
mkdir ../minio1
mkdir ../minio2
mkdir ../minio3
mkdir ../minio4
mkdir ../miniosolo
sudo chown -R 1001:1001 ../minio*

Если коротко, то мы удаляем все ранее созданные сертификаты в диреткории certsgen, удаляем директорию …/certs (в которую сложим новые сертификаты и прокинем в контейнеры), удаляем старые директории для хранения персистентных данных minio и создадим новые с необходимыми правами. Запустим скрипт:

cd certsgen
./generate_ssl_cert.ssh

Если все ок, то мы увидим вывод наших сертификатов, а также сообщения об удалении старых директорий и создании новых.

Основной кластер min.io

Теперь разберем основной compose и развернем кластер.

Для разворачивания будем использовать контейнеры minio от bitnami (https://github.com/bitnami/containers/tree/main/bitnami/minio). В docker-compose мы зададим админский пароль, хостнеймы контейнеров, настроим «кластерность», https, постоянное хранилище, healtcheck, а также nginx, который будет выступать в виде прокси перед нашим кластером.

Отдельно стоит отметить, что в документации указано, что минимальное количество нод в кластере minio — 4. Также, указано что в продакшн среде необходимо использовать минимум 4 диска на каждой ноде. Это вся связано с внутренним устройством minio, и тем как он разбивает всё на объекты и хранит данные объекты в дальнейшем. На сайте minio есть удобный калькулятор для продакшн решений: https://min.io/product/erasure-code-calculator.

Также отмечу, что в документации указано, использовать на дисках для хранения «data» файловую систему xfs.

Разберем более детально файл docker-compose.yaml, а именно первый контейнер minio (остальные 3 совершенно идентичны) и контейнер с nginx:

cat docker-compose.yaml            
version: '3.7'

services:
  # first cluster
  minio1:
    # рестарт в случае падения - всегда
    restart: always
    # указываем использование образа bitnami minio с тегом 2024.7.26
    image: bitnami/minio:2024.7.26
    # отдельно указываем название контейнера
    container_name: minio1
    # указываем hostname по которому к нему можно будет обращаться внутри docker network
    hostname: minio1.local
    # блок с переменными, которые будут загружаться в minio
    environment:
      # указываем админский логин и пароль
      - MINIO_ROOT_USER=minioadmin
      - MINIO_ROOT_PASSWORD=MBVfbuu2NDS3Aw
      # указываем использование distributed mode (кластера)
      - MINIO_DISTRIBUTED_MODE_ENABLED=yes
      # указываем из каких нод будет состоять кластер.
      # в нашем случае это будут контейнеры, обращаемся к ним по хостнеймам
      - MINIO_DISTRIBUTED_NODES=minio1.local,minio2.local,minio3.local,minio4.local
      # данную переменную подсмотрел где-то на стаковерфлоу, фиксит некоторые падения
      - MINIO_SKIP_CLIENT=yes
      # указываем явное использование https
      - MINIO_SCHEME=https
      # указывает порт, по которому можем попасть на web-ui
      - MINIO_CONSOLE_PORT_NUMBER=9001
      # следующие 2 переменные следует добавлять при использовании nginx перед кластером
      # первая переменная указывает адрес всего кластер
      - MINIO_SERVER_URL=https://minio.local
      # вторая указываем конкретное расположение web-ui
      - MINIO_BROWSER_REDIRECT_URL=https://minio.local/minio/ui
      # обе эти переменные решают вопрос взаимодействия и переадресации между нодами minio и nginx
    volumes:
      # указываем директорию для хранения данных. монтируем в /bitnami/minio/data
      # обратите внимание что в разных контейнерах (разных сборщиков) разное место монтирования 
      - ./minio1:/bitnami/minio/data
      # монтируем директорию с сертификатами, для взаимодействия по https
      - ./certs:/certs
    healthcheck:
      # производим очень простой healthcheck
      test: [ "CMD", "curl", "-k", "https://localhost:9000/minio/health/live" ]
      interval: 30s
      timeout: 20s
      retries: 3
...
...
...
  minio:
    # контейнер с nginx. указываем используемый образ
    image: nginx:1.19.2-alpine
    # указываем имя контейнера
    container_name: minio
    # указываем hostname, по которому сможем взаимодействовать с контейнером внутри docker сети
    hostname: minio.local
    volumes:
      # монтируем конфиг nginx
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      # монтируем сертификаты, для взаимодействия с нодами по https
      - ./certs:/certs
    ports:
      # выбрасываем порт 443, причем только в nginx. так как соединения будет принимать только nginx
      - "443:443"

Остальные 3 контейнера minio имеют идентичные настройки. Только порядковые номера в названии, хостнейме и директории для монтирования отличаются.

Рассмотрим файл конфигурации nginx.conf. Он в общем виде представлен в документации minio (https://min.io/docs/minio/linux/integrations/setup-nginx-proxy-with-minio.html). Нам его необходимо лишь немного подправить. Обратите внимание на раздел upstream:

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  4096;
}

http {
   include       /etc/nginx/mime.types;
   default_type  application/octet-stream;

   log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                   '$status $body_bytes_sent "$http_referer" '
                   '"$http_user_agent" "$http_x_forwarded_for"';

   access_log  /var/log/nginx/access.log  main;
   sendfile        on;
   keepalive_timeout  65;

   # include /etc/nginx/conf.d/*.conf;
   upstream minio_s3 {
      least_conn;
      server minio1.local:9000;
      server minio2.local:9000;
      server minio3.local:9000;
      server minio4.local:9000;
   }

   upstream minio_console {
      least_conn;
      server minio1.local:9001;
      server minio2.local:9001;
      server minio3.local:9001;
      server minio4.local:9001;
   }

   server {
      listen       443 ssl;
      listen  [::]:443 ssl;
      server_name  minio.local;

      # Allow special characters in headers
      ignore_invalid_headers off;
      # Allow any size file to be uploaded.
      # Set to a value such as 1000m; to restrict file size to a specific value
      client_max_body_size 0;
      # Disable buffering
      proxy_buffering off;
      proxy_request_buffering off;
      ssl_certificate      /certs/public.crt;
      ssl_certificate_key  /certs/private.key;
      ssl_protocols TLSv1.2 TLSv1.3;
      ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
      ssl_prefer_server_ciphers off;
      ssl_verify_client off;

      location / {
         proxy_set_header Host $http_host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto $scheme;

         proxy_connect_timeout 300;
         # Default is HTTP/1, keepalive is only enabled in HTTP/1.1
         proxy_http_version 1.1;
         proxy_set_header Connection "";
         chunked_transfer_encoding off;

         proxy_pass https://minio_s3; # This uses the upstream directive definition to load balance
      }

      location /minio/ui/ {
         rewrite ^/minio/ui/(.*) /$1 break;
         proxy_set_header Host $http_host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto $scheme;
         proxy_set_header X-NginX-Proxy true;

         # This is necessary to pass the correct IP to be hashed
         real_ip_header X-Real-IP;

         proxy_connect_timeout 300;

         # To support websockets in MinIO versions released after January 2023
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection "upgrade";
         # Some environments may encounter CORS errors (Kubernetes + Nginx Ingress)
         # Uncomment the following line to set the Origin request to an empty string
         # proxy_set_header Origin '';

         chunked_transfer_encoding off;

         proxy_pass https://minio_console; # This uses the upstream directive definition to load balance
      }
   }
}

Посмотрим на директорию certs. Мы увидим, что в нее поместили сертификат public.crt, который по факту является файлом certsgen/server-cert.pem (т.е. грубо говоря сертификат сервера). Также ключ от упомянутого выше сертификата — private.key, который является файлом certsgen/server-key.pem. И положили исходный самоподписанный ca-cert.pem, переименовав в ca.crt. Также создали директорию certs/CAs и в нее скопировали ca.crt. Директорию CAs необходимо для minio, так как по дефолту он в ней ищет необходимый ему ca.crt. (https://min.io/docs/minio/linux/operations/network-encryption.html) Все сертификаты пробрасываются в контейнеры с minio.

Запустим кластерную версию:

docker compose -f docker-compose.yaml up -d

Подождем минуту пока все запустится и пройдет healthcheck’и. Сделаем

docker ps -a

И мы должны увидеть, что все наши контейнеры поднялись:

docker ps

docker ps

Заходим в webui

Теперь добавим наш ca.crt в браузер и попробуем зайти в webui.

Я использую firefox, поэтому опишу как добавить сертификат в firefox: Переходим в settings => в строке поиска вводим «cert» => нажимаем на кнопку «View Certificates» =>

View Certificates

View Certificates

В появившемся окне выбираем крайнюю правую вкладку «Authorities» => далее снизу «Import» => теперь выбираем наш ca.crt из certs/ca.crt => проставляем необходимые галки => жмем «OK» => еще раз «OK».

Также, добавим у себя локально запись в /etc/hosts:

cat /etc/hosts
...
127.0.0.1 minio.local

Теперь открываем браузер и переходим на https://127.0.0.1/minio/ui/login и вводим логин и пароль, которые мы указали в docker-compose.yaml в переменных MINIO_ROOT_USER и MINIO_ROOT_PASSWORD. Обратите внимание на замочек возле нашего адреса, он должен быть без восклицательного знака, что будет свидетельствовать о защищенности подключения.

TLS

TLS

После успешного логина, мы попадем в web интерфейс minio. Можем убедиться что все хорошо, выбрав в левой части экрана пункты: monitoring => metrics. Мы увидим сводную информацию по кластеру:

Встроенный мониторинг

Встроенный мониторинг

Создание policy

Теперь создадим policy. Перейдем в «policies» => create policy. Введем testpolicy в строку с названием, и добавим свой блок Statemenet.

  "Statement": [
    {
      "Action": [
        "s3:PutBucketPolicy",
        "s3:GetBucketPolicy",
        "s3:DeleteBucketPolicy",
        "s3:ListAllMyBuckets",
        "s3:ListBucket"
      ],
      "Effect": "Allow",
      "Resource": [
        "arn:aws:s3:::testbucket"
      ],
      "Sid": ""
    },
    {
      "Action": [
        "s3:AbortMultipartUpload",
        "s3:DeleteObject",
        "s3:GetObject",
        "s3:ListMultipartUploadParts",
        "s3:PutObject"
      ],
      "Effect": "Allow",
      "Resource": [
        "arn:aws:s3:::testbucket/*"
      ],
      "Sid": ""
    }
  ]

Этот полиси дает полный контроль над бакетом с названием testbucket. Жмем «Save»

testpolicy

testpolicy

Создание user’a

Теперь создадим пользователя. Перейдем в Identify => Users => Create User. Введем в поле Name: testuser. В поле password: testpassword. Также чуть ниже, среди существующих политик, выберем созданную нами ранее testpolicy. Жмем Save.

User

User

Создание bucket’a

Ну и создадим тестовый бакет. Переходим в Buckets => Create Bucket. В Name вносим testbucket и обязательно вклю1чаем версионирование. Нам оно понадобится при включении site replication в дальнейшем. Жмем create bucket.

testbucket

testbucket

s3cmd

Попробуем повзаимодействовать с бакетом из операционной системы. Устанавливаем утилиту s3cmd

sudo apt install -y s3cmd 

Настраиваем ее, создам файл ~/.s3cfg. Приведу пример моего конфига:

# Setup endpoint
# указываем наш сервер
host_base = 127.0.0.1
host_bucket = 127.0.0.1
# оставляем по дефолту
bucket_location = us-east-1
# указываем что используем https
use_https = True

# указываем логин и пароль нашего тестового пользователя
# Setup access keys
access_key = testuser
secret_key = testpassword
 
# Enable S3 v4 signature APIs
# при выполнении большого количества тестов, 
# у меня возникла необходимость подключить указанную ниже директиву
# однако при выполнении дефолтных действий она не нужна
# поэтому я ее закоментил, но оставил на всякий случай
# signature_v2 = False

Теперь нам необходимо установить сертификат в нашу операционную систему (у меня ubuntu)

sudo cp certs/ca.crt /usr/local/share/ca-certificates/ca-minio.crt
sudo update-ca-certificates

Произведем тест. Попробуем получить список бакетов в нашем s3:

s3cmd ls

s3cmd ls

Видим, что отражается наш testbucket. В случае, если вместо успеха, вы получаете сообщение, говорящее о проблемах с сертификатом, то можете подложить его к утилите s3cmd вручную при помощи аргумента ca-certs:

s3cmd --ca-certs ./certs/ca.crt ls s3://

Получим тот же вывод:

beaa9ba915432cf35db08c9bdab143a9.png

Положим наш файл nginx.conf в бакет и проверим его в web-ui:

f18d5b75e1ee63facdcf203287142cbf.png

Переходим в webui, object browser и открываем testbucket. Файл на месте:

testbucket

testbucket

Отдельный minio

Для организации site-replication или tiering нам понадобиться второй minio. Однако необходимо в кластерном решении у нас нет, поэтому развернем отдельностоящий minio из одного контейнера.

Рассмотрим docker-compose.yaml. Остановлюс только на отличиях от кластерного варианта:

cat docker-compose-solo.yaml 
version: '3.7'

services:
  # minio solo
  miniosolo:
    restart: always
    image: bitnami/minio:2024.7.26
    container_name: miniosolo
    hostname: miniosolo.local
    # в данном случае мы обязательно выбрасываем порты, по которым будем
    # взаимодействовать с нашим minio
    # 9001 - webuil 9000 - api
    ports:
      - '9000:9000'
      - '9001:9001'
    environment:
      # также указываем login & password
      - MINIO_ROOT_USER=minioadmin
      - MINIO_ROOT_PASSWORD=2eG1~B/j{70d
      - MINIO_SKIP_CLIENT=yes
      # указываем использование https
      - MINIO_SCHEME=https
      # указываем порт для web-ui
      - MINIO_CONSOLE_PORT_NUMBER=9001
    extra_hosts:
      # вносим в /etc/hosts данного контейнера связку minio.local c 192.168.0.199
      - "minio.local:192.168.0.199"
    volumes:
      - ./miniosolo:/bitnami/minio/data
      - ./certs:/certs
    healthcheck:
      test: [ "CMD", "curl", "-k", "https://localhost:9000/minio/health/live" ]
      interval: 30s
      timeout: 20s
      retries: 3

Разворачиваем minio и проверяем контейнер:

docker compose -f docker-compose-solo.yaml up -d
docker ps

Убеждаемся, что контейнер поднялся. После этого заходим в webui: https://127.0.0.1:9001/login (обратите внимание, данный минио доступен по конкретным портам. для webui — 9001).

и логинимся, используя переменные из docker-compose-solo.yaml. Удостоверяемся секьюрности коннекта (тот же замочек рядом с адресом). А также удостоверяемся, что это совершенно другой minio. У него только одна нода в metrics. А также нет пользователей, политик и бакетов.

927c5e7adf9290f4468a266ec9a11381.png

Site Replication

Настроим репликацию между этими кластерами

Переходим в webui основого кластера на вкладку site replication в самом низу панели администрирования. Жмем Add Sites и заполняем все поля. В данный момент воспользуемся админской учеткой, однако для этих целей лучше создать отдельную учетную запись:

Site Replication

Site Replication

Здесь нам понадобятся ранее внесенные записи в /etc/hosts. К первому (main) кластеру мы обратимся по minio.local. А ко второму кластеру по внешнему ip-адресу (192.168.0.199 — мы его прописали в самом начале, при создании сертификата). Жмем Save и получаем:

e27321b9401ff28df4248f58f02a9f32.png

Можем перейти на вкладку Replication Status и убедиться, в том, что все сущности перенесены. Также можем перейти в webui stand-alone minio (https://127.0.0.1:9001/) и посмотреть содержимое его бакеты:

cc4a78ad977201cb753e819ecdbf3663.png

Видим что бакет создан, а вот количество objects = 0. Проверим через утилиту s3cmd. Изменим s3cfg, указав использовать stand-alone minio (добавим только порт, остальное оставим таким же):

cat ~/.s3cfg                
# Setup endpoint
host_base = 127.0.0.1
host_bucket = 127.0.0.1
bucket_location = us-east-1
use_https = True
 
# Setup access keys
access_key = testuser
secret_key = testpassword
 
# Enable S3 v4 signature APIs
#signature_v2 = False

Попытаемся загрузить файл nginx.conf:

f999fed546d252ab46be051749e8566e.png

Видим, что все прошло успешно. Насколько я понял, это «фича» отображения. Теперь в web-ui видим, что объект — 1.

Tiering

Особо сильно останавливаться не будем на этом пункте. Опишу только основные собственные выводы. Minio не умеет делить диски на ssd и hdd. Он их просто собирает все в одну кучу. Причем, насколько указано в документации — по принципу самого слабого звена. Т.е. если диски разных размеров, то по факту использоваться будет только емкость в диске, соответствующая самому маленькому по объему диску.

Ну и так как minio не умеет делить диски на ssd и hdd, то он и не может создавать bucket’ы только на каких-то конкретных дисках. Т.е. бакет размазывается в общем, а не на конкретных дисках. Из этого следует, что для использования tiering, вам необходимый рядом стоящие кластеры, или арендованные бакеты, которые развернут на соответствующих дисках. Т.е. например свой кластер на ssd в виде горячего хранилище. И арендованный s3 бакет где-то еще в виде холодного хранилища.

Для настройки tiering’a есть соответствующий раздел слева на панели: tiering. После подключения бакета в этом разделе, мы можем его использовать в уже конкретном нашем бакете: buckets => testbucket => lifecycle => add lifecycle rule. Т.е. по факту tiering в minio = lifecycle management.

В правилах мы устанавливаем какие конкретно файлы в какой tiering-bucket выгрузятся после какого именно периода времени. Т.е. например, мы можем хранить в нашем бакете файлы только 1 месяц. После этого, они все выгружаются на удаленный бакет. Причем они остаются доступны из нашего основого бакета. Также отмечу, что движения файлов обратно — нет. Если файлы выгрузились в tiering bucket — то настройки чтобы их поместить обратно в горячее хранилище нет (по крайней мере я не нашел).

Стоит отдельно отметить. При настройке site replication + tiering нам необходимо настраивать каждому кластеру свой tiering отдельно. В документации отдельно указано, что tiering не реплецируемая сущность.

По данному разделу приветствуются комменты. Не сказать, что в документации эта часть расписана предельно понятно. Здесь ответы на вопросы можно получить только после поднятия кластера и самостоятельных тестов.

Minio client

Для настройки мониторинга нам понадобится утилита minio client. Установим ее по инструкции из оф документации: https://min.io/docs/minio/linux/reference/minio-mc.html

После установки, добавим наш main кластер в minio client:

# в случае если у вас уже установлена утилита midnight commander, 
# то обратитесь к mc например через ~/minio-binaries/mc
~/minio-binaries/mc alias set mainminio https://127.0.0.1 minioadmin MBVfbuu2NDS3Aw

Увидим сообщение об успехе:

85345c06204991336b2d6d0fa0e33a3e.png

Проверим и получим краткую информацию о кластере:

minio client

minio client

Monitoring

Как мы видели ранее, в web интерфейса минио уже есть раздел monitoring, в котором можно посмотреть некоторую информацию о кластере. Однако мы развернем prometheus, настроим scrapeconfig и отрисуем собранные метрики в grafana.

Для начала сгенерируем токен для сбора метрик:

~/minio-binaries/mc admin prometheus generate mainminio

Получим следующее:

1ed602cf19ebe2cf345066381c6a5538.png

Нам необходимо скопировать bearer_token и заменить его в файле prometheus/prometheus.yml в двух местах (строки 7 и 16):

global:
  scrape_interval: 15s
  scrape_timeout: 10s
  evaluation_interval: 15s
scrape_configs:
- job_name: minio-job-server
  bearer_token: #####ВОТ_ЗДЕСЬ######
  metrics_path: /minio/v2/metrics/cluster
  scheme: https
  tls_config:
    ca_file: /certs/ca.crt
    #insecure_skip_verify: true
  static_configs:
  - targets: [minio.local]
- job_name: minio-job-bucket
  bearer_token: #####И_ВОТ_ЗДЕСЬ######
  metrics_path: /minio/v2/metrics/bucket
  scheme: https
  tls_config:
    ca_file: /certs/ca.crt
    #insecure_skip_verify: true
  static_configs:
  - targets: [minio.local]

В данном скрейпконфиге мы указываем прометеусу, что нужно собирать метрики с minio.local, перейдя по https://minio.local/minio/v2/metrics/cluster и https://minio.local/minio/v2/metrics/bucket. Также указываем какой ca.crt использовать при сборе метрик (/certs/ca.crt — директорию certs мы прокинем в prometheus).

Посмотрим docker-compose-monitoring.yaml:

version: "3.5"

services:
  prometheus:
    image: prom/prometheus:v2.51.2
    container_name: prometheus
    # указываем прометеусу что нужно подхватить в качестве конфиг-файла наш, 
    # который лежит в /etc/prometheus/prometheus.yml
    # это конфиг, который мы правили немного выше
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
    # "выбрасываем" порт 9090
    ports:
      - 9090:9090
    volumes:
      # монтируем директорию prometheus с нашим конфигом
      - ./prometheus:/etc/prometheus
      # для хранения данных используем volume
      # мне не нужно хранить данные в директории с кластером minio
      # поэтому будем класть их в volume
      - prom_data:/prometheus
      # прокидываем директорию certs с нашими сертификатами
      - ./certs:/certs
    healthcheck:
      test: wget --no-verbose --tries=1 --spider localhost:9090 || exit 1
      interval: 5s
      timeout: 10s
      retries: 3
      start_period: 5s  
  
  grafana:
    image: grafana/grafana:10.4.2
    container_name: grafana
    ports:
      # порт, по которому мы будем ходить в grafana
      - 3000:3000
    environment:
      # указываем логин и пароль от админа
      # AUTH
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=admin
      # указываем возможность просматривать дашборды без аутентификации
      - GF_AUTH_ANONYMOUS_ENABLED=true
    volumes:
      # монтируем директорию provisioning, в которой настройки по 
      # добавлению datasources и dashboards
      - ./grafana/provisioning:/etc/grafana/provisioning
      # монтируем директорию c json наших дашбордов
      - ./grafana/dashboards:/var/lib/grafana/dashboards
    depends_on:
      # перед разворачиванием графаны, дожидаемся готовности prometheus
      - prometheus
    healthcheck:
      test: curl --fail localhost:3000
      interval: 5s
      timeout: 10s
      retries: 3
      start_period: 10s

# раздел описания томов (volume)
volumes:
  prom_data:

Поднимаем мониторинг:

docker compose -f docker-compose-monitoring.yaml up -d

Через docker ps убеждаемся что оба контейнера healthy. Даем еще некоторое время подняться графане и заходим на http://127.0.0.1:3000. Выбираем слева вверху меню (три горизонтальные черты) и переходим в dashboards.

Minio Dashboard

Minio Dashboard

Minio dashboard выдает информацию по кластеру в общем.

Minio Bucket Dashboard выводит информацию конкретно по бакетам:

e3a85d39bc531339c2af0531fb3569e9.png

Обратите внимание, что прометеусу нужно некоторое время, чтобы собрать хоть какие-то метрики. Также, если видите что пустыми только часть полей — то видимо именно эти метрики еще пусты. Например так может случится если бакетов еще нет совсем, тогда и дашборд с бакетами будет выводить «no data». Или например если обращений к api еще не было, то и S3 Api Request тоже будет пуст.

Разделение на виртуальные машины

Если вы хотите развернуть свой кластер минио на разных машинах, а не на одной, то compose можно немного трансформировать. Рассмотрим отдельно компоуз первого контейнера. Обратите внимание, что в данном случае мы пробрасываем порты 9000 и 9001 для связи с контейнером из вне. А также жестко закрепляем в /etc/hosts (раздел extra_hosts) адреса всех наших виртуальных машин с контейнерами

version: '3.7'

services:
  minio1:
    restart: always
    image: bitnami/minio:2024.7.26
    container_name: minio1.local
    hostname: minio1.local
    ports:
      - '9000:9000'
      - '9001:9001'
    environment:
      - MINIO_ROOT_USER=minioadmin
      - MINIO_ROOT_PASSWORD=MBVfbuu2NDS3Aw
      - MINIO_DISTRIBUTED_MODE_ENABLED=yes
      - MINIO_DISTRIBUTED_NODES=minio1.local,minio2.local,minio3.local,minio4.local
      - MINIO_SKIP_CLIENT=yes
      - MINIO_SCHEME=https
      - MINIO_CONSOLE_PORT_NUMBER=9001
      - MINIO_SERVER_URL=https://minio.local
      - MINIO_BROWSER_REDIRECT_URL=https://minio.local/minio/ui
    extra_hosts:
      - "minio1.local:192.168.0.55"
      - "minio2.local:192.168.0.56"
      - "minio3.local:192.168.0.57"
      - "minio4.local:192.168.0.58"
      - "minio.local:192.168.0.59"
    volumes:
      - /mnt/minio1:/bitnami/minio/data
      - ./certs:/certs
    healthcheck:
      test: [ "CMD", "curl", "-k", "https://localhost:9000/minio/health/live" ]
      interval: 30s
      timeout: 20s
      retries: 3

# раздел с разворачиванием node exporter
  node_exporter:
    image: prom/node-exporter:v1.8.2
    container_name: node_exporter
    command:
      - '--path.rootfs=/host'
    network_mode: host
    pid: host
    restart: unless-stopped
    volumes:
      - '/:/host:ro,rslave'

Листинг nginx:

version: '3.7'
services:
  nginx:
    image: nginx:1.19.2-alpine
    hostname: nginx
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/certs
    extra_hosts:
      - "minio1.local:192.168.0.55"
      - "minio2.local:192.168.0.56"
      - "minio3.local:192.168.0.57"
      - "minio4.local:192.168.0.58"
      - "minio.local:192.168.0.59"
    ports:
      - "443:443"
  node_exporter:
    image: prom/node-exporter:v1.8.2
    container_name: node_exporter
    command:
      - '--path.rootfs=/host'
    network_mode: host
    pid: host
    restart: unless-stopped
    volumes:
      - '/:/host:ro,rslave'

А также посмотрим на nginx.conf. В файле конфигурации nginx, нас интересует прежде всего раздел upstream, там мы перечисляем наши ноды:

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  4096;
}

http {
   include       /etc/nginx/mime.types;
   default_type  application/octet-stream;

   log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                   '$status $body_bytes_sent "$http_referer" '
                   '"$http_user_agent" "$http_x_forwarded_for"';

   access_log  /var/log/nginx/access.log  main;
   sendfile        on;
   keepalive_timeout  65;

   # include /etc/nginx/conf.d/*.conf;
   upstream minio_s3 {
      least_conn;
      server minio1.local:9000;
      server minio2.local:9000;
      server minio3.local:9000;
      server minio4.local:9000;
   }

   upstream minio_console {
      least_conn;
      server minio1.local:9001;
      server minio2.local:9001;
      server minio3.local:9001;
      server minio4.local:9001;
   }

   server {
      listen       443 ssl;
      listen  [::]:443 ssl;
      server_name  minio.local;

      # Allow special characters in headers
      ignore_invalid_headers off;
      # Allow any size file to be uploaded.
      # Set to a value such as 1000m; to restrict file size to a specific value
      client_max_body_size 0;
      # Disable buffering
      proxy_buffering off;
      proxy_request_buffering off;
      ssl_certificate      /certs/public.crt;
      ssl_certificate_key  /certs/private.key;
      ssl_protocols TLSv1.2 TLSv1.3;
      ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
      ssl_prefer_server_ciphers off;
      ssl_verify_client off;

      location / {
         proxy_set_header Host $http_host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto $scheme;

         proxy_connect_timeout 300;
         # Default is HTTP/1, keepalive is only enabled in HTTP/1.1
         proxy_http_version 1.1;
         proxy_set_header Connection "";
         chunked_transfer_encoding off;

         proxy_pass https://minio_s3; # This uses the upstream directive definition to load balance
      }

      location /minio/ui/ {
         rewrite ^/minio/ui/(.*) /$1 break;
         proxy_set_header Host $http_host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto $scheme;
         proxy_set_header X-NginX-Proxy true;

         # This is necessary to pass the correct IP to be hashed
         real_ip_header X-Real-IP;

         proxy_connect_timeout 300;

         # To support websockets in MinIO versions released after January 2023
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection "upgrade";
         # Some environments may encounter CORS errors (Kubernetes + Nginx Ingress)
         # Uncomment the following line to set the Origin request to an empty string
         # proxy_set_header Origin '';

         chunked_transfer_encoding off;

         proxy_pass https://minio_console; # This uses the upstream directive definition to load balance
      }
   }
}

Заключение

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

© Habrahabr.ru