Проблема: Каждый Data Scientist работает в своей среде, модели не воспроизводятся, ресурсы используются неэффективно, эксперименты теряются. Знакомо? Этот гайд — решение.
Почему ML-песочница — must-have в 2025
Если вы работаете в ML-команде из 3+ человек, вы сталкивались с этими проблемами:
- «У меня работает на ноутбуке, а у тебя нет» — различие в окружениях
- Невозможность воспроизвести эксперимент через месяц
- GPU простаивает, пока один Data Scientist ждет завершения обучения
- Каждый устанавливает свои версии библиотек, ломая общие зависимости
ML-песочница на Kubernetes решает эти проблемы, предоставляя:
- Изолированные, воспроизводимые окружения для каждого эксперимента
- Автоматическое масштабирование вычислительных ресурсов
- Единую точку входа для всех Data Scientist'ов
- Версионирование данных, кода и моделей
Архитектура решения
Наша песочница состоит из нескольких ключевых компонентов:
| Компонент | Назначение | Технологии |
|---|---|---|
| Контейнерная среда | Изоляция зависимостей | 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
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'
Что дальше? Продвинутые сценарии
После настройки базовой песочницы можно рассмотреть:
- Автоскейлинг GPU-нод: Настройка Cluster Autoscaler с GPU-нодами в облаке
- Feature Store: Развертывание Feast или Tecton для управления фичами
- ML Pipeline Orchestration: Интеграция с Kubeflow Pipelines или Airflow
- Model Registry: Настройка централизованного хранилища моделей
- Cost Optimization: Мониторинг и оптимизация затрат на GPU
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 перестанет простаивать.