1. 程式人生 > >使用Dockerfile定制自己的docker鏡像

使用Dockerfile定制自己的docker鏡像

star 自動 use 所在 Opens -name RoCE down rfi

我們都知道,在Linux系統下可以通過shell腳本來自動安裝部署應用,這樣不但免去了手動操作的麻煩,而且還可以通過一些自動化工具來實現批量安裝部署。那麽docker是否也可以通過腳本的方式定制鏡像呢?當然可以,那就是Dockerfile,我們可以把創建應用鏡像的操作都寫入一個Dockerfile文件裏,然後通過docker build命令來構建自已的鏡像,這個過程類似shell腳本的功能。
docker環境的安裝部署可以參考我的另一篇博文《Centos 7部署docker環境、基本命令使用及簡單實戰》,http://blog.51cto.com/andyxu/2174652 本文的所有操作都是基於這個環境執行的。
在使用dockerfile之前,我們先來看一個簡單的例子:
實例1:查看本機公網IP地址

docker pull ubuntu
mkdir -p dockerfile/myip

創建Dockerfile文件
vim dockerfile/myip/Dockerfile

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" ]   #運行容器時執行的命令

構建鏡像

cd dockerfile/myip/
docker build -t myip .

技術分享圖片
運行這個鏡像
docker run myip
技術分享圖片

通過以上實例我們可以了解到使用Dockerfile來構建鏡像的整個流程:
1、編寫dockerfile文件;
2、通過docker build來創建新的鏡像;
3、通過docker run來創建並運行新的容器。

build命令

Usage:  docker build [OPTIONS] PATH | URL | -
OPTIONS:
-t, --tag list??#指定構建的鏡像名稱和標記名稱
-f, --file string?#指定Dockerfiile文件路徑

示例:
1、docker build . #不指定鏡像名稱的話,將會默認以<none>作為鏡像名稱和標記名稱
2、docker build -t myip:v1 . #鏡像名稱為nginx,標記名稱為v1
3、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.048kB

Dockerfile指令詳解

1、FROM指令
語法格式:

FROM <image>   #<tag>是可選項,沒有指定<tag>的話,表示使用latest
FROM <image>:<tag>

說明:基於哪個鏡像來構建新的鏡像,FROM指令必須是dockerfile文件的第一行。
例如:FROM ubuntu:latest
2、MAINTAINER指令
語法格式:
MAINTAINER [作者信息]
說明:這個指令用於聲明作者,用於將image的制作者相關的信息寫入到image中。
例如:MAINTAINER xuad或者MAINTAINER xuad.com
3、ENV指令
語法格式:

ENV <key> <value>   #設置一個環境變量
ENV <key1>=<value1> <key2>=<value2>...   #設置多個環境變量

說明:定義環境變量,永久變量,容器運行後仍然有效,即容器內的永久變量。
例如:ENV pashname /usr/local/nginx或者ENV pashname=/usr/local/nginx VERSION=1.0
4、ARG指令
語法格式:

ARG <參數名>   #不設置默認值的話,需要使用--build-arg來設置參數的值
ARG <參數名>[=<默認值>]   #設置參數的默認值

說明:定義參數,即臨時變量,只限於構建鏡像時使用,容器運行後是不會存在的。
例如:ARG APT=apt-get
註:在構建命令docker build中用--build-arg <參數名>=<值> 可以覆蓋此參數的值。
例如:docker build --build-arg APT=yum -t myip:v1 .
5、RUN指令
語法格式:

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的一個例子:

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緩存文件,還卸載了編譯安裝所需要用到的軟件。鏡像是多層存儲,每一層的東西都不會在下一層被刪除,會一直跟隨著鏡像。因此在構建鏡像時,一定要確保每一層只添加真正需要添加的東西,任何無關的東西都應該清理掉。
6、WORKDIR指令
語法格式:

WORKDIR <工作目錄路徑>

說明:指定工作目錄,以後各層指令的當前目錄就是此目錄。
例如:WORKDIR /usr/local/nginx
註:如果目錄不存在,WORKDIR會自動創建這個目錄。
7、COPY指令
語法格式:

COPY <源路徑>... <目標路徑>
COPY ["<源路徑1>",... "<目標路徑>"]

說明:將上下文目錄中的文件或目錄復制到鏡像內指定的目錄下。
例如:COPY nginx.conf /usr/local/nginx/conf/或者COPY ["nginx.conf","/usr/local/nginx/conf/"]
註:源路徑可以是多個,甚至可以使用通配符;目標路徑可以是容器內的絕對路徑,也可以是相對於WORKDIR指定的工作目錄的相對路徑。
8、ADD指令
語法格式:

ADD <源路徑>... <目標路徑>
ADD ["<源路徑1>",... "<目標路徑>"]

說明:更高級的復制指令,與COPY不同的是,針對壓縮包會自動解壓處理。
例如:html.tar.gz /var/www/html或者ADD https://xuad.com/html.tar.gz /var/www/html
註:源路徑是一個壓縮文件的話,將會解壓到容器的目標路徑下;源路徑是一個URL的話,會下載或者解壓到容器的目標路徑下。
9、VOLUME指令
語法格式:

VOLUME <路徑>
VOLUME ["<路徑1>", "<路徑2>"...]

說明:將本地卷掛載到容器中
例如:VOLUME /data
註:容器使用的是AUFS,這種文件系統不能持久化數據,當容器關閉後,所有的更改都會丟失。當數據需要持久化時使用VOLUME指令,向掛載目錄裏寫入的任何信息都不會寫進容器存儲層,從而保證了容器存儲層的無變化。
10、EXPOSE指令
語法格式:

EXPOSE <端口1> [<端口2>...]

說明:聲明容器運行時提供的服務端口
例如:EXPOSE 80 443
註:docker run命令可使用-p <宿主端口>:<容器端口>將聲明的端口映射到本地指定的端口。
11、USER指令
語法格式:

USER <用戶名>
USER <UID>

說明:指定運行的用戶,以後各層的RUN、CMD或者ENTRYPOINT等指令都會以這個用戶身份執行。
12、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;"]。
13、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頭信息成功打印出來了。
14、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下會顯示健康狀態。
15、ONBUILD指令
語法格式:

ONBUILD <其它指令>

說明:指定以當前鏡像為基礎鏡像構建的下一級鏡像運行的命令
例如:ONBUILD COPY ./package.json /app或者ONBUILD RUN [ "npm", "install" ]
註:在當前鏡像構建時不會執行,只有以當前鏡像為基礎鏡像,去構建下一級鏡像的時候才會被執行。

Dockerfile實例

實例2:通過Dockerfile部署nginx容器
nginx版本是1.14.0,使用源碼編譯安裝
(1)Dockerfile文件所在目錄結構
技術分享圖片
(2)Dockerfile文件內容

FROM centos:7
MAINTAINER http://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/
技術分享圖片

使用Dockerfile定制自己的docker鏡像