ENV 與 ARG 是 Dockerfile 的指令,它們能定義變數並且在後面的流程中使用。
ENV 比較容易理解,它其實就是設定 environment,因此概念上,它會是一個全域變數--直到 docker run
的時候都還會存在的變數。
Docker 官方的底層 image,如 PHP 或 Java 等,會版本資訊、安裝路徑等設定成 ENV,在後續的流程可以拿來使用。
至於是應用層級的 image 如 Laravel image,environment 大多都會是執行時期才提供,而 Dockerfile 則可以設定預設值。至於要怎麼設定,則是看 image 最終是否有要拿到線上部署,如果有的話,建議預設 production 設定會比較好:
ENV APP_ENV=production
ENV APP_DEBUG=false
而連線設定則建議不要有預設值,否則部署錯環境加上設定也錯,若網路層沒有防呆的話,將會發生錯寫資料的悲劇。
ARG 是一個很像 ENV 的指令,不一樣的點主要在於,它只能活在 build image 的過程裡。可以從下面這個例子看得出來:
FROM alpine
ENV foo=1
ARG bar=2
# Build image 時 echo
RUN echo ${foo} , ${bar}
# Run image 時 echo
CMD echo ${foo} , ${bar}
build image 過程可以看到 ENV 與 ARG 有正常取值,但 docker run
的時候,則只剩下 ENV 而 ARG 不見了。這代表 ARG 只能活在 build image 階段而已。
ARG 不只是 build image 階段的變數,它可以在下指令的時候一併設值:
docker build --build-arg bar=3 .
ARG 的使用情境在,有時候需要寫很多 Dockerfile,如:
FROM php:7.3-alpine
RUN apk add --no-cache unzip
COPY --from=composer:1.10 /usr/bin/composer /usr/bin/composer
FROM php:7.4-alpine
RUN apk add --no-cache unzip
COPY --from=composer:1.10 /usr/bin/composer /usr/bin/composer
兩個 Dockerfile 差異只在於 PHP 版本,若以這個寫法來看,若未來新增 PHP 版本,就得多一個 Dockerfile。
這個情境就很適合使用 ARG 改寫:
ARG PHP_VER
FROM php:${PHP_VER}-alpine
RUN apk add --no-cache unzip
COPY --from=composer:1.10 /usr/bin/composer /usr/bin/composer
docker build --build-arg PHP_VER=7.4 .
docker build --build-arg PHP_VER=7.3 .
這裡的 ARG 寫法是沒有預設值的,這時 ${PHP_VER}
會取到的是空值。以這個例子,FROM
指令會出現 image 名稱格式錯誤訊息(php:-alpine
):
$ docker build .
Sending build context to Docker daemon 111.6kB
Step 1/4 : ARG PHP_VER
Step 2/4 : FROM php:${PHP_VER}-alpine
invalid reference format
如果在 ARG 設值的時候使用 ENV 的值,或是反過來的話,可以正常 work 的:
FROM alpine
ENV foo=is_env
ARG bar=is_arg
ARG foo_env=${foo}
ENV bar_arg=${bar}
RUN echo ${foo_env} , ${bar_arg}
CMD echo ${foo_env} , ${bar_arg}
可以看到 ARG 有正常取得 ENV,ENV 也有正常取得 ARG。
另一種情境是撞名:
FROM alpine
ENV foo=is_env
ARG foo=is_arg
ARG bar=is_arg
ENV bar=is_env
RUN echo ${foo} , ${bar}
CMD echo ${foo} , ${bar}
若取一樣的名字會發現,它最後都會以 ENV 為主。雖然不會出錯,但建議還是盡可能不要撞名比較好。
下面做一個簡單的實驗,在第一個 stage 設定好值後,在第二個 stage 使用:
FROM alpine
ENV foo=1
ARG bar=2
FROM alpine
RUN echo ${foo} , ${bar}
CMD echo ${foo} , ${bar}
這個實驗非常簡單,一做就馬上理解:每個 stage 要用的 ARG 與 ENV 都需要各自定義的,因此兩個 stage 可以設定兩個同名不同值的 ENV;若兩個 stage 都使用同名的 ARG,則兩個 ARG 在 --build-arg
給值的時候都拿得到。
FROM alpine
ARG foo
RUN echo ${foo}
FROM alpine
ARG foo
RUN echo ${foo}
今天一連串的實作,相信讀者能更了解 ENV 與 ARG 的差異,以及適用的情境。
使用 ENV 可以讓 Dockerfile 更好維護,而 ARG 則是可以讓同一份 Dockerfile 產生更多不一樣的 image。讀者可以視情況運用這兩個指令。
請教一下,
如果我需要透過 ssh git 抓取 code 下來執行
這樣的話,ssh key 要透過 --build-arg 進去、ENV 設定、還是 mount volume
哪一種比較安全? (考慮到 build image 之後會 share 的狀況)
還是只要有 multi-stage 做隔離即可?
感謝您
原則上只要 image 其他人下載下來看不到你的 key,就算安全了。
能達成這個結果的方法包括 ENV、mount volume 應該都能達成。
而 multi stage 和 --build-arg 不一定,要看怎麼用。