Как настроить docker ports и environment в docker compose

В этом руководстве мы подробно разберём параметры docker-compose.yml: открытие портов через ports, настройку сетей через networks, управление переменными окружения (environment variables) с использованием .env-файлов. Все настройки рассмотрим на примере запуска популярного Python-фреймворка Django.

Сетевые настройки сервиса в docker compose: ports и networks

Сетевые настройки в файле docker-compose.yml определяют, как контейнер будет взаимодействовать с другими сервисами или с пользователями. Без этих настроек приложение останется изолированным: оно будет недоступно для подключения из браузера, а другие сервисы не смогут с ним взаимодействовать. В docker-compose.yml за это отвечают два параметра: ports и networks.

Настройка docker-compose.yml: ports

Параметр ports в docker-compose.yml открывает доступ к приложению внутри контейнера для внешних клиентов: браузера, curl или других сервисов. Существует несколько форматов указания портов.

  • Простой проброс порта:
docker-compose.yml
Copy

Самый простой и распространённый способ. В примере порт 8080 на хосте перенаправляется на порт 80 внутри контейнера (формат «хост:контейнер»). Если веб-сервер внутри контейнера слушает порт 80, то после проброса приложение станет доступно в браузере по адресу http://localhost:8080. По умолчанию Docker использует TCP, который гарантирует доставку данных. Указывать протокол явно («8080:80/tcp» или «8080:80/udp») нужно только для UDP-сервисов, где важнее скорость, а не гарантия доставки (например, видеопотоки или DNS), либо когда один порт работает сразу по обоим протоколам.

  • Проброс диапазона портов:

Проброс диапазона портов используется, когда приложению нужно слушать несколько портов одновременно.

docker-compose.yml
Copy

Такая запись создаёт попарное соответствие:

  • хост:8000 → контейнер:8000
  • хост:8001 → контейнер:8001
  • …
  • хост:8010 → контейнер:8010

Количество портов слева и справа должно совпадать. Эту настройку используют для приложений с динамическими портами, например медиа-серверов или SIP-телефонии, чтобы не прописывать каждый порт отдельной строкой.

Настройка docker-compose.yml: networks

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

Чтобы изолировать сервисы, можно использовать секцию networks. Вы можете создать отдельные сети и явно указать их в настройках каждого сервиса. Контейнер будет подключён только к перечисленным сетям и не увидит сервисы из других сетей. Например, так можно отделить базу данных от веб-сервера.

docker-compose.yml
Copy

В этом примере созданы две изолированные сети: frontend и backend.

  • Контейнеры web и api подключены к сети frontend, они видят друг друга по имени.
  • Контейнеры api и db подключены к сети backend и тоже могут общаться с друг к другом по имени.
  • Контейнер web не видит контейнер db, так как у них нет общей сети. Это намеренная изоляция: веб-сервер не должен иметь прямого доступа к базе данных.

Корневая секция networks в конце файла обязательна. Без неё Docker Compose не создаст указанные сети и выдаст ошибку при запуске.

Переменные окружения environment и env_file

Переменные окружения в docker-compose.yml позволяют вынести настройки приложения (например: логин, пароль, параметры подключения) из кода в конфигурационный файл. Это сделано для того, чтобы не хранить чувствительные данные внутри образа и иметь возможность изменить их без пересборки. В docker-compose.yml для этого используются два параметра: environment и env_file.

Параметр docker-compose.yml environment

Параметр environment позволяет задать переменные окружения (environment variables) внутри файла docker-compose.yml. При запуске Docker передаёт эти переменные контейнеру, который использует их как системные настройки.

У переменных окружения нет универсального списка, потому что сервисы работают со своим набор параметров. Например, образу postgres нужны: пользователь (POSTGRES_USER) и пароль (POSTGRES_PASSWORD). Чтобы правильно задать переменные окружения для запуска сервиса необходимо ознакомиться с его документацией.

Переменные задаются отдельно для каждого сервиса. Они не являются общими для всего файла docker-compose.yml. Если двум контейнерам нужна одна и та же переменная (например, TZ), её нужно прописать в секции environment обоих сервисов.

Внутри секции environment используется формат словаря YAML (KEY: value). Если значение (value) содержит особые символы (например, #, {, }), его нужно заключать в двойные кавычки. Без них YAML интерпретирует эти символы как часть синтаксиса и выдаст ошибку парсинга при сборке docker-compose.yml:

  • PASSWORD: my#secret — символ # воспринимается как начало комментария, и значение обрежется до my. Правильный value будет: PASSWORD: «my#secret».
  • CONFIG: {enabled: true} — фигурные скобки воспринимаются как вложенный объект, а не строка. Правильный value будет: CONFIG: «{enabled: true}».
  • LIST: [item1, item2] — квадратные скобки воспринимаются как массив. Правильный value будет: LIST: «[item1, item2]».

Также поддерживается строковый формат KEY=value, но его применяют в основном в .env-файлах.

docker-compose.yml
Copy

В этом примере база данных создастся с суперпользователем homeassistant, паролем mysecretpassword и базой hass. Часовой пояс TZ опционален и нужен, чтобы время в логах и записях совпадало с вашим регионом.

Использование сикретов в файле удобно при локальной разработке, но для продакшена пароли и ключи лучше выносить в отдельный .env-файл, чтобы не хранить их в открытом виде в конфигурации.

Параметр docker-compose.yml env_file

Параметр env_file позволяет вынести переменные окружения (environment variables) в отдельный файл, который обычно называется .env и размещается рядом с docker-compose.yml. В самом compose-файле достаточно указать путь к нему. Один и тот же .env могут использовать сразу несколько сервисов:

docker-compose.yml
Copy

Синтаксис наполнения .env-файла отличается от YAML: каждая строка в нем содержит одну переменную в виде KEY=value, без отступов и кавычек, а комментарии начинаются с #:

.env
Copy

Каждый сервис получает все переменные из файла, но использует только те, которые ему нужны — остальные игнорируются. Если переменная указана и в env_file, и в environment, приоритет будет у значения из environment.

Для разделения конфигураций продакшена и тестовой среды используют разные файлы: .env.prod, .env.test, .env.dev. Путь к нужному файлу можно указать напрямую в docker-compose.yml или переопределить при запуске через флаг —env-file:

Terminal

Файлы с сикретами принято добавлять в .gitignore, чтобы случайно не опубликовать их в репозитории.

Запуск Python-фреймворка Django через Docker Compose

Django — это популярный фреймворк для создания веб-приложений на языке Python. Вместо установки Python, его зависимостей и базы данных на хост — можно использовать технологию Docker для запуска проекта. Этот способ упрощает развёртывание: все настройки для работы с Python уже есть в сборке и готовы к использованию.

Подготовка файлов для Django на хосте

Перед тем как начать описывать сервисы в docker-compose.yml нужно создать базовую структуру проекта Django на хосте. Для работы в Docker будут нужны четыре файла и ниже мы создадим каждый из них.

Для начала, перейдите в директорию проекта (там где лежит docker-compose.yml) и создайте произвольную подпапку для Django-приложения, например, /app.

Terminal
Copy

Все файлы для Django будут находиться внутри этой произвольной папки app. В дальнейшем эту директорию мы укажем в docker-compose.yml как контекст сборки.

Теперь создадим файлы для Django.

requirements.txt — это текстовый файл, который содержит список внешних Python-библиотек, которые нужно загрузить и установить. Каждая строка файла содержит название пакета и, опционально, его версию.

Python-библиотека — это готовый набор кода, который написан ранее другим разработчиком для решения конкретной задачи. Это может быть, например: подключения к сайту по URL, парсинг данных или работа с базой. Вместо того чтобы писать свой код для решения такой задачи вы просто подключаете библиотеку через import.

Файл requirements.txt служит инструкцией для менеджера пакетов pip: он считывает этот список и автоматически загружает все указанные библиотеки из репозитория.

Django в нашем случае — такая же библиотека. В requirements.txt укажем диапазон между 5-й и 6-й версией как наиболее стабильную сборку: Django>=5.0,<6.0.

По умолчанию Django работает со встроенной базой данных SQLite. Её не надо дополнительно настраивать, но при высокой нагрузке она начинает «отваливаться». Для повышения надежности можно использовать БД PostgreSQL. Чтобы переключить Django на неё, нужна отдельная библиотека. Рекомендуется использовать стабильную версию от 2.9: psycopg2-binary>=2.9.

Создайте файл и добавьте эти библиотеки:

Terminal
Copy

requirements.txt
Copy

Актуальные версии библиотек можно проверить на сайте Django и на сайте psycopg2-binary.

Оба этих файла обязательны для работы фреймворка, но создавать их вручную не придется. Их автоматически сгенерирует сам Django.

Для начала мы подготовим минимальный Dockerfile с инструкцией RUN pip install -r requirements.txt и запустим сборку (docker build). Это загрузит фреймворк и библиотеки внутрь образа.

Команду на создание файлов мы отдадим позже, когда запустим контейнер. А чтобы результат сразу появился на диске, мы используем технологию томов (volumes).

Начнем с создания и наполнения минимального Dockerfile внутри директории app:

Terminal
Copy

requirements.txt
Copy

  • FROM python:3.12-slim — базовый образ Python, который содержит только интерпретатор
  • WORKDIR /app — создаст рабочую директорию /app внутри контейнера и сделает её текущей для всех последующих команд
  • COPY requirements.txt ./ — скопирует файл со списком библиотек из корня проекта на хосте в рабочую директорию контейнера (/app/requirements.txt)
  • RUN pip install —no-cache-dir -r requirements.txt — установит библиотеки внутрь образа, включая Django и утилиту django-admin

Теперь соберем образ с произвольным именем django-init:

Terminal
Copy

Далее запустим контейнер из созданного образа django-init и скажем Docker скопировать файлы, установленные через requirements.txt, на хост. В этой команде используется монтирование томов, которое мы подробно разберём в следующей статье, здесь это лишь техническая необходимость:

Terminal
Copy

  • docker run — команда запуска контейнера
  • --rm — автоматическое удаление контейнера после завершения команды
  • -v "$(pwd):/app" — монтирует текущую директорию хоста в папку /app внутри контейнера. То есть, всё что создаётся внутри контейнера в /app, появляется на хосте в текущей папке.
  • -v — флаг для монтирования тома
  • «$(pwd)» — подставляет текущую директорию на хосте
  • :/app — путь внутри контейнера
  • django-init — имя собранного образа.
  • django-admin startproject mysite . — выполняет команду внутри контейнера, где Django установлен (mysite — произвольное имя директории). Точка . в конце указывает создать файлы в текущей рабочей директории (/app), а не во вложенной подпапке.

После выполнения команды в директории app вашего проекта появятся файл manage.py и поддиректория mysite с файлом settings.py внутри.

В Dockerfile мы опишем инструкции по созданию образа, который позже станет основой для контейнера с Python-приложением. Отредактируем Dockerfile файл в директории /app:

Terminal
Copy

Пропишем в него следующие инструкции:

Dockerfile
Copy

  • FROM python:3.12-slim — облегченный образ Python с Docker Hub, который содержит только интерпретатор.
  • WORKDIR /app — создаёт рабочую директорию /app внутри контейнера и делает её текущей для всех последующих команд.
  • COPY requirements.txt ./ — копирует файл зависимостей из директории /app на хосте (где лежит Dockerfile) в рабочую директорию контейнера (/app/requirements.txt). Контекстом сборки является именно папка /app, а не корень всего проекта docker-projects.
  • RUN pip install —no-cache-dir -r requirements.txt — устанавливает библиотеки из requirements.txt внутрь образа. Флаг —no-cache-dir предотвращает сохранение временных файлов установки, уменьшая размер итогового образа.
  • COPY . . — копирует всё содержимое текущей директории на хосте (включая manage.py, папку mysite с settings.py и другой код) в рабочую директорию контейнера /app.
  • EXPOSE 8000 — документирует порт, на котором будет работать приложение. Сам по себе он не открывает порт наружу, это лишь подсказка для чтения конфигурации.
  • CMD [«python», «manage.py», «runserver», «0.0.0.0:8000»] — команда, которая выполняется при запуске контейнера.
  • python — интерпретатор, который входит в состав облегченного образа python:3.12-slim.
  • manage.py — файл, созданный ранее командой django-admin startproject. Он содержит утилиту управления проектом.
  • runserver — команда Django, которая становится доступна только после установки библиотеки из requirements.txt (Django>=5.0,<6.0). Это встроенный веб-сервер для разработки.
  • 0.0.0.0:8000 — адрес и порт. 0.0.0.0 означает «слушать все сетевые интерфейсы», чтобы сервер был доступен извне контейнера. Без этого параметра он слушал бы только localhost внутри контейнера и не отвечал бы на внешние запросы.

Инструкция CMD запускает интерпретатор Python, которому передает файл управления проектом manage.py с командой запуска веб-сервера runserver. Последний аргумент 0.0.0.0:8000 указывает серверу слушать все сетевые интерфейсы контейнера на порту 8000, чтобы сайт был доступен не только изнутри, но и с вашего компьютера.

По умолчанию, в качестве базы данных Django использует SQLite, и эта конфигурация прописана в settings.py.

Файл settings.py в директории mysite проекта Django
Файл settings.py находится в папке mysite, которая была создана вместе с manage.py при выполнении команды django-admin startproject

Чтобы будущее приложение стало использовать PostgreSQL, конфигурацию базы данных нужно заменить. Если файл settings.py был создан ранее через контейнер (от имени root), то чтобы редактировать этот файл под своим пользователем без применения sudo — потребуется выдать своему пользователю права на редактирование файла. Используйте следующую команду:

Terminal
Copy

Теперь у вас достаточно прав и вы можете открыть и отредактировать файл:

Terminal
Copy

Найдите секцию с DATABASES в файле settings.py:

Секция DATABASES в файле settings.py фреймворка Django
Секция DATABASES в файле settings.py с настройками базы данных sqlite

Замените всю секцию на следующие настройки:

settings.py
Copy

import os в начале файла подключает модуль для работы с переменными окружения, которые задаются в секции environment или файле env_file в docker-compose.yml.

os.getenv() считывает значение переменной окружения. Второе значение в скобках (например, ‘mysite_db’) — это запасной вариант на случай, если переменная не объявлена в environment или в файле env_file.

Значение ‘db’ в параметре HOST — это имя сервиса базы данных, которое мы зададим позже в docker-compose.yml. Если вы решите назвать сервис иначе, нужно будет вернуться к этой секции и указать актуальное имя.

Сверьте список файлов в вашей директории с изображением ниже. У вас должны появиться те же файлы и папки.

Файлы проекта Django в директории app: mysite, Dockerfile, manage.py, requirements.txt
В директории проекта docker_projects создана подпапка app, которая содержит все необходимые файлы для приложения Django

Создание docker-compose.yml для запуска Django-приложения

Теперь, когда все файлы готовы, создадим файл docker-compose.yml в корне проекта (docker-projects), рядом с папкой /app. Конфигурация отличается в зависимости от выбранной базы данных.

docker-compose.yml для Django с SQLite

Если вы не дорабатывали settings.py под PostgreSQL и будете использовать SQLite, то база данных будет храниться в файле внутри контейнера. Внешний сервис БД в этом случае не нужен, поэтому конфигурация будет состоять только из веб-сервиса.

Создадим docker-compose.yml:

Terminal
Copy

Наполним файл инструкциями для запуска Django с SQLite:

docker-compose.yml
Copy

Создайте в директории проекта рядом с docker-compose.yml файл .env с переменными для Django. Стандартный settings.py не читает параметры подключения к БД из окружения, поэтому секция DB_* для sqlite не нужна:

Terminal
Copy

.env
Copy

  • WEB_DEBUG=1 — включает режим отладки, удобно при разработке, для продакшена нужно будет изменить на 0.
  • WEB_SECRET_KEY=your-secret-key-here — секретный ключ для защиты куки и веб-форм от подмены. Параметр обязательный, для продакшена лучше использовать надёжный сгенерированный ключ.

Отдельные переменные для подключения к SQLite не нужны. Стандартный settings.py уже содержит конфигурацию SQLite по умолчанию, а база данных хранится в файле db.sqlite3 внутри контейнера.

docker-compose.yml для Django с PostgreSQL

Если вы будете использовать PostgreSQL вместо SQLite и не доработали settings.py ранее, сделайте это сейчас. База данных запускается как отдельный контейнер, а веб-сервис связывается с ней через общую сеть и переменные окружения. Параметр depends_on гарантирует, что веб-сервис запустится только после старта сервиса базы данных.

Создадим docker-compose.yml и наполним его инструкциями:

Terminal
Copy

docker-compose.yml
Copy

Создайте в директории проекта рядом с docker-compose.yml общий файл .env с переменными для Django и PostgreSQL. Значения DB_* из файла должны совпадать со значениями, указанными в os.getenv() внутри settings.py:

Terminal
Copy

.env
Copy

  • POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB — обязательные переменные сервиса PostgreSQL. При первом запуске они создают базу данных и учётную запись администратора. В будущем под этим пользователем можно будет создавать дополнительные базы и роли.
  • DB_HOST=db — имя сервиса базы данных из docker-compose.yml. По нему Django находит контейнер с базой во внутренней сети.
  • DB_PORT, DB_USER, DB_PASSWORD, DB_NAME — параметры подключения Django к PostgreSQL, которые читает settings.py через os.getenv(). Значения DB_USER, DB_PASSWORD и DB_NAME должны совпадать с переменными POSTGRES_*, а также быть прописаны в settings.py.
  • WEB_DEBUG=1 — включает режим отладки, удобно при разработке, для продакшена нужно будет изменить на 0.
  • WEB_SECRET_KEY=your-secret-key-here — секретный ключ для защиты куки и веб-форм от подмены. Параметр обязательный, для продакшена лучше использовать надёжный сгенерированный ключ.

Сборка и запуск контейнеров Django

Теперь все файлы готовы, можно собирать образ и запускать сервисы:

Terminal
Copy

Флаг -d запускает контейнеры в фоне и освобождает терминал. Если позже вы измените код или зависимости и захотите пересобрать образ заново, просто повторите эту команду — Docker автоматически подхватит изменения. Подробнее о том, как работает сборка, пересоздание контейнеров и управление жизненным циклом (остановка, логи, удаление), мы разбирали в прошлой статье.

Независимо от того, какая СУБД была выбрана, Django не создаёт таблицы автоматически при первом запуске приложения, потому нужно выполнить команду миграции вручную:

Terminal
Copy

Эта команда выполняется внутри запущенного контейнера djangoweb. Она создаёт системные таблицы (пользователи, сессии, права доступа) и применяет все миграции ваших приложений.

  • Для PostgreSQL это обязательный шаг: база пуста, пока вы не накатите миграции. Без него приложение выдаст ошибку при попытке обращения к любой модели.
  • Для SQLite файл db.sqlite3 создаётся автоматически, но таблицы в нём всё равно появляются только после выполнения migrate.

Далее откройте в браузере http://localhost:8000 и вы увидите стартовую страницу Django.

Демонстрация работы Django фреймворка по адресу localhost:8000
Стартовая страница Django — фреймворка, запущенная по адресу localhost:8000

Итоги: ports, environment и запуск многоконтейнерных приложений

В этой статье мы успешно собрали многоконтейнерное приложение на примере популярного Python-фреймворка Django. Разобрали как работает связка веб-сервиса и базы данных PostgreSQL через docker-compose.yml. Вынесли чувствительные настройки приложения в .env — файл.

Следующий шаг — научиться работать с томами (volumes). Без них данные в базе и изменения в коде будут теряться при пересоздании контейнеров, что критично для любой разработки.

FAQ. Вопросы и ответы

Чаще всего причина в порядке запуска. Убедитесь, что в docker-compose.yml у сервиса указан параметр depends_on. Также проверьте, что значение DB_HOST в файле .env точно совпадает с именем сервиса базы данных. Если вы меняли имя сервиса, обновите и его в .env, и в settings.py.

Проверьте логи контейнера командой docker compose logs -f «имя сервиса». Самая частая причина — неприменённые миграции (migrate). Вторая возможная причина — неверный WEB_SECRET_KEY или отсутствие переменной WEB_DEBUG в .env.

Зависимости устанавливаются только из requirements.txt и только во время сборки образа. Если вы добавили новую библиотеку в код, но забыли добавить её в requirements.txt, она будет отсутствовать в контейнер. Добавьте зависимость в файл requirements.txt и выполните docker compose up --build -d.

Откройте логи сервиса db через docker compose logs db. Обычно проблема в некорректных переменных POSTGRES_USER, POSTGRES_PASSWORD или POSTGRES_DB в файле .env. Убедитесь, что они заданы и не содержат спецсимволов, которые могут ломать парсинг. Также проверьте, что эти значения совпадают с DB_USER, DB_PASSWORD и DB_NAME.