如何編寫 Dockerfile 文件建立 Docker 鏡像

1、前言

承接上篇文章 docker 鏡像與容器,本篇來說講如何建立 Dockerfile 來構建一個鏡像。上篇文章有講到構建一個自定義鏡像是手動去構建的,雖然步驟清晰,可是操做比較繁瑣,鏡像分發起來也不是很方便,因此有必要用一種更好的辦法去替換這種模式去建立自定義鏡像,因而 Dockerfile 就是最優替代方案。廢話少說,如今就來看看如何編寫一個 Dockerfile 文件並建立容器鏡像的,先說明一個本篇文章的運行環境吧,有看過上篇文章的朋友應該知道,我用的 docker 的鏡像加速地址是阿里雲的,我以爲這是我用 docker 最無痛的環境了。html

2、Dockerfile 示例

# Base images 基礎鏡像
FROM centos

#MAINTAINER 維護者信息
MAINTAINER lorenwe 

#ENV 設置環境變量
ENV PATH /usr/local/nginx/sbin:$PATH

#ADD 文件放在當前目錄下,拷過去會自動解壓
ADD nginx-1.13.7.tar.gz /tmp/ 
#RUN 執行如下命令
RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 \ && yum update -y \ && yum install -y vim less wget curl gcc automake autoconf libtool make gcc-c++ zlib zlib-devel openssl openssl-devel perl perl-devel pcre pcre-devel libxslt libxslt-devel \ && yum clean all \ && rm -rf /usr/local/src/* RUN useradd -s /sbin/nologin -M www 
#WORKDIR 至關於cd
WORKDIR /tmp/nginx-1.13.7 
RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-pcre && make && make install 
RUN cd / && rm -rf /tmp/ 
COPY nginx.conf /usr/local/nginx/conf/ 
#EXPOSE 映射端口
EXPOSE 80 443

#ENTRYPOINT 運行如下命令
ENTRYPOINT ["nginx"] 
#CMD 運行如下命令
CMD ["-h"]複製代碼

以上代碼示例是我編寫的一個認爲頗有表明性的 dockerfile 文件,涉及到的內容很少,但基本上把全部 dockerfile 指令都用上了,也包含一些細節方面的東西,爲了達到示例的效果因此並非最簡潔的 dockerfile,創建一個文件夾將以上 dockerfile 放在該文件內,再去 nginx 官網把 nginx 源碼包下來放到該文件夾內,以後再在該文件夾內打開命令行窗口,最好是以管理員權限打開命令行窗口,以避免出現一些權限問題的錯誤,此時的目錄結構應該是如下樣子的linux

dockerfile catalog
dockerfile catalog

3、指令分析

FROM 表示的是這個 dockerfile 構建鏡像的基礎鏡像是什麼,有點像代碼裏面類的繼承那樣的關係,基礎鏡像所擁有的功能在新構建出來的鏡像中也是存在的,通常用做於基礎鏡像都是最乾淨的沒有通過任何三方修改過的,好比我用的就是最基礎的 centos,這裏有必要說明一下,由於我用的鏡像加速源是阿里雲的,因此我 pull 下來的 centos 是自帶阿里雲的 yum 源的鏡像,若是你用的不是阿里雲的鏡像加速源,pull 下來的鏡像 yum 源也不同,到時 yum 安裝軟件的時候可能會遇到不少問題(你懂得)。nginx

MAINTAINER 就是維護者信息了,填本身名字就可了,不用說什麼了c++

ENV 設置環境變量,簡單點說就是設置這個可以幫助系統找到所須要運行的軟件,好比我上面寫的是 「ENV PATH /usr/local/nginx/sbin:$PATH」,這句話的意思就是告訴系統若是運行一個沒有指定路徑的程序時能夠從 /usr/local/nginx/sbin 這個路徑裏面找,只有設置了這個,後面才能夠直接使用 ngixn 命令來啓動 nginx,否則的話系統會提示找不到應用。git

ADD 顧名思義,就是添加文件的功能了,可是他比普通的添加作的事情多一點,源文件能夠是一個文件,或者是一個 URL 都行,若是源文件是一個壓縮包,在構建鏡像的時候會自動的把壓縮包解壓開來,示例我寫的是 ‘ADD nginx-1.13.7.tar.gz /tmp/’ 其中 nginx-1.13.7.tar.gz 這個壓縮包是必需要在 dockefile 文件目錄內的,不在 dockerfile 文件目錄內的 好比你寫完整路徑 D:test/nginx-1.13.7.tar.gz 都是會提示找不到文件的。docker

RUN 就是執行命令的意思了,RUN 能夠執行多條命令, 用 && 隔開就行,若是命令太長要換行的話在末尾加上 ‘\’ 就能夠換行命令,RUN 的含義很是簡單,就是執行命令,但其中有一些細節仍是須要注意的,如今就經過上面的示例來看看須要注意的地方有哪些吧。其中 RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 的做用就是導入軟件包簽名來驗證軟件包是否被修改過了,爲作到安全除了系統要官方的以外軟件也要保證是可信的。yum update -y 升級全部包,改變軟件設置和系統設置,系統版本內核都升級,咱們知道 linux 的軟件存在依賴關係,有時咱們安裝新的軟件他所依賴的工具軟件也須要是最新的,若是沒有用這個命令去更新原來的軟件包,就很容易形成咱們新安裝上的軟件出問題,報錯提示不明顯的狀況下咱們更是難找到問題了,爲避免此類狀況發生咱們仍是先更新一下軟件包和系統,雖然這會使 docker 構建鏡像時變慢但也是值得的,至於後面的命令天然是安裝各類工具庫了,接着來看這句 yum clean all ,把全部的 yum 緩存清掉,這能夠減小構建出來的鏡像大小,rm -rf /usr/local/src/ 清除用戶源碼文件,都是起到減小構建鏡像大小的做用。RUN 指令是能夠分步寫的,好比上面的 RUN 能夠拆成如下這樣:shell

# 不推薦
RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 \ RUN yum update -y \ RUN yum install -y vim less wget curl gcc automake autoconf libtool make gcc-c++ zlib zlib-devel openssl openssl-devel perl perl-devel pcre pcre-devel libxslt libxslt-devel \ RUN yum clean all \ RUN rm -rf /usr/local/src/*複製代碼

這樣也是能夠的,可是最好不要這樣,由於 dockerfile 構建鏡像時每執行一個關鍵指令都會去建立一個鏡像版本,這有點像 git 的版本管理,好比執行完第一個 RUN 命令後在執行第二個 RUN 命令時是會在一個新的鏡像版本中執行,這會致使 yum clean all 這個命令失效,沒有起到精簡鏡像的做用,雖然不推薦多寫幾個 RUN,但也不是說把全部的操做都放在一個 RUN 裏面,這裏有個原則就是把全部相關的操做都放在同一個 RUN 裏面,就好比我把 yum 更新,安裝工具庫,清除緩存放在一個 RUN 裏面,後面的編譯安裝 nginx 放在另一個 RUN 裏面。vim

WORKDIR 表示鏡像活動目錄變換到指定目錄,就至關於 linux 裏面 cd 到指定目錄同樣,其實徹底沒有必要使用這個指令的,在須要時能夠直接使用 cd 命令就行,由於這裏使用了 WORKDIR,因此後面的 RUN 編譯安裝 nginx 不用切換目錄,講到這裏又想起了另一個問題,以下:centos

RUN cd /tmp/nginx-1.13.7 
RUN ./configure複製代碼

這樣可不能夠呢,我想前面看懂的朋友應該知道答案了吧,這裏仍是再囉嗦一下,這樣是會報找不到 configure 文件錯誤的,緣由很簡單,由於這個兩個命令都不是在同一個鏡像中執行的,第一個鏡像 cd 進入的目錄並不表明後面的鏡像也進入了。瀏覽器

COPY 這個指令很簡單,就是把文件拷貝到鏡像中的某個目錄,注意源文件也是須要在 dockerfile 所在目錄的,示例的意思是拷貝一份 nginx 配置文件,如今就在 dockerfile 所在目錄建立這個文件

user  www;
worker_processes  2;
daemon off;

pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}複製代碼

配置很簡單,就是對官方的配置文件把註釋去掉了,注意裏面的 daemon off; 配置,意思是關閉 nginx 後臺運行,緣由在上一篇文章中講過,這裏再來絮叨一下,容器默認會把容器內部第一個進程是否活動做爲docker容器是否正在運行的依據,若是 docker 容器運行完就退出了,那麼docker容器便會直接退出,docker run 的時候把 command 做爲容器內部命令,若是使用 nginx,那麼 nginx 程序將後臺運行,這個時候 nginx 並非第一個執行的程序,而是執行的 bash,這個 bash 執行了 nginx 指令後就掛了,因此容器也就退出了,若是咱們設置了 daemon off 後
啓動 nginx 那麼 nginx 就會一直佔用命令窗口,天然 bash 無法退出了因此容器一直保持活動狀態。

EXPOSE 示例註釋寫的是映射端口,但我以爲用暴露端口來形容更合適,由於在使用 dockerfile 建立容器的時候不會映射任何端口,映射端口是在用 docker run 的時候來指定映射的端口,好比我把容器的 80 端口映射到本機的 8080 端口,要映射成功就要先把端口暴露出來,有點相似於防火牆的功能,把部分端口打開。

ENTRYPOINT 和 CMD 要放在一塊兒來講,這二者的功能都相似,但又有相對獨特的地方,他們的做用都是讓鏡像在建立容器時運行裏面的命令。固然前提是這個鏡像是使用這個 dockerfile 構建的,也就是說在執行 docker run 時 ENTRYPOINT 和 CMD 裏面的命令是會執行的,二者是能夠單獨使用,並不必定要同時存在,固然這二者仍是有區別的。

先從 CMD 說吧,CMD 的一個特色就是可被覆蓋,好比把以前的 dockerfile 的 ENTRYPOINT 這一行刪除,留下 CMD 填寫["nginx"],構建好鏡像後直接使用 docker run lorenwe/centos_nginx 命令執行的話經過 docker ps 能夠看到容器正常運行了,啓動命令也是 「ngixn」,可是咱們使用 docker run lorenwe/centos_nginx bin/bash 來啓動的話經過 docker ps 查看到啓動命令變成了 bin/bash,這就說明了 dockerfile 的 CMD 指令是可被覆蓋的,也能夠把他看作是容器啓動的一個默認命令,能夠手動修改的。

而 ENTRYPOINT 偏偏相反,他是不能被覆蓋,也就是說指定了值後再啓動容器時無論你後面寫的什麼 ENTRYPOINT 裏面的命令必定會執行,一般 ENTRYPOINT 用法是爲某個鏡像指定必須運行的應用,例如我這裏構建的是一個 centos_nginx 鏡像,也就是說這個鏡像只運行 ngixn,那麼我就能夠在 ENTRYPOINT 寫上["nginx"],有些人在構建本身的基礎鏡像時(基礎鏡像只安裝了一些必要的庫)就只有 CMD 並寫上 ['bin/bash'],當 ENTRYPOINT 和 CMD 都存在時 CMD 中的命令會以 ENTRYPOINT 中命令的參數形式來啓動容器,例如上面的示例 dockerfile,在啓動容器時會以命令爲 nginx -h 來啓動容器,遺憾的是這樣不能保持容器運行,因此能夠這樣啓動 docker run -it lorenwe/centos_nginx -c /usr/local/nginx/conf/nginx.conf,那麼容器啓動時運行的命令就是 nginx -c /usr/local/nginx/conf/nginx.conf,是否是頗有意思,能夠自定義啓動參數了。

固然還有一些沒有用到的指令:

ARG,ARG指令用以定義構建時須要的參數,好比能夠在 dockerfile中寫上這句 ARG a_nother_name=a_default_value,ARG指令定義的參數,在docker build命令中以 --build -arg a_name=a_value 形式賦值,這個用的通常比較少。

VOLUME,VOLUME指令建立一個能夠從本地主機或其餘容器掛載的掛載點,用法是比較多的,都知道 docker 作應用容器比較方便,其實 docker 也可作數據容器,建立數據容器鏡像的 dockerfile 就主要是用 VOLUME 指令,要講明 VOLUME 用法有必要在開一篇文章,再此就不作介紹了,

USER,USER用來切換運行屬主身份的。docker 默認是使用 root 用戶,但若不須要,建議切換使用者身分,畢竟 root 權限太大了,使用上有安全的風險。LABEL,定義一個 image 標籤。

4、構建演示

dockerfile 構建鏡像的命令很簡單,在個人示例中個人命令是 "docker build -t lorenwe/centos_nginx . ",注意後面的點不能省略,表示的從當前目錄中尋找 dockerfile 來構建鏡像

D:\docker\lorenwe>docker build -t lorenwe/centos_nginx .
Sending build context to Docker daemon  995.8kB
Step 1/13 : FROM centos
 ---> d123f4e55e12
Step 2/13 : MAINTAINER lorenwe
 ---> Running in e5c7274f50e8
 ---> 606f7222e69a
Removing intermediate container e5c7274f50e8
Step 3/13 : ENV PATH /usr/local/nginx/sbin:$PATH
 ---> Running in 23716b428809
 ---> 5d8ee1b5a899
         ....
Successfully built eaee6b40b151
Successfully tagged lorenwe/centos_nginx:latest複製代碼

看到以上內容就說明成功,構建過程可能須要一點點時間,畢竟要安裝一些軟件,若是你跟我同樣是配置的阿里雲的容器源構建時應該不會出現什麼問題,由於我以前是有拉取過 centos ,因此在 build 時直接使用本地的 centos,若是你沒有拉取過 centos,那麼在 build 時還會把 centos 拉取下來

D:\docker\lorenwe>docker images
REPOSITORY               TAG     IMAGE ID       CREATED          SIZE
lorenwe/centos_nginx     latest  eaee6b40b151   7 minutes ago    427MB
lorenwe/centos_net_tools latest  35f8073cede1   6 days ago       277MB
centos                   latest  d123f4e55e12   3 weeks ago      197MB
d4w/nsenter              latest  9e4f13a0901e   14 months ago    83.8kB

D:\docker\lorenwe>docker run -itd --name nginx1 lorenwe/centos_nginx
15d4f108dab7c2f276209ebeb501cac0d3be828e1e81bae22d3fd97c617439eb

D:\docker\lorenwe>docker ps
CONTAINER ID    IMAGE    COMMAND     CREATED    STATUS     PORTS     NAMES

D:\docker\lorenwe>docker ps -a
CONTAINER ID   IMAGE                 COMMAND    CREATED   STATUS   PORTS   NAMES
15d4f108dab7   lorenwe/centos_nginx  "nginx -h"                            nginx1

D:\docker\lorenwe>docker run -itd --name nginx2 lorenwe/centos_nginx -c /usr/local/nginx/conf/nginx.conf
b6b0e962ca3056d67c24145b08975ffddb9cc050fce5f09f65310fb323ffc1c3

D:\docker\lorenwe>docker ps
CONTAINER ID   IMAGE                 COMMAND        CREATED    STATUS    PORTS     NAMES
b6b0e962ca30   lorenwe/centos_nginx  "nginx -c /usr/loc..."              80/tcp    nginx2

D:\docker\lorenwe>docker run -itd -p 8080:80 --name nginx3 lorenwe/centos_nginx -c /usr/local/nginx/conf/nginx.conf
2f6997745641e3e3edbbfe5213e6235cab3b5a929f116a2c132df504156090c6

D:\docker\lorenwe>docker ps
CONTAINER ID   IMAGE                 COMMAND    CREATED   STATUS     PORTS                  NAMES
2f6997745641   lorenwe/centos_nginx  "nginx -c /usr/loc..."          0.0.0.0:8080->80/tcp   nginx3
b6b0e962ca30   lorenwe/centos_nginx  "nginx -c /usr/loc..."          80/tcp                 nginx2

D:\docker\lorenwe>docker stop nginx2
nginx2複製代碼

其中 「docker run -itd -p 8080:80 --name nginx3 lorenwe/centos_nginx -c /usr/local/nginx/conf/nginx.conf」 中的 -p 8080:80 表示把主機的 8080 端口映射到容器的 80 端口,由於以前咱們在 dockerfile 中把 80 端口暴露出來了,作好端口映射後如今就能夠在主機中打開瀏覽器訪問 127.0.0.1:8080 就能看到 nginx 的歡迎頁面了 (^v^).

D:\docker\lorenwe>docker run -itd -v D:/docker/lorenwe/html:/usr/local/nginx/html  -p 8081:80 --name nginx4 lorenwe/centos_nginx -c /usr/local/nginx/conf/nginx.conf
cd2d4eb70a39057aed3bfcb64e1f03433e2054d7ff5d50098f49d2e6f2d9e02e複製代碼

我再在原來的參數中加入了 -v 參數,其做用就是把一個本地主機的目錄掛載到容器內部,這個目錄是一個共享的狀態,兩邊均可以進行修改,這就是容器的共享卷,其做用就不言而喻了,如今咱們在 D:\docker\lorenwe 的目錄下新建一個叫 html 的文件夾,再在 html 文件夾內新建一個 index.html 隨便寫上一點內容後再去主機瀏覽器上訪問一下 127.0.0.1:8081 看看是否是你想要看到內容。雖然經過 -v 參數能夠知足大部分應用場景,可是 docker 的 VOLUME 還有其餘更好用法,欲知後事如何,請看下回分解!

相關文章
相關標籤/搜索