標籤:修改 text 訪問 shadow emctl 分享圖片 ESS 相對 serve
我們都知道,在Linux系統下可以通過shell指令碼來自動安裝部署應用,這樣不但免去了手動操作的麻煩,而且還可以通過一些自動化工具來實現大量安裝部署。那麼docker是否也可以通過指令碼的方式定製鏡像呢?當然可以,那就是Dockerfile,我們可以把建立應用鏡像的操作都寫入一個Dockerfile檔案裡,然後通過docker build命令來構建自已的鏡像,這個過程類似shell指令碼的功能。
docker環境的安裝部署可以參考我的另一篇博文《Centos 7部署docker環境、基本命令使用及簡單實戰》,http://blog.51cto.com/andyxu/2174652 本文的所有操作都是基於這個環境執行的。
在使用dockerfile之前,我們先來看一個簡單的例子:
執行個體1:查看本機公網IP地址
docker pull ubuntumkdir -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>的話,表示使用latestFROM <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:jessieRUN 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:latestdocker 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:7MAINTAINER http://blog.51cto.com/andyxuENV TIME_ZOME Asia/ShanghaiARG NV="nginx-1.14.0"COPY nginx.conf /data/nginx/conf/ADD $NV.tar.gz /tmpRUN 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++ makeWORKDIR /data/nginx/EXPOSE 80CMD ["./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/bashdocker 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鏡像