mysql:5.7.28 Docker image analysis

耿星雨
2023-12-01

mysql:5.7.28 Docker image analysis

Overview

考虑到项目中用到mysql的docker镜像mysql:5.7.28,为了方便后续针对该镜像做定制化,这里抽出一些时间对该镜像做一些简单的分析。

我们这里准备好如下运行脚本做测试。

docker run --detach \
--restart always \
--publish 3306:3306 \
--name mysql \
--volume /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime \
--env MYSQL_ROOT_PASSWORD=Gah6kuP7ohfio4 \
mysql:5.7.28 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_unicode_ci

Analysis

Src analysis

Clone

lwk@harbin:~/Public/project/github$ git clone git@github.com:docker-library/mysql.git
Cloning into 'mysql'...
remote: Enumerating objects: 1752, done.
remote: Counting objects: 100% (234/234), done.
remote: Compressing objects: 100% (139/139), done.
remote: Total 1752 (delta 140), reused 165 (delta 95), pack-reused 1518
Receiving objects: 100% (1752/1752), 341.43 KiB | 309.00 KiB/s, done.
Resolving deltas: 100% (826/826), done.
lwk@harbin:~/Public/project/github$

Create branch from git history

我们进入到源码目录

lwk@harbin:~/Public/project/github$ cd mysql/
lwk@harbin:~/Public/project/github/mysql$ ll
total 80
drwxrwxr-x 7 lwk lwk  4096 Mar 24 11:09 ./
drwxrwxr-x 4 lwk lwk  4096 Mar 24 11:09 ../
drwxrwxr-x 2 lwk lwk  4096 Mar 24 11:09 5.7/
drwxrwxr-x 3 lwk lwk  4096 Mar 24 11:09 8.0/
-rwxrwxr-x 1 lwk lwk  1127 Mar 24 11:09 apply-templates.sh*
-rwxrwxr-x 1 lwk lwk  2553 Mar 24 11:09 generate-stackbrew-library.sh*
drwxrwxr-x 8 lwk lwk  4096 Mar 24 11:09 .git/
-rw-rw-r-- 1 lwk lwk   157 Mar 24 11:09 .gitattributes
drwxrwxr-x 3 lwk lwk  4096 Mar 24 11:09 .github/
-rw-rw-r-- 1 lwk lwk    17 Mar 24 11:09 .gitignore
-rw-rw-r-- 1 lwk lwk 18092 Mar 24 11:09 LICENSE
-rw-rw-r-- 1 lwk lwk  2614 Mar 24 11:09 README.md
drwxrwxr-x 2 lwk lwk  4096 Mar 24 11:09 template/
-rwxrwxr-x 1 lwk lwk   134 Mar 24 11:09 update.sh*
-rw-rw-r-- 1 lwk lwk   754 Mar 24 11:09 versions.json
-rwxrwxr-x 1 lwk lwk  3969 Mar 24 11:09 versions.sh*
lwk@harbin:~/Public/project/github/mysql$

查看tag列表

lwk@harbin:~/Public/project/github/mysql$ git tag
lwk@harbin:~/Public/project/github/mysql$

我们发现没有源码管理系统中没有相应的tag列表。这个时候,我们再试着查看一下分支情况。

lwk@harbin:~/Public/project/github/mysql$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
lwk@harbin:~/Public/project/github/mysql$

我们发现只有源码系统中只有主分支。这个时候,我们只能将希望放在从git历史记录中提取出5.7.28最新的提交快照了,然后我们再基于这个历史快照点创建一个名为feature/5.7.28的分支出来。

lwk@harbin:~/Public/project/github/mysql$ git log --pretty=oneline |grep 5.7.28
5fa3526d23a846c6a2982901bc8190025af44336 Update to 5.7.28-1debian9
567028d4e177238c58760bcd69a8766a8f026e2a Switch from pgp.mit.edu to pool.sks-keyservers.net
lwk@harbin:~/Public/project/github/mysql$ git checkout -b feature/5.7.28 5fa3526d23a846
Switched to a new branch 'feature/5.7.28'
lwk@harbin:~/Public/project/github/mysql$ git branch -a
* feature/5.7.28
  master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
lwk@harbin:~/Public/project/github/mysql$ ll
total 64
drwxrwxr-x 7 lwk lwk  4096 Mar 24 11:55 ./
drwxrwxr-x 5 lwk lwk  4096 Mar 24 11:36 ../
drwxrwxr-x 2 lwk lwk  4096 Mar 24 11:55 5.6/
drwxrwxr-x 2 lwk lwk  4096 Mar 24 11:55 5.7/
drwxrwxr-x 3 lwk lwk  4096 Mar 24 11:55 8.0/
-rwxrwxr-x 1 lwk lwk  1765 Mar 24 11:55 generate-stackbrew-library.sh*
drwxrwxr-x 8 lwk lwk  4096 Mar 24 11:55 .git/
-rw-rw-r-- 1 lwk lwk 18092 Mar 24 11:09 LICENSE
-rw-rw-r-- 1 lwk lwk  2542 Mar 24 11:55 README.md
drwxrwxr-x 2 lwk lwk  4096 Mar 24 11:55 .template.Debian/
-rw-rw-r-- 1 lwk lwk   556 Mar 24 11:55 .travis.yml
-rwxrwxr-x 1 lwk lwk  1077 Mar 24 11:55 update.sh*
lwk@harbin:~/Public/project/github/mysql$ ll 5.7/
total 28
drwxrwxr-x 2 lwk lwk  4096 Mar 24 11:55 ./
drwxrwxr-x 7 lwk lwk  4096 Mar 24 11:55 ../
-rwxrwxr-x 1 lwk lwk 12683 Mar 24 11:55 docker-entrypoint.sh*
-rw-rw-r-- 1 lwk lwk  3803 Mar 24 11:55 Dockerfile
lwk@harbin:~/Public/project/github/mysql$

看到这里,我们发现先前的设想是对的,而且我们已经成功提取出了最近的5.7.28相关的快照对应的分支,并从分支中看到了我们想要的关于5.7.28相关的docker文件docker-entrypoint.shDockerfile,接下 来,我们来看一下这两个文件。

Dockerfile

lwk@harbin:~/Public/project/github/mysql$ cat 5.7/Dockerfile 
FROM debian:stretch-slim

# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
RUN groupadd -r mysql && useradd -r -g mysql mysql

RUN apt-get update && apt-get install -y --no-install-recommends gnupg dirmngr && rm -rf /var/lib/apt/lists/*

# add gosu for easy step-down from root
ENV GOSU_VERSION 1.7
RUN set -x \
	&& apt-get update && apt-get install -y --no-install-recommends ca-certificates wget && rm -rf /var/lib/apt/lists/* \
	&& wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture)" \
	&& wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture).asc" \
	&& export GNUPGHOME="$(mktemp -d)" \
	&& gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \
	&& gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \
	&& gpgconf --kill all \
	&& rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc \
	&& chmod +x /usr/local/bin/gosu \
	&& gosu nobody true \
	&& apt-get purge -y --auto-remove ca-certificates wget

RUN mkdir /docker-entrypoint-initdb.d

RUN apt-get update && apt-get install -y --no-install-recommends \
# for MYSQL_RANDOM_ROOT_PASSWORD
		pwgen \
# for mysql_ssl_rsa_setup
		openssl \
# FATAL ERROR: please install the following Perl modules before executing /usr/local/mysql/scripts/mysql_install_db:
# File::Basename
# File::Copy
# Sys::Hostname
# Data::Dumper
		perl \
	&& rm -rf /var/lib/apt/lists/*

RUN set -ex; \
# gpg: key 5072E1F5: public key "MySQL Release Engineering <mysql-build@oss.oracle.com>" imported
	key='A4A9406876FCBD3C456770C88C718D3B5072E1F5'; \
	export GNUPGHOME="$(mktemp -d)"; \
	gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \
	gpg --batch --export "$key" > /etc/apt/trusted.gpg.d/mysql.gpg; \
	gpgconf --kill all; \
	rm -rf "$GNUPGHOME"; \
	apt-key list > /dev/null

ENV MYSQL_MAJOR 5.7
ENV MYSQL_VERSION 5.7.28-1debian9

RUN echo "deb http://repo.mysql.com/apt/debian/ stretch mysql-${MYSQL_MAJOR}" > /etc/apt/sources.list.d/mysql.list

# the "/var/lib/mysql" stuff here is because the mysql-server postinst doesn't have an explicit way to disable the mysql_install_db codepath besides having a database already "configured" (ie, stuff in /var/lib/mysql/mysql)
# also, we set debconf keys to make APT a little quieter
RUN { \
		echo mysql-community-server mysql-community-server/data-dir select ''; \
		echo mysql-community-server mysql-community-server/root-pass password ''; \
		echo mysql-community-server mysql-community-server/re-root-pass password ''; \
		echo mysql-community-server mysql-community-server/remove-test-db select false; \
	} | debconf-set-selections \
	&& apt-get update && apt-get install -y mysql-server="${MYSQL_VERSION}" && rm -rf /var/lib/apt/lists/* \
	&& rm -rf /var/lib/mysql && mkdir -p /var/lib/mysql /var/run/mysqld \
	&& chown -R mysql:mysql /var/lib/mysql /var/run/mysqld \
# ensure that /var/run/mysqld (used for socket and lock files) is writable regardless of the UID our mysqld instance ends up having at runtime
	&& chmod 777 /var/run/mysqld \
# comment out a few problematic configuration values
	&& find /etc/mysql/ -name '*.cnf' -print0 \
		| xargs -0 grep -lZE '^(bind-address|log)' \
		| xargs -rt -0 sed -Ei 's/^(bind-address|log)/#&/' \
# don't reverse lookup hostnames, they are usually another container
	&& echo '[mysqld]\nskip-host-cache\nskip-name-resolve' > /etc/mysql/conf.d/docker.cnf

VOLUME /var/lib/mysql

COPY docker-entrypoint.sh /usr/local/bin/
RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 3306 33060
CMD ["mysqld"]
lwk@harbin:~/Public/project/github/mysql$ 

我们看到,mysql服务程序是以debian9为基础通过apt方式安装的。

docker-entrypoint.sh

接下来,我们再看一下文件docker-entrypoint.sh做了哪些事。

lwk@harbin:~/Public/project/github/mysql$ cat 5.7/docker-entrypoint.sh 
#!/bin/bash
set -eo pipefail
shopt -s nullglob

# logging functions
mysql_log() {
	local type="$1"; shift
	printf '%s [%s] [Entrypoint]: %s\n' "$(date --rfc-3339=seconds)" "$type" "$*"
}
mysql_note() {
	mysql_log Note "$@"
}
mysql_warn() {
	mysql_log Warn "$@" >&2
}
mysql_error() {
	mysql_log ERROR "$@" >&2
	exit 1
}

# 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 var="$1"
	local fileVar="${var}_FILE"
	local def="${2:-}"
	if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
		mysql_error "Both $var and $fileVar are set (but are exclusive)"
	fi
	local val="$def"
	if [ "${!var:-}" ]; then
		val="${!var}"
	elif [ "${!fileVar:-}" ]; then
		val="$(< "${!fileVar}")"
	fi
	export "$var"="$val"
	unset "$fileVar"
}

# check to see if this file is being run or sourced from another script
_is_sourced() {
	# https://unix.stackexchange.com/a/215279
	[ "${#FUNCNAME[@]}" -ge 2 ] \
		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
		&& [ "${FUNCNAME[1]}" = 'source' ]
}

# usage: docker_process_init_files [file [file [...]]]
#    ie: docker_process_init_files /always-initdb.d/*
# process initializer files, based on file extensions
docker_process_init_files() {
	# mysql here for backwards compatibility "${mysql[@]}"
	mysql=( docker_process_sql )

	echo
	local f
	for f; do
		case "$f" in
			*.sh)     mysql_note "$0: running $f"; . "$f" ;;
			*.sql)    mysql_note "$0: running $f"; docker_process_sql < "$f"; echo ;;
			*.sql.gz) mysql_note "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
			*)        mysql_warn "$0: ignoring $f" ;;
		esac
		echo
	done
}

mysql_check_config() {
	local toRun=( "$@" --verbose --help ) errors
	if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then
		mysql_error $'mysqld failed while attempting to check config\n\tcommand was: '"${toRun[*]}"$'\n\t'"$errors"
	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
mysql_get_config() {
	local conf="$1"; shift
	"$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \
		| awk -v conf="$conf" '$1 == conf && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }'
	# match "datadir      /some/path with/spaces in/it here" but not "--xyz=abc\n     datadir (xyz)"
}

# Do a temporary startup of the MySQL server, for init purposes
docker_temp_server_start() {
	if [ "${MYSQL_MAJOR}" = '5.6' ]; then
		"$@" --skip-networking --socket="${SOCKET}" &
		mysql_note "Waiting for server startup"
		local i
		for i in {30..0}; do
			# only use the root password if the database has already been initializaed
			# so that it won't try to fill in a password file when it hasn't been set yet
			extraArgs=()
			if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
				extraArgs+=( '--dont-use-mysql-root-password' )
			fi
			if docker_process_sql "${extraArgs[@]}" --database=mysql <<<'SELECT 1' &> /dev/null; then
				break
			fi
			sleep 1
		done
		if [ "$i" = 0 ]; then
			mysql_error "Unable to start server."
		fi
	else
		# For 5.7+ the server is ready for use as soon as startup command unblocks
		if ! "$@" --daemonize --skip-networking --socket="${SOCKET}"; then
			mysql_error "Unable to start server."
		fi
	fi
}

# Stop the server. When using a local socket file mysqladmin will block until
# the shutdown is complete.
docker_temp_server_stop() {
	if ! mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}"; then
		mysql_error "Unable to shut down server."
	fi
}

# Verify that the minimally required password settings are set for new databases.
docker_verify_minimum_env() {
	if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
		mysql_error $'Database is uninitialized and password option is not specified\n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD'
	fi
}

# creates folders for the database
# also ensures permission for user mysql of run as root
docker_create_db_directories() {
	local user; user="$(id -u)"

	# TODO other directories that are used by default? like /var/lib/mysql-files
	# see https://github.com/docker-library/mysql/issues/562
	mkdir -p "$DATADIR"

	if [ "$user" = "0" ]; then
		# this will cause less disk access than `chown -R`
		find "$DATADIR" \! -user mysql -exec chown mysql '{}' +
	fi
}

# initializes the database directory
docker_init_database_dir() {
	mysql_note "Initializing database files"
	if [ "$MYSQL_MAJOR" = '5.6' ]; then
		mysql_install_db --datadir="$DATADIR" --rpm --keep-my-cnf "${@:2}"
	else
		"$@" --initialize-insecure
	fi
	mysql_note "Database files initialized"

	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
		mysql_note "Initializing certificates"
		mysql_ssl_rsa_setup --datadir="$DATADIR"
		mysql_note "Certificates initialized"
	fi
}

# Loads various settings that are used elsewhere in the script
# This should be called after mysql_check_config, but before any other functions
docker_setup_env() {
	# Get config
	declare -g DATADIR SOCKET
	DATADIR="$(mysql_get_config 'datadir' "$@")"
	SOCKET="$(mysql_get_config 'socket' "$@")"

	# Initialize values that might be stored in a file
	file_env 'MYSQL_ROOT_HOST' '%'
	file_env 'MYSQL_DATABASE'
	file_env 'MYSQL_USER'
	file_env 'MYSQL_PASSWORD'
	file_env 'MYSQL_ROOT_PASSWORD'

	declare -g DATABASE_ALREADY_EXISTS
	if [ -d "$DATADIR/mysql" ]; then
		DATABASE_ALREADY_EXISTS='true'
	fi
}

# Execute sql script, passed via stdin
# usage: docker_process_sql [--dont-use-mysql-root-password] [mysql-cli-args]
#    ie: docker_process_sql --database=mydb <<<'INSERT ...'
#    ie: docker_process_sql --dont-use-mysql-root-password --database=mydb <my-file.sql
docker_process_sql() {
	passfileArgs=()
	if [ '--dont-use-mysql-root-password' = "$1" ]; then
		passfileArgs+=( "$1" )
		shift
	fi
	# args sent in can override this db, since they will be later in the command
	if [ -n "$MYSQL_DATABASE" ]; then
		set -- --database="$MYSQL_DATABASE" "$@"
	fi

	mysql --defaults-file=<( _mysql_passfile "${passfileArgs[@]}") --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@"
}

# Initializes database with timezone info and root password, plus optional extra db/user
docker_setup_db() {
	# Load timezone info into database
	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/' \
			| docker_process_sql --dont-use-mysql-root-password --database=mysql
			# tell docker_process_sql to not use MYSQL_ROOT_PASSWORD since it is not set yet
	fi
	# Generate random root password
	if [ -n "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
		export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)"
		mysql_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD"
	fi
	# Sets root password and creates root users for non-localhost hosts
	local rootCreate=
	# default root to listen for connections from anywhere
	if [ -n "$MYSQL_ROOT_HOST" ] && [ "$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 '' 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

	local passwordSet=
	if [ "$MYSQL_MAJOR" = '5.6' ]; then
		# no, we don't care if read finds a terminating character in this heredoc (see above)
		read -r -d '' passwordSet <<-EOSQL || true
			DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;
			SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MYSQL_ROOT_PASSWORD}') ;

			-- 5.5: https://github.com/mysql/mysql-server/blob/e48d775c6f066add457fa8cfb2ebc4d5ff0c7613/scripts/mysql_secure_installation.sh#L192-L210
			-- 5.6: https://github.com/mysql/mysql-server/blob/06bc670db0c0e45b3ea11409382a5c315961f682/scripts/mysql_secure_installation.sh#L218-L236
			-- 5.7: https://github.com/mysql/mysql-server/blob/913071c0b16cc03e703308250d795bc381627e37/client/mysql_secure_installation.cc#L792-L818
			-- 8.0: https://github.com/mysql/mysql-server/blob/b93c1661d689c8b7decc7563ba15f6ed140a4eb6/client/mysql_secure_installation.cc#L726-L749
			DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ;
			-- https://github.com/docker-library/mysql/pull/479#issuecomment-414561272 ("This is only needed for 5.5 and 5.6")
		EOSQL
	else
		# no, we don't care if read finds a terminating character in this heredoc (see above)
		read -r -d '' passwordSet <<-EOSQL || true
			ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ;
		EOSQL
	fi

	# tell docker_process_sql to not use MYSQL_ROOT_PASSWORD since it is just now being set
	docker_process_sql --dont-use-mysql-root-password --database=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;

		${passwordSet}
		GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ;
		FLUSH PRIVILEGES ;
		${rootCreate}
		DROP DATABASE IF EXISTS test ;
	EOSQL

	# Creates a custom database and user if specified
	if [ -n "$MYSQL_DATABASE" ]; then
		mysql_note "Creating database ${MYSQL_DATABASE}"
		docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;"
	fi

	if [ -n "$MYSQL_USER" ] && [ -n "$MYSQL_PASSWORD" ]; then
		mysql_note "Creating user ${MYSQL_USER}"
		docker_process_sql --database=mysql <<<"CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;"

		if [ -n "$MYSQL_DATABASE" ]; then
			mysql_note "Giving user ${MYSQL_USER} access to schema ${MYSQL_DATABASE}"
			docker_process_sql --database=mysql <<<"GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;"
		fi

		docker_process_sql --database=mysql <<<"FLUSH PRIVILEGES ;"
	fi
}

_mysql_passfile() {
	# echo the password to the "file" the client uses
	# the client command will use process substitution to create a file on the fly
	# ie: --defaults-file=<( _mysql_passfile )
	if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MYSQL_ROOT_PASSWORD" ]; then
		cat <<-EOF
			[client]
			password="${MYSQL_ROOT_PASSWORD}"
		EOF
	fi
}

# Mark root user as expired so the password must be changed before anything
# else can be done (only supported for 5.6+)
mysql_expire_root_user() {
	if [ -n "$MYSQL_ONETIME_PASSWORD" ]; then
		docker_process_sql --database=mysql <<-EOSQL
			ALTER USER 'root'@'%' PASSWORD EXPIRE;
		EOSQL
	fi
}

# check arguments for an option that would cause mysqld to stop
# return true if there is one
_mysql_want_help() {
	local arg
	for arg; do
		case "$arg" in
			-'?'|--help|--print-defaults|-V|--version)
				return 0
				;;
		esac
	done
	return 1
}

_main() {
	# if command starts with an option, prepend mysqld
	if [ "${1:0:1}" = '-' ]; then
		set -- mysqld "$@"
	fi

	# skip setup if they aren't running mysqld or want an option that stops mysqld
	if [ "$1" = 'mysqld' ] && ! _mysql_want_help "$@"; then
		mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started."

		mysql_check_config "$@"
		# Load various environment variables
		docker_setup_env "$@"
		docker_create_db_directories

		# If container is started as root user, restart as dedicated mysql user
		if [ "$(id -u)" = "0" ]; then
			mysql_note "Switching to dedicated user 'mysql'"
			exec gosu mysql "$BASH_SOURCE" "$@"
		fi

		# there's no database, so it needs to be initialized
		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
			docker_verify_minimum_env
			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 "$@"
}

# If we are sourced from elsewhere, don't perform any further actions
if ! _is_sourced; then
	_main "$@"
fi
lwk@harbin:~/Public/project/github/mysql$ 

我们看到,文件docker-entrypoint.sh主要是对生成的docker容器做了些初始化工作,Docker容器启动的时候传进去的参数也会在这里被处理掉。

External analysis

Run

在Linux命令执行如下脚本即可创建一个mysql的Docker实例。

lwk@harbin:/data/docker/mysql$ docker run --detach \
> --restart always \
> --publish 3306:3306 \
> --name mysql \
> --volume /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime \
> --env MYSQL_ROOT_PASSWORD=Gah6kuP7ohfio4 \
> mysql:5.7.28 \
> --character-set-server=utf8mb4 \
> --collation-server=utf8mb4_unicode_ci
24d838bb91df5327834af53466d248cf091fde28d6923de66f5029d0319fb12a
lwk@harbin:/data/docker/mysql$ docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                                                  NAMES
24d838bb91df   mysql:5.7.28   "docker-entrypoint.s…"   8 seconds ago   Up 6 seconds   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   mysql
lwk@harbin:/data/docker/mysql$ 

这个时候,我们看到已经启动了一个mysql的docker实例。

Inspect with docker

我们再用docker inspect查看一下。

lwk@harbin:/data/docker/mysql$ docker inspect mysql
[
    {
        "Id": "24d838bb91df5327834af53466d248cf091fde28d6923de66f5029d0319fb12a",
        "Created": "2022-03-23T08:54:32.978883297Z",
        "Path": "docker-entrypoint.sh",
        "Args": [
            "--character-set-server=utf8mb4",
            "--collation-server=utf8mb4_unicode_ci"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 26597,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2022-03-23T08:54:33.315240494Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:db39680b63ac47a1d989da7b742f7b382af34d85a68214f8977bad59c05901a6",
        "ResolvConfPath": "/var/lib/docker/containers/24d838bb91df5327834af53466d248cf091fde28d6923de66f5029d0319fb12a/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/24d838bb91df5327834af53466d248cf091fde28d6923de66f5029d0319fb12a/hostname",
        "HostsPath": "/var/lib/docker/containers/24d838bb91df5327834af53466d248cf091fde28d6923de66f5029d0319fb12a/hosts",
        "LogPath": "/var/lib/docker/containers/24d838bb91df5327834af53466d248cf091fde28d6923de66f5029d0319fb12a/24d838bb91df5327834af53466d248cf091fde28d6923de66f5029d0319fb12a-json.log",
        "Name": "/mysql",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "docker-default",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": [
                "/usr/share/zoneinfo/Asia/Shanghai:/etc/localtime"
            ],
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {
                "3306/tcp": [
                    {
                        "HostIp": "",
                        "HostPort": "3306"
                    }
                ]
            },
            "RestartPolicy": {
                "Name": "always",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "CgroupnsMode": "host",
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/a24ba560f93f4b532ba065051269fa2986ba0b2bd8d0170c4eda7abdda0bdfc9-init/diff:/var/lib/docker/overlay2/a31a2c5da9b51c231d4d78b37878043d0609d421b31cfff59175607265e19177/diff:/var/lib/docker/overlay2/743a94eaa9d844ff29b68e5fb7784d5f381ca9339f171f82c55d7bca34462e20/diff:/var/lib/docker/overlay2/b4ee6d62e35e82404ccb44ad4c288bacd8f84baf3393d5e2ca74271ad1ee7a66/diff:/var/lib/docker/overlay2/29b74f60917b4d40a05c9ff14f893025ec146606cde6d05dc391a8e9bea09f2d/diff:/var/lib/docker/overlay2/398ace072c15982dcab9b9c7b15cd39610f901bdfa8f1212776371f8a1c3c9bf/diff:/var/lib/docker/overlay2/387776aa58b582052607f745bd283e2e8070dfa93c5ff4681835c100b9390d8b/diff:/var/lib/docker/overlay2/5b2ccbfb57bba65c49ee3280bb35f3ee3cd257804c042a189947c4ffcb94405c/diff:/var/lib/docker/overlay2/3469db242a79018c8108b00f1dc4ff00e8a439b948c3c6591fc6ce050fa5c506/diff:/var/lib/docker/overlay2/cf951b163c28571865b1ff62958752962a32b0c5f35e22e0eab11efe1d6a0fff/diff:/var/lib/docker/overlay2/95fc9b4b73b1a1e67334599d17ef6c402b40f97a9f1565633659ce904b8f5ddd/diff:/var/lib/docker/overlay2/bf9567587ab9d4e9a64101c87e058d001baf51711e294dfb834515e4e1886049/diff",
                "MergedDir": "/var/lib/docker/overlay2/a24ba560f93f4b532ba065051269fa2986ba0b2bd8d0170c4eda7abdda0bdfc9/merged",
                "UpperDir": "/var/lib/docker/overlay2/a24ba560f93f4b532ba065051269fa2986ba0b2bd8d0170c4eda7abdda0bdfc9/diff",
                "WorkDir": "/var/lib/docker/overlay2/a24ba560f93f4b532ba065051269fa2986ba0b2bd8d0170c4eda7abdda0bdfc9/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/usr/share/zoneinfo/Asia/Shanghai",
                "Destination": "/etc/localtime",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            },
            {
                "Type": "volume",
                "Name": "068b674dcf99be98be576e9fb2dc0f6cd8d7e3e98636722b53b5a0d9472299e6",
                "Source": "/var/lib/docker/volumes/068b674dcf99be98be576e9fb2dc0f6cd8d7e3e98636722b53b5a0d9472299e6/_data",
                "Destination": "/var/lib/mysql",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
        "Config": {
            "Hostname": "24d838bb91df",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "3306/tcp": {},
                "33060/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "MYSQL_ROOT_PASSWORD=Gah6kuP7ohfio4",
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "GOSU_VERSION=1.7",
                "MYSQL_MAJOR=5.7",
                "MYSQL_VERSION=5.7.28-1debian9"
            ],
            "Cmd": [
                "--character-set-server=utf8mb4",
                "--collation-server=utf8mb4_unicode_ci"
            ],
            "Image": "mysql:5.7.28",
            "Volumes": {
                "/var/lib/mysql": {}
            },
            "WorkingDir": "",
            "Entrypoint": [
                "docker-entrypoint.sh"
            ],
            "OnBuild": null,
            "Labels": {}
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "c2b5930a6963a12379fa0f31c96c9ac034bd79202964be99a0f865761523bf66",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {
                "3306/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "3306"
                    },
                    {
                        "HostIp": "::",
                        "HostPort": "3306"
                    }
                ],
                "33060/tcp": null
            },
            "SandboxKey": "/var/run/docker/netns/c2b5930a6963",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "51e7f168209783770db9ebd450cf5f9c8c3c34fef0b8dd4fc66b68fd00a3d1a7",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "1cd2c2db90b8c41595d080f78f4c07bc8007ec8a8fc6c9528a844793b06bd1a0",
                    "EndpointID": "51e7f168209783770db9ebd450cf5f9c8c3c34fef0b8dd4fc66b68fd00a3d1a7",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
        }
    }
]
lwk@harbin:/data/docker/mysql$

Internal analysis

接下来,我们进入到容器内部看一下

lwk@harbin:/data/docker/mysql$ docker exec -it mysql /bin/bash
root@24d838bb91df:/# ll
bash: ll: command not found
root@24d838bb91df:/# ls -la
total 80
drwxr-xr-x   1 root root 4096 Mar 23 16:54 .
drwxr-xr-x   1 root root 4096 Mar 23 16:54 ..
-rwxr-xr-x   1 root root    0 Mar 23 16:54 .dockerenv
drwxr-xr-x   1 root root 4096 Dec 29  2019 bin
drwxr-xr-x   2 root root 4096 Sep  8  2019 boot
drwxr-xr-x   5 root root  340 Mar 23 16:54 dev
drwxr-xr-x   2 root root 4096 Dec 29  2019 docker-entrypoint-initdb.d
lrwxrwxrwx   1 root root   34 Dec 29  2019 entrypoint.sh -> usr/local/bin/docker-entrypoint.sh
drwxr-xr-x   1 root root 4096 Mar 23 16:54 etc
drwxr-xr-x   2 root root 4096 Sep  8  2019 home
drwxr-xr-x   1 root root 4096 Dec 24  2019 lib
drwxr-xr-x   2 root root 4096 Dec 24  2019 lib64
drwxr-xr-x   2 root root 4096 Dec 24  2019 media
drwxr-xr-x   2 root root 4096 Dec 24  2019 mnt
drwxr-xr-x   2 root root 4096 Dec 24  2019 opt
dr-xr-xr-x 386 root root    0 Mar 23 16:54 proc
drwx------   1 root root 4096 Dec 29  2019 root
drwxr-xr-x   1 root root 4096 Dec 29  2019 run
drwxr-xr-x   2 root root 4096 Dec 24  2019 sbin
drwxr-xr-x   2 root root 4096 Dec 24  2019 srv
dr-xr-xr-x  13 root root    0 Mar 23 16:54 sys
drwxrwxrwt   1 root root 4096 Mar 23 16:54 tmp
drwxr-xr-x   1 root root 4096 Dec 24  2019 usr
drwxr-xr-x   1 root root 4096 Dec 24  2019 var
root@24d838bb91df:/#

刚才我们通过命令docker inspect探测容器实例的时候,发现容器启动的时候执行了脚本/entrypoint.sh

接下来,我们探查一下mysql的配置文件目录相关信息。

/etc/mysql

root@24d838bb91df:/# ls -la /etc/mysql/
total 24
drwxr-xr-x 4 root root 4096 Dec 29  2019 .
drwxr-xr-x 1 root root 4096 Mar 23 16:54 ..
drwxr-xr-x 2 root root 4096 Dec 29  2019 conf.d
lrwxrwxrwx 1 root root   24 Dec 29  2019 my.cnf -> /etc/alternatives/my.cnf
-rw-r--r-- 1 root root  839 Jul 10  2016 my.cnf.fallback
-rw-r--r-- 1 root root 1215 Sep 27  2019 mysql.cnf
drwxr-xr-x 2 root root 4096 Dec 29  2019 mysql.conf.d
root@24d838bb91df:/#

我们看到这是一个debian系统下通过apt命令安装mysql后标准配置文件目录。

root@24d838bb91df:/# ls -la /etc/alternatives/my.cnf 
lrwxrwxrwx 1 root root 20 Dec 29  2019 /etc/alternatives/my.cnf -> /etc/mysql/mysql.cnf
root@24d838bb91df:/#

我们看到my.cnf其实是mysql.cnf的软链接。接下来看一下这个文件做了哪些事。

root@24d838bb91df:/# cat /etc/mysql/my.cnf
# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2.0,
# as published by the Free Software Foundation.
#
# This program is also distributed with certain software (including
# but not limited to OpenSSL) that is licensed under separate terms,
# as designated in a particular file or component or in included license
# documentation.  The authors of MySQL hereby grant you an additional
# permission to link the program and your derivative works with the
# separately licensed software that they have included with MySQL.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License, version 2.0, for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/
root@24d838bb91df:/# 

我们看到该文件将目录/etc/mysql/conf.d//etc/mysql/mysql.conf.d/包含了进来 ,也就是说,这两个目录下的配置文件都会被系统自动加载。

/etc/mysql/conf.d

root@24d838bb91df:/# ls -la /etc/mysql/conf.d/
total 20
drwxr-xr-x 2 root root 4096 Dec 29  2019 .
drwxr-xr-x 4 root root 4096 Dec 29  2019 ..
-rw-r--r-- 1 root root   43 Dec 29  2019 docker.cnf
-rw-r--r-- 1 root root    8 Jul 10  2016 mysql.cnf
-rw-r--r-- 1 root root   55 Jul 10  2016 mysqldump.cnf
root@24d838bb91df:/# cat /etc/mysql/conf.d/docker.cnf 
[mysqld]
skip-host-cache
skip-name-resolve
root@24d838bb91df:/# cat /etc/mysql/conf.d/mysql.cnf  
[mysql]
root@24d838bb91df:/# cat /etc/mysql/conf.d/mysqldump.cnf 
[mysqldump]
quick
quote-names
max_allowed_packet	= 16M
root@24d838bb91df:/#

我们看到,目录/etc/mysql/conf.d/下的文件主要涉及mysql客户端管理相关的配置。

/etc/mysql/mysql.conf.d

max_allowed_packet	= 16M
root@24d838bb91df:/# ls -la /etc/mysql/mysql.conf.d/
total 12
drwxr-xr-x 2 root root 4096 Dec 29  2019 .
drwxr-xr-x 4 root root 4096 Dec 29  2019 ..
-rw-r--r-- 1 root root 1610 Dec 29  2019 mysqld.cnf
root@24d838bb91df:/# cat /etc/mysql/mysql.conf.d/mysqld.cnf 
# Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2.0,
# as published by the Free Software Foundation.
#
# This program is also distributed with certain software (including
# but not limited to OpenSSL) that is licensed under separate terms,
# as designated in a particular file or component or in included license
# documentation.  The authors of MySQL hereby grant you an additional
# permission to link the program and your derivative works with the
# separately licensed software that they have included with MySQL.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License, version 2.0, for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

#
# The MySQL  Server configuration file.
#
# For explanations see
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

[mysqld]
pid-file	= /var/run/mysqld/mysqld.pid
socket		= /var/run/mysqld/mysqld.sock
datadir		= /var/lib/mysql
#log-error	= /var/log/mysql/error.log
# By default we only accept connections from localhost
#bind-address	= 127.0.0.1
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
root@24d838bb91df:/#

目录/etc/mysql/mysql.conf.d/下面的内容主要涉及mysql服务端的相关配置。

Summary

看到这里,我们可以得出如下结论,只要我们将文件/etc/mysql/mysql.cnf映射到docker容器外部的宿主机上特定目录下的同名文件上,这个时候,对外部外部同名文件的修改都会自动被mysql容器加载,进而实现对mysql容器的定制化配置。

 类似资料: