Como passo variáveis ​​de ambiente para contêineres do Docker?

830

Eu sou novo no Docker e não está claro como acessar um banco de dados externo a partir de um contêiner. É a melhor maneira de codificar na cadeia de conexão?

# Dockerfile
ENV DATABASE_URL amazon:rds/connection?string
AJcodez
fonte

Respostas:

1247

Você pode passar variáveis ​​de ambiente para seus contêineres com o -esinalizador

Um exemplo de um script de inicialização:

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

Ou, se você não quiser ter o valor na linha de comando em que ele será exibido ps, etc., -epoderá extrair o valor do ambiente atual se você o fornecer sem o =:

sudo PASSWORD='foo' docker run  [...] -e PASSWORD [...]

Se você possui muitas variáveis ​​de ambiente e, principalmente, se elas são secretas, pode usar um arquivo env :

$ docker run --env-file ./env.list ubuntu bash

O sinalizador --env-file usa um nome de arquivo como argumento e espera que cada linha esteja no formato VAR = VAL, imitando o argumento passado para --env. As linhas de comentário precisam apenas ser prefixadas com #

errata
fonte
Existe uma maneira mais fácil de fazer isso? É realmente irritante ter que recriar o contêiner com variáveis ​​diferentes sempre. Talvez guardá-lo em um arquivo?
Jason Axelson 27/07
29
Eu armazeno os comandos de execução do docker em scripts de shell (./start_staging.sh etc.) e os executo remotamente usando o Ansible.
errata
1
Estou tendo problemas para que a segunda versão funcione; Defino PASSWORD = foo no ambiente, depois passo --env PASSWORD, e apenas a palavra "PASSWORD" aparece no config.json do contêiner; qualquer outra variável de ambiente possui uma chave e um valor. Estou usando o Docker 1.12.1.
Kevin Burke
@KevinBurke: Eu acho que você quer SENHA -e = $ senha se você está lendo a partir do ambiente shell atual
errata
8
@KevinBurke: Faça isso export PASSWORD=fooe a variável será passada docker runcomo uma variável de ambiente, fazendo o docker run -e PASSWORDtrabalho.
Qerub
93

Você pode passar usando -eparâmetros com o docker run ..comando conforme mencionado aqui e como mencionado por @errata.
No entanto, a possível desvantagem dessa abordagem é que suas credenciais serão exibidas na lista de processos, onde você a executa.
Para torná-lo mais seguro, você pode escrever as suas credenciais em um arquivo de configuração e fazer docker runcom --env-filecomo mencionado aqui . Em seguida, você pode controlar o acesso desse arquivo de configuração para que outras pessoas que tenham acesso a essa máquina não vejam suas credenciais.

Sabin
fonte
2
Adicionei outra maneira de abordar essa preocupação na resposta da @ errata.
217 Bryan
21
Tenha cuidado --env-file, quando você usar --envseus valores env serão citados / escapados com semântica padrão de qualquer shell que estiver usando, mas ao usar --env-fileos valores que você obterá dentro de seu contêiner será diferente. O comando docker run apenas lê o arquivo, faz uma análise muito básica e passa os valores para o contêiner, não é equivalente à maneira como seu shell se comporta. Apenas um pequeno detalhe para você saber se está convertendo várias --enventradas para um --env-file.
Enviado
5
Para elaborar a resposta Shorn, ao usar o arquivo env, tive que colocar o valor de uma variável de ambiente muito longo em uma linha, pois não parece haver maneira de colocar uma quebra de linha ou dividi-la em várias linhas, tais como: $ MY_VAR = coisas $ MY_VAR = $ MY_VAR mais coisas
Jason White
53

Se você estiver usando 'docker-compose' como o método para girar seus contêineres, na verdade, há uma maneira útil de passar uma variável de ambiente definida em seu servidor para o contêiner Docker.

No seu docker-compose.ymlarquivo, digamos que você esteja criando um contêiner hapi-js básico e o código se pareça com:

hapi_server:
  container_name: hapi_server
  image: node_image
  expose:
    - "3000"

Digamos que o servidor local em que o seu projeto de docker esteja possui uma variável de ambiente chamada 'NODE_DB_CONNECT' que você deseja passar para o contêiner hapi-js e que o novo nome seja 'HAPI_DB_CONNECT'. Em seguida, no docker-compose.ymlarquivo, você passaria a variável de ambiente local para o contêiner e a renomearia da seguinte maneira:

hapi_server:
  container_name: hapi_server
  image: node_image
  environment:
    - HAPI_DB_CONNECT=${NODE_DB_CONNECT}
  expose:
    - "3000"

Espero que isso ajude você a evitar codificar permanentemente uma string de conexão com o banco de dados em qualquer arquivo do seu contêiner!

Marquistador
fonte
6
Isso não vai funcionar. Essas variáveis ​​não são passadas para o contêiner.
Frondor
@Frondor realmente? De acordo com esses documentos , parece que deveria.
darda 5/05/19
1
O problema dessa abordagem é que você confirma as variáveis ​​de ambiente no arquivo docker-compose.yml no repositório git, o que não deve ser feito. Como você contorna isso? Idealmente, você teria um arquivo env separado que é gitignored e pode importar carga / na Dockerfile ou estivador-compose.yml
Khaled Osman
35

Usando docker-compose, você pode herdar variáveis ​​env no docker-compose.yml e, posteriormente, qualquer Dockerfile (s) chamado (s) por docker-composepara criar imagens. Isso é útil quando o Dockerfile RUNcomando deve executar comandos específicos para o ambiente.

(seu shell RAILS_ENV=developmentjá existe no ambiente)

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

Dessa forma, não preciso especificar variáveis ​​de ambiente em arquivos ou docker-compose build/ upcomandos:

docker-compose build
docker-compose up
Joshweir
fonte
Eles têm que ter o mesmo nome? Parece meio confuso .. E como eu iria substituir os argumentos se eu quiser executar o desenvolvimento?
CyberMew
@CyberMew Sim, eles devem ter o mesmo nome entre o seu ambiente, docker-compose e Dockerfile. Se você deseja executar o desenvolvimento, antes de executar o docker-compose build, execute RAILS_ENV = development no seu terminal para definir a variável de ambiente, dessa maneira o docker-compose e, por sua vez, o Dockerfile herdarão esse valor do seu ambiente.
joshweir
30

Use -eou --env value para definir variáveis ​​de ambiente (padrão []).

Um exemplo de um script de inicialização:

 docker run  -e myhost='localhost' -it busybox sh

Se você deseja usar vários ambientes na linha de comando, antes de cada variável de ambiente, use o -esinalizador

Exemplo:

 sudo docker run -d -t -i -e NAMESPACE='staging' -e PASSWORD='foo' busybox sh

Nota: Certifique-se de colocar o nome do contêiner após a variável de ambiente, não antes disso.

Se você precisar configurar muitas variáveis, use o --env-filesinalizador

Por exemplo,

 $ docker run --env-file ./my_env ubuntu bash

Para qualquer outra ajuda, consulte a ajuda do Docker:

 $ docker run --help

Documentação oficial: https://docs.docker.com/compose/environment-variables/

Vishnu Mishra
fonte
2
Por que precisamos ubuntu bash? Aplica-se a imagens criadas com o ubuntu como imagem base ou a todas as imagens?
Re
Eu gostaria de ter lido um pouco sobre como colocar o nome do contêiner após os -eargumentos atrás! Eu não posso nem começar a entender por que eles fizeram isso necessário ...
ironchicken
13

Há um truque interessante de como canalizar variáveis ​​de ambiente da máquina host para um contêiner de docker:

env > env_file && docker run --env-file env_file image_name

Use esta técnica com muito cuidado, porque env > env_filefará o dump de TODAS as variáveis ​​ENV da máquina host env_filee as tornará acessíveis no contêiner em execução.

Alex T
fonte
5

Outra maneira é usar os poderes de /usr/bin/env:

docker run ubuntu env DEBUG=1 path/to/script.sh
sanmai
fonte
2

Se você possui as variáveis ​​de ambiente env.shlocalmente e deseja configurá-las quando o contêiner é iniciado, tente

COPY env.sh /env.sh
COPY <filename>.jar /<filename>.jar
ENTRYPOINT ["/bin/bash" , "-c", "source /env.sh && printenv && java -jar /<filename>.jar"]

Este comando iniciaria o contêiner com um shell bash (eu quero um shell bash, pois sourceé um comando bash), origina o env.sharquivo (que define as variáveis ​​de ambiente) e executa o arquivo jar.

Os env.sholhares como este,

#!/bin/bash
export FOO="BAR"
export DB_NAME="DATABASE_NAME"

Eu adicionei o printenvcomando apenas para testar se o comando de origem real funciona. Você provavelmente deve removê-lo quando confirmar que o comando source funciona bem ou se as variáveis ​​de ambiente apareceriam nos logs do docker.

akilesh raj
fonte
2
com essa abordagem, você terá que reconstruir sua imagem do docker toda vez que quiser passar um conjunto de ambientes diferente / modificado. Passar envs durante "docker --run --env-file ./somefile.txt" é uma abordagem superior / dinâmica.
Dmitry Shevkoplyas
2
@DmitryShevkoplyas eu concordo. Meu caso de uso é onde não há opção de especificar o --env-fileargumento para um docker runcomando. Por exemplo, se você estiver implantando um aplicativo usando o Google app engine e o aplicativo em execução no contêiner precisar de variáveis ​​de ambiente definidas no contêiner de docker, você não terá uma abordagem direta para definir as variáveis ​​de ambiente, pois não tem controle sobre o docker runcomando . Nesse caso, você pode ter um script que descriptografa as variáveis ​​env usando, digamos, KMS e as adiciona às env.shquais podem ser originadas para definir as variáveis ​​env.
raj akilesh
você pode usar o .comando POSIX (ponto) disponível em shvez regular source. ( sourceé o mesmo que .) #
go2null
1

Usando jq para converter o env em JSON:

env_as_json=`jq -c -n env`
docker run -e HOST_ENV="$env_as_json" <image>

isso requer jq versão 1.6 ou mais recente

isso coloca o host env como json, essencialmente como no Dockerfile:

ENV HOST_ENV  (all env from the host as json)
Alexander Mills
fonte
Como essa linha está funcionando para você docker run -e HOST_ENV="$env_as_json" <image>? : ? No meu caso, o Docker não parece resolver variáveis ​​ou subshells ( ${}ou $()) quando passados ​​como argumentos do docker. Por exemplo: A=123 docker run --rm -it -e HE="$A" ubuntuentão dentro desse contêiner: root@947c89c79397:/# echo $HE root@947c89c79397:/# .... A HEvariável não faz isso.
Perplexabot 20/02
0

também podemos hospedar variáveis ​​de ambiente da máquina usando -e flag e $:

Antes de executar, é necessário exportar (significa definir) a variável e o arquivo env locais ou imediatamente antes de usar

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 

Usando esse método, defina a variável env automaticamente com o seu nome no meu caso (MG_HOST, MG_USER)

Adicional:

Se você estiver usando python, poderá acessar essas variáveis ​​de ambiente na janela de encaixe,

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')
mobin alhassan
fonte
0

docker run --rm -it --env-file <(bash -c 'env | grep <your env data>') É uma maneira de receber os dados armazenados em um .enve transmiti-los ao Docker, sem que nada seja armazenado de maneira insegura (para que você não possa apenas olhar docker historye pegar as chaves.

Digamos que você tenha um monte de coisas da AWS da .envmesma forma:

AWS_ACCESS_KEY: xxxxxxx
AWS_SECRET: xxxxxx
AWS_REGION: xxxxxx

A execução do docker com o `` docker run --rm -it --env-file <(bash -c 'env | grep AWS_') agarra tudo e passa-o com segurança para ser acessível a partir do contêiner.

MadDanWithABox
fonte