Dockerfile 指令詳解
在上一節的 Dockerfile 例子中,我們用到了幾個指令,比如:FROM, MAINTAINER,RUN,EXPOSE等等,Dockerfile 構建鏡像需要用到的指令當然不止這些,下面是我們將介紹常用的構建指令 。
1. FROM:指明當前的鏡像基于哪個鏡像構建
用法:
FROM <基礎鏡像:版本>
示例:
FROM alpine:latest
寫上這一行指令后,我們的 Dockerfile 就可以構建鏡像了,構建出的鏡像就是未做任何修改、沒有執行任何命令的 alpine:latest
。
在 Docker 官方倉庫里有很多高質量的服務鏡像如 redis、mongo、mysql、httpd、php、tomcat 等;也有一些方便開發、構建、運行各種語言應用的鏡像,如 node、openjdk、python、ruby、golang 等。我們可以在其中尋找一個最符合我們最終目標的鏡像為基礎鏡像進行定制。如果沒有找到對應服務的鏡像,官方鏡像中還提供了一些更為基礎的操作系統鏡像,如 ubuntu、debian、centos、fedora、alpine 等,這些操作系統的軟件庫為我們提供了更廣闊的擴展空間。盡可能使用當前官方倉庫作為構建鏡像的基礎。
2. LABEL: 標記鏡像信息
給鏡像添加標簽來標記鏡像信息,每個標簽一行。
用法:
LABEL <標簽>=<描述>
示例:
LABEL MYLABEL="First Test"
3. MAINTAINER:指定鏡像的作者信息,包含鏡像的所有者和聯系人信息
用法:
MAINTAINER <NAME>
示例:
MAINTAINER [email protected]
這是一種語義化的表達方式,也可以用LABEL來標記
LABEL maintainer="[email protected]"
4. RUN : 運行命令
用法:
RUN <命令>
示例:
RUN echo 'text' > test.txt
為了保持 Dockerfile 文件的可讀性,以及可維護性,建議將長的或復雜的RUN
指令用反斜杠\
分割成多行。
例如:
RUN apt update && apt install -y \
vim \
emacs
這里需要注意一個關于軟件源更新安裝軟件的問題。
如果我們需要更新軟件源并安裝軟件源中的軟件vim,在Linux環境中我們一般會執行這個的命令:
apt update
apt install -y vim
如果需要在鏡像中安裝軟件,我們會想當然地在 Dockerfile 寫成這樣
RUN apt update
RUN apt install -y vim
Dockerfile 構建一次之后,apt update
構建的鏡像層就會緩存到本地,無論后面這個 Dockerfile 如何更新 apt install
的內容,apt update
鏡像緩存也不會更新,這會導致安裝的始終是第一次 Dockerfile構建時獲取的軟件源版本,除非你手動刪除這些緩存鏡像層。
解決的方法很簡單,用 RUN apt-get update && apt-get install -y
可以確保 Dockerfiles
每次安裝的都是包的最新的版本。
5. CMD:指定容器的默認執行的命令。
建議用法:
CMD ["可執行命令", "參數1", "參數2"...]
示例:
CMD ["echo" "hello"]
docker run 沒有指定其他命令時,CMD 指令會在容器執行。Dockerfile 中 CMD 只能有一個,如果寫了多個 CMD,則以最后一個為準。
Tips:ENTRYPOINT 與 CMD 類似,但不會被
docker run
指定的命令覆蓋。
6. EXPOSE:指定容器將要監聽的端口
用法:
EXPOSE 端口號
示例:
EXPOSE 8080
啟動容器時,如果我們使用自動映射 -P
或 --net=host
宿主機網絡模式,容器中 EXPOSE
標記暴露的端口與宿主機網絡會自動建立關聯。
如果沒有指定 EXPOSE
,使用 -p
手動指定端口映射參數也可以訪問到容器內提供服務的端口。
EXPOSE
顯式地標明鏡像開放端口,一定程度上提供了操作的便利,也提高了 Dockerfile 的可讀性和可維護性。
7. ENV:定義環境變量
用法:
ENV 環境變量名 環境變量值
示例:
ENV PATH /usr/local/nginx/bin:$PATH
Tips:
- 通過 ENV 定義的環境變量,可以被后面的所有指令中使用,但是不能被 CMD 指令使用。
- 通過 ENV 定義的環境變量,會永久的保存到該鏡像創建的任何容器中,我們可以在 docker run 命令中通過 -e 標記來傳遞環境變量,啟動的容器將會使用我們指定的變量值。
- ARG 指令與 ENV 作用基本一致,區別在于它僅在構建過程中使用,不會保留到容器中。
8. COPY: 將宿主機文件拷貝到鏡像中
用法:
COPY <宿主機文件路徑> <鏡像文件路徑>
示例:
COPY app.py /web/
除了指定完整的文件名外,COPY 命令還支持 Go 風格的通配符,比如:
# * 是任意字符的占位符,匹配文件 test11 test22
COPY test* /tmp
# ? 是單個字符的占位符,匹配文件 test1.txt test2.txt
COPY test?.txt /tmp
對于目錄而言,COPY 只復制目錄中的內容而不包含目錄自身。 如下目錄結構:
testdir/
├── file1
└── file2
COPY testdir /tmp
鏡像的 /tmp 目錄下,將得到這樣的文件結構:
tmp/
├── file1
└── file2
如果要帶目錄拷貝到鏡像中,需要使用:
COPY testdir /tmp/testdir
ADD
和COPY
用法類似,一般優先使用 COPY。COPY 只支持簡單將本地文件拷貝到容器中,而 ADD 還有從壓縮包中提取文件的功能,如:
# 宿主機壓縮包test.tar 解壓到 鏡像/tmp中
ADD test.tar /tmp
9. VOLUME:指定目錄為數據卷存儲方式
為了防止運行時用戶忘記將需要保存數據的目錄掛載為卷,我們可以事先指定某些目錄掛載為匿名卷,這樣在運行時如果用戶不指定掛載,其應用也不會向容器存儲層寫入大量數據。
用法:
VOLUME ["<路徑1>", "<路徑2>"...]
示例:
VOLUME ["/data"]
這里的 /data
目錄就會在運行時自動掛載為匿名卷,容器運行時使用 -v mydata:/data
可以覆蓋這個掛載設置。
10. USER:指定運行容器時的用戶名或 UID
用法:
USER <user>[:<group>]
或
USER <UID>[:<GID>]
示例:
USER www
當容器中運行的服務不需要管理員權限時,可以先建立一個特定的用戶和用戶組,為它分配必要的權限,然后通過該命令,使用 USER 切換到這個用戶。
Tips:
- 使用USER指定用戶時,可以使用用戶名、UID或GID,或是兩者的組合。
- 使用USER指定用戶后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT都將使用該用戶。
我們可以在docker run
中使用-u
參數指定用戶執行命令,來替代默認設定,如果為了精確控制用戶的id,也可以傳入uid。
docker run -i -t -u 1001 busybox sh
11. WORKDIR: 切換到鏡像中的指定路徑
在WORKDIR
中需要使用絕對路徑,如果鏡像中對應的路徑不存在,會自動創建此目錄。
我們使用 WORKDIR 來替代 RUN cd <path> && <do something>
的這類切換目錄進行操作的指令。
WORKDIR指令對ADD COPY等指令也生效,如下操作會將宿主機的test.txt 文件復制到 鏡像的/tmp/test.txt。
WORKDIR /tmp
COPY test.txt .
12. ONBUILD:引用后構建指令
用法:
ONBUILD <其他指令>
示例:
ONBUILD COPY . /tmp/
ONBUILD
是一個特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等。
FROM alpine:latest
ONBUILD RUN mkdir /app
ONBUILD COPY . /app/
CMD [ "echo", "hello" ]
使用上面 Dockerfile 在構建基礎鏡像的時候,這兩行 ONBUILD 并不會被執行。它的效果等價于:
FROM alpine:latest
CMD [ "echo", "hello" ]
構建出來的鏡像作為基礎鏡像,在其他 Dockerfile 的 FROM
指令中被引用,去構建新鏡像的時候,ONBUILD 后的指令會執行。
13. 小結
本節介紹了我們使用 Dockerfile 構建鏡像的過程中經常會用到的指令,這些指令大多簡單易懂的。但本節中也提到,其中有些指令的用法比較特殊,在某些看似“理所當然”的使用方法下,可能會出現"bug",請大家一定要留意。后面也有實戰章節,幫助大家加深理解。