x86 平台使用 buildx 基于源码构建 MySQL Wsrep 5.7.44 镜像

x86 平台使用 buildx 基于源码构建 MySQL Wsrep 5.7.44 镜像

一、构建环境


二、构建脚本

创建一个shell脚本

bash 复制代码
#!/bin/bash
set -e  # 添加错误处理

#镜像架构设置:x86、arm64
platform=x86

version=3-25.3.37
targetV=$(echo "${version}" | awk -F'[-.]' '{print $3"."$4}')

if [ ! -f "galera-${version}.tar.gz" ]; then
    echo "下载 galera-${version} 源码包..."
    if ! curl -L -f \
    --retry 3 \
    -H 'User-Agent: Mozilla/5.0' \
    -o galera-${version}.tar.gz \
    "https://releases.galeracluster.com/galera-${targetV}/source/galera-${version}.tar.gz"; then
        echo "错误:galera-${version} 源码包下载失败"
        exit 1
    fi
fi

if [ ! -f "boost_1_59_0.tar.gz" ]; then
	echo "下载 boost_1_59_0 源码包..."
    if ! curl -L -kf \
    --retry 3 \
    -H 'User-Agent: Mozilla/5.0' \
    -o boost_1_59_0.tar.gz \
    "https://archives.boost.io/release/1.59.0/source/boost_1_59_0.tar.gz"; then
        echo "错误:boost_1_59_0 源码包下载失败"
        exit 1
    fi
fi 

mysql_version=5.7.44-25.36
if [ ! -f "mysql-wsrep-${mysql_version}.tar.gz" ]; then
	echo "下载 mysql-wsrep-${mysql_version} 源码包..."
    if ! curl -L -f \
    --retry 3 \
    -H 'User-Agent: Mozilla/5.0' \
    -o mysql-wsrep-${mysql_version}.tar.gz \
    "https://releases.galeracluster.com/mysql-wsrep-${mysql_version}/source/mysql-wsrep-${mysql_version}.tar.gz"; then
        echo "错误:mysql-wsrep-${mysql_version} 源码包下载失败"
        exit 1
    fi
fi

# 2. 设置环境变量
export DOCKER_BUILDKIT=1
export DOCKER_CLI_EXPERIMENTAL=enabled

BUILDER_NAME="mybuilder"

echo "设置 Docker Buildx 构建器..."
rm -rf /tmp/buildkitd.toml
cat > /tmp/buildkitd.toml << 'EOF'
debug = true
root = "/var/lib/buildkit"
[registry."docker.io"]
  mirrors = [
  		"https://e2zfh98i.mirror.aliyuncs.com",
  		"https://mirror.baidubce.com",
  		"https://docker.mirrors.ustc.edu.cn",
  		"https://docker-cf.registry.cyou",
  		"https://dockercf.jsdelivr.fyi",
  		"https://docker.jsdelivr.fyi",
  		"https://dockertest.jsdelivr.fyi",
  		"https://dockerproxy.com",
  		"https://docker.m.daocloud.io",
  		"https://docker.nju.edu.cn",
  		"https://docker.mirrors.sjtug.sjtu.edu.cn",
  		"https://mirror.iscas.ac.cn",
  		"https://docker.rainbond.cc",
  		"https://do.nark.eu.org",
  		"https://dc.j8.work",
  		"https://dockerproxy.com",
  		"https://gst6rzl9.mirror.aliyuncs.com",
  		"https://registry.docker-cn.com",
  		"http://hub-mirror.c.163.com",
  		"http://mirrors.ustc.edu.cn/",
  		"https://mirrors.tuna.tsinghua.edu.cn/",
  		"http://mirrors.sohu.com/"
	]
EOF

# 检查并创建构建器
if ! docker buildx inspect $BUILDER_NAME >/dev/null 2>&1; then
    echo "创建构建器: $BUILDER_NAME"
    
    docker buildx create \
        --name $BUILDER_NAME \
        --driver docker-container \
        --driver-opt "image=moby/buildkit:master" \
        --platform linux/arm64 \
        --config /tmp/buildkitd.toml \
        --use
else
    echo "构建器已存在: $BUILDER_NAME"
    docker buildx use $BUILDER_NAME
fi

# 启动构建器
docker buildx inspect --bootstrap

# 4. 创建基于 openEuler 的 Dockerfile
echo "创建 openEuler 20.03 lts sp4 版 Dockerfile..."
cat > Dockerfile << 'DOCKERFILE_EOF'
FROM openeuler/openeuler:20.03-lts-sp4 AS mysql_builder

# 设置参数
ARG TARGETARCH
ARG TARGETPLATFORM
RUN echo "目标架构: $TARGETARCH, 目标平台: $TARGETPLATFORM"

# 设置时区和环境变量
ENV TZ=Asia/Shanghai
ENV GALERA_VERSION=3-25.3.37
ENV GALERA_HOME=/app/galera-source
ENV MYSQL_VERSION=5.7.44-25.36
ARG MYSQL_HOME=/usr/local/mysql
ARG MYSQL_DATA_DIR=/var/lib/mysql
ARG MYSQL_LOG_DIR=/var/log/mysql
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8

# 设置 openEuler 镜像源
RUN cd /etc/yum.repos.d/ && cat > openEuler.repo << 'REPOSD_EOF'
[OS]
name=OS
baseurl=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/OS/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/OS/$basearch/RPM-GPG-KEY-openEuler

[everything]
name=everything
baseurl=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/everything/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/everything/$basearch/RPM-GPG-KEY-openEuler

[EPOL]
name=EPOL
baseurl=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/EPOL/main/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/OS/$basearch/RPM-GPG-KEY-openEuler

[debuginfo]
name=debuginfo
baseurl=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/debuginfo/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/debuginfo/$basearch/RPM-GPG-KEY-openEuler

[source]
name=source
baseurl=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/source/
enabled=1
gpgcheck=1
gpgkey=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/source/RPM-GPG-KEY-openEuler

[update]
name=update
baseurl=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/update/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/OS/$basearch/RPM-GPG-KEY-openEuler
REPOSD_EOF

# 安装编译依赖
RUN dnf makecache && \
    dnf install -y \
    tzdata \
    tar \
    bash \
    make \
    gcc \
    gcc-c++ \
    cmake \
    boost-devel \
    boost-program-options \
    openssl \
    openssl-devel \
    check-devel \
    python3 \
    python3-pip \
    glibc-devel \
    glibc-headers \
    kernel-headers \
    expect \
    findutils \
    wget \
    which \
    xz \
    gzip \
    bzip2 \
    curl \
    gcc-gfortran \
    make \
    openssl openssl-devel \
    ncurses-devel \
    libaio-devel \
    numactl-devel \
    zlib-devel \
    bison \
    flex \
    libedit-devel \
    readline-devel \
    pkg-config \
    libtirpc \
    libtirpc-devel \
    perl \
    perl-Data-Dumper \
    perl-Env \
    perl-ExtUtils-MakeMaker \
    perl-ExtUtils-Embed \
    && mkdir -p $GALERA_HOME \
    && ln -sf /usr/share/zoneinfo/$TZ /etc/localtime \
    && echo $TZ > /etc/timezone && dnf clean all

# 设置工作目录
WORKDIR /app

COPY galera-${GALERA_VERSION}.tar.gz /app/
RUN tar -xf galera-${GALERA_VERSION}.tar.gz -C $GALERA_HOME && \
    rm -f galera-${GALERA_VERSION}.tar.gz && mv -f $GALERA_HOME/galera-${GALERA_VERSION}/* $GALERA_HOME/ && \
    rm -rf $GALERA_HOME/galera-${GALERA_VERSION}

WORKDIR $GALERA_HOME

RUN if [ -f "$GALERA_HOME/wsrep/tests/SConscript" ]; then \
        CNT=$(grep -n "env.Test" $GALERA_HOME/wsrep/tests/SConscript | awk -F ":" '{print $1}') && \
        awk -v n="${CNT}" 'NR==n && !/^#/ {print "#", $0; next} {print}' $GALERA_HOME/wsrep/tests/SConscript > /tmp/temp.txt && \
        mv -f /tmp/temp.txt $GALERA_HOME/wsrep/tests/SConscript; \
    else \
        echo "Warning: SConscript file not found at expected location"; \
        find $GALERA_HOME -name "SConscript" -type f; \
    fi
RUN if [ -f "$GALERA_HOME/gcs/src/unit_tests/SConscript" ]; then \
        CNT=$(cat $GALERA_HOME/gcs/src/unit_tests/SConscript | grep -n "env.Append(LIBS" | grep -n "ssl" | awk -F ":" '{print $2}') && \
        awk -v n="${CNT}" 'NR==n && !/^#/ {print "#", $0; next} {print}' $GALERA_HOME/gcs/src/unit_tests/SConscript > /tmp/temp2.txt && \
        mv -f /tmp/temp2.txt $GALERA_HOME/gcs/src/unit_tests/SConscript; \
    else \
        echo "Warning: SConscript file not found at expected location"; \
        find $GALERA_HOME -name "SConscript" -type f; \
    fi
    
RUN which openssl && \
    openssl version && \
    find /usr/include -name "ssl.h" -o -name "openssl" -type d  && \
    echo "Build dependencies check completed"
   
RUN $GALERA_HOME/scripts/build.sh --cmake --co "-DCMAKE_INSTALL_PREFIX=/usr/lib64/galera" && make install

# 为 RPC 创建必要的符号链接
RUN ln -sf /usr/include/tirpc /usr/include/rpc 2>/dev/null || true \
    && ln -sf /usr/include/tirpc/rpc /usr/include/rpc 2>/dev/null || true && mkdir -p /app

COPY boost_1_59_0.tar.gz /app/ 
COPY mysql-wsrep-${MYSQL_VERSION}.tar.gz /app/ 
RUN cd /app && tar -xf mysql-wsrep-${MYSQL_VERSION}.tar.gz \
    && rm -rf mysql-wsrep-${MYSQL_VERSION}.tar.gz \
    && tar -xf boost_1_59_0.tar.gz \
    && rm -rf boost_1_59_0.tar.gz
# 编译 MySQL
WORKDIR /app/mysql-wsrep-${MYSQL_VERSION}
RUN sed -i '/INCLUDE(cmake\/abi_check.cmake)/d' CMakeLists.txt
# 可选:添加调试信息查看修改结果
RUN echo "=== 查看 CMakeLists.txt 修改结果(包含 abi_check 的行)===" \
    && grep -n "abi_check" CMakeLists.txt || echo "未找到 abi_check 相关行"

RUN cmake . \
    -DCMAKE_INSTALL_PREFIX=${MYSQL_HOME} \
    -DMYSQL_DATADIR=${MYSQL_DATA_DIR} \
    -DSYSCONFDIR=/etc \
    -DWITH_INNOBASE_STORAGE_ENGINE=1 \
    -DWITH_ARCHIVE_STORAGE_ENGINE=1 \
    -DWITH_BLACKHOLE_STORAGE_ENGINE=1 \
    -DWITH_READLINE=1 \
    -DWITH_SSL=system \
    -DWITH_ZLIB=system \
    -DWITH_LIBWRAP=0 \
    -DENABLED_LOCAL_INFILE=1 \
    -DMYSQL_UNIX_ADDR=/tmp/mysql.sock \
    -DEXTRA_CHARSETS=all \
    -DDEFAULT_CHARSET=utf8mb4 \
    -DDEFAULT_COLLATION=utf8mb4_general_ci \
    -DWITH_EMBEDDED_SERVER=1 \
    -DENABLE_DOWNLOADS=1 \
    -DBUILD_CONFIG=mysql_release \
    -DWITH_NUMA=ON \
    -DWITH_DEBUG=OFF \
    -DWITH_RAPID=OFF \
    -DWITH_BOOST=/app/boost_1_59_0 \
    -DWITH_WSREP=ON \
    -DWSREP_LIB=/usr/lib64/galera/lib/libgalera_smm.so \
    -DWSREP_INCLUDE_DIR=$GALERA_HOME/wsrep/src \
    -DWITH_UNIT_TESTS=OFF \
    -DWITH_INNODB_DISALLOW_WRITES=ON

RUN make -j$(nproc) && make -j$(nproc) install

# 生产镜像
FROM openeuler/openeuler:20.03-lts-sp4

# 设置参数
ARG MYSQL_VERSION=5.7.44
ARG MYSQL_HOME=/usr/local/mysql
ARG MYSQL_DATA_DIR=/var/lib/mysql
ARG MYSQL_LOG_DIR=/var/log/mysql
ENV PATH=${MYSQL_HOME}/bin:${PATH}

# 设置参数
ENV TZ=Asia/Shanghai
ENV GALERA_HOME=/usr/lib64/galera
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8

# 设置时区和镜像源
RUN cd /etc/yum.repos.d/ && cat > openEuler.repo << 'REPOSD_EOF'
[OS]
name=OS
baseurl=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/OS/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/OS/$basearch/RPM-GPG-KEY-openEuler

[everything]
name=everything
baseurl=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/everything/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/everything/$basearch/RPM-GPG-KEY-openEuler

[EPOL]
name=EPOL
baseurl=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/EPOL/main/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/OS/$basearch/RPM-GPG-KEY-openEuler

[debuginfo]
name=debuginfo
baseurl=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/debuginfo/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/debuginfo/$basearch/RPM-GPG-KEY-openEuler

[source]
name=source
baseurl=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/source/
enabled=1
gpgcheck=1
gpgkey=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/source/RPM-GPG-KEY-openEuler

[update]
name=update
baseurl=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/update/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://repo.huaweicloud.com/openeuler/openEuler-20.03-LTS-SP4/OS/$basearch/RPM-GPG-KEY-openEuler
REPOSD_EOF

RUN dnf makecache && dnf install -y tzdata openssl perl rsync \
    perl-Data-Dumper \
    libaio \
    numactl shadow-utils \
    ncurses-compat-libs \
    procps-ng \
    hostname \
    iproute \
    net-tools \
    lsof \
    socat \
    nc \
    tar \
    sudo \
    gzip && \
    ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
    echo $TZ > /etc/timezone && \
     mkdir -p $GALERA_HOME && mkdir -p /data && dnf clean all

# 修复 PATH
ENV PATH=/usr/sbin:/sbin:$PATH

# 创建软链接
RUN ln -sf /usr/bin/ip /usr/sbin/ip || true

# 从构建阶段复制文件
COPY --from=mysql_builder $GALERA_HOME $GALERA_HOME
COPY --from=mysql_builder $MYSQL_HOME $MYSQL_HOME

# 创建 MySQL 用户和组
RUN groupadd -r mysql \
    && useradd -r -g mysql -s /bin/false -d ${MYSQL_DATA_DIR} mysql \
    && mkdir -p ${MYSQL_HOME} ${MYSQL_DATA_DIR} ${MYSQL_LOG_DIR} \
    && chown -R mysql:mysql ${MYSQL_HOME} ${MYSQL_DATA_DIR} ${MYSQL_LOG_DIR} $GALERA_HOME

COPY my.cnf /etc/my.cnf
# 复制配置文件
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh \
    && mkdir -p /var/run/mysql \
    && chown -R mysql:mysql /var/run/mysql \
    && ln -sf ${MYSQL_HOME}/bin/* /usr/local/bin/ 2>/dev/null || true

# 创建配置
RUN cd /etc && cat > /etc/rsyncd.conf << 'RSYNCD_EOF'
uid = mysql
gid = mysql
use chroot = no
max connections = 10
pid file = /var/run/rsyncd.pid
log file = /var/log/rsyncd.log
port = 8730
timeout = 300
[data]
    path = /data
    comment = Data Directory
    read only = no
    hosts allow = *
RSYNCD_EOF

RUN chown -R mysql:mysql /data && \
    chown -R mysql:mysql /etc/rsyncd.conf && \
    touch /var/run/rsyncd.pid && \
    chown -R mysql:mysql /var/run/rsyncd.pid && \
    touch /var/log/rsyncd.log && \
    chown -R mysql:mysql /var/log/rsyncd.log && \
    chown -R mysql:mysql /etc/my.cnf && \
    chmod 755 /etc/my.cnf

# 验证架构
RUN echo "镜像架构: $(uname -m)"

# 暴露端口
EXPOSE 3306
EXPOSE 33060
EXPOSE 8730

# 设置数据卷
VOLUME ["${MYSQL_DATA_DIR}", "${MYSQL_LOG_DIR}"]

# 设置工作目录
WORKDIR ${MYSQL_HOME}

USER mysql

# 设置入口点
ENTRYPOINT ["docker-entrypoint.sh"]

# 默认命令
CMD ["mysqld"]
DOCKERFILE_EOF

echo "创建my.cnf..."
cat > my.cnf << 'EOF'
[mysqld]
user=mysql
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysql/error.log
pid-file=/var/run/mysql/mysql.pid

wsrep_on=ON
wsrep_provider=/usr/lib64/galera/lib/libgalera_smm.so
wsrep_cluster_name='galera'
wsrep_cluster_address='gcomm://'
wsrep_node_name='node01'
wsrep_node_address='127.0.0.1'
wsrep_sst_auth=galera:123456
wsrep_sst_method=rsync

# 字符集设置
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
init-connect='SET NAMES utf8mb4'

# 连接设置
max_connections=1000
max_connect_errors=1000
connect_timeout=60
wait_timeout=28800
interactive_timeout=28800

# 缓冲池设置
innodb_buffer_pool_size=256M
innodb_log_file_size=128M
innodb_log_buffer_size=16M
innodb_flush_log_at_trx_commit=1
innodb_lock_wait_timeout=50

# 查询缓存设置
query_cache_type=1
query_cache_size=32M
query_cache_limit=1M

# 二进制日志
server-id=1
log-bin=mysql-bin
expire_logs_days=7
binlog_format=ROW

# 慢查询日志
slow_query_log=1
slow_query_log_file=/var/log/mysql/slow.log
long_query_time=2
log_queries_not_using_indexes=1

# 安全设置
skip-name-resolve
local-infile=0
symbolic-links=0

[client]
default-character-set=utf8mb4
socket=/var/lib/mysql/mysql.sock

[mysql]
default-character-set=utf8mb4
EOF

# 5. 创建启动脚本
echo "创建启动脚本..."
cat > docker-entrypoint.sh << 'ENTRYPOINT_EOF'
#!/bin/sh
set -e

echo "=========================================="
echo "Mysql Wsrep openEuler 20.03 lts sp4 版本启动中..."
echo "镜像架构: $(uname -m)"
echo "时区: ${TZ}"
echo "=========================================="

# 设置环境变量
MYSQL_HOME=${MYSQL_HOME:-/usr/local/mysql}
MYSQL_DATA_DIR=${MYSQL_DATA_DIR:-/var/lib/mysql}
MYSQL_LOG_DIR=${MYSQL_LOG_DIR:-/var/log/mysql}
MYSQL_RSYNC_PASSWORD=${MYSQL_RSYNC_PASSWORD:-123456}
WSREP_CLUSTER_ADDRESS=${WSREP_CLUSTER_ADDRESS:-gcomm://}
WSREP_NODE_IP=${WSREP_NODE_IP:-127.0.0.1}

# 启动 rsyncd
echo "启动 rsyncd 服务..."
rsync --daemon --config=/etc/rsyncd.conf
sleep 2
# 检查 rsyncd 是否运行
if pgrep -x "rsync" > /dev/null; then
    echo "rsyncd 已启动,PID: $(pgrep -x "rsync")"
else
    echo "rsyncd 启动失败"
    exit 1
fi

# 设置权限
chown -R mysql:mysql ${MYSQL_DATA_DIR} ${MYSQL_LOG_DIR} /var/run/mysql

# 判断是否是第一个节点
if [ "${WSREP_CLUSTER_ADDRESS}" = "gcomm://" ]; then
    echo "这是主节点,初始化新集群..."
    
    if [ ! -d "${MYSQL_DATA_DIR}/mysql" ]; then
        echo "初始化主节点数据库..."
        
        tempfile=$(mktemp)
        sed "s|^wsrep_cluster_address=.*|wsrep_cluster_address='${WSREP_CLUSTER_ADDRESS}'|" /etc/my.cnf > "$tempfile"
        cat "$tempfile" > /etc/my.cnf
        rm -rf "$tempfile"
        tempfile=$(mktemp)
        sed "s|^wsrep_sst_auth=[^:]*:.*$|wsrep_sst_auth=galera:${MYSQL_RSYNC_PASSWORD}|" /etc/my.cnf > "$tempfile"
        cat "$tempfile" > /etc/my.cnf
        rm -rf "$tempfile"
        tempfile=$(mktemp)
        sed "s|^wsrep_node_address=.*|wsrep_node_address='${WSREP_NODE_IP}'|" /etc/my.cnf > "$tempfile"
        cat "$tempfile" > /etc/my.cnf
        rm -rf "$tempfile"
        
        # 初始化数据库
        ${MYSQL_HOME}/bin/mysqld \
          --initialize-insecure \
          --user=mysql \
          --datadir=${MYSQL_DATA_DIR} \
          --basedir=${MYSQL_HOME}
        
        echo "主节点初始化完成"
        
        # 启动临时服务来设置 root 密码
        ${MYSQL_HOME}/bin/mysqld \
            --user=mysql \
            --datadir=${MYSQL_DATA_DIR} \
            --skip-networking \
            --socket=/tmp/mysql.sock &
        
        pid="$!"
        
        # 等待 MySQL 启动
        for i in {120..0}; do
            if ${MYSQL_HOME}/bin/mysqladmin ping --socket=/tmp/mysql.sock >/dev/null 2>&1; then
                break
            fi
            echo '等待 MySQL 启动...'
            sleep 1
        done
        
        if [ "$i" = 0 ]; then
            echo >&2 'MySQL 启动失败'
            exit 1
        fi
        
        # 设置 rsync 同步用户密码
        mysql --socket=/tmp/mysql.sock -u root -e \
            "grant all on *.* TO 'galera'@'%' identified by '${MYSQL_RSYNC_PASSWORD}';"
        mysql --socket=/tmp/mysql.sock -u root -e \
            "flush privileges;"
        
        # 设置 root 密码
        if [ -n "${MYSQL_ROOT_PASSWORD}" ]; then
            mysql --socket=/tmp/mysql.sock -u root -e \
                "ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}';"
        else
            mysql --socket=/tmp/mysql.sock -u root -e \
                "ALTER USER 'root'@'localhost' IDENTIFIED BY '';"
        fi
        
        # 创建远程 root 用户
        if [ -n "${MYSQL_ROOT_HOST}" ] && [ "${MYSQL_ROOT_HOST}" != "localhost" ]; then
            mysql --socket=/tmp/mysql.sock -u root -p"${MYSQL_ROOT_PASSWORD}" -e \
                "CREATE USER 'root'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}';" || true
            mysql --socket=/tmp/mysql.sock -u root -p"${MYSQL_ROOT_PASSWORD}" -e \
                "GRANT ALL PRIVILEGES ON *.* TO 'root'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION;" || true
        fi
        
        # 停止临时服务
        if ! kill -s TERM "$pid" || ! wait "$pid"; then
            echo >&2 'MySQL 初始化进程失败'
            exit 1
        fi
        
        echo "主节点初始化完成"
    else
        echo "主节点数据已存在,跳过初始化"
    fi
    
    # 以主节点方式启动
    echo "以主节点方式启动 Galera 集群..."
    
else
    echo "从节点,等待加入集群..."
    
    # 从节点不需要初始化数据库,会从主节点同步
    if [ ! -d "${MYSQL_DATA_DIR}/mysql" ]; then
        echo "从节点数据目录为空,将加入集群同步数据"
        
        tempfile=$(mktemp)
        sed "s|^wsrep_cluster_address=.*|wsrep_cluster_address='${WSREP_CLUSTER_ADDRESS}'|" /etc/my.cnf > "$tempfile"
        cat "$tempfile" > /etc/my.cnf
        rm -rf "$tempfile"
        tempfile=$(mktemp)
        sed "s|^wsrep_sst_auth=[^:]*:.*$|wsrep_sst_auth=galera:${MYSQL_RSYNC_PASSWORD}|" /etc/my.cnf > "$tempfile"
        cat "$tempfile" > /etc/my.cnf
        rm -rf "$tempfile"
        tempfile=$(mktemp)
        sed "s|^wsrep_node_address=.*|wsrep_node_address='${WSREP_NODE_IP}'|" /etc/my.cnf > "$tempfile"
        cat "$tempfile" > /etc/my.cnf
        rm -rf "$tempfile"

        touch /var/log/mysql/error.log

    else
        echo "从节点已有数据,清理旧状态文件"
        # 清理旧的 Galera 状态文件
        rm -f ${MYSQL_DATA_DIR}/grastate.dat
        rm -f ${MYSQL_DATA_DIR}/gvwstate.dat
    fi
    
    # 以从节点方式启动
    echo "以从节点方式启动,加入集群..."
fi

# 在后台启动 MySQL
echo "启动 MySQL..."
"$@" &

# 获取 MySQL 进程 ID
MYSQL_PID=$!

# 在前台运行 tail 查看日志
echo "开始输出 MySQL 日志..."
tail -f /var/log/mysql/error.log &

# 捕获退出信号
trap "echo '收到停止信号,停止 MySQL...'; kill $MYSQL_PID" TERM INT

# 等待 MySQL 进程
wait $MYSQL_PID
ENTRYPOINT_EOF

chmod +x docker-entrypoint.sh

if echo "$platform" | grep -q "arm64"; then
    imageTagArm64=mysql-wsrep-openeuler-arm64:5.7.44
    # 执行构建
    if unbuffer docker buildx build \
      --platform linux/arm64 \
      -t ${imageTagArm64} \
      --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
      --load \
      --progress=plain \
      .; then
        echo "✅ 镜像构建成功: ${imageTagArm64}"
        sudo docker push ${imageTagArm64}
    else
        echo "❌ 错误: 镜像构建失败" >&2
        exit 1
    fi
    sudo docker rmi ${imageTagArm64} 2>/dev/null || true
else
    imageTagAmd64=mysql-wsrep-openeuler-amd64:5.7.44
    # 执行构建
    if unbuffer docker buildx build \
      --platform linux/amd64 \
      -t "${imageTagAmd64}" \
      --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
      --progress=plain \
      --load .; then
        echo "✅ 镜像构建成功: ${imageTagAmd64}"
        sudo docker push ${imageTagAmd64}
    else
        echo "❌ 错误: 镜像构建失败" >&2
        exit 1
    fi
    sudo docker rmi ${imageTagAmd64} 2>/dev/null || true
fi

三、使用 docker 启动容器

bash 复制代码
#基本运行
docker run -dit \
  --name mysql \
  -p 3306:3306 -p 4567:4567 -p 4568:4568 -p 4444:4444 -p 8730:8730 \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -e MYSQL_ROOT_HOST=% \
  -e TZ=Asia/Shanghai \
  mysql-wsrep-openeuler-amd64:5.7.44

# 带数据持久化
docker run -d \
  --name mysql \
  -p 3306:3306 -p 4567:4567 -p 4568:4568 -p 4444:4444 -p 8730:8730 \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -e MYSQL_ROOT_HOST=% \
  -e TZ=Asia/Shanghai \
  -v /path/to/mysql/data:/var/lib/mysql \
  -v /path/to/mysql/logs:/var/log/mysql \
  -v /my/custom/my.cnf:/etc/my.cnf \
  mysql-wsrep-openeuler-amd64:5.7.44

四、使用 docker-compose 运行 Galera 集群

4.1、创建持久化目录和相关文件

bash 复制代码
#创建本地持久化目录
mkdir -p /data/mysql{1,2,3}/{data,logs,conf}

# 创建完3个my.cnf文件再授权
chown -R 997:995 /data/mysql{1,2,3}

# 如果需要重新初始化,则删除data目录,重新创建并授权
rm -rf /data/mysql{1,2,3}/data
mkdir -p /data/mysql{1,2,3}/data
chown -R 997:995 /data/mysql{1,2,3}

4.1.1 mysql1.cnf

bash 复制代码
cat > /data/mysql1/conf/my.cnf << 'EOF'
[mysqld]
user=mysql
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysql/error.log
pid-file=/var/run/mysql/mysql.pid

wsrep_on=ON
wsrep_provider=/usr/lib64/galera/lib/libgalera_smm.so
wsrep_cluster_name='galera'
wsrep_cluster_address='gcomm://'
wsrep_node_name='node01'
wsrep_node_address='mysql1'
wsrep_sst_auth=galera:123456
wsrep_sst_method=rsync

# 字符集设置
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
init-connect='SET NAMES utf8mb4'

# 连接设置
max_connections=1000
max_connect_errors=1000
connect_timeout=60
wait_timeout=28800
interactive_timeout=28800

# 缓冲池设置
innodb_buffer_pool_size=256M
innodb_log_file_size=128M
innodb_log_buffer_size=16M
innodb_flush_log_at_trx_commit=1
innodb_lock_wait_timeout=50

# 查询缓存设置
query_cache_type=1
query_cache_size=32M
query_cache_limit=1M

# 二进制日志
server-id=1
log-bin=mysql-bin
expire_logs_days=7
binlog_format=ROW

# 慢查询日志
slow_query_log=1
slow_query_log_file=/var/log/mysql/slow.log
long_query_time=2
log_queries_not_using_indexes=1

# 安全设置
skip-name-resolve
local-infile=0
symbolic-links=0

[client]
default-character-set=utf8mb4
socket=/var/lib/mysql/mysql.sock

[mysql]
default-character-set=utf8mb4
EOF

4.1.2 mysql2.cnf

bash 复制代码
cat > /data/mysql2/conf/my.cnf << 'EOF'
[mysqld]
user=mysql
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysql/error.log
pid-file=/var/run/mysql/mysql.pid

wsrep_on=ON
wsrep_provider=/usr/lib64/galera/lib/libgalera_smm.so
wsrep_cluster_name='galera'
wsrep_cluster_address='gcomm://mysql1'
wsrep_node_name='node02'
wsrep_node_address='mysql2'
wsrep_sst_auth=galera:123456
wsrep_sst_method=rsync

# 字符集设置
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
init-connect='SET NAMES utf8mb4'

# 连接设置
max_connections=1000
max_connect_errors=1000
connect_timeout=60
wait_timeout=28800
interactive_timeout=28800

# 缓冲池设置
innodb_buffer_pool_size=256M
innodb_log_file_size=128M
innodb_log_buffer_size=16M
innodb_flush_log_at_trx_commit=1
innodb_lock_wait_timeout=50

# 查询缓存设置
query_cache_type=1
query_cache_size=32M
query_cache_limit=1M

# 二进制日志
server-id=1
log-bin=mysql-bin
expire_logs_days=7
binlog_format=ROW

# 慢查询日志
slow_query_log=1
slow_query_log_file=/var/log/mysql/slow.log
long_query_time=2
log_queries_not_using_indexes=1

# 安全设置
skip-name-resolve
local-infile=0
symbolic-links=0

[client]
default-character-set=utf8mb4
socket=/var/lib/mysql/mysql.sock

[mysql]
default-character-set=utf8mb4
EOF

4.1.3 mysql3.cnf

bash 复制代码
cat > /data/mysql3/conf/my.cnf << 'EOF'
[mysqld]
user=mysql
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysql/error.log
pid-file=/var/run/mysql/mysql.pid

wsrep_on=ON
wsrep_provider=/usr/lib64/galera/lib/libgalera_smm.so
wsrep_cluster_name='galera'
wsrep_cluster_address='gcomm://mysql1,mysql2'
wsrep_node_name='node03'
wsrep_node_address='mysql3'
wsrep_sst_auth=galera:123456
wsrep_sst_method=rsync

# 字符集设置
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
init-connect='SET NAMES utf8mb4'

# 连接设置
max_connections=1000
max_connect_errors=1000
connect_timeout=60
wait_timeout=28800
interactive_timeout=28800

# 缓冲池设置
innodb_buffer_pool_size=256M
innodb_log_file_size=128M
innodb_log_buffer_size=16M
innodb_flush_log_at_trx_commit=1
innodb_lock_wait_timeout=50

# 查询缓存设置
query_cache_type=1
query_cache_size=32M
query_cache_limit=1M

# 二进制日志
server-id=1
log-bin=mysql-bin
expire_logs_days=7
binlog_format=ROW

# 慢查询日志
slow_query_log=1
slow_query_log_file=/var/log/mysql/slow.log
long_query_time=2
log_queries_not_using_indexes=1

# 安全设置
skip-name-resolve
local-infile=0
symbolic-links=0

[client]
default-character-set=utf8mb4
socket=/var/lib/mysql/mysql.sock

[mysql]
default-character-set=utf8mb4
EOF

4.2、galera-docker-compose.yml

yaml 复制代码
version: '3.3'

services:
  mysql1:
    image: mysql-wsrep-openeuler-amd64:5.7.44
    container_name: mysql1
    hostname: mysql1
    ports:
      - "3306:3306"
    volumes:
      - "/data/mysql1/data:/var/lib/mysql"
      - "/data/mysql1/logs:/var/log/mysql"
      - "/data/mysql1/conf/my.cnf:/etc/my.cnf"
    environment:
      - TZ=Asia/Shanghai
      - MYSQL_ROOT_PASSWORD=123456
      - MYSQL_ROOT_HOST=%
      # 主节点
      - WSREP_CLUSTER_ADDRESS=gcomm://
      - MYSQL_RSYNC_PASSWORD=123456
    networks:
      - mysql_network
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p123456"]
      timeout: 20s
      retries: 10
      interval: 10s
    ulimits:
      nofile:
        soft: 65536
        hard: 65536

  mysql2:
    image: mysql-wsrep-openeuler-amd64:5.7.44
    container_name: mysql2
    hostname: mysql2
    ports:
      - "3307:3306"
    volumes:
      - "/data/mysql2/data:/var/lib/mysql"
      - "/data/mysql2/logs:/var/log/mysql"
      - "/data/mysql2/conf/my.cnf:/etc/my.cnf"
    environment:
      - TZ=Asia/Shanghai
      - MYSQL_ROOT_PASSWORD=123456
      - MYSQL_ROOT_HOST=%
      - WSREP_CLUSTER_ADDRESS=gcomm://mysql1
      - MYSQL_RSYNC_PASSWORD=123456
    networks:
      - mysql_network
    depends_on:
      mysql1:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p123456"]
      timeout: 20s
      retries: 30
      interval: 10s

  mysql3:
    image: mysql-wsrep-openeuler-amd64:5.7.44
    container_name: mysql3
    hostname: mysql3
    ports:
      - "3308:3306"
    volumes:
      - "/data/mysql3/data:/var/lib/mysql"
      - "/data/mysql3/logs:/var/log/mysql"
      - "/data/mysql3/conf/my.cnf:/etc/my.cnf"
    environment:
      - TZ=Asia/Shanghai
      - MYSQL_ROOT_PASSWORD=123456
      - MYSQL_ROOT_HOST=%
      - WSREP_CLUSTER_ADDRESS=gcomm://mysql1,mysql2
      - MYSQL_RSYNC_PASSWORD=123456
    networks:
      - mysql_network
    depends_on:
      mysql2:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p123456"]
      timeout: 20s
      retries: 30
      interval: 10s

networks:
  mysql_network:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16

4.3、运行命令

bash 复制代码
#启动
docker compose -f galera-docker-compose.yml up -d
#停止,注意,停止之后不能直接使用 up -d启动,要根据离开集群的顺序启动
docker compose -f galera-docker-compose.yml down


bash 复制代码
docker exec -it mysql1 /usr/local/bin/mysql -uroot -p123456 -e "show status like 'wsrep%';"
docker exec -it mysql2 /usr/local/bin/mysql -uroot -p123456 -e "show status like 'wsrep%';"
docker exec -it mysql3 /usr/local/bin/mysql -uroot -p123456 -e "show status like 'wsrep%';"
sql 复制代码
-- 查看 WSREP 状态
SHOW STATUS LIKE 'wsrep%';

-- 查看集群节点
SHOW STATUS LIKE 'wsrep_cluster_size';
SHOW STATUS LIKE 'wsrep_cluster_status';
SHOW STATUS LIKE 'wsrep_ready';

五、环境变量说明

  • MYSQL_ROOT_PASSWORD:root用户密码(必须)
  • MYSQL_ROOT_HOST:root远程访问主机,默认为 localhost
  • TZ:时区设置,默认为 Asia/Shanghai
  • LANG:语言设置,默认 C.UTF-8
  • MYSQL_RSYNC_PASSWORD:galera同步用户的密码。默认123456,如果要修改,则设置环境变量
  • WSREP_CLUSTER_ADDRESS:wsrep集群地址,默认:gcomm://,从节点必须设置,否则都会按照主节点初始化
  • WSREP_NODE_IP:节点ip,默认127.0.0.1。从节点必填,需要填从节点的ip或者hostname,否则从节点会初始化失败
相关推荐
沙漏无语4 小时前
(二)TIDB搭建正式集群
linux·数据库·tidb
姚不倒4 小时前
三节点 TiDB 集群部署与负载均衡搭建实战
运维·数据库·分布式·负载均衡·tidb
隔壁小邓4 小时前
批量更新方式与对比
数据库
数据知道4 小时前
MongoDB复制集架构原理:Primary、Secondary 与 Arbiter 的角色分工
数据库·mongodb·架构
人道领域4 小时前
苍穹外卖:菜品新增功能全流程解析
数据库·后端·状态模式
修行者Java4 小时前
(七)从 “非结构化数据难存储” 到 “MongoDB 灵活赋能”——MongoDB 实战进阶指南
数据库·mongodb
小哥哥咯4 小时前
Oracle 19c 与 MySQL 8.0 字符串数据类型对比
mysql·oracle
野犬寒鸦4 小时前
TCP协议核心:TCP详细图解及TCP与UDP核心区别对比(附实战解析)
服务器·网络·数据库·后端·面试