咱們都知道,在Linux系統下能夠經過shell腳原本自動安裝部署應用,這樣不但免去了手動操做的麻煩,並且還能夠經過一些自動化工具來實現批量安裝部署。那麼docker是否也能夠經過腳本的方式定製鏡像呢?固然能夠,那就是Dockerfile,咱們能夠把建立應用鏡像的操做都寫入一個Dockerfile文件裏,而後經過docker build命令來構建自已的鏡像,這個過程相似shell腳本的功能。
docker環境的安裝部署能夠參考個人另外一篇博文《Centos 7部署docker環境、基本命令使用及簡單實戰》,http://www.javashuo.com/article/p-hqpgquik-bu.html 本文的全部操做都是基於這個環境執行的。
在使用dockerfile以前,咱們先來看一個簡單的例子:
實例1:查看本機公網IP地址html
docker pull ubuntu mkdir -p dockerfile/myip
建立Dockerfile文件vim dockerfile/myip/Dockerfile
nginx
FROM ubuntu:latest #基於ubuntu:latest鏡像來構建新的鏡像 MAINTAINER xuad #描述鏡像的做者信息 ARG APT=apt-get #臨時變量,只有在執行docker build命令構建容器時有效 RUN $APT update \ #構建容器時執行的命令 && $APT install -y curl \ && rm -rf /var/lib/apt/lists/* CMD [ "curl", "-s", "http://ip.cn" ] #運行容器時執行的命令
構建鏡像c++
cd dockerfile/myip/ docker build -t myip .
運行這個鏡像docker run myip
redis
經過以上實例咱們能夠了解到使用Dockerfile來構建鏡像的整個流程: 一、編寫dockerfile文件; 二、經過docker build來建立新的鏡像; 三、經過docker run來建立並運行新的容器。
Usage: docker build [OPTIONS] PATH | URL | - OPTIONS: -t, --tag list #指定構建的鏡像名稱和標記名稱 -f, --file string #指定Dockerfiile文件路徑
示例:
一、docker build . #不指定鏡像名稱的話,將會默認以<none>做爲鏡像名稱和標記名稱
二、docker build -t myip:v1 . #鏡像名稱爲nginx,標記名稱爲v1
三、docker build -t myip:v1 -f /path/Dockerfile /path #指定dockerfile文件路徑
注:在構建鏡像時,Docker daemon會首先將Dockerfile所在的目錄構建成一個context(上下文),而後經過Dockerfile裏的COPY或者ADD語句將context裏的文件複製到指定的鏡像目錄裏。因此須要複製到鏡像裏的文件,不管是腳本、安裝包仍是配置文件,都須要跟Dockerfile文件放在同一個目錄下。
當咱們執行docker build命令後,返回的第一條信息即是在構建上下文。
Sending build context to Docker daemon 2.048kBdocker
一、FROM指令
語法格式:shell
FROM <image> #<tag>是可選項,沒有指定<tag>的話,表示使用latest FROM <image>:<tag>
說明:基於哪一個鏡像來構建新的鏡像,FROM指令必須是dockerfile文件的第一行。
例如:FROM ubuntu:latest
二、MAINTAINER指令
語法格式:MAINTAINER [做者信息]
說明:這個指令用於聲明做者,用於將image的製做者相關的信息寫入到image中。
例如:MAINTAINER xuad或者MAINTAINER xuad.com
三、ENV指令
語法格式:npm
ENV <key> <value> #設置一個環境變量 ENV <key1>=<value1> <key2>=<value2>... #設置多個環境變量
說明:定義環境變量,永久變量,容器運行後仍然有效,即容器內的永久變量。
例如:ENV pashname /usr/local/nginx或者ENV pashname=/usr/local/nginx VERSION=1.0
四、ARG指令
語法格式:json
ARG <參數名> #不設置默認值的話,須要使用--build-arg來設置參數的值 ARG <參數名>[=<默認值>] #設置參數的默認值
說明:定義參數,即臨時變量,只限於構建鏡像時使用,容器運行後是不會存在的。
例如:ARG APT=apt-get
注:在構建命令docker build中用--build-arg <參數名>=<值> 能夠覆蓋此參數的值。
例如:docker build --build-arg APT=yum -t myip:v1 .
五、RUN指令
語法格式:ubuntu
RUN <command> RUN ["executable", "param1", "param2" ...]
說明:構建鏡像時運行的shell命令
例如:RUN yum install httpd或者RUN ["yum", "install", "httpd"]
Dockerfile中每個指令都會創建一層,RUN也不例外。多少個RUN就構建了多少層鏡像,多個RUN會產生很是臃腫、多層的鏡像,不只僅增長了構建部署的時間,還容易出錯。咱們在寫Dockerfile的時候,要儘可能避免使用多個RUN,儘可能將須要執行的命令都寫在一個RUN裏。多條命令可以使用\來換行,換行的命令前面加上&&,最後還須要將不須要的文件及目錄刪除,好比安裝包、臨時目錄等,減小容器的大小。
注:Union FS是有最大層數限制的,好比AUFS,曾經是最大不得超過42層,如今是不得超過127層。
如下是安裝redis的一個例子:vim
FROM debian:jessie RUN buildDeps='gcc libc6-dev make' \ && apt-get update \ && apt-get install -y $buildDeps \ && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \ && mkdir -p /usr/src/redis \ && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \ && make -C /usr/src/redis \ && make -C /usr/src/redis install \ && rm -rf /var/lib/apt/lists/* \ && rm redis.tar.gz \ && rm -r /usr/src/redis \ && apt-get purge -y --auto-remove $buildDeps
經過上面的命令咱們能夠看到後面添加的清理命令,刪除了下載的安裝包及解壓出來的文件,刪除了apt緩存文件,還卸載了編譯安裝所須要用到的軟件。鏡像是多層存儲,每一層的東西都不會在下一層被刪除,會一直跟隨着鏡像。所以在構建鏡像時,必定要確保每一層只添加真正須要添加的東西,任何無關的東西都應該清理掉。
六、WORKDIR指令
語法格式:
WORKDIR <工做目錄路徑>
說明:指定工做目錄,之後各層指令的當前目錄就是此目錄。
例如:WORKDIR /usr/local/nginx
注:若是目錄不存在,WORKDIR會自動建立這個目錄。
七、COPY指令
語法格式:
COPY <源路徑>... <目標路徑> COPY ["<源路徑1>",... "<目標路徑>"]
說明:將上下文目錄中的文件或目錄複製到鏡像內指定的目錄下。
例如:COPY nginx.conf /usr/local/nginx/conf/或者COPY ["nginx.conf","/usr/local/nginx/conf/"]
注:源路徑能夠是多個,甚至可使用通配符;目標路徑能夠是容器內的絕對路徑,也能夠是相對於WORKDIR指定的工做目錄的相對路徑。
八、ADD指令
語法格式:
ADD <源路徑>... <目標路徑> ADD ["<源路徑1>",... "<目標路徑>"]
說明:更高級的複製指令,與COPY不一樣的是,針對壓縮包會自動解壓處理。
例如:html.tar.gz /var/www/html或者ADD https://xuad.com/html.tar.gz /var/www/html
注:源路徑是一個壓縮文件的話,將會解壓到容器的目標路徑下;源路徑是一個URL的話,會下載或者解壓到容器的目標路徑下。
九、VOLUME指令
語法格式:
VOLUME <路徑> VOLUME ["<路徑1>", "<路徑2>"...]
說明:將本地卷掛載到容器中
例如:VOLUME /data
注:容器使用的是AUFS,這種文件系統不能持久化數據,當容器關閉後,全部的更改都會丟失。當數據須要持久化時使用VOLUME指令,向掛載目錄裏寫入的任何信息都不會寫進容器存儲層,從而保證了容器存儲層的無變化。
十、EXPOSE指令
語法格式:
EXPOSE <端口1> [<端口2>...]
說明:聲明容器運行時提供的服務端口
例如:EXPOSE 80 443
注:docker run命令可以使用-p <宿主端口>:<容器端口>將聲明的端口映射到本地指定的端口。
十一、USER指令
語法格式:
USER <用戶名> USER <UID>
說明:指定運行的用戶,之後各層的RUN、CMD或者ENTRYPOINT等指令都會以這個用戶身份執行。
十二、CMD指令
語法格式:
CMD <命令> CMD ["可執行文件", "參數1", "參數2"...]
說明:容器運行時執行的shell命令,CMD指令通常應爲Dockerfile文件的最後一行。
例如:CMD echo $HOME或者CMD [ "sh", "-c", "echo $HOME" ]
舉個例子,啓動nginx服務不能使用CMD systemctl start nginx,而應該使用CMD ["nginx", "-g", "daemon off;"]。由於docker會將CMD systemctl start nginx命令理解爲CMD ["sh", "-c", "systemctl start nginx"],主進程其實是sh,當systemctl start nginx命令執行完後,sh做爲主進程退出了,天然容器也會退出。這就是爲何使用CMD systemctl start nginx指令,容器運行不起來的緣由。正確的做法是將nginx之前臺形式運行,即CMD ["nginx", "-g", "daemon off;"]。
1三、ENTRYPOINT指令
語法格式:
ENTRYPOINT <命令> ENTRYPOINT ["可執行文件", "參數1", "參數2"...]
說明:容器運行時執行的shell命令,通常應爲Dockerfile文件的最後一行。
例如:ENTRYPOINT ["nginx", "-g", "daemon off;"]
注:與CMD不一樣的是,當指定了ENTRYPOINT後,CMD再也不是直接運行命令,而是將CMD的內容做爲參數傳給ENTRYPOINT指令。
例如在Dockerfile文件中同時寫了ENTRYPOINT和CMD,而CMD指令不是一個完整的命令,而是一個參數,以下:
ENTRYPOINT ["nginx", "-g"] CMD ["daemon off;"]
此時CMD的內容會做爲參數傳遞給ENTRYPOINT,至關於ENTRYPOINT ["nginx", "-g", "daemon off;"]。
若是CMD是一個完整的命令,以下:
ENTRYPOINT ["nginx", "-g", "daemon off;"] CMD echo $HOME
那麼ENTRYPOINT和CMD指令會互相覆蓋,誰在最後誰生效。即ENTRYPOINT ["nginx", "-g", "daemon off;"]不會執行,執行的是CMD echo $HOME。
經過docker run命令給ENTRYPOINT傳遞參數,例如上面查看本機公網IP地址的例子,若是咱們想查看HTTP頭信息,那curl命令須要加上-i參數,運行docker run myip -i時會報下圖錯誤。
將Dockerfile文件中CMD指令替換爲ENTRYPOINT,即ENTRYPOINT [ "curl", "-s", "http://ip.cn" ],而後再從新構建鏡像。
docker rmi -f myip:latest docker build -t myip . docker run myip -i
此時HTTP頭信息成功打印出來了。
1四、HEALTHCHECK指令
語法格式:
HEALTHCHECK [選項] CMD <命令> HEALTHCHECK NONE #若是基礎鏡像有健康檢查指令,使用這行能夠取消健康檢查指令
說明:容器健康狀態檢查指令
例如:HEALTHCHECK --interval=5s --timeout=3s CMD curl -fs http://localhost/ || exit 1
HEALTHCHECK的選項有: --interval=<間隔>:兩次健康檢查的間隔時間,默認爲30秒 --timeout=<時長>:每次檢查的超時時間,超過這個時間,本次檢查就視爲失敗,默認30秒 --retries=<次數>:指定健康檢查的次數,默認3次,若是指定次數都失敗後,則容器的健康狀態爲unhealthy(不健康)
CMD命令的返回值決定了本次健康檢查是否成功,命令的返回值以下:0: success - 成功 1: unhealthy - 失敗 2: reserved - 保留
若是鏡像添加了健康狀態檢查,容器運行後,在STATUS下會顯示健康狀態。
1五、ONBUILD指令
語法格式:
ONBUILD <其它指令>
說明:指定以當前鏡像爲基礎鏡像構建的下一級鏡像運行的命令
例如:ONBUILD COPY ./package.json /app或者ONBUILD RUN [ "npm", "install" ]
注:在當前鏡像構建時不會執行,只有以當前鏡像爲基礎鏡像,去構建下一級鏡像的時候纔會被執行。
實例2:經過Dockerfile部署nginx容器
nginx版本是1.14.0,使用源碼編譯安裝
(1)Dockerfile文件所在目錄結構
(2)Dockerfile文件內容
FROM centos:7 MAINTAINER https://blog.51cto.com/andyxu ENV TIME_ZOME Asia/Shanghai ARG NV="nginx-1.14.0" COPY nginx.conf /data/nginx/conf/ ADD $NV.tar.gz /tmp RUN yum -y install gcc gcc-c++ make openssl-devel pcre-devel \ && mkdir -p /data \ && cd /tmp/$NV \ && ./configure --prefix=/data/nginx \ && make -j 2 \ && make install \ && echo "${TIME_ZOME}" > /etc/timezone \ && ln -sf /usr/share/zoneinfo/${TIME_ZOME} /etc/localtime \ && rm -rf /tmp/nginx* \ && yum clean all \ && yum -y remove gcc gcc-c++ make WORKDIR /data/nginx/ EXPOSE 80 CMD ["./sbin/nginx","-g","daemon off;"]
(3)準備好nginx配置文件
nginx配置文件的內容我就不貼上來了,請自行準備好修改後的nginx配置文件。
(4)構建nginx鏡像
cd dockerfile/nginx/ docker build -t nginx:1.14.0 .
此時會先下載centos鏡像,而後再構建新鏡像,時間會比較久,可喝杯茶耐心等待
(5)建立並運行nginx容器
運行nginx鏡像的腳本內容以下:
#!/bin/bash docker run --name nginx --restart=always -p 8080:80 \ -v /data/docker/nginx/html:/data/nginx/html \ -v /data/docker/nginx/logs:/data/nginx/logs \ -d nginx:1.14.0
查看nginx容器是否成功啓動
(6)測試
建立一個index.html文件來作測試
echo "Welcome to nginx" > /data/docker/nginx/html/index.html
經過瀏覽器訪問http://192.168.2.227:8080/