Docker Variable control

我們在Docker Image的打包時,最簡單當然就是每個步驟都使用最新版本。例如Docker Base Image,大家可能選用latest tag,安裝linux package (Linux包),也可能就apt install / yum 安裝最新的穩定版本。但如果我們想要更好地做測試,就要使用指定版本,方便追蹤問題。而Docker在打包和運行時,都有不同的方式讓大家定義或覆寫指定參數。

Docker build arg

我們先從打包Image開始。

例如我們需要使用一個Base image為 ubuntu,版本預設為22.04,但有需要時可以經build指令覆寫,可以這樣寫

ARG ubuntu_version=22.04
FROM ubuntu:${ubuntu_version}
# default ubuntu_version=22.04
docker image build -t test2204 ./
# or overwrite by --build-arg
docker image build -t test2404 --build-arg="ubuntu_version=24.04"

雖然Dockerfile的RUN指令都是使用linux shell,但在Dockerfile中想表達條件控制(if else statment)就不太易看。在外部加入script做控制,是另一個可行的後備選擇,它更可以連image名字也進行參數化。

# in bash script, you also can
if [ $beta == true ]
then
    ubuntu_version=24.04
else
    ubuntu_version=22.04
fi

docker image build -t test:${ubuntu_version} --build-arg ubuntu_version=${ubuntu_version}

Docker Container Run and Docker Compose

一般來講,Linux Container 在執行時,就等於進入Linux Shell。也就是,我們可以使用Shell中的環境變數。

我們在打包Image前,已經可以在Dockerfile中定義自己的ENV數參(也就是環境變數)。與前面的Build Arg有所不同的是,ENV是定義在Dockerfile中,在Container運行時以環境變數的形式存在,它也可以在運行中被改變。而Arg,則只在打包Image時存在,運行期間就不存在了。(當然,你在打包時,用Arg傳入Env,以運到這個目的。)

另一個更特別的性質是,那怕ENV沒有定義在Dockerfile中,我們運行時也可以加入更多的環境變數,大家就當成是一般Linux操作,隨時在自己的shell中加入變數。

# -e, --env for inline variable
# --env-file for file
docker container run -e MYVAR1 --env MYVAR2=foo --env-file ./env.list ubuntu bash

同樣地Docker compose,也支援環境變數。筆者建議environment可以使用Array格式,日後可以更方便地直接改為env_file。

# docker-compose.yaml
services:
  ubuntu:
    image: ubuntu:22.04
    environment:
      - RACK_ENV=development
      - SHOW=true
      - USER_INPUT

上述的寫法沒有任何問題,不過如果你的docker-compose.yaml是放在git等版本控制中,你更新環境變數就有可能會影響到其他人,這時你就會想轉成env_file。

docker-compose.yaml預設就會讀當前資料夾的.env,就算不存在,也可以正常運行。(當然,大家的Image/Container應該要有預設值)

# docker-compose.yaml
services:
  ubuntu:
    image: ubuntu:22.04
    # if env_file is not defined, it will load .env.
    # or you can load the specific file.
    # env_file:
    #   - ./a.env 

env_file內,每一行就是一個變數

# .env or a.env
RACK_ENV=development
SHOW=true
USER_INPUT

使用預設的.env還有一個好處,就是我們可以把docker-compose.yaml也變成受環境變數控制。

# docker-compose.yaml with variable control, only works in default .env
services:
  ubuntu:
    image: ubuntu:${ubuntu_version}
# .env
ubuntu_version=22.04