Другое

Docker Compose: Ports vs Expose - Полное руководство

Узнайте ключевые различия между директивами ports и expose в Docker Compose. Узнайте, как каждая опция влияет на сетевую доступность, безопасность и коммуникацию контейнеров. Освойте правильную конфигурацию для безопасных развертываний.

В чём разница между опциями ports и expose в файле docker-compose.yml?

Опции ports и expose в docker-compose.yml служат принципиально разным целям: ports сопоставляет порты контейнера с хост-машиной, делая сервисы доступными извне, в то время как expose только открывает порты для внутреннего взаимодействия контейнеров в одной сети Docker без их публикации на хосте. Это различие критически важно для безопасности, так как ports открывает сервисы для внешнего мира, а expose поддерживает изоляцию только для взаимодействия между контейнерами. Понимание этого различия помогает в правильной настройке сети и планировании безопасности в среде Docker.

Содержание

Базовые определения и цели

Директивы ports и expose в Docker Compose служат разным целям и имеют разное поведение:

Ports используется для публикации портов контейнера на хост-машине, обеспечивая внешний доступ к вашим сервисам. Когда вы указываете ports, Docker создает сопоставление между портом хоста и портом контейнера, позволяя трафику поступать извне Docker-окружения в ваши контейнеры. Это необходимо, когда вашим сервисам требуется доступ из интернета, локальной сети или других внешних систем.

Expose, с другой стороны, только раскрывает порты контейнера для внутреннего взаимодействия в сети Docker. Согласно официальной документации Docker, инструкция EXPOSE “не публикует порт на самом деле”, а скорее служит документацией о том, на каких портах контейнер ожидает подключения. В Docker Compose директива expose делает эти порты доступными для взаимодействия с другими контейнерами в той же сети, но не сопоставляет их с хост-машиной.

Ключевое отличие: Основное различие заключается в доступности - ports открывает ворота для всего мира, в то время как expose поддерживает взаимодействие строго внутри экосистемы сети Docker.

Синтаксис и шаблоны использования

Синтаксис и шаблоны реализации значительно различаются между этими двумя директивами:

Синтаксис Ports

Директива ports поддерживает два основных синтаксических шаблона:

yaml
services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"    # Порт хоста 8080 сопоставлен с портом контейнера 80
      - "8081:8080"  # Порт хоста 8081 сопоставлен с портом контейнера 8080

Вы также можете указать только порт контейнера, что позволит Docker выбрать случайный порт хоста:

yaml
services:
  web:
    image: nginx:latest
    ports:
      - "80"  # Порт контейнера 80, случайный порт хоста назначен автоматически

Синтаксис Expose

Директива expose использует более простой синтаксис:

yaml
services:
  database:
    image: mysql:8.0
    expose:
      - "3306"  # Порт контейнера 3306, не сопоставлен с хостом
      - "3307"  # Порт контейнера 3307, не сопоставлен с хостом

Как отмечено в обсуждении на Stack Overflow, синтаксис expose “служит только для документации” в контексте Dockerfile, но в Docker Compose он фактически обеспечивает внутреннее сетевое взаимодействие.

Таблица: Сравнение синтаксиса

Функция Ports Expose
Требования к синтаксису ХОСТ:КОНТЕЙНЕР или КОНТЕЙНЕР Только КОНТЕЙНЕР
Сопоставление с хостом Да, создает сопоставление хост-контейнер Нет, нет сопоставления с хостом
Внешний доступ Включен Отключен
Внутренний доступ Включен Включен
Назначение случайного порта Поддерживается при указании только порта контейнера Не применимо

Различия в сети и доступности

Сетевое поведение этих директив значительно отличается:

Поведение сети Ports

При использовании ports Docker создает мостовую сеть по умолчанию и сопоставляет указанные порты контейнера с IP-адресом и портами хост-машины. Это означает:

  • Сервисы становятся доступными извне Docker-окружения
  • Трафик может достичь сервиса через http://localhost:ПОРТ_ХОСТА
  • Сервис доступен с других машин в сети
  • Возможны конфликты портов, если порты хоста уже используются

Как показано в примере документации Docker, при указании ports: - 8080:80 сервис, работающий на порту 80 контейнера, становится доступным через http://localhost:8080.

Поведение сети Expose

Директива expose создает иное сетевое поведение:

  • Порты доступны только внутри сети Docker
  • Сервисы могут взаимодействовать друг с другом, используя порты контейнера
  • Внешний доступ к хост-машине не предоставляется
  • Сервисы остаются изолированными от внешнего трафика

Согласно документации Warp, “свойство expose используется для определения портов контейнера, которые открыты для внутреннего взаимодействия с другими контейнерами в той же сети и которые не сопоставлены с портами хоста.”

Это поведение особенно полезно для:

  • Контейнеров баз данных, которым необходимо взаимодействовать только с контейнерами приложений
  • Архитектуры микросервисов, где сервисы взаимодействуют внутренне
  • Безопасно-ориентированных развертываний, где внешний доступ должен быть минимизирован

Сетевая изоляция: В то время как ports нарушает изоляцию контейнера, открывая сервисы для хоста, expose поддерживает границу безопасности, сохраняя сервисы внутри сети Docker.

Влияние на безопасность

Влияние выбора между ports и expose на безопасность значительно:

Соображения безопасности Ports

Использование ports подвергает ваши сервисы потенциальным рискам безопасности:

  • Поверхность атаки: Сервисы становятся доступными извне Docker-окружения
  • Сканирование портов: Внешние системы могут обнаружить и потенциально атаковать открытые сервисы
  • Обход брандмауэра: Даже если брандмауэр хоста безопасен, открытые порты создают новые точки входа
  • Сетевая безопасность: Требуются дополнительные меры безопасности для защиты открытых конечных точек

Как объясняет один источник, “expose решает эту проблему, не сопоставляя с портами хоста, тем самым обеспечивая большую изоляцию и уменьшая потенциальную поверхность атаки.”

Преимущества безопасности Expose

Директива expose предоставляет несколько преимуществ безопасности:

  • Уменьшенная поверхность атаки: Сервисы недоступны извне сети Docker
  • Сегментация сети: Взаимодействие остается в контролируемом Docker-окружении
  • Изоляция по умолчанию: Сервисы запускаются в более безопасном состоянии по умолчанию
  • Принцип минимальных привилегий: Разрешено только необходимое внутреннее взаимодействие

Согласно сообществу Better Stack, “порты, указанные в docker-compose.yml файле, будут доступны только связанным сервисам и не будут открыты для хоста.”

Таблица: Сравнение безопасности

Аспект безопасности Ports Expose
Внешний доступ Доступен Заблокирован
Поверхность атаки Увеличена Минимизирована
Изоляция по умолчанию Низкая Высокая
Сетевое воздействие Публичное Только внутреннее
Риск соответствия требованиям Выше Ниже

Практические примеры

Пример 1: Веб-приложение с базой данных

yaml
version: '3.8'
services:
  webapp:
    image: my-webapp:latest
    ports:
      - "8080:3000"  # Веб-приложение доступно извне на порту 8080
    depends_on:
      - database
    networks:
      - app-network

  database:
    image: postgres:13
    expose:
      - "5432"  # Порт базы данных доступен только другим контейнерам
    environment:
      POSTGRES_PASSWORD: secret
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

В этой настройке:

  • Веб-приложение доступно извне через http://localhost:8080
  • База данных недоступна извне Docker
  • Веб-приложение может подключаться к базе данных через database:5432

Пример 2: Архитектура микросервисов

yaml
version: '3.8'
services:
  api-gateway:
    image: my-gateway:latest
    ports:
      - "80:8080"  # Шлюз доступен извне
    expose:
      - "3000"     # Также доступен другим сервисам
    networks:
      - micro-network

  auth-service:
    image: my-auth:latest
    expose:
      - "8080"     # Только внутренний доступ
    networks:
      - micro-network

  user-service:
    image: my-users:latest
    expose:
      - "8080"     # Только внутренний доступ
    networks:
      - micro-network

Здесь API-шлюз - единственный сервис, доступный извне, в то время как внутренние сервисы поддерживают изоляцию.

Лучшие практики

Когда использовать Ports

Используйте ports, когда:

  • Вашему сервису требуется доступ из веб-браузеров или внешних клиентов
  • Вы разрабатываете и вам нужен доступ к сервисам с хост-машины
  • Вы запускаете тестовые среды, требующие внешнего доступа
  • Вы реализуете обратные прокси или балансировщики нагрузки

Когда использовать Expose

Используйте expose, когда:

  • Сервисам требуется только внутреннее взаимодействие внутри сети Docker
  • Вы хотите минимизировать поверхность атаки для соответствия требованиям безопасности
  • Вы реализуете архитектуру микросервисов с четкими границами сервисов
  • Вы работаете с чувствительными сервисами, такими как базы данных или внутренние API

Стратегия комбинированного использования

Для оптимальной безопасности и функциональности:

  1. Внешний уровень доступа: Используйте ports только для внешнего уровня, требующего внешнего доступа
  2. Внутренний уровень взаимодействия: Используйте expose для внутренних сервисов
  3. Сегментация сети: Создавайте отдельные сети для разных уровней безопасности
  4. Документация: Используйте expose для документирования портов, на которых контейнеры действительно ожидают подключения

Как резюмируется в статье на Medium, “хотя синтаксически незначительное, различие между ports и expose в docker-compose отмечает значительное расхождение в поведении и целях.”

Заключение

Понимание различия между ports и expose в Docker Compose фундаментально важно для правильного оркестрирования контейнеров и планирования безопасности:

  • Ports обеспечивает внешний доступ, сопоставляя порты контейнера с хост-машиной, делая сервисы доступными извне Docker
  • Expose поддерживает только внутреннее взаимодействие, сохраняя сервисы изолированными внутри сети Docker
  • Безопасность: Используйте expose для внутренних сервисов для минимизации поверхности атаки, и ports только при реальной необходимости внешнего доступа
  • Архитектура: В средах микросервисов обычно используйте ports для граничных сервисов и expose для внутренних сервисов
  • Документация: Обе директивы служат документацией, но только ports фактически обеспечивает внешнюю подключаемость

Тщательно выбирая между этими директивами, вы можете создавать безопасные, хорошо спроектированные Docker-развертывания, которые балансируют доступность с правильной изоляцией. Ключевой принцип - минимальные привилегии - открывайте только то, что абсолютно необходимо для функциональности вашего приложения.

Источники

  1. Publishing and exposing ports | Docker Docs
  2. What is the difference between ports and expose in docker-compose? - Stack Overflow
  3. Understand Port Mapping in Docker Compose - Warp
  4. Difference Between Expose and Ports in Docker Compose | Baeldung
  5. What Is the Difference between Docker-Compose Ports and Expose? | Better Stack Community
  6. Does “ports” on docker-compose.yml have the same effect as EXPOSE on Dockerfile? - Stack Overflow
  7. What is the difference between ports and expose in docker-compose? | Medium
Авторы
Проверено модерацией
Модерация