Dockerfile
一. Dockerfile是什么
Dockerfile是docker中鏡像文件的的描述文件,說的直白點就是鏡像文件到底是由什么東西一步步構成的。例如我們在淘寶上買了一件商品,但是這個商品需要組裝才能使用,於是賣家就給了你一張圖紙,你就按照圖紙一步一步的組裝起來,然后就成了你所需要的樣子。那么Dockerfile就是這張圖紙,鏡像文件就是你需要的商品。Dockerfile名字可以隨便命名,但是不建議你這做,還是按照規范來使用,首字母要大寫。下面給出我們前幾個章節使用到的centos為例,來看看它的Dockerfile是怎么樣的,如下圖所示:
二. Dockerfile、鏡像、容器
Dockerfile:是鏡像的構建文件,描述的鏡像是一步步怎么來的。
鏡像:是通過Dockerfile做出來的,包含操作系統基礎文件和軟件運行環境,它使用分層的存儲方式。
容器:是運行起來的鏡像,簡單理解,Docker鏡像相當於程序,容器相當於進程。
三. Dockerfile的基本語法及執行流程
3.1 Dockerfile的基本語法
a.每個保留字必須放在每一行的開頭,可以大寫也可以小寫,但是建議大寫;
b.指令按照順序,從上往下依次執行;
c.#表示注解;
d.每條執行指令都會創建一個新的鏡像,並且執行提交。
3.2 Dockerfile的執行流程
a.docker從基礎鏡像中執行一個容器;
b.執行一條指令對鏡像進行修改;
c.執行docker commit命令,提交新的鏡像;
d.在基於剛剛提交的鏡像運行一個新的容器;
e.執行Dockerfile中的下一條指令,按照b、c、d依次循環下去,知道所有的指令執行完畢。
3.3 演示講解
FROM centos #指定要生成的鏡像的基礎鏡像,開頭第一句話必須也只能是FROM MAINTAINER zhengjingmao@163.com #指定作者是誰 RUN yum install -y vim #執行 yum install -y vim 命令,安裝vim RUN yum install -y net-tools #執行 yum install -y net-tools, 安裝net-tools工具
WORKDIR /dev/ #啟動容器后,如果啟動交互模式,直接進入到哪個目錄 CMD ["/bin/bash"] #啟動容器的時候,進入到/bin/bash這種命令行
如上代碼所示,FROM、MAINTAINER、RUN、WORKDIR、CMD均為關鍵字,按照標准的規范需要大寫;FROM centos表示我們的基礎鏡像是centos,然后會啟動centos這個容器,執行第二行代碼又會生成新的鏡像文件,然后提交,周而復始。
四. Dockerfile關鍵字
關鍵字 | 作用 |
FROM | 指定基礎鏡像 |
MAINTAINER | 作者的信息 |
RUN | 執行什么命令 |
EXPOSE | 容器對外暴露的端口 |
WORKDIR | 進入到容器后進入到哪個目錄 |
ENV | 配置環境變量 |
ADD | 將文件拷貝到鏡像中並解壓 |
COPY | 將文件拷貝到鏡像中 |
VOLUME | 配置數據卷 |
CMD | 容器啟動時候執行的命令 |
ENTRYPOINT | 容器啟動時候執行的命令 |
a.ADD指令,我們現在定義這樣一個Dockerfile,代碼如下所示:
FROM centos MAINTAINER zhengjingmao@163.com RUN mkdir /datas ADD jdk-8u60-linux-x64.tar.gz /datas/ WORKDIR /datas/ CMD ["/bin/bash"]
首先必須將jdk-8u60-linux-x64.tar.gz文件拷貝到Dockerfile同級目錄下,如下圖所示:
執行命令:docker build -t mycentos . 構建mycentos鏡像,如下圖所示:
然后啟動容器,進入到 /datas/目錄下,會發現 jdk1.8.0_60 目錄,表示add命令還執行了解壓命令,如下圖所示:
b.COPY命令,我們定義一個Dockerfile, 代碼如下:
FROM centos MAINTAINER zhengjingmao@163.com RUN mkdir /datas ADD jdk-8u60-linux-x64.tar.gz /datas/ WORKDIR /datas/ CMD ["/bin/bash"]
執行命令:docker build -t mycentos . 構建mycentos鏡像。
然后啟動容器,進入到 /datas/目錄下,會看到jdk-8u60-linux-x64.tar.gz文件,並沒有解壓,如下圖所示:
c.ENV命令,配置環境變量,我們還是用上面提到的jdk為例,做過Java開發的朋友都知道,jdk需要配置環境變量,Dockerfile的內容如下所示:
FROM centos MAINTAINER zhengjingmao@163.com RUN mkdir -p /datas/ ADD jdk-8u60-linux-x64.tar.gz /datas/ ENV JAVA_HOME=/datas/jdk1.8.0_60 #配置JAVA_HOME ENV PATH=$JAVA_HOME/bin:$PATH #配置PATH CMD ["/bin/bash"]
因為內容的重復性,在這里就不贅述了。當我們容器啟動后,我們輸入:java -version命令,就能看到我們熟悉的內容了。
d.CMD關鍵字,在鏡像構建階段不執行,在容器啟動階段執行(而我們的RUN關鍵字定義的指令在容器構建階段就會執行,請記住它與CMD的區別)。如果一個Dockerfile中有多個CMD命令,后面的會覆蓋前面的,說白了只有最后一個生效,如下代碼和注釋:
............省略............ CMD echo "<<<<<<<<<<<nice to meet you>>>>>>>>>>" CMD /bin/bash CMD echo "==========How are you?=============" #當容器啟動的時候只有該行代碼會執行,會將前兩行代碼覆蓋
CMD還有一個問題,就是當我們使用 docker run命令的時候,我們可以在整個docker命令的最后加上其他額外的命令,那么額外的命令會覆蓋Dockerfile中所有的CMD命令,例如我們執行如下命令:docker run -it centos ls / -l,最終的結果如下圖所示:
e. ENTRYPOINT指令,和CMD命令差不多,如果一個Dockerfile中有多個ENTRYPOINT,只有最后一個生效。但是他們還是有區別的,如果ENTRYPOINT后面有CMD,當以exec的方式運行的時候,CMD的值會作為參數給ENTRYPOINT,可能很多人看到這句話不大理解,那么我們來兩個例子:
例一:
FROM centos MAINTAINER zhengjingmao@163.com RUN mkdir -p /datas/ ENTRYPOINT ["echo", "hello"] CMD ["world"] #會將world作為echo hello的參數,最后的命令其實為echo hello world
例二:
FROM centos MAINTAINER zhengjingmao@163.com RUN mkdir -p /datas/ ENTRYPOINT ["echo", "hello"]
總結:當我們理解了CMD和ENTRYPOINT兩個命令的區別后,以后在使用的過程中就不會出現各種問題了。