ENTRYPOINT 的設計,可以保證 container 啟動執行指令的時候,都一定會包含 ENTRYPOINT 設定。因此可以藉由這個特性讓 image 用起來更靈活。
以下會介紹幾個還不錯的設計,讓讀者參考。
以 Composer 為例:
#!/bin/sh
isCommand() {
# sh 需要特別例外,因為 composer help 會誤以為是 show 指令
if [ "$1" = "sh" ]; then
return 1
fi
composer help "$1" > /dev/null 2>&1
}
# 當第一個參數看起來像 option 的話(如:-V、--help)
if [ "${1#-}" != "$1" ]; then
set -- /sbin/tini -- composer "$@"
# 當第一個參數就是 composer 的話
elif [ "$1" = 'composer' ]; then
set -- /sbin/tini -- "$@"
# 當第一個參數是 composer 的子指令(如:install、update 等)
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
的:
# 使用 Composer
alias composer="docker run -it --rm -v \````$PWD:/source -w /source composer:1.10"
這個 alias 設定,正是把 docker run
取代原指令,但這也必須 image 的 ENTRYPOINT 配合才行。ENTRYPOINT 設計可以有兩種方向:
以 MySQL 為例,它的 shell script 寫得比較複雜,這裡單純截取 _main() function:
_main() {
# 當第一個參數看起來像 option 的話,就用 mysqld 執行它
if [ "${1:0:1}" = '-' ]; then
set -- mysqld "$@"
fi
# 當是 mysqld 且沒有會讓 mysqld 停止的參數時,執行 setup
if [ "$1" = 'mysqld' ] && ! _mysql_want_help "$@"; then
mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started."
mysql_check_config "$@"
# 取得 Docker ENV 以及初始化目錄
docker_setup_env "$@"
docker_create_db_directories
# 若是 root 身分,則強制使用 mysql 身分啟動
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."
# 初始化資料庫,這裡將會用到 MYSQL_ROOT_PASSWORD、MYSQL_DATABASE、MYSQL_USER、MYSQL_PASSWORD 等環境變數
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
# 不是 mysqld 就直接執行了
exec "$@"
}
雖然有點長,不過概念簡單來說,ENTRYPOINT 的任務主要是:
ENTRYPOINT 會寫這麼複雜,正是為了 mysqld 與環境變數(MYSQL_ROOT_PASSWORD
)的搭配下能正常執行。
若有想寫 Dockerfile 的話,了解 ENTRYPOINT 會是必要的。因為 ENTRYPOINT 是啟動 container 的必經之路,善用它將可以讓 image 用起來更加靈活。