一、基礎概念
1、基本概念
Dockerfile 是一個文本文件,其內包含了一條條的指令,每一條指令構建一層,因此每一條指令的內容,就是描述該層應當如何構建。有了 Dockerfile,當我們需要定制額外的
需求時,只需在 Dockerfile上添加或者修改指令,重新生成image即可,省去了敲命令的麻煩。
2、文件格式
Dockerfile由一行行命令語句組成,並且支持用“#”開頭作為注釋,一般的Dockerfile分為四部分:基礎鏡像信息
,維護者信息
,鏡像操作指令
和容器啟動時執行的指令
。
二、基礎命令
1、FROM
指定base鏡像。
# 制作基准鏡像
FROM 鏡像
# 比如我們要發布一個應用到tomcat里,那么的第一步就是FROM tomcat
FROM tomcat<:tags>
第一條指令必須為FROM指令,並且,如果在同一個Dockerfile中創建多個鏡像時,可以使用多個FROM指令(每個鏡像一次)
2、LABEL&MAINTAINER
指定維護者的信息。
# MAINTAINER,一般寫個人id或組織id
# LABEL 就是注釋,方便閱讀的,純注釋說明。不會對Dockerfile造成任何影響
# 比如:
MAINTAINER zhangsan
LABEL version = "1.0.0"
LABEL description = "我們是大百度!"
# ...等等描述性信息,純注釋。
3、WORKDIR
類似於Linux中的cd命令,但是他比cd高級的地方在於,我先cd,發現沒有這個目錄,我就自動創建出來,然后在cd進去,為后續的RUN 、 CMD 、 ENTRYPOINT
指令配置工作目錄。
WORKDIR /usr/local/testdir
4、COPY
將文件從 本地 復制到鏡像。
# 示例
# 將1.txt拷貝到根目錄下。它不僅僅能拷貝單個文件,還支持Go語言風格的通配符,比如如下:
COPY 1.txt /
# 拷貝所有 abc 開頭的文件到testdir目錄下
COPY abc* /testdir/
# ? 是單個字符的占位符,比如匹配文件 abc1.log
COPY abc?.log /testdir/
5、ADD
將文件從 本地 復制到鏡像。可以是Dockerfile所在的目錄的一個相對路徑;可以是URL,也可以是tar.gz(自動解壓)
# 示例
# 將1.txt拷貝到根目錄的abc目錄下。若/abc不存在,則會自動創建
ADD 1.txt /abc
# 將test.tar.gz解壓縮然后將解壓縮的內容拷貝到/home/work/test
ADD test.tar.gz /home/work/test
docker官方建議當要從遠程復制文件時,盡量用curl/wget命令來代替ADD。因為用ADD的時候會創建更多的鏡像層。鏡像層的size也大。
6、COPY與ADD比較
1)COPY能干的事ADD都能干,甚至還有附加功能。
2) ADD可以支持拷貝的時候順帶解壓縮文件,以及添加遠程文件(不在本宿主機上的文件)類似wget。
3) 只是文件拷貝的話可以用COPY,有額外操作只能用ADD代替。
7、ENV
設置環境變量,環境變量可被后面的指令使用。例如:
# 設置環境常量,方便下文引用,比如:
ENV JAVA_HOME /usr/local/jdk1.8
# 引用上面的常量,下面的RUN指令可以先不管啥意思,目的是想說明下文可以通過${xxx}的方式引用
RUN ${JAVA_HOME}/bin/java -jar xxx.jar
8、VOLUME
創建一個可以從本地主機或其他容器掛載的掛載點,一般用來存放數據庫和需要保持的數據等。后面單獨文章講解。
VOLUME ["/data"]
三、運行指令
一共有三個:RUN、CMD、ENTRYPOINT
1、RUN
構建鏡像
時執行的命令。
1.1 執行時機
RUN指令是在構建鏡像時運行,在構建時能修改鏡像內部的文件。每條指令將在當前鏡像基礎上執行,並提交為新的鏡像。
1.2 命令格式
注
: 命令格式不光是RUN獨有,而是下面的CMD和ENTRYPOINT都通用。
SHELL命令格式
RUN yum -y install vim
EXEC命令格式
RUN ["yum","-y","install","vim"]
二者對比
SHELL:當前shell是父進程,生成一個子shell進程去執行腳本,腳本執行完后退出子shell進程,回到當前父shell進程。
EXEC:用EXEC進程替換當前進程,並且保持PID不變,執行完畢后直接退出,不會退回原來的進程。
總結
:也就是說shell會創建子進程執行,EXEC不會創建子進程。
1.3 舉例
舉個最簡單的例子,構建鏡像時輸出一句話,那么在Dockerfile里寫如下即可:
RUN ["echo", "image is building!!!"]
再比如我們要下載vim,那么在Dockerfile里寫如下即可:
RUN ["yum","-y","install","vim"]
下面會有實戰來完完整整的演示。
2、CMD
2.1 執行時機
容器啟動時執行,而不是鏡像構建時執行。
2.2 解釋說明
容器啟動時運行指定的命令。
Dockerfile 中可以有多個 CMD 指令,但只有最后一個生效。重點在於如果容器啟動的時候有其他額外的附加指令,則CMD指令不生效
。
2.3 舉例
CMD ["echo", "container starting..."]
3、ENTRYPOINT
3.1 執行時機
容器創建時執行,而不是鏡像構建時執行。
3.2 解釋說明
在容器啟動的時候執行此命令,且Dockerfile中只有最后一個ENTRYPOINT會被執行,推薦用EXEC格式。
3.3 舉例
ENTRYPOINT ["ps","-ef"]
4、RUN vs CMD vs ENTRYPOINT
簡單的說:
1、RUN 執行命令並創建新的鏡像層,RUN 經常用於安裝軟件包。
2、CMD 設置容器啟動后默認執行的命令及其參數,但 CMD 能夠被 docker run 后面跟的命令行參數替換。
3、ENTRYPOINT 配置容器啟動時運行的命令。
RUN
RUN是在構建層面的,就是每執行一個RUN,就代表多一層。所以我們經常會用於安裝軟件包,好比 RUN yum -y install vim。執行這個命令就是讓當前鏡像可以支持vim指令。
CMD
CMD 指令允許用戶指定容器的默認執行的命令。此命令會在容器啟動且 docker run 沒有指定其他命令時運行。如果 docker run 指定了其他命令,CMD 命令將被忽略。
如果 Dockerfile 中有多個 CMD 指令,只有最后一個 CMD 有效。
ENTRYPOINT
ENTRYPOINT 指令可讓容器以應用程序或者服務的形式運行。ENTRYPOINT 看上去與 CMD 很像,它們都可以指定要執行的命令及其參數。不同的地方在於 ENTRYPOINT
不會被忽略,一定會被執行,即使運行 docker run 時指定了其他命令。
5、RUN VS CMD案例
上面雖然用文字闡述了它們之間的區別,但是估計還是會有點不太明白,所以這里通過一個小小案例來理解。
創建Dockerfile,並添加如下內容
FROM centos
RUN ["echo", "image building!!!"]
CMD ["echo", "container starting..."]
構建鏡像
docker build -t runvscmd-test .

可以看出構建鏡像的過程中發現RUN的image building!!! 輸出了,所以RUN命令是在鏡像構建時執行。而並沒有container starting…的輸出。
啟動容器
docker run runvscmd-test

啟動容器的時候 container starting...,足以發現CMD命令是在容器啟動的時候執行。
總結
: 這就是上面所說的 run是 構建鏡像 時候的指令,CMD和ENTRYPOINT是啟動容器時的指令。
接下來在舉個例子來理解區分CMD和ENTRYPOINT。
6、CMD VS ENTRYPOINT案例
ENTRYPOINT和CMD可以共用,若共用則他會一起合並執行。如下Demo:
FROM centos
RUN ["echo", "image building!!!"]
ENTRYPOINT ["ps"]
CMD ["-ef"]
構建啟動容器
# 構建鏡像
docker build -t docker-test .
# 啟動容器
docker run docker-test
輸出結果
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 13:02 ? 00:00:00 ps -ef
他給我們合並執行了:ps -ef
,這么做的好處在於如果容器啟動的時候添加額外指令,CMD會失效,可以理解成我們可以動態的改變CMD內容而不需要重新構建鏡像等操作。比如
docker run docker-test -aux
輸出結果:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 2.0 0.0 46340 1692 ? Rs 13:02 0:00 ps -aux
結果直接變成了 ps -aux
,CMD命令不執行了。
總結
:從這個示例中就可以看出區別,ENTRYPOINT無論容器啟動是否帶參數,都會執行。而CMD就不一樣。上面沒帶參數那么它就會被執行。而下面帶了 -aux,
CMD指令就不會執行了。
四、項目實戰
這里就是以當前自己的vue項目來演示,正常不用docker,那么就是先打包項目,npm run bulid,再啟動項目 npm run start。
但是因為vue項目依賴Node,就好像我們java項目依賴JDk一樣。所以不同環境版本一直很重要,所以這里通過鏡像來啟動項目。
1、DockerFile文件
在vue項目當前目錄創建DockerFile文件,並寫入以下腳本。
#獲取基礎鏡像
FROM node:12.17
#指定作者
MAINTAINER xiaoxiao
#指定工作目錄
WORKDIR /app
#將當前根目錄的vue項目所有文件,都移動到/app目錄下
#COPY package*.json ./
COPY . .
##安裝項目相關依賴
RUN npm install
#將當前根目錄的vue項目所有文件,都移動到/app目錄下
#打包項目
RUN npm run build
#暴露端口
EXPOSE 8000
#啟動項目
ENTRYPOINT ["npm","run","start"]
2、構建鏡像
命令
# docker build代表構建鏡像 -t后面指定生成鏡像名稱 .代表在當前目錄構建
docker build -t xiaoxiao-web .

構建鏡像命令執行完后,我們可以看下該鏡像有沒有創建成功

可以看出鏡像已經構建成功,不過鏡像有點大。
3、啟動容器
# docker run表示啟動容器 -d 在后台運行 --name 容器的名稱 -p端口映射 xiaoxiao-web就是指定哪個鏡像
docker run -d --name=xiaoxiao-run-web -p 7000:8000 xiaoxiao-web

說明容器已經啟動成功。
4、進入容器
#09ff5738660e 為容器ID
docker exec -it 09ff5738660e /bin/bash

從這里可以看出2點 1:我一進來就是根目錄就是/app 這就是上面我們自己設置的工作目錄。2:前端項目文件已經都拷貝到當前根目錄下。
5、訪問項目

成功
參考
1、《每天5分鍾玩轉 Docker 容器技術》書籍
2、面試官:你說你精通 Docker,那你來詳細說說 Dockerfile 吧