LoginSignup
3
1

More than 5 years have passed since last update.

DockerのMySQLのdocker-entrypoint.shのメモ

Last updated at Posted at 2019-02-16

DockerのMySQLのdocker-entrypoint.shについて、解読できた部分をメモに残す。

# コマンドが失敗したら途中で止まる
# -o pipefail でパイプでコマンドをつなげた場合も止まる
set -eo pipefail
# ワイルドカードで該当ファイルがない場合、*自体を対象としない
shopt -s nullglob

# if command starts with an option, prepend mysqld
# 引数の最初の1文字が '-' だった場合、mysqldと"$@"を位置パラメータに設定する
# $1 = mysqld, $2 = "$@"
if [ "${1:0:1}" = '-' ]; then
    set -- mysqld "$@"
fi

# skip setup if they want an option that stops mysqld
# for argで引数のループ
# -'?'は引数が-?に一致
wantHelp=
for arg; do
    case "$arg" in
        -'?'|--help|--print-defaults|-V|--version)
            wantHelp=1
            break
            ;;
    esac
done

# usage: file_env VAR [DEFAULT]
#    ie: file_env 'XYZ_DB_PASSWORD' 'example'
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
#  "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
file_env() {
    # localのスコープは関数内
    local var="$1"
    local fileVar="${var}_FILE"
    # デフォルト値のないパラメータ展開?
    local def="${2:-}"
    # 間接参照で$varや$fileVarの値を変数名とみなす
    if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
        echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
        exit 1
    fi
    local val="$def"
    if [ "${!var:-}" ]; then
        val="${!var}"
    elif [ "${!fileVar:-}" ]; then
        val="$(< "${!fileVar}")"
    fi
    export "$var"="$val"
    unset "$fileVar"
}

# usage: process_init_file FILENAME MYSQLCOMMAND...
#    ie: process_init_file foo.sh mysql -uroot
# (process a single initializer file, based on its extension. we define this
# function here, so that initializer scripts (*.sh) can use the same logic,
# potentially recursively, or override the logic used in subsequent calls)
process_init_file() {
    # 引数を1つずらす
    local f="$1"; shift
    # 配列mysqlに残りの引数を代入
    local mysql=( "$@" )

    case "$f" in
        *.sh)     echo "$0: running $f"; . "$f" ;;
        # ${mysql[@]}で配列を展開
        *.sql)    echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
        # gunzip -cで解凍して標準出力へ出力
        *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
        *)        echo "$0: ignoring $f" ;;
    esac
    echo
}

_check_config() {
    toRun=( "$@" --verbose --help )
    # 標準出力は捨ててエラーをリダイレクト
    if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then
        cat >&2 <<-EOM
            ERROR: mysqld failed while attempting to check config
            # 配列を展開、${toRun[@]}と同じ 
            command was: "${toRun[*]}"
            $errors
        EOM
        exit 1
    fi
}

# Fetch value from server config
# We use mysqld --verbose --help instead of my_print_defaults because the
# latter only show values present in config files, and not server defaults
_get_config() {
    local conf="$1"; shift
    # mktemp -uはdry-run
    # mysqld --verbose --helpで設定値を表示
    "$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \
        # よく分からない、datadirは/var/lib/mysql/
        | awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }'
    # match "datadir      /some/path with/spaces in/it here" but not "--xyz=abc\n     datadir (xyz)"
}

# allow the container to be started with `--user`
# -aはAND、id -uでユーザID表示、0はroot
if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then
    _check_config "$@"
    DATADIR="$(_get_config 'datadir' "$@")"
    mkdir -p "$DATADIR"
    chown -R mysql:mysql "$DATADIR"
    # gosuで指定したユーザで実行、$BASH_SOURCEはファイルの絶対パス
    # つまり、mysqlユーザでdocker-entrypoint.shを再実行する
    exec gosu mysql "$BASH_SOURCE" "$@"
fi

if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
    # still need to check config, container may have started with --user
    _check_config "$@"
    # Get config
    DATADIR="$(_get_config 'datadir' "$@")"

    # -dはディレクトリのチェック
    if [ ! -d "$DATADIR/mysql" ]; then
        file_env 'MYSQL_ROOT_PASSWORD'
        if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
            echo >&2 'error: database is uninitialized and password option is not specified '
            echo >&2 '  You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD'
            exit 1
        fi

        mkdir -p "$DATADIR"

        echo 'Initializing database'
        # --initialize-insecureでrootのパスワードを生成しない
        "$@" --initialize-insecure
        echo 'Database initialized'

        # command -vでコマンドのパス取得、-eでファイルが存在するとき
        if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then
            # https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/packaging/deb-in/extra/mysql-systemd-start#L81-L84
            echo 'Initializing certificates'
            mysql_ssl_rsa_setup --datadir="$DATADIR"
            echo 'Certificates initialized'
        fi

        SOCKET="$(_get_config 'socket' "$@")"
        # --skip-networkingでネットワーク経由の接続を許可しない
        "$@" --skip-networking --socket="${SOCKET}" &
        pid="$!"

        mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" )

        for i in {30..0}; do
            # &> /dev/nullでstdoutとstderrを捨てる(>/dev/null 2>&1)
            if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then
                break
            fi
            echo 'MySQL init process in progress...'
            sleep 1
        done
        if [ "$i" = 0 ]; then
            echo >&2 'MySQL init process failed.'
            exit 1
        fi

        if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then
            # sed is for https://bugs.mysql.com/bug.php?id=20545
            # タイムゾーンテーブルをロード
            mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql
        fi

        if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
            export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)"
            echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD"
        fi

        rootCreate=
        # default root to listen for connections from anywhere
        file_env 'MYSQL_ROOT_HOST' '%'
        if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then
            # no, we don't care if read finds a terminating character in this heredoc
            # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151
            # <<-はヒアドキュメントでタブを取り除き、標準入力として扱われる
            # readは標準入力から読み込んで変数に格納、-rで\をエスケープ文字としない、-d ''でデリミタなし
            read -r -d '' rootCreate <<-EOSQL || true
                CREATE USER 'root'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ;
                GRANT ALL ON *.* TO 'root'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION ;
            EOSQL
        fi

        "${mysql[@]}" <<-EOSQL
            -- What's done in this file shouldn't be replicated
            --  or products like mysql-fabric won't work
            SET @@SESSION.SQL_LOG_BIN=0;
            ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ;
            GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ;
            ${rootCreate}
            DROP DATABASE IF EXISTS test ;
            FLUSH PRIVILEGES ;
        EOSQL

        if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then
            mysql+=( -p"${MYSQL_ROOT_PASSWORD}" )
        fi

        file_env 'MYSQL_DATABASE'
        if [ "$MYSQL_DATABASE" ]; then
            echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}"
            mysql+=( "$MYSQL_DATABASE" )
        fi

        file_env 'MYSQL_USER'
        file_env 'MYSQL_PASSWORD'
        if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then
            echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}"

            if [ "$MYSQL_DATABASE" ]; then
                echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}"
            fi

            echo 'FLUSH PRIVILEGES ;' | "${mysql[@]}"
        fi

        echo
        ls /docker-entrypoint-initdb.d/ > /dev/null
        for f in /docker-entrypoint-initdb.d/*; do
            process_init_file "$f" "${mysql[@]}"
        done

        if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then
            "${mysql[@]}" <<-EOSQL
                ALTER USER 'root'@'%' PASSWORD EXPIRE;
            EOSQL
        fi
        if ! kill -s TERM "$pid" || ! wait "$pid"; then
            echo >&2 'MySQL init process failed.'
            exit 1
        fi

        echo
        echo 'MySQL init process done. Ready for start up.'
        echo
    fi
fi

exec "$@"
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1