Как реализовано ограничение ресурсов в Docker

«Как реализовано ограничение ресурсов в Docker» — вопрос из категории Docker, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В production средах я использую несколько уровней ограничения ресурсов Docker контейнеров для обеспечения стабильности и предотвращения "шумного соседа".

1. Ограничения на уровне docker run:

# CPU ограничения
docker run --cpus="1.5" myapp  # Максимум 1.5 ядра
docker run --cpu-shares=512 myapp  # Относительный вес (по умолчанию 1024)
docker run --cpuset-cpus="0-3" myapp  # Закрепление за конкретными ядрами

# Память
docker run --memory="512m" --memory-swap="1g" myapp
# memory-swap = memory + swap, -1 отключает swap

# I/O ограничения
docker run 
  --device-read-bps="/dev/sda:1mb" 
  --device-write-iops="/dev/sda:100" 
  myapp

# Количество процессов (PID)
docker run --pids-limit=100 myapp  # Защита от fork-бомб

2. Docker Compose (версия 3.x+):

version: '3.8'
services:
  app:
    image: myapp:latest
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.1'
          memory: 128M

3. Практические примеры из моей практики:

Для базы данных PostgreSQL:

docker run --name postgres 
  --memory="2g" --memory-swap="2g"   # Без swap для БД
  --cpus="2" 
  --blkio-weight=500   # Приоритет дискового I/O
  -e POSTGRES_PASSWORD=secret 
  postgres:14

Для кэша Redis:

docker run --name redis 
  --memory="1g" --memory-swap="1g" 
  --cpus="0.5" 
  --sysctl net.core.somaxconn=1024 
  redis:7-alpine

4. Мониторинг и диагностика:

# Проверка текущего использования
docker stats

# Детальная информация
docker inspect --format='{{.HostConfig.Memory}}' container_name

# Просмотр cgroups
cat /sys/fs/cgroup/memory/docker/<container-id>/memory.limit_in_bytes

5. Продвинутые сценарии:

  • CPU Quota и Period:

    # Ограничение: 50% CPU каждые 100ms
    docker run --cpu-period=100000 --cpu-quota=50000 myapp
  • OOM Killer настройки:

    docker run --oom-kill-disable 
    --memory="1g" 
    --oom-score-adj=-500   # Меньше шансов быть убитым
    myapp
  • ULIMITs наследование:

    docker run --ulimit nofile=1024:1024 myapp

6. Интеграция с оркестраторами:

В Kubernetes эти ограничения задаются через:

resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
  limits:
    memory: "512Mi"
    cpu: "500m"

Ключевые принципы, которых я придерживаюсь:

  1. Всегда устанавливаю limits для production контейнеров
  2. Начинаю с консервативных значений и настраиваю по метрикам
  3. Использую мониторинг (Prometheus + cAdvisor) для сбора метрик
  4. Тестирую ограничения под нагрузкой
  5. Для stateful сервисов (БД) отключаю или строго ограничиваю swap
  6. Регулярно ревьюю и корректирую лимиты на основе фактического использования