Как заставить файл 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! -- вы иногда будете наблюдать странное поведение при работе с переменными окружения, которые используются для настройки контейнера. Рассмотрим такую последовательность событий:
Запускаем
docker-compose up
в первый раз, используя значения из вашего.env
файла.Подтверждаем, что мы можем подключиться к базе данных под пользователем
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=#
Остановим контейнер, набрав
CTRL-C
.Запускаем контейнер с новым значением для
DB_USER
в нашей переменной окружения:DB_USER=tommyboy docker-compose up
Пробуем подключиться, используя
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/