接上一篇:30分鐘快速上手Docker,看這篇就對了!
一、 帶著問題學Dockerfile
1、疑問
我們都知道從遠程倉庫可以pull一個tomcat等鏡像下來,然后docker run啟動容器,然后docker exec -it 容器id /bin/bash進入容器,往webapps下仍我們的程式,等等這一系列操作,都需要人工一步步的去操作,那我問你:你沒qa和生產環境的部署權限,你咋操作這些?這就需要將所有人工一步步操作的地方都寫到Dockerfile檔案里,然后將檔案給到運維人員,他們build成鏡像然后進行啟動,
2、舉例
比如:你要用tomcat部署一個war包,這時候你的Dockerfile檔案內容會包含如下:
-
將tomcat從遠程倉庫拉下來
-
進入到tomcat的webapps目錄
-
將宿主機上的war包扔到容器的webapps目錄下
然后運維拿著這個Dockerfile進行build成image,在run一下啟動容器,大功告成
3、好處
上面的例子好處不難發現
-
Dockerfile解放了手工操作很多步驟
-
Dockerfile保證了環境的統一
再也不會出現:QA是正常的,線上就是不行的情況了(前提是由于環境問題導致的 ),因為Dockerfile是同一份,大到環境,小到版本全都一致,再有問題那也是代碼問題,節省了和運維人員大量“親密接觸”的時間,
二、什么是Dockerfile
知道Dockerfile是干嘛的了,那Dockerfile的定義到底是啥呢?
Dockerfile中文名叫鏡像描述檔案,是一個包含用于組合鏡像目錄的文本檔案,也可以叫“腳本”,他通過讀取Dockerfile中的指令安裝步驟自動生成鏡像,
補充:檔案名稱必須是:Dockerfile
三、Dockerfile命令
1、構建鏡像命令
docker build -t 機構/鏡像名稱<:tags> Dockerfile目錄 # 比如如下,最后一個.代表當前目錄,因為我的Dockerfile檔案就在這,也可以用絕對路徑 docker build -t chentongwei.com/mywebapp:1.0.0 . # 然后執行docker images 進行查看會發現有我們剛才構建的鏡像 docker images

2、基礎命令
2.1、FROM
# 制作基準鏡像 FROM 鏡像 # 比如我們要發布一個應用到tomcat里,那么的第一步就是FROM tomcat FROM tomcat<:tags>
先有個印象,下面會實戰操作,
2.2、LABEL&MAINTAINER
# MAINTAINER,一般寫個人id或組織id # LABEL 就是注釋,方便閱讀的,純注釋說明,不會對Dockerfile造成任何影響 # 比如: MAINTAINER baidu.com LABEL version = "1.0.0" LABEL description = "我們是大百度!" # ...等等描述性資訊,純注釋,
2.3、WORKDIR
# 類似于Linux中的cd命令,但是他比cd高級的地方在于,我先cd,發現沒有這個目錄,我就自動創建出來,然后在cd進去
WORKDIR /usr/local/testdir
這個路徑建議使用絕對路徑,
2.4、ADD©
2.4.1、COPY
# 將1.txt拷貝到根目錄下,它不僅僅能拷貝單個檔案,還支持Go語言風格的通配符,比如如下: COPY 1.txt / # 拷貝所有 abc 開頭的檔案到testdir目錄下 COPY abc* /testdir/ # ? 是單個字符的占位符,比如匹配檔案 abc1.log COPY abc?.log /testdir/
2.4.2、ADD
# 將1.txt拷貝到根目錄的abc目錄下,若/abc不存在,則會自動創建 ADD 1.txt /abc # 將test.tar.gz解壓縮然后將解壓縮的內容拷貝到/home/work/test ADD test.tar.gz /home/work/test
docker官方建議當要從遠程復制檔案時,盡量用curl/wget命令來代替ADD,因為用ADD的時候會創建更多的鏡像層,鏡像層的size也大,
2.4.3、對比
-
二者都是只復制目錄中的檔案,而不包含目錄本身,
-
COPY能干的事ADD都能干,甚至還有附加功能,
-
ADD可以支持拷貝的時候順帶解壓縮檔案,以及添加遠程檔案(不在本宿主機上的檔案),
-
只是檔案拷貝的話可以用COPY,有額外操作可以用ADD代替,
2.5、ENV
# 設定環境常量,方便下文參考,比如:
ENV JAVA_HOME /usr/local/jdk1.8
# 參考上面的常量,下面的RUN指令可以先不管啥意思,目的是想說明下文可以通過${xxx}的方式參考
RUN ${JAVA_HOME}/bin/java -jar xxx.jar
ENV設定的常量,其他地方都可以用${xxx}來參考,將來改的時候只改ENV的變數內容就行,
3、運行指令
一共有三個:RUN&CMD&ENTRYPOINT
1、RUN
1.1、執行時機
RUN指令是在構建鏡像時運行,在構建時能修改鏡像內部的檔案,
1.2、命令格式
命令格式不光是RUN獨有,而是下面的CMD和ENTRYPOINT都通用,
-
SHELL命令格式
比如
RUN yum -y install vim
-
EXEC命令格式
比如
RUN ["yum","-y","install","vim"]
-
二者對比
SHELL:當前shell是父行程,生成一個子shell行程去執行腳本,腳本執行完后退出子shell行程,回到當前父shell行程,
EXEC:用EXEC行程替換當前行程,并且保持PID不變,執行完畢后直接退出,不會退回原來的行程,
總結:也就是說shell會創建子行程執行,EXEC不會創建子行程,
-
推薦EXEC命令格式
1.3、舉例
舉個最簡單的例子,構建鏡像時輸出一句話,那么在Dockerfile里寫如下即可:
RUN ["echo", "image is building!!!"]
再比如我們要下載vim,那么在Dockerfile里寫如下即可:
RUN ["yum","-y","install","vim"]
莫慌,下面會有實戰來完完整整的演示,
2、CMD
2.1、執行時機
容器啟動時執行,而不是鏡像構建時執行,
2.2、解釋說明
在容器啟動的時候執行此命令,且Dockerfile中只有最后一個ENTRYPOINT會被執行,推薦用EXEC格式,重點在于如果容器啟動的時候有其他額外的附加指令,則CMD指令不生效,
2.3、舉例
CMD ["echo", "container starting..."]
3、ENTRYPOINT
3.1、執行時機
容器創建時執行,而不是鏡像構建時執行,
3.2、解釋說明
在容器啟動的時候執行此命令,且Dockerfile中只有最后一個ENTRYPOINT會被執行,推薦用EXEC格式,
3.3、舉例
ENTRYPOINT ["ps","-ef"]
4、代碼演示
4.1、執行時機演示
FROM centos RUN ["echo", "image building!!!"] CMD ["echo", "container starting..."] docker build -t chentongwei.com/test-docker-run .
構建鏡像的程序中發現我們RUN的image building!!! 輸出了,所以RUN命令是在鏡像構建時執行,而并沒有container starting…的輸出,

docker run chentongwei.com/test-docker-run
結果:
container starting...,足以發現CMD命令是在容器啟動的時候執行,
4.2、CMD和ENTRYPOINT演示
ENTRYPOINT和CMD可以共用,若共用則他會一起合并執行,如下Demo:
FROM centos RUN ["echo", "image building!!!"] ENTRYPOINT ["ps"] CMD ["-ef"]
# 構建鏡像 docker build -t chentongwei.com/docker-run . # 啟動容器 docker run chentongwei.com/docker-run
輸出結果:
UID PID PPID C STIME TTY TIME CMD root 1 0 0 13:02 ? 00:00:00 ps -ef
他給我們合并執行了:ps -ef,這么做的好處在于如果容器啟動的時候添加額外指令,CMD會失效,可以理解成我們可以動態的改變CMD內容而不需要重新構建鏡像等操作,比如
docker run chentongwei.com/docker-run -aux
輸出結果:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 2.0 0.0 46340 1692 ? Rs 13:02 0:00 ps -aux
結果直接變成了 ps -aux,CMD命令不執行了,但是ENTRYPOINT一定執行,這也是CMD和ENTRYPOINT的區別之一,
四、實戰
1、部署應用到tomcat
1.1、準備作業
# 在服務器上創建test-dockerfile檔案夾 mkdir test-dockerfile # 進入test-dockerfile目錄 cd test-dockerfile # 創建需要部署到tomcat的應用 mkdir helloworld # 在helloworld目錄下創建index.html寫上hello dockerfile cd helloworld/ vim index.html
效果如下圖:


1.2、Dockerfile
# 在test-dockerfile目錄下創建Dockerfile檔案,注意大寫D,沒有后綴, touch Dockerfile
在Dockerfile里寫上如下內容
FROM tomcat:latest MAINTAINER baidu.com WORKDIR /usr/local/tomcat/webapps ADD helloworld ./helloworld
逐行解釋:
第一行:因為我們要部署應用到tomcat上,所以需要從遠程倉庫里拉取tomcat作為基礎鏡像,
第二行:描述性東西,還可以LABEL XXX XXX 添加更詳細的注釋資訊,
第三行:cd到/usr/local/tomcat/webapps,發現沒有這個目錄,我就自動創建出來,然后在cd進去
為什么是這個目錄呢?因為當我們制作完鏡像把容器run起來的時候tomcat的位置是在/usr/local/tomcat,加個/webapps是因為我們要將我們的應用程式扔到webapps下才能跑,如果懵,繼續往下看就懂了,
第四行:tomcat有了,tomcat的webapps我們也cd進去了,那還等啥?直接把我們的應用程式拷貝到webapps下就歐了,所以ADD命令宿主機上的helloworld檔案夾下的內容拷貝到當前目錄(webapps,上一步剛cd進來的)的helloworld檔案夾下,
1.3、制作鏡像
docker build -t baidu.com/test-helloworld:1.0.0 .
. 代表當前目錄,這些命令不懂的看上面的【三、Dockerfile命令】,都是上面提到的,沒新知識,
命令執行后的結果
[root@izm5e3qug7oee4q1y4opibz test-dockerfile]# docker build -t baidu.com/test-helloworld:1.0.0 . Sending build context to Docker daemon 3.584kB Step 1/4 : FROM tomcat:latest ---> 1b6b1fe7261e Step 2/4 : MAINTAINER baidu.com ---> Running in ac58299b3f38 Removing intermediate container ac58299b3f38 ---> 5d0da6398f7e Step 3/4 : WORKDIR /usr/local/tomcat/webapps ---> Running in 1c21c39fc58e Removing intermediate container 1c21c39fc58e ---> 9bf9672cd60e Step 4/4 : ADD helloworld ./helloworld ---> 6d67c0d48c20 Successfully built 6d67c0d48c20 Successfully tagged baidu.com/test-helloworld:1.0.0
好像分了1/2/3/4步呢?這是啥意思,這是鏡像分層的概念,下面說,現在只看到SuccessFully就哦了,
再查看下我們的鏡像真實存在了嗎?
docker images
完美

1.4、啟動容器
docker run -d -p 8100:8080 baidu.com/test-helloworld:1.0.0 # 然后docker ps查看容器是否存在 docker ps
瀏覽器訪問:http://服務器ip:8100/helloworld/index.html,很完美,這個helloworld就是我們Dockerfile里自己的應用程式,

1.5、進入容器
docker exec -it 730f9e144f68 /bin/bash

疑問1:怎么進入容器后直接在webapps目錄下,這就是因為我們這個鏡像是用Dockerfile制作的,Dockerfile上面我們自己WORKDIR到webapps目錄下的呀,
答疑1:我們ls下可以看到Dockerfile里的helloworld應用就在這里
root@730f9e144f68:/usr/local/tomcat/webapps# ls helloworld
答疑2:Dockerfile里WORKDIR /usr/local/tomcat/webapps了,為啥是這個目錄也很清晰了,容器里的tomcat就在這
root@730f9e144f68:/usr/local/tomcat/webapps# pwd usr/local/tomcat/webapps
2、從0制作Redis鏡像
一般沒人制作Redis鏡像,Redis有官方的docker鏡像,docker pull一下就行,這里是為了演示上面的命令,從0到1的程序,
2.1、準備作業
1.去官網下載Redis的原始碼包,因為我們演示的是Redis從無到有的程序,
2.準備Redis的組態檔redis-6379.conf
2.2、Dockerfile
# 將Redis運行在centos上 FROM centos # 安裝Redis所需要的基礎庫 RUN ["yum", "install", "-y", "gcc", "gcc-c++", "net-tools", "make"] # 將Redis目錄放到/usr/local WORKDIR /usr/local # 別忘了ADD命令自帶解壓縮的功能 ADD redis-4.0.14.tag.gz . WORKDIR /usr/local/redis-4.0.14/src # 編譯安裝Redis RUN make && make install WORKDIR /usr/local/redis-4.0.14 # 將組態檔仍到Redis根目錄 ADD redis-6379.conf . # 宣告容器中Redis的埠為6379 EXPOSE 6379 # 啟動Redis redis-server redis-6379.conf CMD ["redis-server", "redis-6379.conf"]
2.3、制作鏡像&&啟動容器
# 制作鏡像 docker build -t chentongwei.com/docker-redis . # 查看 docker images # 啟動容器 docker run -p 6379:6379 chentongwei.com/docker-redis
上面三套小連招執行完后redis就起來了,可以redis-cli去鏈接了,也可以docker exec進入容器去查看,
3、用docker部署jar包
FROM openjdk:8-jdk-alpine:latest ADD target/helloworld-0.0.1-SNAPSHOT.jar /helloworld.jar ENTRYPOINT ["java","-jar","/helloworld.jar"]
然后build成鏡像再run啟動容器,很簡單粗暴,
五、補充:鏡像分層的概念
1、Dockerfile
FROM tomcat:latest MAINTAINER baidu.com WORKDIR /usr/local/tomcat/webapps ADD helloworld ./helloworld
2、鏡像分層
就拿上面的Dockerfile來build的話,執行程序是如下的:
Sending build context to Docker daemon 3.584kB Step 1/4 : FROM tomcat:latest ---> 1b6b1fe7261e Step 2/4 : MAINTAINER baidu.com ---> Running in ac58299b3f38 Removing intermediate container ac58299b3f38 ---> 5d0da6398f7e Step 3/4 : WORKDIR /usr/local/tomcat/webapps ---> Running in 1c21c39fc58e Removing intermediate container 1c21c39fc58e ---> 9bf9672cd60e Step 4/4 : ADD helloworld ./helloworld ---> 6d67c0d48c20 Successfully built 6d67c0d48c20 Successfully tagged baidu.com/test-helloworld:1.0.0
會發現我們Dockerfile檔案內容一共四行,執行程序也是Step 1/2/3/4四步,這就知道了Dockerfile內容的行數決定了Step的步驟數,
那么每一步都代表啥呢?
其實每一步都會為我們創建一個臨時容器,這樣做的好處是如果下次再構建這個Dockerfile的時候,直接從cache里讀出已有的容器,不重復創建容器,這樣大大節省了構建時間,也不會浪費資源重復創建容器,比如如下:
FROM tomcat:latest MAINTAINER baidu.com WORKDIR /usr/local/tomcat/webapps ADD helloworld ./helloworld ADD helloworld ./helloworld2
啥也沒動,就是多部署一份helloworld且在容器內部改名為helloworld2,接下來看執行程序
Step 1/5 : FROM tomcat:latest ---> 1b6b1fe7261e Step 2/5 : MAINTAINER baidu.com ---> Using cache ---> 5d0da6398f7e Step 3/5 : WORKDIR /usr/local/tomcat/webapps ---> Using cache ---> 9bf9672cd60e Step 4/5 : ADD helloworld ./helloworld ---> Using cache ---> 6d67c0d48c20 Step 5/5 : ADD helloworld ./helloworld2 ---> 4e5ffc24522f Successfully built 4e5ffc24522f Successfully tagged baidu.com/test-helloworld:1.0.1
首先可以發現如下:
1.Step變成了5步,
2.前四步驟用了快取Using Cache,并沒有重復創建容器,Step 1 沒有Using Cache是因為它是從本地倉庫直接拉取了tomcat:latest當作基礎鏡像,run的時候會創建容器,
3.第五步重新創建了臨時容器,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/182415.html
標籤:Java
上一篇:【報錯】匯入專案后報錯:Target runtime Apache Tomcat v7.0 is not defined.
