Как передать переменные среды в контейнеры Docker?
Я новичок в Docker, и неясно, как получить доступ к внешней базе данных из контейнера. Является ли лучший способ жесткого кода в строке подключения?
# Dockerfile
ENV DATABASE_URL amazon:rds/connection?string
22 ответа
Вы можете передавать переменные окружения в ваши контейнеры с помощью -e
флаг.
Пример из скрипта запуска:
sudo docker run -d -t -i -e REDIS_NAMESPACE='staging' \
-e POSTGRES_ENV_POSTGRES_PASSWORD='foo' \
-e POSTGRES_ENV_POSTGRES_USER='bar' \
-e POSTGRES_ENV_DB_NAME='mysite_staging' \
-e POSTGRES_PORT_5432_TCP_ADDR='docker-db-1.hidden.us-east-1.rds.amazonaws.com' \
-e SITE_URL='staging.mysite.com' \
-p 80:80 \
--link redis:redis \
--name container_name dockerhub_id/image_name
Или, если вы не хотите иметь значение в командной строке, где оно будет отображаться ps
, так далее., -e
может извлечь значение из текущей среды, если вы просто дадите его без =
:
sudo PASSWORD='foo' docker run [...] -e PASSWORD [...]
Если у вас много переменных окружения и особенно если они предназначены для секретности, вы можете использовать env-файл:
$ docker run --env-file ./env.list ubuntu bash
Флаг --env-file принимает имя файла в качестве аргумента и ожидает, что каждая строка будет в формате VAR=VAL, имитируя аргумент, передаваемый --env. Строки комментариев должны быть только с префиксом #
Вы можете пройти с помощью -e
параметры с docker run ..
команда, как упомянуто здесь и как упомянуто @errata.
Однако возможный недостаток этого подхода заключается в том, что ваши учетные данные будут отображаться в списке процессов, где вы его запускаете.
Чтобы сделать его более безопасным, вы можете записать свои учетные данные в файл конфигурации и сделать docker run
с --env-file
как уже упоминалось здесь. Затем вы можете контролировать доступ к этому файлу конфигурации, чтобы другие, имеющие доступ к этому компьютеру, не увидели ваши учетные данные.
Использование -e
или значение --env для установки переменных среды (по умолчанию []).
Пример из скрипта запуска:
docker run -e myhost='localhost' -it busybox sh
Если вы хотите использовать несколько сред из командной строки, то перед каждой переменной среды используйте -e
флаг.
Пример:
sudo docker run -d -t -i -e NAMESPACE='staging' -e PASSWORD='foo' busybox sh
Примечание. Убедитесь, что имя контейнера указано после переменной среды, а не до этого.
Если вам нужно установить много переменных, используйте --env-file
флаг
Например,
$ docker run --env-file ./my_env ubuntu bash
Для любой другой помощи, посмотрите в справку Docker:
$ docker run --help
Официальная документация: https://docs.docker.com/compose/environment-variables/
Если вы используете "docker-compose" в качестве метода ускорения ваших контейнеров, на самом деле есть полезный способ передать переменную среды, определенную на вашем сервере, в контейнер Docker.
В вашем docker-compose.yml
файл, скажем, вы раскручиваете базовый контейнер hapi-js и код выглядит так:
hapi_server:
container_name: hapi_server
image: node_image
expose:
- "3000"
Предположим, что на локальном сервере, на котором находится ваш проект Docker, есть переменная среды с именем 'NODE_DB_CONNECT', которую вы хотите передать в свой контейнер hapi-js, и вы хотите, чтобы его новое имя было 'HAPI_DB_CONNECT'. Тогда в docker-compose.yml
файл, вы передадите локальную переменную среды в контейнер и переименуете ее так:
hapi_server:
container_name: hapi_server
image: node_image
environment:
- HAPI_DB_CONNECT=${NODE_DB_CONNECT}
expose:
- "3000"
Я надеюсь, что это поможет вам избежать жесткого кодирования строки подключения к базе данных в любом файле вашего контейнера!
С помощью docker-compose
приведенный ниже пример показывает, как вы можете наследовать переменные оболочки env как в docker-compose.yml, так и в любом Dockerfile(s), вызываемых docker-compose
строить изображения. Я нашел это полезным, если, скажем, в Dockerfile
RUN
Команда Мне нужно выполнить команды, специфичные для окружающей среды.
(ваша оболочка имеет RAILS_ENV=development
уже существует в среде)
docker-compose.yml:
version: '3.1'
services:
my-service:
build:
#$RAILS_ENV is referencing the shell environment RAILS_ENV variable
#and passing it to the Dockerfile ARG RAILS_ENV
#the syntax below ensures that the RAILS_ENV arg will default to
#production if empty.
#note that is dockerfile: is not specified it assumes file name: Dockerfile
context: .
args:
- RAILS_ENV=${RAILS_ENV:-production}
environment:
- RAILS_ENV=${RAILS_ENV:-production}
Dockerfile:
FROM ruby:2.3.4
#give ARG RAILS_ENV a default value = production
ARG RAILS_ENV=production
#assign the $RAILS_ENV arg to the RAILS_ENV ENV so that it can be accessed
#by the subsequent RUN call within the container
ENV RAILS_ENV $RAILS_ENV
#the subsequent RUN call accesses the RAILS_ENV ENV variable within the container
RUN if [ "$RAILS_ENV" = "production" ] ; then echo "production env"; else echo "non-production env: $RAILS_ENV"; fi
Таким образом, мне не нужно указывать переменные среды в файлах или docker-compose
build
/up
команды:
docker-compose build
docker-compose up
Мы также можем разместить переменную среды машины, используя флаг -e и $:
Перед запуском необходимо экспортировать (означает установить) локальную переменную env и файл или непосредственно перед использованием
docker run -it -e MG_HOST=$MG_HOST -e MG_USER=$MG_USER -e MG_PASS=$MG_PASS -e MG_AUTH=$MG_AUTH -e MG_DB=$MG_DB -t image_tag_name_and_version
Используя этот метод, автоматически установите переменную env с вашим именем в моем случае (MG_HOST,MG_USER)
Дополнительно:
Если вы используете python, вы можете получить доступ к этой переменной envment внутри докера с помощью
import os
host,username,password,auth,database=os.environ.get('MG_HOST'),os.environ.get('MG_USER'),os.environ.get('MG_PASS'),os.environ.get('MG_AUTH'),os.environ.get('MG_DB')
Есть хороший хак, как передать переменные среды хост-машины в докер-контейнер:
env > env_file && docker run --env-file env_file image_name
Используйте эту технику очень осторожно, потому что
env > env_file
сбросит ВСЕ переменные ENV хост-машины вenv_file
и сделать их доступными в работающем контейнере.
Проблема, с которой я столкнулся, заключалась в том, что я помещал --env-файл в конец команды
docker run -it --rm -p 8080:80 imagename --env-file ./env.list
Исправить
docker run --env-file ./env.list -it --rm -p 8080:80 imagename
docker run --rm -it --env-file <(bash -c 'env | grep <your env data>')
Это способ поиска данных, хранящихся в .env
и передать их в Docker, чтобы ничего не сохранялось небезопасно (так что вы не можете просто смотреть на docker history
и возьмите ключи.
Скажем, у вас в вашем .env
вот так:
AWS_ACCESS_KEY: xxxxxxx
AWS_SECRET: xxxxxx
AWS_REGION: xxxxxx
запуск докера с командой `` docker run --rm -it --env-file <(bash -c 'env | grep AWS_') захватит все это и безопасно передаст для доступа изнутри контейнера.
Есть несколько способов передать переменные среды в контейнер, включая использование docker-compose (наилучший выбор, если это возможно).
Я рекомендую использовать файл env для упрощения организации и обслуживания.
ПРИМЕР (docker-compose
интерфейс командной строки)
docker-compose -f docker-compose.yml --env-file ./.env up
ПРИМЕР (командная строка)
docker run -it --name "some-ctn-name" --env-file ./.env "some-img-name:Dockerfile"
ВАЖНО :docker
CLI имеет некоторые ограничения в отношении (см. ниже) переменных окружения.
ПРОБЛЕМА: запуск Docker и переменные среды с кавычками и двойными кавычками
Как ни странно, подкоманда не принимает файлы env , отформатированные как действительные сценарии BASH («Shell»), поэтому она рассматривает окружающие кавычки и двойные кавычки как часть значения переменных среды, поэтому контейнер получит значение (в файле env , для пример)...
SOME_ENV_VAR_A="some value a"
... как"some value a"
и неsome value a
. Кроме этого, у нас будут проблемы с использованием одного и того же файла env в других контекстах (включая сам BASH).
Это довольно странное поведение, поскольку файлы .env являются обычными скриптами BASH ("Shell").
Однако BASH («Shell») предлагает нам мощные функции, поэтому давайте воспользуемся ими в качестве обходного решения.
Мое решение включает в себя Dockerfile, файл env , файл сценария BASH и подкоманду () особым образом.
Стратегия состоит во внедрении ваших переменных среды с использованием другой переменной среды, установленной вrun
подкоманду и используя сам контейнер для установки этих переменных.
Обходное решение
Создать Dockerfile
ПРИМЕР
FROM python:3.10-slim-buster
WORKDIR /some-name
COPY . /some-name/
RUN apt-get -y update \
&& apt-get -y upgrade \
[...]
ENTRYPOINT bash entrypoint.bash
Создайте файл env (файл сценария BASH) ( .env )
ПРИМЕР
#!/bin/bash
# Some description a
SOME_ENV_VAR_A="some value a"
# Some description b
SOME_ENV_VAR_B="some value b"
# Some description c
SOME_ENV_VAR_C="some value c"
[...]
Создайте файл сценария BASH для ENTRYPOINT ( entrypoint.bash )
ПРИМЕР
#!/bin/bash
set -a;source <(echo -n "$ENV_VARS");set +a
python main.py
Внедрение переменных среды с помощью подкоманды run
ПРИМЕР
docker run -it --name "some-ctn-name" --env ENV_VARS="$(cat ./.env)" "some-img-name:Dockerfile"
ПЛЮС
У docker-compose этой проблемы нет, так как он использует YAML. YAML не рассматривает окружающие кавычки и двойные кавычки как часть значения переменных среды, чего нельзя сделать сdocker run
подкоманда.
Спасибо!
Другой способ заключается в использовании полномочий /usr/bin/env
:
docker run ubuntu env DEBUG=1 path/to/script.sh
Если у вас есть переменные среды в env.sh
локально и хотите настроить его при запуске контейнера, вы можете попробовать
COPY env.sh /env.sh
COPY <filename>.jar /<filename>.jar
ENTRYPOINT ["/bin/bash" , "-c", "source /env.sh && printenv && java -jar /<filename>.jar"]
Эта команда запускает контейнер с оболочкой bash (я хочу оболочку bash, так как source
команда bash), источники env.sh
файл (который устанавливает переменные среды) и выполняет файл JAR.
env.sh
выглядит так,
#!/bin/bash
export FOO="BAR"
export DB_NAME="DATABASE_NAME"
Я добавил printenv
Команда только для проверки того, что фактическая команда источника работает. Вы, вероятно, должны удалить его, когда подтвердите, что команда источника работает нормально, или переменные окружения появятся в ваших журналах докера.
Для передачи нескольких переменных среды через docker-compose файл среды также можно использовать в файле docker-compose.
web:
env_file:
- web-variables.env
https://docs.docker.com/compose/environment-variables/#the-env_file-configuration-option
Для Amazon AWS ECS/ECR вы должны управлять переменными среды (особенно секретами) с помощью частного сегмента S3. См. Сообщение в блоге " Как управлять секретами для приложений на основе сервиса контейнера EC3 Amazon" с помощью Amazon S3 и Docker.
Вы можете использовать -e или --env в качестве аргумента, за которым следует формат ключ-значение.
Например:
docker build -f file_name -e MYSQL_ROOT_PASSWORD=root
Используя jq для конвертации env в JSON:
env_as_json=`jq -c -n env`
docker run -e HOST_ENV="$env_as_json" <image>
это требует JQ версии 1.6 или новее
это pust хост env как json, по сути, так в Dockerfile:
ENV HOST_ENV (all env from the host as json)
Самое простое решение: просто запустите эти команды
sudo docker container run -p 3306:3306 -e MYSQL_RANDOM_ROOT_PASSWORD=yes --name mysql -d mysql
sudo docker container logs mysql
Что там происходит?
- Первая команда запускает контейнер MySQL со случайным паролем.
- Вторая команда показывает журналы контейнера, где вы сможете узнать, какой случайный пароль предоставлен.
Явное решение: здесь мы можем не только передать собственный пароль и имя базы данных, но и создать определенную сеть, через которую любое приложение будет взаимодействовать с этой базой данных. Более того, мы также можем получить доступ к базе данных Docker и просмотреть ее содержимое. Пожалуйста, смотрите ниже
docker network create todo-app
docker run -d \
--network todo-app --network-alias mysql \
-v todo-mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=todos \
mysql:8.0
docker exec -it <mysql-container-id> mysql -u root -p
SHOW DATABASES;
Вот как я смог это решить
docker run --rm -ti -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_SESSION_TOKEN -e AWS_SECURITY_TOKEN amazon/aws-cli s3 ls
еще один пример:
export VAR1=value1
export VAR2=value2
$ docker run --env VAR1 --env VAR2 ubuntu env | grep VAR
VAR1=value1
VAR2=value2
Garnet - это сборка глобального менеджера переменных env именно для этого: https://github.com/garnet-labs/garnet-oss . У них есть специальное руководство по развертыванию Docker по адресу: https://docs.usegarnet.com/integration-guides/docker.
Существуют некоторые несоответствия документации для установки переменных среды с помощью
docker run
.
Интернет-справочник говорит одно:
--env , -e Установить переменные среды
Man - страница немного отличается:
-e, --env=[] Установить переменные окружения
В
docker run --help
снова дает что-то другое:
-e, --env список Установить переменные окружения
Что-то, что не обязательно ясно в любой доступной документации:
Завершающий пробел после или может быть заменен на
=
, или в случае
-e
можно вообще исключить:
$ docker run -it -ekey=value:1234 ubuntu env
key=value:1234
Уловка, которую я нашел методом проб и ошибок (и подсказки выше)...
Если вы получаете сообщение об ошибке:
неизвестный флаг: --env
Тогда вам может быть полезно использовать знак равенства с
--env
, Например:
--env=key=value:1234
Разные способы запуска контейнера могут иметь разные сценарии парсинга.
Эти приемы могут быть полезны при использовании Docker в различных конфигурациях компоновки, таких как Visual Studio Code devcontainer.json, где пробелы не допускаются в
runArgs
множество.
Чтобы импортировать среду в контейнеры, вы можете использоватьenv_file:
в вашем файле docker-compose.yaml или вы можете скопировать.env
файл в контейнер, а затем читать с помощью расширенных библиотек.
Python-проект
Вы можете использоватьpython-dotenv
упаковка:
pip install python-dotenv
Затем в коде:
import os
from dotenv import load_dotenv
load_dotenv()
SECRET_KEY = os.getenv("MY_SECRET")
Перейти проект
github.com/joho/godotenv
упаковка:
go get github.com/joho/godotenv
В вашем коде:
package main
import (
"github.com/joho/godotenv"
"log"
"os"
)
func main() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
secretKey := os.Getenv("MY_SECRET")
}
Пример: предположим, у вас есть вариант использования для запуска контейнера базы данных MySQL, поэтому вам нужно передать следующие переменные
docker run -dit --name db1 -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=mydb -e MYSQL_USER=jack -e MYSQL_PASSWORD=redhat mysql:5.7