Как заставить файл docker-compoe ".env" иметь приоритет над параметрами env оболочки?

Я хочу, чтобы мой файл docker-compose.yml использовал файл ".env" в том же каталоге, что и файл "docker-compose.yml", для установки некоторых переменных окружения, и чтобы они имели приоритет перед любыми другими переменными окружения, установленными в оболочке. Сейчас у меня

$ echo $DB_USER
tommyboy

и в моем .env файле у меня есть

$ cat .env
DB_NAME=directory_data
DB_USER=myuser
DB_PASS=mypass
DB_SERVICE=postgres
DB_PORT=5432

У меня есть это в моем файле docker-compose.yml ...

version: '3'

services:

  postgres:
    image: postgres:10.5
    ports:
      - 5105:5432
    environment:
      POSTGRES_DB: directory_data
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: password

  web:
    restart: always
    build: ./web
    ports:           # to access the container from outside
      - "8000:8000"
    environment:
      DEBUG: 'true'
      SERVICE_CREDS_JSON_FILE: '/my-app/credentials.json'
      DB_SERVICE: host.docker.internal
      DB_NAME: directory_data
      DB_USER: ${DB_USER}
      DB_PASS: password
      DB_PORT: 5432
    command: /usr/local/bin/gunicorn directory.wsgi:application --reload -w 2 -b :8000
    volumes:
    - ./web/:/app
    depends_on:
      - postgres 

В моем проекте Python 3/Django 3 в файле settings.py моего приложения есть следующее

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ['DB_NAME'],
        'USER': os.environ['DB_USER'],
        'PASSWORD': os.environ['DB_PASS'],
        'HOST': os.environ['DB_SERVICE'],
        'PORT': os.environ['DB_PORT']
    }
}

Однако когда я запускаю свой проект, используя "docker-compose up", я вижу

maps-web-1       |   File "/usr/local/lib/python3.9/site-packages/django/db/backends/postgresql/base.py", line 187, in get_new_connection
maps-web-1       |     connection = Database.connect(**conn_params)
maps-web-1       |   File "/usr/local/lib/python3.9/site-packages/psycopg2/__init__.py", line 127, in connect
maps-web-1       |     conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
maps-web-1       | psycopg2.OperationalError: FATAL:  role "tommyboy" does not exist

Кажется, что контейнер Django использует env var оболочки, а не то, что передано, и мне интересно, есть ли способ заставить контейнер Python/Django использовать файл ".env" в корне для своих env var.

Сначала я подумал, что неправильно понял ваш вопрос, но думаю, что мой первоначальный комментарий был правильным. Как я уже говорил, обычно локальная среда оболочки переопределяет параметры в файле .env; это позволяет вам переопределять параметры командной строки. Другими словами, если в вашем .env файле есть:

DB_USER=tommyboy

Если вы хотите переопределить значение DB_USER для одного вызова docker-compose up, вы можете выполнить:

DB_USER=alice docker-compose up

Поэтому значения в вашей локальной среде имеют приоритет.


При использовании docker-compose с вещами, которые хранят постоянные данные - например, Postgres! -- вы иногда будете наблюдать странное поведение при работе с переменными окружения, которые используются для настройки контейнера. Рассмотрим такую последовательность событий:

  1. Запускаем docker-compose up в первый раз, используя значения из вашего .env файла.

  2. Подтверждаем, что мы можем подключиться к базе данных под пользователем myuser:

    $ docker-compose exec postgres psql -U myuser directory_data
    psql (10.5 (Debian 10.5-2.pgdg90+1))
    Type "help" for help.
    
    directory_data=#
    
  3. Остановим контейнер, набрав CTRL-C.

  4. Запускаем контейнер с новым значением для DB_USER в нашей переменной окружения:

    DB_USER=tommyboy docker-compose up
    
  5. Пробуем подключиться, используя tommyboy имя пользователя...

    $ docker-compose exec postgres psql -U tommyboy directory_data
    psql: FATAL:  role "tommyboy" does not exist
    

    ... и это не удается.

Что здесь происходит?

Переменные среды POSTGRES_*, которые вы используете для конфигурации Postgres, имеют значение только в том случае, если база данных еще не была инициализирована. Когда вы останавливаете и перезапускаете службу с помощью , он не создает новый контейнер; он просто перезапускает существующий.docker-compose

Это означает, что в описанной выше последовательности событий база данных была первоначально создана с именем пользователя myuser, и запуск ее во второй раз при установке в нашем окружении изменил второй раз при установке DB_USER в нашем окружении ничего не изменилось. ничего.

Решением здесь является использование команды docker-compose down, которая удаляет контейнеры...

docker-compose down

А затем создайте новый с обновленной переменной окружения:

DB_USER=tommyboy docker-compose up

Теперь мы можем получить доступ к базе данных, как и ожидалось:

$ docker-compose exec postgres psql -U tommyboy directory_data
psql (10.5 (Debian 10.5-2.pgdg90+1))
Type "help" for help.

directory_data=#

Я не могу дать лучший ответ, чем тот, который дал @larsks, но, пожалуйста, позвольте мне попробовать дать вам несколько идей.

Как отметил @larsks, любая переменная окружения оболочки будет иметь приоритет над теми, которые определены в вашем файле docker-compose .env.

Этот факт также указан в документации docker-compose, когда речь идет о переменных окружения, выделение мое:

Вы можете установить значения по умолчанию для переменных окружения с помощью файла .env, который Compose автоматически ищет в директории проекта (родительская папка вашего файла Compose). Значения, установленные в окружении оболочки, переопределяют значения установленные в .env файле .

Это означает, что, например, предоставление переменной оболочки следующим образом:

DB_USER= tommyboy docker-compose up

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

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

Ища информацию о вашей проблеме, я наткнулся на эту замечательную статью.

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

Я не знал об этой библиотеке, но, похоже, она предоставляет альтернативный способ конфигурирования вашего приложения, читая конфигурацию непосредственно из конфигурационного файла:

import environ
import os

env = environ.Env(
    # set casting, default value
    DEBUG=(bool, False)
)

# Set the project base directory
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Take environment variables from .env file
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))

# False if not in os.environ because of casting above
DEBUG = env('DEBUG')

# Raises Django's ImproperlyConfigured
# exception if SECRET_KEY not in os.environ
SECRET_KEY = env('SECRET_KEY')

# Parse database connection url strings
# like psql://user:pass@127.0.0.1:8458/db
DATABASES = {
    # read os.environ['DATABASE_URL'] and raises
    # ImproperlyConfigured exception if not found
    #
    # The db() method is an alias for db_url().
    'default': env.db(),

    # read os.environ['SQLITE_URL']
    'extra': env.db_url(
        'SQLITE_URL',
        default='sqlite:////tmp/my-tmp-sqlite.db'
    )
}

#...

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

Возможно, python-dotenv позволит вам следовать аналогичному подходу.

Вам все еще нужно справиться с конфигурацией контейнера PostgreSQL, но я надеюсь, что это будет полезно.

Значения в оболочке имеют приоритет над значениями, указанными в файле .env.

Если вы установили TAG на другое значение в вашей оболочке, подстановка в изображении использует его вместо этого:

export TAG=v2.0

docker compose convert

 
version: '3'
services:
  web:
    image: 'webapp:v1.5'

Для получения более подробной информации обратитесь к ссылке: https://docs.docker.com/compose/environment-variables/

Вернуться на верх