Przeglądając grupy tematyczne związane z Dockerem, zauważyłem, że wiele osób ma problem z zrozumieniem, w jaki sposób można przekazywać zmienne środowiskowe do obrazu i do kontenera. Zmotywowało mnie to do stworzenia tego artykułu.
Z artykułu dowiesz się WSZYSTKO na temat zmiennych środowiskowych w Dockerze. Oczywiście wszystko na przykładach, tak by łatwiej było zrozumieć, a następnie móc zastosować u siebie.
Post ten jest kontynuacją serii, w której opisują poszczególne instrukcje Dockerfile. Jeżeli jeszcze nie widziałeś, koniecznie sprawdź COPY vs ADD oraz CMD vs ENTRYPOINT.
Zastosowania instrukcji ENV
Wewnątrz pliku Dockerfile, instrukcja ENV służy do tworzenia zmiennych środowiskowych. Podczas uruchamiania kontenera na podstawie obrazu, możemy nadpisać ich wartość. Taka praktyka jest bardzo często stosowana.
Do czego mogą służyć zmienne środowiskowe? Niemal każda aplikacja ma jakąś konfigurację.
Przykłady:
- adres IP do bazy danych
- adres IP do innych usług (Elasticsearch, Vault etc.)
- zmienne związane z technologią (NODE_ENV, JAVA_VERSION etc)
- zmienne związane z logiką biznesową
Sposoby na przekazywanie zmiennych środowiskowych
1. Uruchamianie kontenera z terminala
Jeżeli tworzysz kontener korzystając z terminala, do nadpisania lub ustawienia zmiennej środowiskowej służy argument -e KLUCZ=WARTOŚĆ
. Poniżej tworzymy kontener WordPressa, przekazując w zmiennych środowiskowych połączenie do bazy danych.
docker run -e WORDPRESS_DB_HOST=10.1.2.3:3306 \ -e WORDPRESS_DB_USER=user \ -e WORDPRESS_DB_PASSWORD=somepassword \ -d wordpress
2. Uruchamianie z pomocą docker-compose
Ten sam efekt możesz uzyskać wewnątrz pliku docker-compose.yml
version: '3.1' services: wordpress: image: wordpress environment: - WORDPRESS_DB_HOST: 10.1.2.3:3306 - WORDPRESS_DB_USER: user - WORDPRESS_DB_PASSWORD: somepassword - WORDPRESS_DB_NAME: exampledb
3. Bezpośrednio w Dockerfile
Istnieją pewne przypadki, dla których na stałe chcemy ustawić zmienną środowiskową. Jak już wyżej wspominałem, może dotyczyć to technologii, w której tworzymy aplikację. Przykładowo, chcemy na stałe ustawić zmienną środowiskową NODE_VERSION
. Przykład poniżej.
FROM scratch # set up node ENV NODE_VERSION 8.9.4 ENV NODE_DOWNLOAD_SHA 21fb4690e349f82d708ae766def01d7fec1b085ce1f5ab30d9bda8ee126ca8fc RUN curl -SL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz" --output nodejs.tar.gz \ && echo "$NODE_DOWNLOAD_SHA nodejs.tar.gz" | sha256sum -c - \ && tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 \ && rm nodejs.tar.gz \ && ln -s /usr/local/bin/node /usr/local//nodejs
Stosowanie plików .env
Istnieje możliwość stworzenia pliku o nazwie .env
. Zawartość tego pliku może posłużyć do nadpisania wartości znajdujących się w pliku docker-compose.yml
. Warto zaznaczyć, że oba pliki muszą znajdować się w tym samym katalogu.
Zawartość pliku .env
defniujemy za pomocą schematu klucz=wartość
DB_PORT=5434 DB_USER=myuser
Przykład pliku docker-compose, gdzie to zadziała. Zwróć uwagę w jaki sposób określamy, że zmienna ma zostać podstawiona. Jest to składnia ${DB_PORT} oraz ${DB_USER}
version: '3.2' services: postgres: image: postgres:9.6 ports: - ${DB_PORT}:5432 environment: POSTGRES_PASSWORD: secretpassword POSTGRES_USER: ${DB_USER}
Aby upewnić się, że wartości są podstawiane prawidłowo wystarczy użyć komendy docker-compose config
$ docker-compose config services: postgres: environment: POSTGRES_PASSWORD: secretpassword POSTGRES_USER: myuser image: postgres:9.6 ports: - published: 5434 target: 5432 version: '3.2'
W miejsce ${DB_USER} została podstawiona wartość myuser, a w miejsce ${DB_PORT} wartość 5434. Wartości te pochodzą z pliku .env
. Warto podkreślić, że oba pliki muszą znajdować się w tym samym katalogu.
Gdzie takie podejście może mieć zastosowanie?
Plik docker-compose.yml
jest dodawany do kontroli wersji, natomiast plik .env
już nie. Dzięki temu, każdy z członków zespołu może mieć inną zawartość pliku .env
według własnych potrzeb, co nie będzie powodować żadnych zmian w repozytorium.
Zmienne środowiskowe na hoście nadpisują wpisy w pliku .env
Jeżeli na hoście istnieje zmienna o takiej samej nazwie jak wewnątrz pliku .env
, wartość zostanie pobrana z zmiennej środowiskowej hosta! Sprawdźmy to na przykładzie. Na hoście ustawiłem wartość zmiennej środowiskowej JAVA_HOME na C:\Program Files\Java\jdk1.8.0_221
Mamy następujące plik docker-compose.yml
, w którym odwołujemy się do ${JAVA_HOME}
version: '3.2' services: myapp: image: openjdk:7 environment: JAVA_DIRECTORY: ${JAVA_HOME}
W tym samym katalogu znajduje się również plik .env
zawierający zmienną JAVA_HOME=D:/java
JAVA_HOME=D:/java
Teraz pozostaje sprawdzić całość za pomocą polecenia docker-compose config
$ docker-compose config services: myapp: environment: JAVA_DIRECTORY: C:\Program Files\Java\jdk1.8.0_221 image: openjdk:7 version: '3.2'
Rezulat? Zamiast wartości zdefiniowanej w pliku .env
, została podstawiona wartość zmiennej środowiskowej z hosta! (C:\Program Files\Java\jdk1.8.0_221)
Zmienne w terminalu nadpisują wszystko
Nie można było o tym nie wspomnieć. Definiując zmienną środowiskową wewnątrz terminala, ma ona najwyższy priorytet.
Ustawiamy zmienną środowiskową (system Windows):
$ set JAVA_HOME=D:\shell_variable_path
Obecny stan zmiennych o nazwie JAVA_HOME:
- na hoście: C:\Program Files\Java\jdk1.8.0_221
- w pliku .env: D:\java
- w terminalu: D:\shell_variable_path
Tradycyjnie sprawdzamy rezultat poleceniem docker-compose config
$ docker-compose config services: myapp: environment: JAVA_DIRECTORY: D:\shell_variable_path image: openjdk:7 version: '3.2'
Tym sposobem mamy potwierdzenie, iż zmienne środowiskowe zdefiniowane w terminalu mają najwyższy priorytet!
UWAGA: Plik .env nie ma nic wspólnego z instrukcjami ENV wewnątrz Dockerfile oraz argumentem `-e` w terminalu! Oznacza to, że wartości zdefiniowane w pliku .env, NIE trafią bezpośrednio do kontenera, a służą tylko do podstawiania wartości w pliku docker-compose.yml
Inne sposoby na przekazywanie zmiennych środowiskowych do kontenera
Po zbudowaniu obrazu, możemy uruchamiać kontenery i przekazywać do nich zmienne środowiskowe na kilka sposobów. W każdym przypadku, spowoduje to nadpisanie domyślnych wartości zdefiniowanych w Dockerfile.
1. Zmienne środowiskowe z hosta
Jak to działa? Standardowo korzystamy z argumentu -e
, ale z małą różnicą. Podajemy tylko nazwę zmiennej, bez wartości. W ten sposób docker pobiera zmienną środowiskową zdefiniowaną w terminalu (jeżeli istnieje) lub w systemie hosta.
Obecnie w terminalu wartość zmiennej JAVA_HOME=D:\shell_variable_path
Uruchamiamy kontener w trybie interaktywnym z konsolą, wskazując -e JAVA_HOME
. Następnie wewnątrz kontenera, wyświetlamy zawartość zmiennej.
$ docker run -it -e JAVA_HOME alpine sh / # $JAVA_HOME sh: D:\shell_variable_path
Widzimy, że wewnątrz kontenera została podstawiona wartość z hosta (a dokładnie z terminala hosta).
2. Wartości z pliku (env_file)
Tworzymy plik o dowolnej nazwie, a jego zawartość powinna opierać się o wpisach typu klucz=wartość. Dla przykładu, plik ten będzie miał nazwę my_env_file
(bez żadnego rozszerzenia), a jego zawartość będzie następująca:
VARIABLE_NAME=variable_value
Uruchamiamy kontener z parametrem env (spowoduje wyświetlenie wszystkich zmiennych środowiskowych) wskazując na plik my_env_file
:
$ docker run --env-file=my_env_file alpine env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=77929624bad0 VARIABLE_NAME=variable_value HOME=/root
Możemy również wykorzystać to samo podejście w pliku docker-compose.yml
, dodając wpis env_file: <ścieżka_do_pliku>
version: '3.2' services: alpine: image: alpine env_file: my_env_file
Podsumowanie
Stosowanie zmiennych środowiskowych w Dockerze jest czymś naturalnym i często wykorzystywanym. Na przykładzie WordPressa widzimy, że nawet oficjalne obrazy, które możemy znaleźć na Docker Hubie stosują ten mechanizm. Warto go zatem znać i rozumieć jak działa.
Mam nadzieję, że nieco pomogłem zrozumieć Ci jak działają zmienne środowiskowe w Dockerze, lub bynajmniej post ten spowodował odświeżenie Twojej dotychczasowej wiedzy. Ważne, że w razie wątpliwości można tutaj wrócić i rozwiać swoje wątpliwości, do czego gorąco zachęcam!