6 Dockerfile指令詳解 && ENTRYPOINT 指令

ENTRYPOINT 的格式和 RUN 指令格式同樣,分爲 exec 格式和 shell 格式。html

ENTRYPOINT 的目的和 CMD 同樣,都是在指定容器啓動程序及參數。 mysql

ENTRYPOINT 在運行時也能夠替代,不過比 CMD 要略顯繁瑣,須要經過docker run 的參數 --entrypoint 來指定。linux

當指定了 ENTRYPOINT 後, CMD 的含義就發生了改變,再也不是直接的運行其命令,而是將 CMD 的內容做爲參數傳給 ENTRYPOINT 指令,換句話說實際執行時,將變爲:nginx

<ENTRYPOINT> "<CMD>"

那麼有了 CMD 後,爲何還要有 ENTRYPOINT 呢?這種redis

<ENTRYPOINT> "<CMD>"

有什麼好處麼?讓咱們來看幾個場景。sql

場景一:讓鏡像變成像命令同樣使用docker

假設咱們須要一個得知本身當前公網 IP 的鏡像,那麼能夠先用 CMD 來實現:shell

FROM ubuntu:16.04
RUN apt-get update \
    && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*
CMD [ "curl", "-s", "http://ip.cn" ]

假如咱們使用 docker build -t myip . 來構建鏡像的話,若是咱們須要查詢當前公網 IP,只須要執行:數據庫

$ docker run myip
當前 IP:61.148.226.66 來自:北京市 聯通

嗯,這麼看起來好像能夠直接把鏡像當作命令使用了,不過命令總有參數,若是我們但願加參數呢?ubuntu

好比從上面的 CMD 中能夠看到實質的命令是 curl ,那麼如果咱們但願顯示 HTTP 頭信息,就須要加上 -i 參數。那麼咱們能夠直接加 -i 

參數給 docker run myip 麼?

$ docker run myip -i
docker: Error response from daemon: invalid header field value "
oci runtime error: container_linux.go:247: starting container pr
ocess caused \"exec: \\\"-i\\\": executable file not found in $P
ATH\"\n".

咱們能夠看到可執行文件找不到的報錯, executable file not found 。以前咱們說過,跟在鏡像名後面的是 command ,運行時會替換 CMD 的默認值。所以

這裏的 -i 替換了原來的 CMD ,而不是添加在原來的 curl -s http://ip.cn 後面。而 -i 根本不是命令,因此天然找不到。
那麼若是咱們但願加入 -i 這參數,咱們就必須從新完整的輸入這個命令:

$ docker run myip curl -s http://ip.cn -i

這顯然不是很好的解決方案,而使用 ENTRYPOINT 就能夠解決這個問題。如今我們從新用 ENTRYPOINT 來實現這個鏡像:

 

FROM ubuntu:16.04
RUN apt-get update \
    && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]

 

此次咱們再來嘗試直接使用 docker run myip -i :

$ docker run myip
當前 IP:61.148.226.66 來自:北京市 聯通

 $ docker run myip -i
 HTTP/1.1 200 OK
 Server: nginx/1.8.0
 Date: Tue, 22 Nov 2016 05:12:40 GMT
 Content-Type: text/html; charset=UTF-8
 Vary: Accept-Encoding
 X-Powered-By: PHP/5.6.24-1~dotdeb+7.1
 X-Cache: MISS from cache-2
 X-Cache-Lookup: MISS from cache-2:80
 X-Cache: MISS from proxy-2_6
 Transfer-Encoding: chunked
 Via: 1.1 cache-2:80, 1.1 proxy-2_6:8006
 Connection: keep-alive
 當前 IP:61.148.226.66 來自:北京市 聯通

能夠看到,此次成功了。這是由於當存在 ENTRYPOINT 後, CMD 的內容將會做爲參數傳給 ENTRYPOINT ,而這裏 -i 就是新的 CMD ,所以會做爲參數傳給curl ,從而達到了咱們預期的效果。

場景二:應用運行前的準備工做

啓動容器就是啓動主進程,但有些時候,啓動主進程前,須要一些準備工做。

好比 mysql 類的數據庫,可能須要一些數據庫配置、初始化的工做,這些工做要在最終的 mysql 服務器運行以前解決。

此外,可能但願避免使用 root 用戶去啓動服務,從而提升安全性,而在啓動服務前還須要以 root 身份執行一些必要的準備工做,最後切換到服務用戶身份啓動服務。或者除了服務外,其它命令依舊可使用 root 身份執行,方便調試等。

這些準備工做是和容器 CMD 無關的,不管 CMD 爲何,都須要事先進行一個預處理的工做。這種狀況下,能夠寫一個腳本,而後放入 ENTRYPOINT 中去執行,而這個腳本會將接到的參數(也就是 <CMD> )做爲命令,在腳本最後執行。

好比官方鏡像 redis 中就是這麼作的:

 

FROM alpine:3.4
...
RUN addgroup -S redis && adduser -S -G redis redis
...
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 6379
CMD [ "redis-server" ]

 

能夠看到其中爲了 redis 服務建立了 redis 用戶,並在最後指定了 ENTRYPOINT 爲 docker-entrypoint.sh 腳本。

 

#!/bin/sh
...
# allow the container to be started with `--user`
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
    chown -R redis .
    exec su-exec redis "$0" "$@"
fi
exec "$@"

 

該腳本的內容就是根據 CMD 的內容來判斷,若是是 redis-server 的話,則切

換到 redis 用戶身份啓動服務器,不然依舊使用 root 身份執行。好比:

$ docker run -it redis id
uid=0(root) gid=0(root) groups=0(root)
相關文章
相關標籤/搜索