Comment passer des variables d'environnement aux conteneurs Docker?

831

Je suis nouveau sur Docker et on ne sait pas comment accéder à une base de données externe à partir d'un conteneur. Est le meilleur moyen de coder en dur dans la chaîne de connexion?

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

Réponses:

1247

Vous pouvez transmettre des variables d'environnement à vos conteneurs avec l' -eindicateur.

Un exemple tiré d'un script de démarrage:

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, si vous ne voulez pas avoir la valeur sur la ligne de commande où elle sera affichée ps, etc., vous -epouvez extraire la valeur de l'environnement actuel si vous la donnez sans =:

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

Si vous avez de nombreuses variables d'environnement et surtout si elles sont censées être secrètes, vous pouvez utiliser un fichier env :

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

L'indicateur --env-file prend un nom de fichier comme argument et attend que chaque ligne soit au format VAR = VAL, imitant l'argument passé à --env. Les lignes de commentaires doivent uniquement être préfixées avec #

errata
la source
Existe-t-il un moyen plus simple de procéder? C'est vraiment énervant de devoir recréer le conteneur avec différentes variables à chaque fois. Peut-être le stocker dans un fichier?
Jason Axelson
29
Je stocke les commandes docker run dans des scripts shell, (./start_staging.sh etc.) puis les exécute à distance en utilisant Ansible.
errata
1
J'ai du mal à faire fonctionner la deuxième version; Je mets PASSWORD = foo dans l'environnement, puis passe --env PASSWORD, et seul le mot "PASSWORD" apparaît dans le fichier config.json du conteneur; chaque autre variable d'environnement a une clé et une valeur. J'utilise Docker 1.12.1.
Kevin Burke
@KevinBurke: Je pense que vous voulez -e MOT DE PASSE = $ MOT DE PASSE si vous lisez à partir de l'environnement shell actuel
errata
8
@KevinBurke: Faites à la export PASSWORD=fooplace et la variable sera transmise en docker runtant que variable d'environnement, ce qui rendra le docker run -e PASSWORDtravail.
qerub
93

Vous pouvez passer en utilisant des -eparamètres avec la docker run ..commande comme mentionné ici et comme mentionné par @errata.
Cependant, l'inconvénient possible de cette approche est que vos informations d'identification seront affichées dans la liste des processus, où vous l'exécutez.
Pour ce faire plus sûr, vous pouvez écrire vos informations d' identification dans un fichier de configuration et faire docker runavec --env-filecomme mentionné ici . Ensuite, vous pouvez contrôler l'accès à ce fichier de configuration afin que les autres personnes ayant accès à cette machine ne voient pas vos informations d'identification.

Sabin
la source
2
J'ai ajouté une autre façon de répondre à cette préoccupation dans la réponse de @ errata.
Bryan
21
Soyez prudent --env-file, lorsque vous utilisez --envvos valeurs env seront citées / échappées avec la sémantique standard de tout shell que vous utilisez, mais lors de l'utilisation --env-filedes valeurs que vous obtiendrez à l'intérieur de votre conteneur sera différente. La commande docker run lit simplement le fichier, effectue une analyse très basique et transmet les valeurs au conteneur, ce n'est pas équivalent à la façon dont votre shell se comporte. Juste un petit truc à savoir si vous convertissez un tas d' --enventrées en un --env-file.
Shorn
5
Pour élaborer sur la réponse Shorn, lors de l'utilisation du fichier env, j'ai dû mettre la valeur d'une variable d'environnement très longue sur une seule ligne car il ne semble pas y avoir de moyen d'y mettre un saut de ligne ou de le diviser en plusieurs lignes telles que: $ MY_VAR = stuff $ MY_VAR = $ MY_VAR plus de trucs
Jason White
54

Si vous utilisez 'docker-compose' comme méthode pour faire tourner vos conteneurs, il existe en fait un moyen utile de passer une variable d'environnement définie sur votre serveur au conteneur Docker.

Dans votre docker-compose.ymlfichier, disons que vous faites tourner un conteneur hapi-js de base et que le code ressemble à:

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

Supposons que le serveur local sur lequel se trouve votre projet Docker possède une variable d'environnement nommée 'NODE_DB_CONNECT' que vous souhaitez transmettre à votre conteneur hapi-js et que vous souhaitez que son nouveau nom soit 'HAPI_DB_CONNECT'. Ensuite, dans le docker-compose.ymlfichier, vous passeriez la variable d'environnement local au conteneur et le renommeriez comme suit:

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

J'espère que cela vous aidera à éviter de coder en dur une chaîne de connexion de base de données dans n'importe quel fichier de votre conteneur!

Marquistador
la source
6
Ça ne marchera pas. Ces variables ne sont pas transmises au conteneur.
Frondor
@Frondor vraiment? Selon ces documents, il semble que ce devrait être le cas.
darda
1
Le problème avec cette approche est que vous validez les variables d'environnement dans le fichier docker-compose.yml dans le référentiel git, ce que vous ne devriez pas. Comment contournez-vous cela? idéalement, vous auriez un fichier env séparé qui est gitignoré et peut être importé / chargé dans le Dockerfile ou docker-compose.yml
Khaled Osman
35

En utilisant docker-compose , vous pouvez hériter des variables env dans docker-compose.yml et ensuite tout Dockerfile appelé par docker-composepour construire des images. Ceci est utile lorsque la Dockerfile RUNcommande doit exécuter des commandes spécifiques à l'environnement.

(votre shell existe RAILS_ENV=developmentdéjà dans l'environnement)

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

De cette façon, je n'ai pas besoin de spécifier de variables d'environnement dans les fichiers ou les commandes docker-compose build/ up:

docker-compose build
docker-compose up
joshweir
la source
Doivent-ils être du même nom? Semble un peu déroutant .. Et comment pourrais-je remplacer les arguments si je veux exécuter le développement à la place?
CyberMew
@CyberMew Oui, ils doivent être du même nom entre votre environnement, docker-compose et Dockerfile. Si vous souhaitez exécuter le développement à la place, avant d'exécuter la construction de docker-compose, exécutez RAILS_ENV = development dans votre terminal pour définir la variable d'environnement, de cette façon, docker-compose et à son tour Dockerfile hériteront de cette valeur de votre environnement.
joshweir
31

Utilisation -e valeur ou --env pour définir les variables d'environnement (par défaut []).

Un exemple tiré d'un script de démarrage:

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

Si vous souhaitez utiliser plusieurs environnements à partir de la ligne de commande, avant chaque variable d'environnement, utilisez l' -eindicateur.

Exemple:

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

Remarque: assurez-vous de mettre le nom du conteneur après la variable d'environnement, pas avant.

Si vous devez configurer de nombreuses variables, utilisez le --env-file indicateur

Par exemple,

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

Pour toute autre aide, consultez l'aide de Docker:

 $ docker run --help

Documentation officielle: https://docs.docker.com/compose/environment-variables/

Vishnu Mishra
la source
2
Pourquoi avons-nous besoin ubuntu bash? Cela s'applique-t-il aux images créées avec ubuntu comme image de base ou à chaque image?
Reyansh Kharga,
J'aurais aimé lire un peu comment mettre le nom du conteneur après les -earguments il y a longtemps! Je ne peux même pas commencer à comprendre pourquoi ils ont rendu cela nécessaire ...
ironchicken
13

Il existe un bon hack pour diriger les variables d'environnement de la machine hôte vers un conteneur Docker:

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

Utilisez cette technique très soigneusement, car env > env_fileelle videra TOUTES les variables ENV de la machine hôte env_fileet les rendra accessibles dans le conteneur en cours d'exécution.

Alex T
la source
5

Une autre façon consiste à utiliser les pouvoirs de /usr/bin/env:

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

Si vous avez les variables d'environnement env.shlocalement et que vous souhaitez les configurer au démarrage du conteneur, vous pouvez essayer

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

Cette commande démarrerait le conteneur avec un shell bash (je veux un shell bash car sourcec'est une commande bash), source le env.shfichier (qui définit les variables d'environnement) et exécute le fichier jar.

Le env.shressemble à ça,

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

J'ai ajouté la printenvcommande uniquement pour tester le fonctionnement de la commande source réelle. Vous devez probablement le supprimer lorsque vous confirmez que la commande source fonctionne correctement ou que les variables d'environnement apparaissent dans vos journaux Docker.

akilesh raj
la source
2
avec cette approche, vous devrez reconstruire votre image docker à chaque fois que vous voulez passer un ensemble env différent / modifié. Passer envs pendant "docker --run --env-file ./somefile.txt" est une approche supérieure / dynamique.
Dmitry Shevkoplyas
2
@DmitryShevkoplyas Je suis d'accord. Mon cas d'utilisation est celui où il n'y a pas d'option pour spécifier l' --env-fileargument à une docker runcommande. Par exemple, si vous déployez une application à l'aide du moteur d'application Google et que l'application s'exécutant à l'intérieur du conteneur nécessite des variables d'environnement définies dans le conteneur Docker, vous n'avez pas d'approche directe pour définir les variables d'environnement car vous n'avez pas le contrôle sur la docker runcommande . Dans un tel cas, vous pourriez avoir un script qui déchiffre les variables env à l'aide de disons, KMS, et les ajoute à celui env.shqui peut être obtenu pour définir les variables env.
akilesh raj
vous pouvez utiliser la commande POSIX .(dot) disponible dans regular shau lieu de source. ( sourceest le même que .)
go2null
1

Utiliser jq pour convertir l'env en JSON:

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

cela nécessite jq version 1.6 ou plus récente

cela pust l'hôte env comme json, essentiellement comme dans Dockerfile:

ENV HOST_ENV  (all env from the host as json)
Alexander Mills
la source
Comment cette ligne fonctionne-t-elle pour vous docker run -e HOST_ENV="$env_as_json" <image>? : ? Dans mon cas, Docker ne semble pas résoudre les variables ou les sous-coquilles ( ${}ou $()) lorsqu'il est passé en tant qu'arguments de docker. Par exemple: A=123 docker run --rm -it -e HE="$A" ubuntupuis à l'intérieur de ce conteneur: root@947c89c79397:/# echo $HE root@947c89c79397:/# .... La HEvariable ne le fait pas.
Perplexabot
0

nous pouvons également héberger la variable d'environnement de la machine en utilisant l'option -e et $:

Avant d'exécuter, vous devez exporter (signifie définir) la variable et le fichier env locaux ou juste avant d'utiliser

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 

En utilisant cette méthode, définissez la variable env automatiquement avec votre prénom dans mon cas (MG_HOST, MG_USER)

Additionnel:

Si vous utilisez python, vous pouvez accéder à ces variables d'environnement dans docker en

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
la source
0

docker run --rm -it --env-file <(bash -c 'env | grep <your env data>') Est un moyen de récupérer les données stockées dans un .envet de les transmettre à Docker, sans que rien ne soit stocké de manière non sécurisée (vous ne pouvez donc pas simplement regarder docker historyet saisir des clés.

Supposons que vous ayez un tas de trucs AWS .env:

AWS_ACCESS_KEY: xxxxxxx
AWS_SECRET: xxxxxx
AWS_REGION: xxxxxx

l'exécution de docker avec `` `docker run --rm -it --env-file <(bash -c 'env | grep AWS_') récupérera tout et le passera en toute sécurité pour être accessible à partir du conteneur.

MadDanWithABox
la source