В этой статье я бы хотел рассказать о такой технологии как S3 со стороны дата-инженерии.

S3 – это один из сервисов, который используется для построения Data Lake и обмена файлами.

Давайте начнем с определения:
S3 (Simple Storage Service) — протокол передачи данных, разработанный компанией Amazon. Также — объектное хранилище.

Что мы имеем по итогу? Это по своей идеи файлообменник. Как вы у себя на компьютере организуете хранение файлов точно таким же образом это можно и организовать и в S3.

Ниже в статье мы S3 будем использовать для создания некого Data Lake.

Поэтому давайте введем несколько терминов, которые нам понадобятся:

  • bucket – это контейнер, в котором вы можете хранить свои файлы (папка)

  • path – это ссылка, которая указывает на конкретную часть bucket (путь в проводнике)

  • object – это какой-то физический файл, который находится по path в bucket. Может иметь разные форматы. (файл)

  • access key – ключ доступа к bucket (логин)

  • secret key – секретный ключ доступа для bucket (пароль)

Так как обычно S3 разворачивается где-то, то для подключения к нему можно использовать разные клиенты с UI:

  • CyberDuck

  • Commander One

  • etc

Но в нашем случае мы будем общаться с S3 через Python.

Давайте для начала развернем этот сервис локально в Docker (весь код и все исходники будут доступны в моём репозитории)

version: "3.9"  
  
services:  
  minio:  
    image: minio/minio:RELEASE.2024-07-04T14-25-45Z  
    restart: always  
    volumes:  
      - ./data:/data  
    environment:  
      - MINIO_ROOT_USER=minioadmin  
      - MINIO_ROOT_PASSWORD=minioadmin  
    command: server /data --console-address ":9001"  
    ports:  
      - "9000:9000"  
      - "9001:9001"

И для запуска сервиса необходимо выполнить команду: docker-compose up -d.

Затем перейдем по адресу http://localhost:9001/browser и увидим Web UI нашего объектного хранилища.

Начнём с перехода в пункт Access Keys и создадим первые ключи доступа, при нажатии на кнопку Create access key + мы перейдем в интерфейс, в котором мы сможем создать наши Access Key и Secret Key. Для дальнейшего использования S3 их необходимо сохранить.

Теперь мы можем работать с нашим S3 через Python

Для этого сначала создадим локальное окружение командой и установим все зависимости для проекта:

python3.12 -m venv venv && \
source venv/bin/activate && \
pip install --upgrade pip && \
pip install -r requirements.txt

Затем создадим небольшой код, который будет проверять существование bucket в нашем S3:

from minio import Minio  
  
# Импорт из локальной переменной секретных данных  
from cred import s3_minio_access_key, s3_minio_secret_key  
  
# Не меняется, это единый endpoint для подключения к S3  
endpoint = 'localhost:9000'  
# access key для подключения к bucket  
access_key = s3_minio_access_key  
# secret key для подключения к bucket  
secret_key = s3_minio_secret_key  
  
client = Minio(  
    endpoint=endpoint,  
    access_key=access_key,  
    secret_key=secret_key,  
    secure=False,  # https://github.com/minio/minio/issues/8161#issuecomment-631120560  
)  
  
buckets = client.list_buckets()  
for bucket in buckets:  
    print(bucket.name, bucket.creation_date)

Если его запустить, то мы ничего не увидим, так как в нашем S3 ещё нет ни одного bucket. Так давайте же создадим новый bucket следующим кодом:

from minio import Minio  
  
# Импорт из локальной переменной секретных данных  
from cred import s3_minio_access_key, s3_minio_secret_key  
  
# Не меняется, это единый endpoint для подключения к S3  
endpoint = 'localhost:9000'  
# access key для подключения к bucket  
access_key = s3_minio_access_key  
# secret key для подключения к bucket  
secret_key = s3_minio_secret_key  
  
client = Minio(  
    endpoint=endpoint,  
    access_key=access_key,  
    secret_key=secret_key,  
    secure=False,  # https://github.com/minio/minio/issues/8161#issuecomment-631120560  
)  
  
client.make_bucket(  
    bucket_name='test-local-bucket'  
)

И если ещё раз запустить предыдущий код для проверки bucket. то он он отобразит наличие bucket test-local-bucket

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

Давайте теперь загрузим какой-нибудь файл в наш bucket. Для этого воспользуемся следующим кодом:

import pandas as pd  
  
# Импорт из локальной переменной секретных данных  
from cred import s3_minio_access_key, s3_minio_secret_key  
  
bucket_name = 'test-local-bucket'  
file_name = 'titanic.csv'  
  
df = pd.read_csv('https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv')  
  
df.to_csv(  
    path_or_buf=f's3://{bucket_name}/{file_name}',  
    index=False,  
    escapechar='\\',  
    compression='gzip',  
    storage_options={  
        "key": s3_minio_access_key,  
        "secret": s3_minio_secret_key,  
        # https://github.com/mlflow/mlflow/issues/1990#issuecomment-659914180  
        "client_kwargs": {"endpoint_url": "http://localhost:9000"},  
    },  
)

В Web UI вы можете проверить наличие файла

Наличие файла titanic.csv
Наличие файла titanic.csv

Важное свойство, которое стоит знать при работе с path в bucket – это то что можно указать свой path руками. Если посмотреть на пример выше, то у нас path выглядит так:
file_name = 'titanic.csv', но можно задать любой path, для примера вот так: file_name = 'raw/kaggle/2022-04-01/titanic.csv'
и получим в bucket такую структуру:

Прописанный путь руками к titanic.csv
Прописанный путь руками к titanic.csv

Также стоит отметить, что если мы удалим файл по данному path, то весь path исчезнет и не нужно будет очищать пустой path.

Теперь также для чтения нам необходимо будет указать весь этот path. Давайте воспользуемся кодом ниже для чтения нашего .csv из bucket

import pandas as pd  
  
# Импорт из локальной переменной секретных данных  
from cred import s3_minio_access_key, s3_minio_secret_key  
  
bucket_name = 'test-local-bucket'  
file_name = 'titanic.csv'  
# file_name = 'raw/kaggle/2022-04-01/titanic.csv'  
  
df = pd.read_csv(  
    filepath_or_buffer=f's3://{bucket_name}/{file_name}',  
    escapechar='\\',  
    storage_options={  
        "key": s3_minio_access_key,  
        "secret": s3_minio_secret_key,  
        # https://github.com/mlflow/mlflow/issues/1990#issuecomment-659914180  
        "client_kwargs": {"endpoint_url": "http://localhost:9000"},  
    },  
    compression='gzip'  
)  
  
print(df)

Вообще, все примеры того, как можно использовать S3 Minio описаны в официальном GitHub пакета.

В данной статье я показал только основы того как можно взаимодействовать с S3 для своих пет-проектов.

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

Также S3 можно улучшать при помощи сторонних сервисов:

  1. Apache Hudi

  2. LakeFS

  3. etc

Пишите в комментариях, что ещё хотели бы узнать про S3 и сервисы для дата-инженеров.

Также если вам необходима консультация/менторство/мок-собеседование и другие вопросы по дата-инженерии, то вы можете обращаться ко мне. Все контакты указаны по ссылке.

Комментарии (8)


  1. temadiary
    06.07.2024 05:45

    так какие зависимости?
    из команды установки сложно разобраться какие именно package's нужны

    pip install -r requirements.txt


    1. k0rsakov Автор
      06.07.2024 05:45

      minio==7.2.7
      pandas==2.2.2
      fsspec==2024.6.1
      s3fs==2024.6.1


      1. vanzhiganov
        06.07.2024 05:45

        Вот же полный список зависимостей:

        aiobotocore==2.13.1
        aiohttp==3.9.5
        aioitertools==0.11.0
        aiosignal==1.3.1
        argon2-cffi==23.1.0
        argon2-cffi-bindings==21.2.0
        attrs==23.2.0
        botocore==1.34.131
        certifi==2024.7.4
        cffi==1.16.0
        frozenlist==1.4.1
        fsspec==2024.6.1
        idna==3.7
        jmespath==1.0.1
        minio==7.2.7
        multidict==6.0.5
        numpy==2.0.0
        pandas==2.2.2
        pycparser==2.22
        pycryptodome==3.20.0
        python-dateutil==2.9.0.post0
        pytz==2024.1
        s3fs==2024.6.1
        six==1.16.0
        typing_extensions==4.12.2
        tzdata==2024.1
        urllib3==2.2.2
        wrapt==1.16.0
        yarl==1.9.4


  1. temadiary
    06.07.2024 05:45

    не ясно почему выбраны и для чего те или иные пакеты
    на веру? ну такое, всё равно что копипастить с SO


    1. k0rsakov Автор
      06.07.2024 05:45

      minio – нужен для общения с Minio
      pandas – для чтения удаленного .csv и удобной работы с ним
      fsspec – необходимый пакет для записи в S3
      s3fs – необходимый пакет для записи в S3


  1. lair
    06.07.2024 05:45

    S3 (Simple Storage Service) — протокол передачи данных, разработанный компанией Amazon. Также — объектное хранилище.

    Вообще-то, это сервис, предлагаемый Amazon (даже из названия - Simple Storage Service - это следует). MinIO - это имитация амазоновского сервиса, которая поддерживает тот же протокол, чтобы было проще заменять одно на другое. При этом внутренняя функциональность у них не обязана быть одинаковой.

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


    1. lev
      06.07.2024 05:45
      +1

      Тоже немного позанудствую - никакого path в s3 нет, есть понятие key, который может выглядеть как '0/1/path/to/file.txt'

      https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html


      1. lair
        06.07.2024 05:45

        ...и использование /, афаик,только конвенция, и штатный апи позволяет при запросе списка объектов бить по разным сепараторам.