基于StatefulSet搭建MySQL主从

https://github.com/docker-library/mysql 可去下载构建镜像的文件。

1. 构建master镜像

1.1 Dockerfile

#
# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh"
#
# PLEASE DO NOT EDIT IT DIRECTLY.
#

FROM debian:buster-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
# https://github.com/tianon/gosu/releases
ENV GOSU_VERSION 1.14
RUN set -eux; \
    savedAptMark="$(apt-mark showmanual)"; \
    apt-get update; \
    apt-get install -y --no-install-recommends ca-certificates wget; \
    rm -rf /var/lib/apt/lists/*; \
    dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
    wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
    wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
    export GNUPGHOME="$(mktemp -d)"; \
    gpg --batch --keyserver hkps://keys.openpgp.org --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; \
    apt-mark auto '.*' > /dev/null; \
    [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
    apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
    chmod +x /usr/local/bin/gosu; \
    gosu --version; \
    gosu nobody true

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 \
# install "xz-utils" for .sql.xz docker-entrypoint-initdb.d files
        xz-utils \
    && 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 keyserver.ubuntu.com --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 8.0
ENV MYSQL_VERSION 8.0.27-1debian10

RUN echo 'deb http://repo.mysql.com/apt/debian/ buster mysql-8.0' > /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-community-client="${MYSQL_VERSION}" \
        mysql-community-server-core="${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 1777 /var/run/mysqld /var/lib/mysql

VOLUME /var/lib/mysql

ENV AUTO_RUN_DIR /docker-entrypoint-initdb.d

# Config files
COPY config/ /etc/mysql/
COPY docker-entrypoint.sh /usr/local/bin/
COPY init.sh $AUTO_RUN_DIR/
RUN chmod a+x $AUTO_RUN_DIR/init.sh
RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat

ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 3306 33060
CMD ["mysqld"]

1.2 docker-entrypoint.sh

#!/bin/bash
set -eo pipefail
shopt -s nullglob

# logging functions
mysql_log() {
    local type="$1"; shift
    # accept argument string or stdin
    local text="$*"; if [ "$#" -eq 0 ]; then text="$(cat)"; fi
    local dt; dt="$(date --rfc-3339=seconds)"
    printf '%s [%s] [Entrypoint]: %s\n' "$dt" "$type" "$text"
}
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)
                # https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
                # https://github.com/docker-library/postgres/pull/452
                if [ -x "$f" ]; then
                    mysql_note "$0: running $f"
                    "$f"
                else
                    mysql_note "$0: sourcing $f"
                    . "$f"
                fi
                ;;
            *.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 ;;
            *.sql.xz) mysql_note "$0: running $f"; xzcat "$f" | docker_process_sql; echo ;;
            *)        mysql_warn "$0: ignoring $f" ;;
        esac
        echo
    done
}

# arguments necessary to run "mysqld --verbose --help" successfully (used for testing configuration validity and for extracting default/configured values)
_verboseHelpArgs=(
    --verbose --help
    --log-bin-index="$(mktemp -u)" # https://github.com/docker-library/mysql/issues/136
)

mysql_check_config() {
    local toRun=( "$@" "${_verboseHelpArgs[@]}" ) 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
    "$@" "${_verboseHelpArgs[@]}" 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' ] || [ "${MYSQL_MAJOR}" = '5.7' ]; then
        "$@" --skip-networking --default-time-zone=SYSTEM --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 initialized
            # 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 --default-time-zone=SYSTEM --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 <<-'EOF'
            Database is uninitialized and password option is not specified
                You need to specify one of the following:
                - MYSQL_ROOT_PASSWORD
                - MYSQL_ALLOW_EMPTY_PASSWORD
                - MYSQL_RANDOM_ROOT_PASSWORD
        EOF
    fi

    # This will prevent the CREATE USER from failing (and thus exiting with a half-initialized database)
    if [ "$MYSQL_USER" = 'root' ]; then
        mysql_error <<-'EOF'
            MYSQL_USER="root", MYSQL_USER and MYSQL_PASSWORD are for configuring a regular user and cannot be used for the root user
                Remove MYSQL_USER="root" and use one of the following to control the root user password:
                - MYSQL_ROOT_PASSWORD
                - MYSQL_ALLOW_EMPTY_PASSWORD
                - MYSQL_RANDOM_ROOT_PASSWORD
        EOF
    fi

    # warn when missing one of MYSQL_USER or MYSQL_PASSWORD
    if [ -n "$MYSQL_USER" ] && [ -z "$MYSQL_PASSWORD" ]; then
        mysql_warn 'MYSQL_USER specified, but missing MYSQL_PASSWORD; MYSQL_USER will not be created'
    elif [ -z "$MYSQL_USER" ] && [ -n "$MYSQL_PASSWORD" ]; then
        mysql_warn 'MYSQL_PASSWORD specified, but missing MYSQL_USER; MYSQL_PASSWORD will be ignored'
    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}" --default-time-zone=SYSTEM
    else
        "$@" --initialize-insecure --default-time-zone=SYSTEM
    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-extra-file=<( _mysql_passfile "${passfileArgs[@]}") --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" --comments "$@"
}

# 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
    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-extra-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

            # check dir permissions to reduce likelihood of half-initialized database
            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 "$@"
}

# If we are sourced from elsewhere, don't perform any further actions
if ! _is_sourced; then
    _main "$@"
fi

1.3 init.sh

#!/bin/bash
mysql -uroot -p$MYSQL_ROOT_PASSWORD <<EOF
CREATE USER '$MYSQL_REPLICATION_USER'@'%' IDENTIFIED WITH 'mysql_native_password' BY '$MYSQL_REPLICATION_PASSWORD';
GRANT REPLICATION SLAVE ON *.* TO '$MYSQL_REPLICATION_USER'@'%';
FLUSH PRIVILEGES ;
EOF

1.4 config

1.4.1 my.cnf

# Copyright (c) 2017, 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 as published by
# the Free Software Foundation; version 2 of the License.
#
# 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 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
secure-file-priv= NULL

# Custom config should go here
!includedir /etc/mysql/conf.d/

1.4.2 conf.d/docker.cnf

[mysqld]
server-id=1
log-bin
skip-host-cache
skip-name-resolve

1.5 构建镜像

这里使用了阿里云个人版 容器镜像服务

docker build -t registry.cn-shanghai.aliyuncs.com/ligan0404/mysql-master:v1.1.7 .
docker push registry.cn-shanghai.aliyuncs.com/ligan0404/mysql-master:v1.1.7

2. 构建slave镜像

2.1 Dockerfile

和master一样的,在ENTRYPOINT上面加上下面的命令即可:

RUN RAND="$(date +%s | rev | cut -c 1-2)$(echo ${RANDOM})" && sed -i '/\[mysqld\]/a server-id='$RAND'\nlog-bin' /etc/mysql/conf.d/docker.cnf

2.2 docker-entrypoint.sh

和master一样的,没任何变化。

2.3 init.sh

构建主从语句

#!/bin/bash
mysql -uroot -p$MYSQL_ROOT_PASSWORD <<EOF
CREATE USER '$MYSQL_REPLICATION_USER'@'%' IDENTIFIED WITH 'mysql_native_password' BY '$MYSQL_REPLICATION_PASSWORD';
GRANT REPLICATION SLAVE ON *.* TO '$MYSQL_REPLICATION_USER'@'%';
FLUSH PRIVILEGES ;
EOF

2.4 config

2.4.1 my.cnf

和master一致,无变化。

2.4.2 conf.d/docker.cnf

没有server-id了,通过dockerfile去随机生成了。

[mysqld]
skip-host-cache
skip-name-resolve

2.5 构建镜像

docker build -t registry.cn-shanghai.aliyuncs.com/ligan0404/mysql-slave:v1.1.0 .
docker push registry.cn-shanghai.aliyuncs.com/ligan0404/mysql-slave:v1.1.0

3. 创建pv,pvc(nfs)

storageClassName: nfs-data 的创建,直接参考 NFS存储 动态创建 PVC PV文档。

4. 创建master StatefulSet,svc

apiVersion: apps/v1
kind: StatefulSet
metadata:
  annotations: {}
  labels:
    app: mysql-master
  name: mysql-master
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql-master
  template:
    metadata:
      labels:
        app: mysql-master
    spec:
      imagePullSecrets:
      - name: ali-secret
      containers:
        - env:
            - name: MYSQL_ROOT_PASSWORD
              value: '123456'
            - name: MYSQL_REPLICATION_USER
              value: repl
            - name: MYSQL_REPLICATION_PASSWORD
              value: '123456'
          image: 'registry.cn-shanghai.aliyuncs.com/ligan0404/mysql-master:v1.0.0'
          imagePullPolicy: Always
          name: mysql-master
          ports:
            - containerPort: 3306
              name: mysql-master
              protocol: TCP
          volumeMounts:
            - mountPath: /var/lib/mysql
              name: pv-nfs-mysql-master
  volumeClaimTemplates:
  - metadata:
      name: pv-nfs-mysql-master
    spec:
      accessModes: [ "ReadWriteMany","ReadWriteOnce"]
      resources:
        requests:
          storage: 1G
      storageClassName: nfs-data
---
apiVersion: v1
kind: Service
metadata:
  annotations: {}
  labels:
    app: mysql-master
  name: mysql-master
spec:
  ports:
    - name: mysql-master
      nodePort: 30066
      port: 3306
      protocol: TCP
      targetPort: 3306
  selector:
    app: mysql-master
  type: NodePort

5. 创建slave StatefulSet,svc

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  annotations: {}
  labels:
    app: mysql-slave
  name: mysql-slave
spec:
  replicas: 2
  selector:
    matchLabels:
      app: mysql-slave
  template:
    metadata:
      labels:
        app: mysql-slave
    spec:
      containers:
        - env:
            - name: MYSQL_ROOT_PASSWORD
              value: '123456'
            - name: MYSQL_REPLICATION_USER
              value: repl
            - name: MYSQL_REPLICATION_PASSWORD
              value: '123456'
          image: 'registry.cn-shanghai.aliyuncs.com/ligan0404/mysql-slave:v1.1.0'
          imagePullPolicy: Always
          name: mysql-slave
          ports:
            - containerPort: 3306
              name: mysql-slave
              protocol: TCP
          volumeMounts:
            - mountPath: /var/lib/mysql
              name: pv-nfs-mysql-slave
      imagePullSecrets:
        - name: ali-secret
  volumeClaimTemplates:
  - metadata:
      name: pv-nfs-mysql-slave
    spec:
      accessModes: [ "ReadWriteMany","ReadWriteOnce"]
      resources:
        requests:
          storage: 1G
      storageClassName: nfs-data

---
apiVersion: v1
kind: Service
metadata:
  annotations: {}
  labels:
    app: mysql-slave
  name: mysql-slave
spec:
  ports:
    - name: mysql-slave
      port: 3306
      protocol: TCP
      targetPort: 3306
  selector:
    app: mysql-slave
  type: NodePort

6. 测试

master中执行:

mysql -uroot -p
show master status;
create database test; use test; create table test_tb(id int(3),name char(10)); insert into test_tb values(001,'ok');

slave执行:

mysql -uroot -p
show slave status\G;

show databases;
use test;
show tables;
select* from test_tb;

7. 参考资料

K8S 使用NFS存储 动态创建 PVC/PV 并通过 Statefulset 部署 MySQL
kubernetes部署mysql
基于K8s部署MySQL cluster
在kubernetes集群中部署mysql主从
利用Kubernetes搭建mysql主从复制集群
k8s使用nfs持久存储mysql数据的一次踩坑
k8s搭建mysql主从同步
使用Docker快速部署MySQL并初始数据
Dockerfile 部署MySql 8并初始化数据脚本

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345