ML-песочница на Kubernetes и Docker: пошаговый гайд для Data Science команд | AiManual
AiManual Logo Ai / Manual.
28 Дек 2025 Гайд

Гайд: Как построить ML-песочницу для Data Scientist'ов на k8s и Docker

Полное руководство по созданию ML-инфраструктуры для Data Scientist'ов. Docker, Kubernetes, воспроизводимые эксперименты, масштабирование вычислений.

Проблема: Каждый Data Scientist работает в своей среде, модели не воспроизводятся, ресурсы используются неэффективно, эксперименты теряются. Знакомо? Этот гайд — решение.

Почему ML-песочница — must-have в 2025

Если вы работаете в ML-команде из 3+ человек, вы сталкивались с этими проблемами:

  • «У меня работает на ноутбуке, а у тебя нет» — различие в окружениях
  • Невозможность воспроизвести эксперимент через месяц
  • GPU простаивает, пока один Data Scientist ждет завершения обучения
  • Каждый устанавливает свои версии библиотек, ломая общие зависимости

ML-песочница на Kubernetes решает эти проблемы, предоставляя:

  • Изолированные, воспроизводимые окружения для каждого эксперимента
  • Автоматическое масштабирование вычислительных ресурсов
  • Единую точку входа для всех Data Scientist'ов
  • Версионирование данных, кода и моделей
💡
Песочница — это не production-среда! Это development-среда, где Data Scientist'ы могут экспериментировать, не боясь сломать что-то важное. Production-развертывание требует дополнительных шагов валидации и мониторинга.

Архитектура решения

Наша песочница состоит из нескольких ключевых компонентов:

Компонент Назначение Технологии
Контейнерная среда Изоляция зависимостей Docker, NVIDIA Container Toolkit
Оркестратор Управление ресурсами Kubernetes, Helm
Хранилище данных Доступ к датасетам NFS, S3-совместимое хранилище
Интерфейс Работа с песочницей JupyterHub, VS Code Server
Мониторинг Отслеживание ресурсов Prometheus, Grafana

1 Подготовка инфраструктуры Kubernetes

Начнем с развертывания Kubernetes-кластера. Для песочницы подойдет managed-решение или локальный кластер.

Вариант A: Локальный кластер (для тестирования)

# Установка minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

# Запуск с поддержкой GPU (если есть NVIDIA)
minikube start --driver=docker --cpus=4 --memory=8192 \
  --addons=ingress,metrics-server \
  --feature-gates=DevicePlugins=true

# Проверка установки
kubectl get nodes
kubectl get pods -A

Вариант B: Managed Kubernetes (для production-ready песочницы)

# Для AWS EKS
aws eks create-cluster --name ml-sandbox \
  --role-arn arn:aws:iam::123456789012:role/eks-service-role \
  --resources-vpc-config subnetIds=subnet-abc,subnet-def,securityGroupIds=sg-abc

# Для GCP GKE
gcloud container clusters create ml-sandbox \
  --num-nodes=3 \
  --machine-type=n1-standard-4 \
  --zone=us-central1-a \
  --cluster-version=1.28

2 Настройка GPU-поддержки в Kubernetes

Для работы с ML-моделями, особенно с большими языковыми моделями, нужен доступ к GPU. Вот как настроить:

# Установка NVIDIA Device Plugin для Kubernetes
kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.1/nvidia-device-plugin.yml

# Проверка доступности GPU
kubectl get nodes \"-o=jsonpath='{.items[*].status.allocatable}'"
# Должны увидеть: nvidia.com/gpu: 1 (или больше)

# Создание namespace для ML-работ
kubectl create namespace ml-sandbox

Важно: Если у вас AMD видеокарты, вам понадобится другая настройка. Рекомендую изучить нашу статью про оптимизацию llama.cpp под AMD видеокарты для понимания особенностей работы с ROCm и Vulkan.

3 Создание базового Docker-образа для Data Science

Единый базовый образ — основа воспроизводимости. Создадим Dockerfile с наиболее распространенными библиотеками:

# Dockerfile.ml-base
FROM nvidia/cuda:12.1.1-cudnn8-runtime-ubuntu22.04

# Установка системных зависимостей
RUN apt-get update && apt-get install -y \
    python3.10 \
    python3-pip \
    git \
    curl \
    wget \
    && rm -rf /var/lib/apt/lists/*

# Настройка Python
RUN ln -s /usr/bin/python3.10 /usr/bin/python
RUN pip3 install --upgrade pip setuptools wheel

# Базовые ML-библиотеки
RUN pip3 install \
    numpy==1.24.3 \
    pandas==2.0.3 \
    scikit-learn==1.3.0 \
    matplotlib==3.7.2 \
    seaborn==0.12.2 \
    jupyter==1.0.0 \
    notebook==6.5.4 \
    ipython==8.14.0

# Глубокое обучение
RUN pip3 install \
    torch==2.1.0 \
    torchvision==0.16.0 \
    torchaudio==2.1.0 \
    --index-url https://download.pytorch.org/whl/cu121

# Дополнительные библиотеки
RUN pip3 install \
    transformers==4.35.0 \
    datasets==2.14.6 \
    accelerate==0.24.1 \
    xgboost==2.0.0 \
    lightgbm==4.1.0 \
    catboost==1.2.2

# Настройка рабочей директории
WORKDIR /workspace
RUN mkdir -p /workspace/data /workspace/models /workspace/experiments

# Переменные окружения
ENV PYTHONPATH=/workspace
ENV PYTHONUNBUFFERED=1

CMD ["/bin/bash"]

Собираем и пушим образ:

# Сборка образа
docker build -f Dockerfile.ml-base -t your-registry/ml-base:2025.01 .

# Тегирование и отправка в registry
docker tag your-registry/ml-base:2025.01 your-registry/ml-base:latest
docker push your-registry/ml-base:2025.01
docker push your-registry/ml-base:latest

4 Развертывание JupyterHub для доступа к песочнице

JupyterHub — идеальный интерфейс для Data Scientist'ов. Развернем его с помощью Helm:

# Добавление репозитория JupyterHub
helm repo add jupyterhub https://hub.jupyter.org/helm-chart/
helm repo update

# Создание конфигурационного файла values.yaml
cat > jupyterhub-values.yaml << EOF
singleuser:
  image:
    name: your-registry/ml-base
    tag: latest
  
  extraEnv:
    JUPYTER_ENABLE_LAB: "1"
    
  storage:
    type: static
    static:
      pvcName: ml-workspace-{username}
      subPath: "{username}"
    
  resources:
    limits:
      memory: 8Gi
      cpu: 2
      nvidia.com/gpu: 1  # Запрашиваем GPU по необходимости
    requests:
      memory: 4Gi
      cpu: 1

hub:
  config:
    JupyterHub:
      authenticator_class: dummy  # Для тестов, в production используйте OAuth
    DummyAuthenticator:
      password: "ml-sandbox"

proxy:
  service:
    type: LoadBalancer

prePuller:
  continuous:
    enabled: true
EOF

# Установка JupyterHub
helm upgrade --install jupyterhub jupyterhub/jupyterhub \
  --namespace ml-sandbox \
  --version=3.0.0 \
  --values jupyterhub-values.yaml \
  --create-namespace

# Проверка установки
kubectl get pods -n ml-sandbox
kubectl get svc -n ml-sandbox

5 Настройка хранилища данных

Data Scientist'ам нужен доступ к общим датасетам. Настроим Persistent Volumes:

# storage-class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ml-fast
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
---
# pv-datasets.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: ml-datasets
spec:
  capacity:
    storage: 500Gi
  volumeMode: Filesystem
  accessModes:
    - ReadOnlyMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: ml-fast
  local:
    path: /mnt/ml-datasets
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - node-with-storage
---
# pvc-datasets.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ml-datasets
  namespace: ml-sandbox
spec:
  accessModes:
    - ReadOnlyMany
  resources:
    requests:
      storage: 500Gi
  storageClassName: ml-fast

Применяем конфигурацию:

kubectl apply -f storage-class.yaml
kubectl apply -f pv-datasets.yaml
kubectl apply -f pvc-datasets.yaml
💡
Для больших датасетов (100GB+) рассмотрите использование S3-совместимого хранилища через s3fs или Goofys. Это позволит монтировать данные как локальную файловую систему без копирования всего датасета в каждый контейнер.

6 Создание шаблонов для экспериментов

Чтобы стандартизировать работу, создадим Kubernetes Job templates для запуска экспериментов:

# experiment-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: "experiment-{experiment-id}"
  namespace: ml-sandbox
  labels:
    experiment-id: "{experiment-id}"
    user: "{username}"
spec:
  ttlSecondsAfterFinished: 86400  # Автоудаление через 24 часа
  backoffLimit: 0
  template:
    spec:
      containers:
      - name: experiment-runner
        image: your-registry/ml-base:latest
        command: ["python", "/workspace/experiments/{experiment-id}/run.py"]
        resources:
          limits:
            memory: "16Gi"
            cpu: "4"
            nvidia.com/gpu: "1"
          requests:
            memory: "8Gi"
            cpu: "2"
        volumeMounts:
        - name: datasets
          mountPath: /workspace/data
          readOnly: true
        - name: workspace
          mountPath: /workspace/experiments
        env:
        - name: EXPERIMENT_ID
          value: "{experiment-id}"
        - name: WANDB_API_KEY
          valueFrom:
            secretKeyRef:
              name: ml-secrets
              key: wandb-api-key
      restartPolicy: Never
      volumes:
      - name: datasets
        persistentVolumeClaim:
          claimName: ml-datasets
      - name: workspace
        persistentVolumeClaim:
          claimName: ml-workspace-{username}
---
# experiment-cronjob.yaml (для периодических задач)
apiVersion: batch/v1
kind: CronJob
metadata:
  name: "data-pipeline-{pipeline-name}"
  namespace: ml-sandbox
spec:
  schedule: "0 2 * * *"  # Каждый день в 2:00
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: pipeline-runner
            image: your-registry/ml-base:latest
            command: ["python", "/workspace/pipelines/{pipeline-name}/run.py"]
            resources:
              limits:
                memory: "8Gi"
                cpu: "2"
          restartPolicy: OnFailure

Расширенные возможности песочницы

7.1 Интеграция с MLflow для отслеживания экспериментов

MLflow — стандарт де-факто для ML-экспериментов. Развернем его в нашем кластере:

# mlflow-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mlflow-tracking-server
  namespace: ml-sandbox
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mlflow
  template:
    metadata:
      labels:
        app: mlflow
    spec:
      containers:
      - name: mlflow
        image: ghcr.io/mlflow/mlflow:v2.8.0
        command: ["mlflow", "server", "--host", "0.0.0.0", "--port", "5000"]
        env:
        - name: MLFLOW_S3_ENDPOINT_URL
          value: "http://minio:9000"
        - name: AWS_ACCESS_KEY_ID
          valueFrom:
            secretKeyRef:
              name: ml-secrets
              key: aws-access-key
        - name: AWS_SECRET_ACCESS_KEY
          valueFrom:
            secretKeyRef:
              name: ml-secrets
              key: aws-secret-key
        ports:
        - containerPort: 5000
        volumeMounts:
        - name: mlflow-artifacts
          mountPath: /mlflow
      volumes:
      - name: mlflow-artifacts
        persistentVolumeClaim:
          claimName: mlflow-storage
---
apiVersion: v1
kind: Service
metadata:
  name: mlflow-service
  namespace: ml-sandbox
spec:
  selector:
    app: mlflow
  ports:
  - port: 5000
    targetPort: 5000
  type: ClusterIP

7.2 Мониторинг ресурсов с Prometheus и Grafana

Важно понимать, как используются ресурсы:

# Установка Prometheus Stack
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

helm install prometheus prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --create-namespace \
  --set grafana.adminPassword=ml-sandbox \
  --set prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false

# Дашборд для мониторинга GPU
kubectl apply -f - << EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: gpu-dashboard
  namespace: monitoring
data:
  gpu-dashboard.json: |
    {
      "dashboard": {
        "title": "GPU Usage - ML Sandbox",
        "panels": [
          {
            "title": "GPU Memory Usage",
            "targets": [
              {
                "expr": "DCGM_FI_DEV_FB_USED{}\n/ DCGM_FI_DEV_FB_FREE{}",
                "legendFormat": "{{gpu}}"
              }
            ]
          }
        ]
      }
    }
EOF

Типичные ошибки и их решение

Проблема Причина Решение
«Ошибка CUDA: out of memory» Несколько процессов используют один GPU Настроить limits/requests в Pod spec, использовать GPU sharing (MIG)
Медленный I/O при работе с данными Сетевое хранилище с высокой latency Кэширование данных локально, использование tmpfs для промежуточных данных
Контейнеры не запускаются с GPU Отсутствует NVIDIA Container Toolkit Установить nvidia-container-toolkit на всех нодах с GPU
OOMKiller убивает процессы Не настроены limits памяти Установить реалистичные memory limits, добавить swap если возможно

Оптимизация для работы с LLM

Если ваша команда работает с большими языковыми моделями, нужны дополнительные оптимизации:

# Dockerfile.llm-specialized
FROM your-registry/ml-base:latest

# Установка оптимизированных библиотек для LLM
RUN pip3 install \
    vllm==0.2.7 \
    llama-cpp-python==0.2.26 \
    flash-attn==2.3.3 \
    bitsandbytes==0.41.3 \
    optimum==1.15.0

# Для работы с llama.cpp может потребоваться дополнительная оптимизация
# См. нашу статью: /article/optimizatsiya-llamacpp-pod-amd-videokartyi-vulkan-vs-rocm---polnyij-gajd-2025/

# Настройка для эффективного использования памяти
ENV PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128
ENV XLA_PYTHON_CLIENT_MEM_FRACTION=0.8

Важно для LLM: При работе с очень большими моделями (70B+ параметров) даже одного GPU может не хватить. Рассмотрите использование NPU в AI MAX 395 для оффлоадинга части вычислений или реализацию model parallelism через Tensor Parallelism.

Безопасность песочницы

ML-песочница должна быть безопасной, но не мешать работе:

# network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: ml-sandbox-policy
  namespace: ml-sandbox
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
  
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: ml-sandbox
    ports:
    - protocol: TCP
      port: 8888  # Jupyter
    - protocol: TCP
      port: 5000  # MLflow
      
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 10.0.0.0/8
        - 172.16.0.0/12
        - 192.168.0.0/16
    ports:
    - protocol: TCP
      port: 443
    - protocol: TCP
      port: 80
---
# psp.yaml (Pod Security Policy - для старых версий k8s)
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: ml-sandbox-psp
spec:
  privileged: false
  allowPrivilegeEscalation: false
  requiredDropCapabilities:
    - NET_RAW
    - SYS_ADMIN
  volumes:
    - 'configMap'
    - 'emptyDir'
    - 'persistentVolumeClaim'
  hostNetwork: false
  hostIPC: false
  hostPID: false
  runAsUser:
    rule: 'MustRunAsNonRoot'
  seLinux:
    rule: 'RunAsAny'
  fsGroup:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'RunAsAny'

Что дальше? Продвинутые сценарии

После настройки базовой песочницы можно рассмотреть:

  1. Автоскейлинг GPU-нод: Настройка Cluster Autoscaler с GPU-нодами в облаке
  2. Feature Store: Развертывание Feast или Tecton для управления фичами
  3. ML Pipeline Orchestration: Интеграция с Kubeflow Pipelines или Airflow
  4. Model Registry: Настройка централизованного хранилища моделей
  5. Cost Optimization: Мониторинг и оптимизация затрат на GPU
💡
Для команд, работающих с агентами на основе LLM, рекомендую ознакомиться с нашей статьей про неазиатские open-source модели для агентов. В песочнице можно легко тестировать разные модели и их комбинации.

FAQ: Часто задаваемые вопросы

В чем разница между этой песочницей и Kubeflow?

Kubeflow — это полноценная MLOps-платформа, включающая pipelines, serving, feature store и многое другое. Наша песочница — более легковесное решение, фокусирующееся на этапе экспериментирования. Kubeflow можно развернуть поверх этой песочницы позже.

Сколько это будет стоить в облаке?

Зависит от использования GPU. Примерные расходы:

  • Без GPU (только CPU): $100-300/месяц
  • С 1x NVIDIA T4: $300-500/месяц
  • С 1x NVIDIA A100: $2000-3000/месяц

Можно ли использовать это для production?

Песочница оптимизирована для экспериментов. Для production нужны дополнительные компоненты: canary deployments, A/B testing, мониторинг дрейфа данных, автоматическое переобучение.

Как работать с приватными репозиториями кода?

# secret для доступа к GitHub
apiVersion: v1
kind: Secret
metadata:
  name: github-auth
  namespace: ml-sandbox
type: kubernetes.io/ssh-auth
data:
  ssh-privatekey: 
---
# В Pod spec добавить:
volumes:
- name: ssh-key-volume
  secret:
    secretName: github-auth
    defaultMode: 0600
containers:
- volumeMounts:
  - name: ssh-key-volume
    mountPath: "/root/.ssh"

ML-песочница на Kubernetes и Docker — это не просто технологический выбор, это культурный сдвиг в работе Data Science команд. Она превращает хаотичные эксперименты в управляемый, воспроизводимый процесс, где каждый участник команды может сосредоточиться на том, что у него получается лучше всего — на создании качественных ML-моделей.

Начните с минимальной конфигурации, попросите команду поработать в ней 2 недели, соберите feedback, и итеративно улучшайте. Через месяц вы не узнаете свою команду — эксперименты станут быстрее, результаты — воспроизводимее, а GPU перестанет простаивать.