[TOC]
Docker
可以從Dockerfile
中讀取指令來自動構建鏡像绞佩。Dockerfile
是一個文本文件,它包含了用戶可以在命令調用以制作鏡像的命令铁追。用戶可以使用docker build
連續(xù)執(zhí)行一些命令行指令來開啟一個自動構建拙毫。
此文檔描述了在Dockerfile
中可以使用的命令脑又。當你讀完這個文檔時融虽,請參閱Dockfile
最佳實踐獲取進階指南柒竞。
使用
docker build
命令從Dockerfile
和上下文構建鏡像匣掸。構建上下文是一個
構建的上下文是指定位置PATH或URL處的文件集
The docker build command builds an image from a Dockerfile and a context. The build’s context is the set of files at a specified location PATH or URL. The PATH is a directory on your local filesystem. The URL is a Git repository location.
A context is processed recursively. So, a PATH includes any subdirectories and the URL includes the repository and its submodules. This example shows a build command that uses the current directory as context:
$ docker build .
Sending build context to Docker daemon 6.51 MB
...
The build is run by the Docker daemon, not by the CLI. The first thing a build process does is send the entire context (recursively) to the daemon. In most cases, it’s best to start with an empty directory as context and keep your Dockerfile in that directory. Add only the files needed for building the Dockerfile.
CMD
CMD
指令有三種用法:
-
CMD ["executable","param1","param2"]
(exec
形式, 這是首選形式) -
CMD ["param1","param2"]
(作為ENTRYPOINT
默認參數) -
CMD command param1 param2
(shell 形式)
一個Dockerfile
里只能有一個CMD
指令涝登。如果你有多個CMD
指令雄家,只有 最后一個 生效。
CMD
的主要目的是為運行容器提供默認值胀滚。 默認值可以包含一個可執(zhí)行文件趟济,也忽略可執(zhí)行文件,在此情況下必須同時指定ENTRYPOINT
指令咽笼。
注: 如果
CMD
用于為ENTRYPOINT
指令提供默認參數顷编,CMD
和ENTRYPOINT
都應該使用json
數組格式。
注:
exec
形式傳遞json
數組剑刑,意味著你必須使用雙引號(")而不是單引號(')引用字符
注: 與
shell
形式不同媳纬,exec
形式不會像,那樣調用命令行shell
施掏。這意味著沒有通常的shell
處理钮惠。例如,CMD [ "echo", "$HOME" ]
將不會對$HOME
做變量替換七芭。如果你想使用shell
處理可使用shell
形式或直接執(zhí)行一個shell
素挽,例如:["sh", "-c", "echo $HOME"]
。當使用exec
形式并且直接執(zhí)行一個shell
狸驳,在這種情況下shell
形式预明,執(zhí)行環(huán)境變量擴展的是shell
,而不是docker
耙箍。
當使用shell
或exec
格式時撰糠,CMD
指令設置鏡像運行時執(zhí)行的命令。
如果你使用CMD
的shell
形式辩昆,<command>
將以/bin/sh -c
的形式運行:
FROM ubuntu
CMD echo "This is a test." | wc -
如果你想不使用shell
運行你的<command>
就必須以JSON
數組的形式表示并且使用可執(zhí)行文件的完整路徑阅酪。數組形式是CMD
的首選格式。任何獨立的參數都必須表達為數組的一個獨立的字符串卤材。
FROM ubuntu
CMD ["/usr/bin/wc","--help"]
如果你系統(tǒng)容器每次運行相同的可執(zhí)行文件遮斥,你應該考慮ENTRYPOINT
和CMD
結合使用。
如果用戶為docker run
指定了參數扇丛,那么他們將覆蓋CMD
中指定的默認參數术吗。
注:不要混淆
RUN
和CMD
。RUN
實際上運行命令并提交結果帆精;CMD
在構建時什么都不執(zhí)行较屿,只是指定鏡像將要執(zhí)行的命令隧魄。
EXPOSE
EXPOSE <port> [<port>...]
EXPOSE
指令通知Docker
容器運行時監(jiān)聽指定的網絡端口。EXPOSE
不會使容器端口對宿主機可訪問隘蝎。要那么做购啄,你必須使用-p
標記來發(fā)布一系列端口或者-P
標記發(fā)布所有暴露端口。你可以暴露一個端口號并可以使用另一個端口對外發(fā)布嘱么。
要在宿主機系統(tǒng)上設置端口重定向狮含,使用-P
標記。Docker
網絡功能支持網絡內創(chuàng)建網絡而不需要暴露端口曼振,詳細信息請查看功能概述几迄。
ADD
ADD
有兩種形式:
ADD <src>... <dest>
-
ADD ["<src>",... "<dest>"]
(路徑中包含空格需要這種形式)
ADD
指令
ENTRYPOINT
ENTRYPOINT
有2中形式:
-
ENTRYPOINT ["executable", "param1", "param2"]
(exec
形式, 首選) -
ENTRYPOINT command param1 param2
(shell
形式)
ENTRYPOINT
允許你配置一個將作為可執(zhí)行程序運行的容器。
例如冰评,以下命令將啟動一個nginx
默認監(jiān)控80端口:
docker run -i -t --rm -p 80:80 nginx
docker run <image>
的命令行參數將被追加到以exec
形式的ENTRYPOINT
所有元素后面映胁,并且覆蓋使用CMD
指定的所有元素。這使得參數可以被傳遞給入口, 例如甲雅,docker run <image> -d
將傳遞 -d
參數給入口解孙。你可以使用docker run --entrypoint
標記覆蓋ENTRYPOINT
執(zhí)行。
shell
形式阻止任何CMD
或者run
的命令行參數被使用抛人,但是有個弊端弛姜,你的ENTRYPOINT
將被作為/bin/sh -c
的一個子命令啟動,不能傳遞信號函匕。這意味著可執(zhí)行程序不是容器ID為1的進程 - 并且不會接受Unix信號 - 所以你的可執(zhí)行程序不會接受來自docker stop <container>
的SIGTERM
娱据。
只有Dockerfile
最后一個ENTRYPOINT
指令會生效蚪黑。
VOLUME
VOLUME ["/data"]
VOLUME
指令創(chuàng)建一個具有指定名稱的掛載點并且將其標記作為從宿主機或者其他容器外部掛載卷盅惜。值可以是一個JSON
數組,VOLUME ["/var/log"]
忌穿,或者有多參數的純字符串抒寂,比如:VOLUME /var/log
或者VOLUME /var/log /var/db
。更多Docker
客戶端的掛載指令信息/例子掠剑,移步文檔通過卷共享目錄屈芜。
docker run
命令使用基礎鏡像內指定位置存在的任意數據初始化新創(chuàng)建的卷。比如朴译,認為以下Dockerfile
片段:
FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol
這個Dockerfile
的結果是致使docker run
會創(chuàng)建一個新的掛載點/myvol
并且拷貝gretting
文件到新創(chuàng)建的卷井佑。
指定volumes的注意事項
關于Dockerfile
中的volumes
,請注意以下事項眠寿。
-
基于
Windows
容器的Volumes
: 當使用基于Windows
的容器躬翁,容器內volume
的目標位置必須是以下之一:- 一個不存在的或者空目錄
-
C
盤以外的驅動器:
-
從
Dockerfile
內更改卷: 如果任何構建步驟在volume
聲明之后修改了數據,這些修改將會被丟棄盯拱。 -
JSON 格式: 列表將會被作為一個
JSON
數組解析盒发。你必須使用雙引號(")而不是單引號(')將單詞包起來例嘱。 -
主機目錄在容器運行時聲明: 主機目錄(掛載點)本質上是與主機相關的。這是為了保證鏡像的可移植性宁舰。因為一個指定的主機目錄不能保證在所有的主機上可用拼卵。因此,你不能在
Dockerfile
內掛載一個主機目錄蛮艰。VOLUME
指令不支持指定一個主機目錄
參數腋腮。你必須在容器創(chuàng)建或運行時指定掛載點。
Exec形式ENTRYPOINT實例
你可以使用ENTRYPOINT
的exec
形式設置相當穩(wěn)定的默認命令和參數壤蚜,然后使用CMD
任意一種形式設置額外的更可能被修改的其他附加默認值低葫。
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
但你運行該容器時,你僅僅可以看到top
進程:
$ docker run -it --rm --name test top -H
top - 08:25:00 up 7:27, 0 users, load average: 0.00, 0.01, 0.05
Threads: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.1 us, 0.1 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem: 2056668 total, 1616832 used, 439836 free, 99352 buffers
KiB Swap: 1441840 total, 0 used, 1441840 free. 1324440 cached Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 19744 2336 2080 R 0.0 0.1 0:00.04 top
要進一步檢查結果仍律,可以使用docker exec
:
$ docker exec -it test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 2.6 0.1 19752 2352 ? Ss+ 08:24 0:00 top -b -H
root 7 0.0 0.1 15572 2164 ? R+ 08:25 0:00 ps aux
并且你可以使用docker stop test
請求top
優(yōu)雅的退出嘿悬。
以下Dockerfile
展示了使用ENTRYPOINT
在前端運行Apache
(例如,PID為1)水泉。
FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
如果你需要為單個可執(zhí)行程序寫一個啟動腳本善涨,你可以使用exec
和gosu
命令來確保最終執(zhí)行程序可以收到Unix
信號。
#!/usr/bin/env bash
set -e
if [ "$1" = 'postgres' ]; then
chown -R postgres "$PGDATA"
if [ -z "$(ls -A "$PGDATA")" ]; then
gosu postgres initdb
fi
exec gosu postgres "$@"
fi
exec "$@"
最后草则,如果你需要在退出時做一些額外的清理(或者與其他容器通信)钢拧,或者配合執(zhí)行多個可執(zhí)行文件,你可能需要確保ENTRYPOINT
腳本接受Unix
信號炕横,傳遞他們并做更多工作:
#!/bin/sh
# Note: I've written this using sh so it works in the busybox container too
# USE the trap if you need to also do manual cleanup after the service is stopped,
# or need to start multiple services in the one container
trap "echo TRAPed signal" HUP INT QUIT TERM
# start service in background here
/usr/sbin/apachectl start
echo "[hit enter key to exit] or run 'docker stop <container>'"
read
# stop service and clean up here
echo "stopping apache"
/usr/sbin/apachectl stop
echo "exited $0"
如果你使用docker run -it -p 80:80 --name test apache
運行該鏡像源内,然后你可以使用docker exec
檢查容器進程,或者docker top
份殿,并且可以通過腳本停止Apache
膜钓。
$ docker exec -it test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.0 4448 692 ? Ss+ 00:42 0:00 /bin/sh /run.sh 123 cmd cmd2
root 19 0.0 0.2 71304 4440 ? Ss 00:42 0:00 /usr/sbin/apache2 -k start
www-data 20 0.2 0.2 360468 6004 ? Sl 00:42 0:00 /usr/sbin/apache2 -k start
www-data 21 0.2 0.2 360468 6000 ? Sl 00:42 0:00 /usr/sbin/apache2 -k start
root 81 0.0 0.1 15572 2140 ? R+ 00:44 0:00 ps aux
$ docker top test
PID USER COMMAND
10035 root {run.sh} /bin/sh /run.sh 123 cmd cmd2
10054 root /usr/sbin/apache2 -k start
10055 33 /usr/sbin/apache2 -k start
10056 33 /usr/sbin/apache2 -k start
$ /usr/bin/time docker stop test
test
real 0m 0.27s
user 0m 0.03s
sys 0m 0.03s
注:你可以使用
--entrypoint
覆蓋ENTRYPOINT
配置,但是這只會將二進制設置為exec
(sh -c
不會被使用)卿嘲。
注:
exec
形式被解析為JSON
數組颂斜,意味著你必須使用雙引號(")包裹單詞而不是單引號(')。
注:不像
shell
形式拾枣,exec
形式并不會調用shell
命令沃疮。這意味著不會做普通的shell
處理。例如梅肤,ENTRIPOIN ["echo", "$HOME"]
將不能對$HOME
做變量置換司蔬。如果你既想shell
處理又想使用shell
形式或直接執(zhí)行一shell
,例如:ENTRYPOINT ["sh", "-c", "echo $HOME"]
姨蝴。當使用exec
形式和直接執(zhí)行shell
時俊啼,在shell
形式這種情況下,是shell
做的環(huán)境變量擴展似扔,而不是docker
吨些。
Shell形式ENTRYPOINT實例
你可以為ENTRYPOINT
指定一個純文本的字符串搓谆,它會以/bin/sh -c
的形式運行。這種形式將使用shell
處理shell
代替shell
環(huán)境變量豪墅,并且將忽略任何CMD
或者docker run
命令的命令行參數泉手。為了確保docker stop
能夠正常發(fā)出信號給任何長時間運行的ENTRYPOINT
可執(zhí)行文件,您需要記住使用exec
啟動它:
FROM ubuntu
ENTRYPOINT exec top -b
當你啟動鏡像偶器,你會看到PID
為1的進程:
$ docker run -it --rm --name test top
Mem: 1704520K used, 352148K free, 0K shrd, 0K buff, 140368121167873K cached
CPU: 5% usr 0% sys 0% nic 94% idle 0% io 0% irq 0% sirq
Load average: 0.08 0.03 0.05 2/98 6
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
1 0 root R 3164 0% 0% top -b
它將會在執(zhí)行docker stop
時徹底退出:
$ /usr/bin/time docker stop test
test
real 0m 0.20s
user 0m 0.02s
sys 0m 0.04s
如果你忘記了在ENTRYPOINT
開頭增加exec
:
FROM ubuntu
ENTRYPOINT top -b
CMD --ignored-param1
你可以啟動它(為了下一步給它指定名稱):
$ docker run -it --name test top --ignored-param2
Mem: 1704184K used, 352484K free, 0K shrd, 0K buff, 140621524238337K cached
CPU: 9% usr 2% sys 0% nic 88% idle 0% io 0% irq 0% sirq
Load average: 0.01 0.02 0.05 2/101 7
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
1 0 root S 3168 0% 0% /bin/sh -c top -b cmd cmd2
7 1 root R 3164 0% 0% top -b
你可以看到top
的輸出斩萌,ENTRYPOINT
指定的不是PID 1。
如果你接下來執(zhí)行docker stop test
屏轰,容器不會被徹底退出 - 超時以后top
命令會被發(fā)送一個SIGKILL
颊郎。
$ docker exec -it test ps aux
PID USER COMMAND
1 root /bin/sh -c top -b cmd cmd2
7 root top -b
8 root ps aux
$ /usr/bin/time docker stop test
test
real 0m 10.19s
user 0m 0.04s
sys 0m 0.03s
理解CMD和ENTRYPOINT如何交互
CMD
和ENTRYPOINT
指令都定義了當啟動一個容器時執(zhí)行什么命令。描述他們如何一起工作的規(guī)則很少霎苗。
-
Dockerfile
至少應該指定一個CMD
或ENTRYPOINT
命令姆吭。 - 當容器做一個可執(zhí)行程序時,
ENTRYPOINT
應該被定義唁盏。 -
CMD
應該被用作一種給ENTRYPOINT
定義默認參數的方式内狸,或在容器中執(zhí)行ad-hoc
命令的方式。 - 當運行容器時是用了交互參數時厘擂,
CMD
將被會被覆蓋昆淡。
下表顯示了對不同ENTRYPOINT
/ CMD
組合執(zhí)行的命令:
No ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [“exec_entry”, “p1_entry”] | |
---|---|---|---|
No CMD | error, not allowed | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
CMD [“exec_cmd”, “p1_cmd”] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry exec_cmd p1_cmd |
CMD [“p1_cmd”, “p2_cmd”] | p1_cmd p2_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry p1_cmd p2_cmd |
CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |