簡體   English   中英

如何使用 Dockerfile 在容器中傳遞環境變量,但使用單獨的環境變量文件(如 .env)

[英]How pass environment variables in the container using Dockerfile but with a separate file for environment variables like .env

所以我現在被這個問題困擾了很長一段時間。

我知道我們應該這樣編寫代碼,以便在任何時候我們都可以在不影響機密方面的情況下使項目開源。

所以,我似乎明白直接在 Dockerfile 或 docker-compose.yml 中定義環境變量是不可取的,我們應該在一個單獨的文件中定義它們,例如.env

現在,我知道在處理 docker-compose 時,我們可以使用docker-compose -f docker-compose.yml --env-file=.env up

我想知道以下兩件事:

  1. 如何處理 Dockerfile 從構建到運行(環境變量文件部分,如--env-file
  2. 我們應該在其中構建已定義環境變量的映像,然后與其他人共享該映像,以便他們不必在運行映像時重新定義環境變量而煩惱,還是應該構建一個通用映像,然后讓用戶可以隨意定義環境變量。 什么被認為是最佳實踐?

有人也可以評論以下內容(使用Dockerfile)

方法一:

example_Dockerfile

FROM python:latest

# many other steps in between

RUN printf 'key=21345' > config

ENTRYPOINT ["./boot.sh"]

構建和運行步驟:

  1. docker build -f example_Dockerfile -t my_container.
  2. docker run --env-file=example.env my_container

我知道方法 1 是正確的,但我想知道是否可以完成方法 2(以下)之類的操作。

方法二:

example_Dockerfile

FROM python:latest

# many other steps in between 

RUN printf 'key=${KEY}' > config

ENTRYPOINT ["./boot.sh"]

例子.env

# many other environment variables

KEY = 21345

如果您還可以提供命令步驟來以這種方法構建和運行容器來執行此操作,那就太棒了。

這兩種方法的主要區別在於,在方法 2 中,我在.env文件中定義了KEY變量,並希望以某種方式將其集成到Dockerfile中(通過使用${KEY}就像我們在docker-compose.yml中所做的那樣)沒有提到Dockerfile中的值

如果有人可以通過為上述兩點舉一個例子來解釋,我將不勝感激。

非常感謝先進。

這個用例有兩個方面:

  • 如果僅構建需要環境變量,則最好使用 docker 構建參數。 Build args 與 env vars 的工作方式相同,但僅在docker build期間,它們未打包在最終映像中。 有關更多信息,請查看此鏈接
    • docker build --build-arg VAR_A=val_a -f Dockerfile.
    • Dockerfile
       FROM python:3.7-slim ARG VAR_A RUN echo $VAR_A
  • 在某些情況下,環境變量是通用的並且運行時需要,您可以使用 Dockerfile 中的ENV指令安全地 package 它們

[Bonus Tip] - 最好避免在 Dockerfile 中使用帶有FROM指令的latest圖像標簽,因為這使得很難跟蹤哪個版本的圖像正在運行並且難以回滾。

我的一般經驗是,最好將環境變量用於可以(或應該或必須)在運行時更改的設置,但這比命令行 arguments 或配置文件等其他機制更可取。 這包括您所依賴的其他服務的主機名之類的內容。 典型的最小docker-compose.yml設置可能如下所示:

version: '3.8'
services:
  app:
    build: .
    ports: ['8080:8080']
    environment:
      - REDIS_HOST=redis
      - PUBLIC_URL=http://example.com/
  redis:
    image: redis:6.0

我傾向於最小化 Dockerfile 中的環境變量。 您在問題中暗示了一些保密問題; 任何擁有圖像的人都可以docker inspect它或docker run --rm imagename env並將它們轉儲出來,無論它們是如何添加的; 構建時.env文件無濟於事。 在其他 SO 問題中,我看到用於文件系統路徑或端口的變量,但這些不需要在運行時更改。

# Avoid setting filesystem paths or ports as environment variables:
ENV APP_DIR=/app
COPY . $APP_DIR
RUN chmod +x $APP_DIR/entrypoint.sh
ENV PORT=8080
EXPOSE $PORT
# It's fine to hard-code these values, and use relative paths:
WORKDIR /app
COPY . .
RUN chmod +x entrypoint.sh
EXPOSE 8080
# No matter what, the container will always be run as
docker run -v $PWD/data:/app/data -p 8080:8080 imagename
# and changing the build-time environment variables has no effect

風格方面,讓 Dockerfile 盡可能簡單(但不會更簡單)會更好一些。 如果你有一個帶有固定鍵和固定值的配置文件,不要嘗試在 Dockerfile 中生成它; 直接在磁盤上維護它,如果對您有意義,可能使用不同的名稱。

# Don't RUN echo ... > file; instead just
COPY config.docker.yaml ./config.yaml

這樣做的另一面是不需要設置環境變量,所以你需要容忍一些未設置的東西。 我發現有用的模式是猜測如果程序直接運行,它是由不需要手動配置的開發人員,如果它在容器中運行,像 Compose 或 Kubernetes 這樣的自動化將提供環境變量給你。

# Check that $PUBLIC_URL is set
public_url = os.environ.get('PUBLIC_URL')
if public_url is None:
    print('Please set $PUBLIC_URL environment variable')
    os.exit(1)

# Get the Redis host, or assume a developer environment
redis_host = os.environ.get('REDIS_HOST', 'localhost')

我前面說過,我更喜歡直接使用環境變量而不是配置文件。 您的示例 1 和 2 的輸出是相同的,因為配置文件在構建時是固定的。 如果您需要基於環境變量重新創建配置文件,則必須在容器啟動時在入口點腳本中執行此操作。 一個典型的例子可能是使用 GNU envsubst工具(它不會預裝在基於 Alpine 的圖像上)來重寫它:

#!/bin/sh
# Rewrite the config file
envsubst < config.tmpl > config
# Switch over to the main container process
exec "$@"
# ENTRYPOINT _must_ be JSON-array syntax for this
ENTRYPOINT ["./entrypoint.sh"]
CMD ["./boot.sh"]

envsubst替換文件中的 shell 樣式$ENVIRONMENT_VARIABLE引用,因此此處的config.tmpl文件可能如下所示

key=$KEY

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM