ENTRYPOINT 的設計,可以保證 container 啟動執行指令的時候,都一定會包含 ENTRYPOINT 設定。因此可以藉由這個特性讓 image 用起來更靈活。
以下會介紹幾個還不錯的設計,讓讀者參考。
純執行指令類型的 image
以 Composer 為例:
#!/bin/sh
isCommand() { if [ "$1" = "sh" ]; then return 1 fi
composer help "$1" > /dev/null 2>&1 }
if [ "${1#-}" != "$1" ]; then set -- /sbin/tini -- composer "$@"
elif [ "$1" = 'composer' ]; then set -- /sbin/tini -- "$@"
elif isCommand "$1"; then set -- /sbin/tini -- composer "$@" fi
exec "$@"
|
依照上面的腳本,可以轉換出以下 Docker 指令與實際執行指令的對照表:
docker run |
actual |
docker run composer:1.10 --help |
/sbin/tini -- composer --help |
docker run composer:1.10 composer --help |
/sbin/tini -- composer --help |
docker run composer:1.10 install |
/sbin/tini -- composer install |
docker run composer:1.10 sh |
sh |
為節省版面,把 --rm
先省略。
從這個對照表可以看得出來,平常我們能把 docker run composer
作為取代 Composer 的指令,若需要使用 sh 進入 container 也可以順利執行,因為 ENTRYPOINT 都幫我們處理好了。
我們再回頭看一下 Container 應用是怎麼設定 alias
的:
alias composer="docker run -it --rm -v \````$PWD:/source -w /source composer:1.10"
|
這個 alias 設定,正是把 docker run
取代原指令,但這也必須 image 的 ENTRYPOINT 配合才行。ENTRYPOINT 設計可以有兩種方向:
- 額外寫 shell script 做為 ENTRYPOINT,可以同時處理原指令,與系統的指令。官方的 image 大多都有這樣設計。
- 直接把指令設定為 ENTRYPOINT,這個 image 就只能用來執行它的子指令,不能做其他用途,如 oryd/hydra。
服務類型的 image
以 MySQL 為例,它的 shell script 寫得比較複雜,這裡單純截取 _main() function:
_main() { if [ "${1:0:1}" = '-' ]; then set -- mysqld "$@" fi
if [ "$1" = 'mysqld' ] && ! _mysql_want_help "$@"; then mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started."
mysql_check_config "$@" docker_setup_env "$@" docker_create_db_directories
if [ "$(id -u)" = "0" ]; then mysql_note "Switching to dedicated user 'mysql'" exec gosu mysql "$BASH_SOURCE" "$@" fi
if [ -z "$DATABASE_ALREADY_EXISTS" ]; then docker_verify_minimum_env
ls /docker-entrypoint-initdb.d/ > /dev/null
docker_init_database_dir "$@"
mysql_note "Starting temporary server" docker_temp_server_start "$@" mysql_note "Temporary server started."
docker_setup_db
docker_process_init_files /docker-entrypoint-initdb.d/*
mysql_expire_root_user
mysql_note "Stopping temporary server" docker_temp_server_stop mysql_note "Temporary server stopped"
echo mysql_note "MySQL init process done. Ready for start up." echo fi fi
exec "$@" }
|
雖然有點長,不過概念簡單來說,ENTRYPOINT 的任務主要是:
- 以執行 mysqld 指令優先
- 準備環境、切換使用者
- 初始化資料庫、使用者、資料表等任務
ENTRYPOINT 會寫這麼複雜,正是為了 mysqld 與環境變數(MYSQL_ROOT_PASSWORD
)的搭配下能正常執行。
今日自我回顧
若有想寫 Dockerfile 的話,了解 ENTRYPOINT 會是必要的。因為 ENTRYPOINT 是啟動 container 的必經之路,善用它將可以讓 image 用起來更加靈活。