Learning Platform
Глоссарий Troubleshooting
Урок 14.02 · 26 мин
Начальный
dockerminios3data-engineering

MinIO как локальный S3

Большая часть DE-пайплайнов сегодня работает с S3: data lake на S3 (или S3-совместимый GCS/Cloudflare R2), Parquet-файлы на S3, dump’ы Postgres на S3, intermediate-данные между шагами Airflow на S3. Когда ты пишешь DAG на localhost, поднимать настоящий AWS-аккаунт для тестов — это медленно (round-trip к AWS US-East), дорого (storage + GET requests) и хрупко (credentials в коде, забыл — закоммитил).

MinIO — это open-source S3-совместимый сторадж, написанный на Go. Одна команда docker run — и у тебя локально работает «AWS S3». Любой клиент, который умеет S3 API (boto3, AWS CLI, s3fs, polars, Spark) — работает без изменений кода, нужно только указать другой endpoint URL.


Анатомия HTTP-запроса — что внутри request и response

Что такое MinIO

MinIO появился в 2014 как distributed object storage для on-premise / private cloud. Сейчас это две вещи:

  • MinIO Server — сам сервер, S3 API-совместимый. Образ minio/minio на Docker Hub.
  • MinIO Client (mc) — CLI, по аналогии с aws s3, но проще и быстрее. Образ minio/mc.

S3-совместимость значит: те же endpoint’ы (PUT /bucket/key, GET /bucket/key, LIST /bucket?prefix=...), та же signed-URL-логика, тот же multipart upload. Не 100% (некоторые edge-cases типа Object Lock Legal Hold отличаются), но 99% реальных DE-задач идут без модификаций.

Где MinIO в типичном DE-стенде
Python ETLETL-джоб использует boto3 или polars/duckdb для чтения и записи Parquet
boto3
MinIO :9000Сервер MinIO в Docker. S3 API на порту 9000
Web UI :9001MinIO Console — браузерный UI для управления бакетами, ключами, политиками
HTTP
MinIO :9000Console обращается к серверу через тот же S3 API
data volumeВнутри MinIO бакеты лежат как директории в /data — обычный layout, можно посмотреть через docker exec ls

В production AWS S3 заменяется на настоящий S3 / GCS / R2 одной правкой env-переменной endpoint URL. Это и есть смысл — локально пишешь и тестируешь без сети до AWS, в проде — обычный AWS.


Запуск MinIO

Минимальная команда:

docker run -d \
  --name minio \
  -p 9000:9000 \
  -p 9001:9001 \
  -e MINIO_ROOT_USER=admin \
  -e MINIO_ROOT_PASSWORD=admin12345 \
  -v minio-data:/data \
  minio/minio server /data --console-address ":9001"

Разберём по строкам:

  • -p 9000:9000 — S3 API endpoint. Сюда стучатся boto3 / mc / aws cli.
  • -p 9001:9001 — Web Console. Открываешь в браузере http://localhost:9001, логинишься admin/admin12345.
  • MINIO_ROOT_USER / MINIO_ROOT_PASSWORD — root credentials. Пароль минимум 8 символов, иначе MinIO откажется стартовать. В проде, естественно, не admin12345.
  • -v minio-data:/data — volume для самих объектов. MinIO хранит бакеты как поддиректории в /data.
  • minio/minio server /data --console-address ":9001" — это команда внутри контейнера. server /data = запустить сервер с data-dir = /data. Флаг --console-address выносит UI на отдельный порт.

Запустим, проверим логи:

docker logs minio --tail 10

В выводе увидим что-то типа:

MinIO Object Storage Server
Copyright: 2015-2026 MinIO, Inc.
License: GNU AGPLv3
Version: RELEASE.2026-04-15T19-23-45Z (go1.23.0 linux/amd64)

API: http://172.17.0.2:9000  http://127.0.0.1:9000
WebUI: http://172.17.0.2:9001 http://127.0.0.1:9001

Docs: https://docs.min.io

Открой http://localhost:9001 в браузере, залогинься admin/admin12345 — увидишь Web Console с пустым списком бакетов. Через UI можно создавать бакеты, заливать файлы, генерить access keys. Но мы будем делать это из CLI — это более воспроизводимо и удобно для скриптов.


Создание bucket через mc

mc — это MinIO Client, аналог aws s3. Можно установить локально (brew install minio/stable/mc на macOS) или запускать в контейнере:

docker run --rm -it \
  --network host \
  minio/mc \
  alias set local http://localhost:9000 admin admin12345

Команда alias set local ... создаёт алиас local для нашего MinIO. После этого все mc-команды можно адресовать через local/bucket/key.

NOTE

--network host нужно, чтобы из контейнера mc дотянуться до localhost:9000, который пробрасывает другой контейнер. На macOS / Windows с Docker Desktop --network host не работает так же, как на Linux — там надо использовать host.docker.internal:9000. На OrbStack — --network host работает.

Альтернатива — установить mc локально:

brew install minio/stable/mc

mc alias set local http://localhost:9000 admin admin12345
mc mb local/datalake
mc ls local

mc mb local/datalake создаст bucket datalake. mc ls local покажет список бакетов:

[2026-05-15 09:35:42 MSK]     0B datalake/

Положим файл:

echo "hello s3" > test.txt
mc cp test.txt local/datalake/test.txt

mc ls local/datalake
mc cat local/datalake/test.txt

Вывод:

[2026-05-15 09:36:10 MSK]     9B STANDARD test.txt
hello s3

Работает как S3.


Подключение через AWS CLI

Можно использовать привычный AWS CLI с флагом --endpoint-url:

aws configure set aws_access_key_id admin
aws configure set aws_secret_access_key admin12345

aws --endpoint-url http://localhost:9000 s3 ls

aws --endpoint-url http://localhost:9000 s3 mb s3://logs
aws --endpoint-url http://localhost:9000 s3 cp test.txt s3://logs/test.txt
aws --endpoint-url http://localhost:9000 s3 ls s3://logs/

Ровно та же логика, что и с настоящим S3 — только --endpoint-url меняется. В скриптах можно завести env-переменную:

export AWS_ENDPOINT_URL=http://localhost:9000
aws s3 ls   # пойдёт в MinIO
unset AWS_ENDPOINT_URL
aws s3 ls   # пойдёт в настоящий AWS

Подключение из Python (boto3)

Это самый частый use case для DE — Python-скрипт, который пишет Parquet в MinIO:

import boto3

s3 = boto3.client(
    "s3",
    endpoint_url="http://localhost:9000",
    aws_access_key_id="admin",
    aws_secret_access_key="admin12345",
    region_name="us-east-1",  # MinIO игнорирует, но boto3 требует
)

s3.create_bucket(Bucket="warehouse")

s3.put_object(
    Bucket="warehouse",
    Key="raw/users.json",
    Body=b'{"id": 1, "name": "Alice"}',
)

for obj in s3.list_objects_v2(Bucket="warehouse")["Contents"]:
    print(obj["Key"], obj["Size"])

Если убрать endpoint_url, тот же код пойдёт в настоящий AWS S3 (если есть AWS credentials в ~/.aws/credentials). Это означает, что pipeline-код тестируется локально без модификаций.


Compose-вариант MinIO

В реальных DE-стендах MinIO живёт рядом с Postgres и Airflow в одном compose-файле:

services:
  minio:
    image: minio/minio:latest
    command: server /data --console-address ":9001"
    ports:
      - "9000:9000"
      - "9001:9001"
    environment:
      MINIO_ROOT_USER: admin
      MINIO_ROOT_PASSWORD: admin12345
    volumes:
      - minio-data:/data
    healthcheck:
      test: ["CMD", "mc", "ready", "local"]
      interval: 5s
      timeout: 3s
      retries: 5

  minio-init:
    image: minio/mc:latest
    depends_on:
      minio:
        condition: service_healthy
    entrypoint: >
      /bin/sh -c "
      mc alias set local http://minio:9000 admin admin12345;
      mc mb -p local/datalake;
      mc mb -p local/warehouse;
      mc mb -p local/logs;
      exit 0;
      "

volumes:
  minio-data:

Сервис minio-init — это одноразовый init-container: ждёт healthy MinIO, создаёт бакеты, выходит. После docker compose up -d есть готовые 3 bucket’а.

MinIO в compose-стенде DE
docker compose upЗапускает все сервисы в одной user-defined bridge сети
minio запускаетсяContainer starts, listening :9000 и :9001 внутри сети compose
healthcheck mc readyMinIO готов когда mc ready возвращает success — это занимает 1-3 секунды после старта
healthy
minio-init стартуетdepends_on: service_healthy ждёт пока minio пройдёт healthcheck, только потом запускает init-контейнер
mc mb datalake/warehouse/logsmc создаёт три bucket'а внутри MinIO, потом init-контейнер выходит со статусом 0
airflow/etl работаютДругие сервисы, которые depends_on minio, могут начать использовать его как S3

Реальный кейс: ETL pipeline на MinIO локально

Допустим, ты пишешь DAG, который читает Parquet из data lake и пишет агрегаты обратно. В проде это AWS S3, локально — MinIO. Pipeline:

import polars as pl
import boto3
import os

S3_ENDPOINT = os.getenv("S3_ENDPOINT_URL")
S3_KEY = os.getenv("AWS_ACCESS_KEY_ID")
S3_SECRET = os.getenv("AWS_SECRET_ACCESS_KEY")

storage_options = {
    "aws_endpoint_url": S3_ENDPOINT,
    "aws_access_key_id": S3_KEY,
    "aws_secret_access_key": S3_SECRET,
    "aws_region": "us-east-1",
}

df = pl.read_parquet(
    "s3://datalake/raw/events/2026-05-15/*.parquet",
    storage_options=storage_options,
)

agg = df.group_by("user_id").agg(pl.count().alias("events_total"))

agg.write_parquet(
    "s3://datalake/agg/users/2026-05-15.parquet",
    storage_options=storage_options,
)

Локально запускаем с S3_ENDPOINT_URL=http://localhost:9000 и admin/admin12345 — pipeline читает и пишет в MinIO. В проде убираем S3_ENDPOINT_URL, ставим реальные AWS credentials — тот же код идёт в AWS S3. Pipeline не знает, что под ним.


Попробуй сам

  1. Подними MinIO с командой выше (с MINIO_ROOT_USER=admin, MINIO_ROOT_PASSWORD=admin12345).
  2. Открой http://localhost:9001, залогинься. Создай bucket lab через UI.
  3. Поставь mc (brew install minio/stable/mc или apt install mc) и сделай mc alias set local http://localhost:9000 admin admin12345.
  4. Залей файл: echo "data" > a.txt && mc cp a.txt local/lab/a.txt.
  5. Из Python (pip install boto3) подключись к endpoint_url=http://localhost:9000, выведи список объектов в lab.
  6. Удали MinIO-контейнер (docker rm -f minio), запусти снова с тем же volume — bucket lab и файл a.txt должны остаться.

Проверка знанийKnowledge check
Чем MinIO отличается от настоящего AWS S3 с точки зрения кода Python-приложения, и зачем DE использует MinIO в локальной разработке?
ОтветAnswer
MinIO — это open-source S3-совместимый объектный сторадж. С точки зрения кода (boto3, polars, awscli) разница в одном параметре: endpoint_url. Если указать endpoint_url=http://localhost:9000 — boto3 пойдёт в локальный MinIO. Если убрать — пойдёт в настоящий AWS. Тот же S3 API, та же signed URL логика, тот же multipart upload — 99% задач работают без модификаций. DE использует MinIO локально по трём причинам: (1) скорость — нет round-trip к AWS US-East, всё на localhost; (2) деньги — настоящий S3 стоит за storage и GET requests, локально бесплатно; (3) безопасность — не надо хранить AWS credentials на dev-машине, никаких рисков утечки. В проде endpoint меняется на настоящий S3, код не меняется. Это и есть смысл S3-совместимости.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Какие два порта обычно пробрасывают наружу для MinIO и для чего нужен каждый из них?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5