Dockerfile是一個用來構建鏡像的文本文件,文本內容包含了一條條構建鏡像所需的指令和說明。使用docker build命令可以根據Dockerfile里面的指令編排來打包定制我們自己的docker鏡像,首先我們來看一個通用的例子,制作自己的nginx鏡像。
1.構建nginx鏡像
# Base image
FROM centos:7
# MAINTAINER
MAINTAINER cbmiao <miaocbin@126.com>
# 將nginx以及pcre源代碼加入鏡像
ADD nginx-1.20.1.tar.gz /usr/local/src/
ADD pcre-8.45.tar.gz /usr/local/src/
# 安裝編譯器
RUN yum install -y gcc gcc-c++ make openssl-devel lsof
RUN useradd -s /sbin/nologin -M nginx
# 指定工作目錄
WORKDIR /usr/local/src/nginx-1.20.1/
# 編譯nginx
RUN ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module --with-pcre=/usr/local/src/pcre-8.45 && make && make install
RUN echo "daemon off;" >> /usr/local/nginx/conf/nginx.conf
# 設置環境變量
ENV PATH /usr/local/nginx/sbin:$PATH
# 暴露80端口
EXPOSE 80
# 容器默認啟動命令
ENTRYPOINT ["nginx"]
以上是通過Dockerfile來定制我們自己的nginx的Dockerfile,包含了如下指令:

2. Dockerfile基本格式
2.1 FROM指令
FROM指令用於指定基礎鏡像,必須為Dockerfile的第一個指令
# 格式:
# FROM <image>
# FROM <image>:<tag>
# 示例:
FROM mysql:5.7
# 注意:
# tag是可選的,如果不使用tag,則默認會使用latest版本的基礎鏡像
參數解釋:
-
FROM mysql:5.7:第一行必須指定 基礎鏡像信息
2.2 MAINTAINER 指令
用於說明鏡像維護者的信息,名字,郵箱,聯系方式,實例如下
# 格式:
# MAINTAINER <name>
# 示例:
MAINTAINER Michael Miu
MAINTAINER miaocbin@126.com
MAINTAINER Michael Miu <miaocbin@126.com>
2.3 COPY|ADD指令
這兩個命令的用法類似,都可以用於添加本地文件到鏡像中(同樣需求下,官方推薦使用 COPY),功能也類似,如:
- 源路徑可以有多個,相對於執行build的路徑,如果源路徑是一個目錄,則該目錄下面的所有內容都會被加入到容器,但目錄本身不會被加入到容器;
- 目標路徑:必須是絕對路徑或者相對於WORKDIR的相對路徑;若目標路徑不存在,則會自動創建完整路徑;目標路徑如果是文件夾,則必須已/結尾;
- 路徑中可以使用通配符
二者區別如下:
- ADD 的優點:在執行 <源文件> 為 tar 壓縮文件的話,壓縮格式為 gzip, bzip2 以及 xz 的情況下,會自動復制並解壓到 <目標路徑>。
- ADD 的缺點:在不解壓的前提下,無法復制 tar 壓縮文件。會令鏡像構建緩存失效,從而可能會令鏡像構建變得比較緩慢。具體是否使用,可以根據是否需要自動解壓來決定。
- COPY:指令能夠將構建命令所在的主機本地的文件或目錄,復制到鏡像文件系統。
- ADD:指令不僅能夠將構建命令所在的主機本地的文件或目錄,而且能夠將遠程URL所對應的文件或目錄,作為資源復制到鏡像文件系統。
所以,可以認為ADD是增強版的COPY,支持將遠程URL的資源加入到鏡像的文件系統。
# 格式:
# ADD|COPY <src>... <dest>
# 示例:
ADD|COPY tes* /mydir/ # 添加所有以"tes"開頭的文件
ADD|COPY test mydir/ # 添加 "test" 到 WORKDIR/mydir/
ADD|COPY test /opt/ # 添加 "test" 到 /opt/
# [--chown=<user>:<group>]:可選參數,用戶改變復制到容器內文件的擁有者和屬組。
說明:
- 對於從遠程URL獲取資源的情況,由於ADD指令不支持認證,如果從遠程獲取資源需要認證,則只能使用RUN wget或RUN curl替代。
- 如果源路徑的資源發生變化,則該ADD指令將使Docker Cache失效,Dockerfile中后續的所有指令都不能使用緩存。因此盡量將ADD指令放在Dockerfile的后面。
- <源路徑>:源文件或者源目錄,這里可以是通配符表達式,其通配符規則要滿足 Go 的 filepath.Match 規則。
- <目標路徑>:容器內的指定路徑,該路徑不用事先建好,路徑不存在的話,會自動創建。
2.4 WORKDIR
指定工作目錄,通過WORKDIR設置工作目錄后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都會在該目錄下執行。(WORKDIR 指定的工作目錄,必須是提前創建好的)
# 格式:
# WORKDIR /path/to/workdir
# 示例:
WORKDIR /abc (這時工作目錄為/abc)
2.5 RUN
構建鏡像過程中執行命令(注意:RUN指令創建的中間鏡像會被緩存,並會在下次構建中使用。如果不想使用這些緩存鏡像,可以在構建時指定--no-cache參數,如:docker build --no-cache)
# 格式:
# RUN <command>
# 示例:
RUN yum -y install nginx
RUN pip install djaong
RUN mkdir test && rm -rf /var/lib/testfile
2.6 CMD
類似於RUN指令,用於運行程序,但二者運行的時間點不同:
- CMD:用於指定在容器啟動時所要執行的命令,在docker run 時運行。
- RUN:RUN用於指定鏡像構建時所要執行的命令,是在 docker build時運行。
作用:為啟動的容器指定默認要運行的程序,也就是在容器啟動時才進行調用。程序運行結束,容器也就結束。CMD 指令指定的程序可被 docker run 命令行參數中指定要運行的程序所覆蓋。
# 格式:
# CMD ["executable","param1","param2"] (執行可執行文件,優先)
# CMD ["param1","param2"] (設置了ENTRYPOINT,則直接調用ENTRYPOINT添加參數)
# CMD command param1 param2 (執行shell內部命令)
# 示例:
CMD ["/usr/bin/wc","--help"]
CMD ping www.baidu.com
注意:如果 Dockerfile 中如果存在多個 CMD 指令,僅最后一個生效。
2.7 ENTRYPOINT
類似於 CMD 指令,但其不會被 docker run 的命令行參數指定的指令所覆蓋,而且這些命令行參數會被當作參數送給 ENTRYPOINT 指令指定的程序。但是, 如果運行 docker run 時使用了 --entrypoint 選項,將覆蓋 CMD 指令指定的程序。
# 格式:
# ENTRYPOINT ["executable", "param1", "param2"] (可執行文件, 優先)
# ENTRYPOINT command param1 param2 (shell內部命令)
# 示例:
ENTRYPOINT ["/usr/bin/wc","--help"]
注意:ENTRYPOINT與CMD非常類似,不同的是通過docker run執行的命令不會覆蓋ENTRYPOINT,而docker run命令中指定的任何參數,都會被當做參數再次傳遞給ENTRYPOINT。
優點:在執行 docker run 的時候可以指定 ENTRYPOINT 運行所需的參數。
注意:Dockerfile中只允許有一個ENTRYPOINT命令,多指定時會覆蓋前面的設置,而只執行最后的ENTRYPOINT指令。
2.8 ENV
設置環境變量,定義了環境變量,那么在后續的指令中,就可以使用這個環境變量。
# 格式:
# ENV <key> <value>
# ENV <key>=<value>
# 示例:
ENV myName John
ENV myCat=tomcat
2.9 EXPOSE指令
聲明端口,作用如下:
- 1)幫助鏡像使用者理解這個鏡像服務的守護端口,以方便配置映射;
- 2)在運行時使用隨機端口映射時,也就是 docker run -P 時,會自動隨機映射 EXPOSE 的端口;但是,EXPOSE並不會讓容器的端口訪問到主機。要使其可訪問,需要在docker run運行容器時通過-p來發布這些端口,或通過-P參數來發布EXPOSE導出的所有端口。
- 3)docker ps的時候看到的PORTS字段信息就是這里聲明的信息;
- 4)需要說明的是,即便這里不寫EXPOSE指令,也不影響鏡像構建和運行,只是會導致其他的使用者不清楚鏡像監聽的端口,使用的時候映射端口沒有那么直觀。
# 格式:
# EXPOSE <port> [<port>...]
# 示例:
EXPOSE 80 443
EXPOSE 8080
EXPOSE 11211/tcp 11211/udp
2.10 LABEL指令
用來給鏡像添加一些元數據(metadata),以鍵值對的形式
# 語法格式:
# LABEL <key>=<value> <key>=<value> <key>=<value> ...
# 例如:添加鏡像作者
LABEL org.opencontainers.image.authors="runoob"
2.11 VOLUME指令
定義匿名數據卷。在啟動容器時忘記掛載數據卷,會自動掛載到匿名卷。作用:
- 避免重要的數據,因容器重啟而丟失,這是非常致命的。
- 避免容器不斷變大。
# 格式:
# VOLUME ["<路徑1>", "<路徑2>"...]
# VOLUME <路徑>
# 舉例:
VOLUME [/var/lib/mysql]
3. Dockerfile鏡像構建命令
利用Dockerfile構建自己的鏡像命令
docker build . -t ImageName:ImageTag -f Dockerfile
- . 是上下文路徑
-
上下文路徑,是指 docker 在構建鏡像,有時候想要使用到本機的文件(比如復制),docker build 命令得知這個路徑后,會將路徑下的所有內容打包。
解析:由於 docker 的運行模式是 C/S。我們本機是 C,docker 引擎是 S。實際的構建過程是在 docker 引擎下完成的,所以這個時候無法用到我們本機的文件。這就需要把我們本機的指定目錄下的文件一起打包提供給 docker 引擎使用。
如果未說明最后一個參數,那么默認上下文路徑就是 Dockerfile 所在的位置。
注意:上下文路徑下不要放無用的文件,因為會一起打包發送給 docker 引擎,如果文件過多會造成過程緩慢。
3.1 執行構建過程
docker build . -t my-nginx:v1.20.1 -f Dockerfile
Sending build context to Docker daemon 3.163MB
Step 1/13 : FROM centos:7
---> 8652b9f0cb4c
Step 2/13 : LABEL maintainer="cbmiao <miaocbin@126.com>"
---> Using cache
---> 16c2487146bc
Step 3/13 : ENV DEBIAN_FRONTEND noninteractive
---> Using cache
---> 5f3132177398
Step 4/13 : ADD nginx-1.20.1.tar.gz /usr/local/src/
---> Using cache
---> 116344a28ea2
Step 5/13 : ADD pcre-8.45.tar.gz /usr/local/src
---> Using cache
---> 23451b87c29b
Step 6/13 : RUN yum install -y wget gcc gcc-c++ make openssl-devel
---> Using cache
---> dcaa1cbaf4bd
Step 7/13 : RUN useradd -s /sbin/nologin -M nginx
---> Using cache
---> e0ace7f20ffc
Step 8/13 : WORKDIR /usr/local/src/nginx-1.20.1/
---> Using cache
---> d437722febe2
Step 9/13 : RUN ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module --with-pcre=/usr/local/src/pcre-8.45 && make && make install
---> Using cache
---> 307b6228d110
Step 10/13 : RUN echo "daemon off;" >> /usr/local/nginx/conf/nginx.conf
---> Using cache
---> dc8e0e800987
Step 11/13 : ENV PATH /usr/local/nginx/sbin:$PATH
---> Using cache
---> e5a1119dced2
Step 12/13 : EXPOSE 80
---> Using cache
---> 63c669be20e4
Step 13/13 : ENTRYPOINT ["nginx"]
---> Using cache
---> 391f75b825bd
Successfully built 391f75b825bd
Successfully tagged my-nginx:v1.20.1
3.2 查看構建結果
docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
my-nginx v1.20.1 391f75b825bd 17 minutes ago 490MB
3.3 運行鏡像
# 利用我們剛構建的鏡像來啟動容器
docker run -d --name nginx-test my-nginx:v1.20.1
348a73f420181c36abf7a99c958ed76862503e930793331ab819629ea2bdc4a2
# 進入容器查看nginx狀態
docker exec -ti nginx-test bash
[root@348a73f42018 nginx-1.20.1]#
# 確認80端口
[root@348a73f42018 nginx-1.20.1]# lsof -i :80
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1 root 6u IPv4 645923 0t0 TCP *:http (LISTEN)
# 確認nginx進程已經啟動成功
[root@348a73f42018 nginx-1.20.1]# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.0 46096 3280 ? Ss 05:43 0:00 nginx: master process nginx
nginx 7 0.0 0.0 46532 1880 ? S 05:43 0:00 nginx: worker process
root 8 0.2 0.0 11828 1908 pts/0 Ss 05:43 0:00 bash
root 26 0.0 0.0 51732 1712 pts/0 R+ 05:43 0:00 ps aux
# 訪問nginx也正常
[root@348a73f42018 nginx-1.20.1]# curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
若想讓我們的nginx向外提供服務,還需要在啟動容器時,將端口映射到主機,然后我們通過主機IP加上映射端口,就可以訪問了
docker run -d --name nginx-test1 -p 80:80 my-nginx:v1.20.1
以上就添加了一個[-p 80:80]參數,將容器內的nginx的80端口,映射到宿主機。然后我們就可以通過宿主機的IP加上80端口訪問剛才啟動的nginx容器里的nginx服務,有關docker啟動容器有不清楚的,可以看我的另外一篇博客:Docker簡介及基本使用
4. Dockerfile構建鏡像的原則
-
不必要的內容不要放在鏡像中:以免增大鏡像,同時也帶來安全隱患;
-
減少不必要的層文件
-
減少網絡傳輸操作:盡量使用本地文件,加快鏡像構建速度,特別是后面我們將利用Jenkins做自動化構建的時候;
-
可以適當的包含一些調試命令
5. JAR包制作成docker鏡像實例
FROM java:8u211
ENV JAVA_OPTS "\
-Xmx4096m \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=256m"
ENV JAVA_HOME /usr/local/java
ENV PATH ${PATH}:${JAVA_HOME}/bin
COPY target/myweb.jar myweb.jar
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
EXPOSE 8080
CMD java ${JAVA_OPTS} -jar myweb.jar
