ENTRYPOINT
兩種寫法
# exec 格式 ENTRYPOINT ["executable", "param1", "param2"] # shell 格式 ENTRYPOINT command param1 param2
重點
- ENTRYPOINT 指定鏡像的默認入口命令,該入口命令會在啓動容器時作爲根命令執行,所有其他傳入值作爲該命令的參數
- ENTRYPOINT 的值可以通過 docker run --entrypoint 來覆蓋掉
- 只有 Dockerfile 中的最後一條 ENTRYPOINT 指令會起作用
ENTRYPOINT 和 CMD 聯合使用
- 當指定了 ENTRYPOINT 後,CMD 的含義就發生了改變,不再是直接的運行其命令,而是將 CMD 的內容作爲參數傳給 ENTRYPOINT 指令
- 換句話說實際執行時,會變成
<ENTRYPOINT> "<CMD>"
靈魂拷問
那麼有了 CMD 後,爲什麼還要有 ENTRYPOINT 呢?這種 <ENTRYPOINT> "<CMD>" 有什麼好處麼?
CMD 和 ENTRYPOINT 區別
CMD # 指定這個容器啓動的時候要運行的命令,不可以追加命令 ENTRYPOINT # 指定這個容器啓動的時候要運行的命令,可以追加命令
啥意思?這其實也是 ENTRYPOINT 的應用場景之一,下面來看
測試 CMD
編寫 dockerfile 文件
FROM centos CMD ["ls","-a"]
構建鏡像
docker build -f CMD.dockerfile -t test .
運行容器
> docker run test . .. .dockerenv bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
運行容器並追加命令
> docker run test -l docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "-l": executable file not found in $PATH: unknown.
- 看到可執行文件找不到的報錯,executable file not found
- 跟在鏡像名後面的是 command,運行時會替換 CMD 的默認值,因此這裏的 -l 替換了原來的 CMD,而不是追加在原來的 ls -a 後面
- 而 -l 根本不是命令,所以自然找不到
如果想加入 -i 參數,必須重寫 ls 命令
> docker run test ls -a -l total 56 drwxr-xr-x 1 root root 4096 Oct 28 09:36 . drwxr-xr-x 1 root root 4096 Oct 28 09:36 .. -rwxr-xr-x 1 root root 0 Oct 28 09:36 .dockerenv lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin drwxr-xr-x 5 root root 340 Oct 28 09:36 dev drwxr-xr-x 1 root root 4096 Oct 28 09:36 etc drwxr-xr-x 2 root root 4096 Nov 3 2020 home lrwxrwxrwx 1 root root 7 Nov 3 2020 lib -> usr/lib lrwxrwxrwx 1 root root 9 Nov 3 2020 lib64 -> usr/lib64 drwx------ 2 root root 4096 Sep 15 14:17 lost+found drwxr-xr-x 2 root root 4096 Nov 3 2020 media drwxr-xr-x 2 root root 4096 Nov 3 2020 mnt drwxr-xr-x 2 root root 4096 Nov 3 2020 opt dr-xr-xr-x 221 root root 0 Oct 28 09:36 proc dr-xr-x--- 2 root root 4096 Sep 15 14:17 root drwxr-xr-x 11 root root 4096 Sep 15 14:17 run lrwxrwxrwx 1 root root 8 Nov 3 2020 sbin -> usr/sbin drwxr-xr-x 2 root root 4096 Nov 3 2020 srv dr-xr-xr-x 13 root root 0 Oct 28 09:36 sys drwxrwxrwt 7 root root 4096 Sep 15 14:17 tmp drwxr-xr-x 12 root root 4096 Sep 15 14:17 usr drwxr-xr-x 20 root root 4096 Sep 15 14:17 var
可以了,但這明顯不是最優選擇,ENTRYPOINT 就可以解決這個問題
測試 ENTRYPOINT
編寫 dockerfile 文件
FROM centos ENTRYPOINT ["ls","-a"]
構建鏡像
docker build -f ENTRYPOINT.dockerfile -t test .
運行容器並追加命令
> docker run test -l total 56 drwxr-xr-x 1 root root 4096 Oct 28 09:38 . drwxr-xr-x 1 root root 4096 Oct 28 09:38 .. -rwxr-xr-x 1 root root 0 Oct 28 09:38 .dockerenv lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin drwxr-xr-x 5 root root 340 Oct 28 09:38 dev drwxr-xr-x 1 root root 4096 Oct 28 09:38 etc drwxr-xr-x 2 root root 4096 Nov 3 2020 home lrwxrwxrwx 1 root root 7 Nov 3 2020 lib -> usr/lib lrwxrwxrwx 1 root root 9 Nov 3 2020 lib64 -> usr/lib64 drwx------ 2 root root 4096 Sep 15 14:17 lost+found drwxr-xr-x 2 root root 4096 Nov 3 2020 media drwxr-xr-x 2 root root 4096 Nov 3 2020 mnt drwxr-xr-x 2 root root 4096 Nov 3 2020 opt dr-xr-xr-x 207 root root 0 Oct 28 09:38 proc dr-xr-x--- 2 root root 4096 Sep 15 14:17 root drwxr-xr-x 11 root root 4096 Sep 15 14:17 run lrwxrwxrwx 1 root root 8 Nov 3 2020 sbin -> usr/sbin drwxr-xr-x 2 root root 4096 Nov 3 2020 srv dr-xr-xr-x 13 root root 0 Oct 28 09:38 sys drwxrwxrwt 7 root root 4096 Sep 15 14:17 tmp drwxr-xr-x 12 root root 4096 Sep 15 14:17 usr drwxr-xr-x 20 root root 4096 Sep 15 14:17 var
ENTRYPOINT 的第二個應用場景
- 啓動容器就是啓動主進程,但啓動主進程前,可能需要一些準備工作,比如 mysql 可能需要一些數據庫配置、初始化的工作,這些工作要在最終的 mysql 服務器運行之前解決
- 還可能希望避免使用 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" ]
docker-entrypoint.sh
#!/bin/sh ... # allow the container to be started with `--user` if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then find . \! -user redis -exec chown redis '{}' + exec gosu redis "$0" "$@" fi exec "$@"
該腳本的內容就是根據 CMD 的內容來判斷,如果是 redis-server 的話,則切換到 redis 用戶身份啓動服務器,否則依舊使用 root 身份執行
[root@poloyy ~]# docker run -it redis id uid=0(root) gid=0(root) groups=0(root) # 直接進入容器內部 [root@poloyy ~]# docker run -it redis root@565f89976d63:/#