Learning Platform
Глоссарий Troubleshooting
Урок 13.02 · 20 мин
Средний
dockercomposeprofiles

Profiles: опциональные сервисы в одном compose

В реальном проекте бывает: «есть core-сервисы (Postgres, app), а есть опциональные — Adminer для UI к БД, Kafka-UI для просмотра топиков, debug-контейнер с tcpdump». Их не нужно держать запущенными всегда, но хочется иметь под рукой. Profiles — механизм compose для именно этого. В этом уроке разбираем синтаксис, паттерны и подводные камни.


Базовый синтаксис

services:
  postgres:
    image: postgres:17
    environment: { POSTGRES_PASSWORD: secret }

  app:
    image: myapp
    depends_on: [postgres]

  adminer:
    image: adminer:4
    profiles: ["ui"]
    ports: ["8080:8080"]
# Без флага — Adminer не стартует.
docker compose up -d
# Up: postgres, app. Adminer пропущен.

# С флагом — стартует.
docker compose --profile ui up -d
# Up: postgres, app, adminer.

Сервисы без profiles: ключа стартуют всегда. Сервисы с профилями — только когда хотя бы один их профиль активирован через --profile.


Несколько профилей на сервисе

services:
  jaeger:
    image: jaegertracing/all-in-one
    profiles: ["debug", "observability"]

jaeger запустится, если активирован debug ИЛИ observability:

docker compose --profile debug up
docker compose --profile observability up
docker compose --profile debug --profile observability up   # оба, но jaeger стартует один раз

Активация нескольких профилей

docker compose --profile ui --profile debug up

Через переменную среды:

COMPOSE_PROFILES=ui,debug docker compose up

Все профили из списка активны, все их сервисы стартуют.

--profile "*" — активировать все профили сразу:

docker compose --profile "*" up

— стартует абсолютно всё, что есть в compose-файле.


Реальные кейсы

1. Optional UI к БД

Как Docker разрешает имена сервисов
services:
  postgres:
    image: postgres:17
    environment: { POSTGRES_PASSWORD: secret }

  adminer:
    image: adminer:4
    profiles: ["ui"]
    depends_on: [postgres]
    ports: ["127.0.0.1:8080:8080"]
    environment:
      ADMINER_DEFAULT_SERVER: postgres

В обычной работе UI не нужен — psql из терминала хватает. Когда нужен веб-интерфейс: docker compose --profile ui up -d.

2. Debug-toolbox

services:
  tcpdump:
    image: nicolaka/netshoot
    profiles: ["debug"]
    network_mode: "service:app"     # делит netns с app
    command: tcpdump -i any -w /captures/dump.pcap
    volumes:
      - ./captures:/captures

netshoot — образ с tcpdump, dig, curl, nslookup. Запускается только когда нужно дебажить трафик. network_mode: "service:app" подсаживается в namespace другого контейнера — видит его трафик.

docker compose --profile debug up tcpdump
# Снифит трафик app до Ctrl+C.

3. Тестовые сервисы

services:
  app:
    image: myapp

  mock-s3:
    image: minio/minio
    profiles: ["test"]
    command: server /data

  test-runner:
    image: myapp
    profiles: ["test"]
    depends_on: [mock-s3, app]
    command: pytest

В обычной работе mock S3 и runner не запускаются. При запуске в CI или вручную для smoke-test:

docker compose --profile test up --abort-on-container-exit

--abort-on-container-exit останавливает весь стенд, как только хоть один контейнер вышел — полезно для test-run, чтобы по exit-code узнать результат.


Default profile

Сервисы без profiles: — это «default-профиль», они всегда запускаются. Это даёт чистую модель:

  • Core: без profile, всегда поднимается. Это твой минимум для работы.
  • Optional: с profile, поднимается по запросу.
Compose-стенд: какие сервисы стартуют при разных вызовах
docker compose upБез флагов — стартуют только сервисы без profiles. Core-стенд для нормальной работы
--profile uiДополнительно поднимается adminer. Core тоже стартует — добавляется к нему
--profile debugCore + debug-инструменты: tcpdump, jaeger, etc
--profile testCore + test-runner + mock-сервисы. Для CI
--profile '*' Все профили активны — стартует всё. Удобно для smoke-теста compose-файла или для развёртывания production-стенда
postgres + app + adminer + tcpdump + testsЧтобы запустить полностью, нужно много памяти и CPU. Для прод-стенда обычно делят 'compose файлы по environment', а не 'все профили на проде'

Profiles vs override files

Profiles похожи на override-файлы (следующий урок) — оба позволяют менять стенд под ситуацию. Когда что использовать?

СлучайProfileOverride-файл
Добавить optional-сервис (один и тот же сервис)данет
Изменить параметры существующего сервисанетда
Сделать вариант «без UI» / «с UI»давозможно
dev vs prod (разные образы, разные volumes)возможнода, идиоматично
dev vs CI (только дополнительные сервисы)давозможно

Profile — добавление сервиса. Override — изменение сервиса. Они часто комбинируются.


Зависимости с профилями

Если app зависит от kafka, а kafka под profile streaming:

services:
  app:
    image: myapp
    depends_on: [kafka]   # kafka в профиле streaming

  kafka:
    image: confluentinc/cp-kafka
    profiles: ["streaming"]
docker compose up
# Error: service "app" depends on "kafka" which is in profile streaming, not active

Compose не запустит app, потому что depends_on указывает на сервис, который сейчас не активирован.

Решение:

  • Запустить с профилем: --profile streaming up.
  • Объединить app и kafka в один профиль, либо вынести app тоже в profiles: ["streaming"].
  • Использовать профили на верхнем уровне продумано: depends_on на не-активный сервис — это ошибка дизайна.
WARNING

Авто-активация: если ты делаешь docker compose up app — compose активирует все профили, в которых app участвует, плюс профили зависимостей. Но если app сам не в профиле — он не подтянет за собой профильные зависимости автоматически. Будь явным с —profile.


Реальный compose-файл с profiles

services:
  postgres:
    image: postgres:17
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-secret}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s

  redis:
    image: redis:7-alpine

  app:
    build: ./app
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    environment:
      DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-secret}@postgres:5432/postgres

  # Optional UI
  adminer:
    image: adminer:4
    profiles: ["ui"]
    ports: ["127.0.0.1:8080:8080"]

  redis-commander:
    image: rediscommander/redis-commander
    profiles: ["ui"]
    environment:
      REDIS_HOSTS: local:redis:6379
    ports: ["127.0.0.1:8081:8081"]

  # Optional observability
  prometheus:
    image: prom/prometheus
    profiles: ["observability"]
    volumes: ["./prometheus.yml:/etc/prometheus/prometheus.yml:ro"]
    ports: ["127.0.0.1:9090:9090"]

  # Optional tests
  test-runner:
    profiles: ["test"]
    build: ./app
    depends_on: [app]
    command: pytest tests/

volumes:
  pgdata:

Сценарии запуска:

# Каждодневная работа.
docker compose up -d

# Поковыряться в БД через UI.
docker compose --profile ui up -d

# Включить мониторинг.
docker compose --profile observability up -d

# CI: запустить тесты.
docker compose --profile test up --abort-on-container-exit

# Всё разом.
docker compose --profile ui --profile observability up -d

Один compose.yml для всех режимов работы. Никаких разных файлов для dev/staging.


Попробуй сам

mkdir -p profiles-demo && cd profiles-demo

cat > compose.yml <<'YAML'
services:
  core:
    image: alpine:3.20
    command: sh -c 'while true; do echo "core tick $(date)"; sleep 5; done'

  ui:
    image: alpine:3.20
    profiles: ["ui"]
    command: sh -c 'echo "UI service started"; sleep 1000'

  debug:
    image: alpine:3.20
    profiles: ["debug"]
    command: sh -c 'echo "debug service started"; sleep 1000'
YAML

# 1. Без флага.
docker compose up -d
docker compose ps
# Только core стартует.

# 2. С --profile ui.
docker compose --profile ui up -d
docker compose ps
# core + ui.

# 3. С двумя профилями.
docker compose --profile ui --profile debug up -d
docker compose ps
# core + ui + debug.

# 4. Через переменную окружения.
COMPOSE_PROFILES=ui,debug docker compose up -d
# Тот же эффект.

# 5. Все профили.
docker compose --profile "*" up -d
docker compose ps

# Cleanup.
docker compose down
cd ..
rm -rf profiles-demo
TIP

Хорошее правило: core-сервисы — без profiles. UI и админ-инструменты — profiles: ["ui"]. Тесты — profiles: ["test"]. Опциональные observability-сервисы — profiles: ["observability"]. Это даёт чистый, читаемый compose-файл и понятную модель «зачем мне нужен какой профиль».

В следующем уроке — override-файлы: автомерж compose.override.yml, явный merge через -f, паттерн base+dev+prod.


Проверка знанийKnowledge check
У тебя в compose: app без profile, adminer с profiles: ["ui"] . Опиши что запустится для трёх команд: (1) docker compose up , (2) docker compose --profile ui up , (3) docker compose up adminer . Объясни, почему третий вариант работает (или не работает).
ОтветAnswer
(1) docker compose up — стартует только app (и любые другие сервисы без profile). adminer пропускается, потому что ни один профиль не активирован. (2) docker compose --profile ui up — стартует app + adminer. Профиль ui активирован, все сервисы с этим профилем поднимаются. Сервисы без profile всегда стартуют. (3) docker compose up adminer — стартует adminer (и зависимости через depends_on, если есть). Compose автоматически активирует профили выбранных сервисов: если ты явно указал adminer, compose понимает, что нужен профиль ui, и активирует его сам. Это удобный shortcut: вместо --profile ui up adminer достаточно up adminer. Однако сервисы из других профилей (например, debug) не активируются — только тот, что нужен для запрошенных сервисов. Если adminer depends_on какой-то сервис из ещё одного профиля — этот профиль тоже подтянется.

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

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. В compose: app без profile, adminer с profiles: ['ui']. Что запустится при docker compose up, docker compose --profile ui up, docker compose up adminer?

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

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

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

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