這裏的所有命令都是用於創建鏡像所用,都算是爲定製鏡像的屬性指令。
一. COPY
COPY [--chown=<user>:<group>] <src>... <dst> #類shell指令,從上下文中src路徑中拷貝文件到鏡像的dst路徑下
以上命令,將鏡像構建上下文(docker build打包的文件中的文件拷貝到鏡像目標目錄。可以用--chown=user:group修改所屬用戶和組
COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/
二. ADD
此功能不常用
三. CMD
1. 背景:
用於指定利用鏡像創建容器時默認執行的命令
Docker不是虛擬機,是容器,容器就是進程。那麼根據指定鏡像創建容器時,就必須指定容器所運行的程序及參數(命令)。CMD指令用於指定默認的容器主進程啓動命令。當我們製作鏡像時,
在Dockerfile寫入
CMD /bin/bash
最後創建了image:tag鏡像
docker build -t image:latest .
當我們用該鏡像創建容器時輸入
docker run -it image:tag
此時容器默認執行/bin/bash命令(Dockerfile中CMD描述的指令,因爲以上命令缺省了命令),當然我們也可以指定容器運行其他命令
docker run -it image:tag ls
ls就是其他命令。
2. 命令格式
1)shell格式
CMD <命令>
如果利用shell格式的CMD,那麼指令解析爲exec格式,只不過可執行文件爲shell,第一個參數是-c。例如
CMD echo $HOME
被解析爲
CMD ["sh", "-c", "echo $HOME"]
2) exec 格式
CMD ["可執行文件", "arg1", "arg2", ... "argn"]
推薦使用這種方式,該種方式會把方括號中的內容解析爲JSON Array,所以一定要用雙引號包圍內容。
3. 注意:這裏需要指出Docker不是虛擬機,是容器,是進程,那麼容器中沒有後臺的概念,所有程序都需要在前臺執行,在後臺執行意味着退出。如果在Dockerfile中寫出如下CMD
CMD service nginx start
被解析爲:
CMD ["sh", "-c", "service nginx start"]
這個命令會以shell先啓動,創建shell進程,然後shell會fork出一個進程執行 service nginx start命令,該命令結束後,shell進程便會退出,此時容器的主進程停止退出,容器就會退出,因爲容器的主進程退出,而容器並不關心輔助進程。
正確的辦法是讓nginx以前臺方式運行(其實就是我們平時啓動一個程序,程序會在終端中運行並實時打印信息到終端,後臺就是不會和終端交互)。製作鏡像是需要寫成
CMD ["nginx", "-g", "daemon off;"]
這樣容器運行時默認的進程是nginx,而且以前臺方式運行。
四、ENTRYPOINT
4.1. 目的:
該命令的格式也是分爲exec和shell兩種格式,也是用於指定在利用製作完成的鏡像創建容器時默認啓動程序及參數,並且該參數也可以在創建容器是重新指定,只不過需要用 --entrypoint參數明確指出。如果指定了ENTRYPOINT,CMD的內容會變成參數傳遞給ENTRYPOINT。即
<ENTRYPOINT> "<CMD>"
4.2. 場景:
1)將鏡像變成一個指令一樣使用
例如Dockerfile如下:
FROM ubuntu:16.04
RUN agt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
CMD ["curl", "-s","https://ip.cn"]
利用該Dockerfile創建鏡像myip:latest
docker build -t myip:latest .
運行容器:
docker run myip:latest
此時會輸出ip信息,運行curl,但是此時如果想查看請求頭,如何實現?簡單想法:
docker run myip:latest -i
這種方式會報錯-i不存在,docker run 命令鏡像後的內容會被當做一個命令(CMD),運行時會替換製作鏡像時dockerfile中CMD指定的命令。所以-i會被當做一個程序,當然不存在。合理的辦法是
docker run myip:latest curl -s https://ip.cn -i
但是這樣很明顯不對,需要重複輸入指令,沒有達到效果,那麼更合理方方式是將-i變成參數,前面提到了-i是覆蓋Dockerfile中CMD內容(容器默認啓動命令),所以導致出錯。另外在有ENTRYPOINT時,CMD內容會被當成參數,所以只要在Dockerfile中加入ENTRYPOINT就可以了。如下
FROM ubuntu:16.04
RUN agt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["curl", "-s","https://ip.cn"]
此時運行:
docker run myip:latest -i #等同於 docker run myip:latest curl -s https://ip.cn -i
總結:如果有ENTRYPOINT,則ENTRYPOINT內容會變成容器創建時執行命令,且CMD變成該命令的參數,如果不存在ENTRYPOINT則CMD是容器創建時執行的命令。即容器創建時默認的執行命令是遵循如下規則.
command = exist(ENTRYPOINT) ? ENTRYPOINT "<CMD>" : CMD
2 )應用指令運行前初始化工作
可以將ENTRYPOINT內容指定爲一個腳本,腳本接受CMD作爲參數,腳本可以先做一些初始化的工作,而後根據CMD運行容器指令。例如MySQL和PostgreSQL在運行前都需要做初始化,而後運行服務,可以將運行服務的指令放入CMD中,將初始化的任務寫成腳本,放入ENTRYPOINT中。
五、ENV
1. 目的:
很簡單,即設置環境變量,設置鏡像的環境變量,容器運行時存在。
ENV key value #格式一
ENV key1=val1 key2=val2 ...
2. 格式
指令的行爲和shell一致,還可以通過$key來獲取變量的值。
六、ARG 構建參數
1. 目的
ARG也是和ENV一樣,設置環境變量。但是ARG設置的是構建鏡像是dockerfile中所用的變量,在創建容器時,是不會存在的。