x86 平台使用 buildx 基于源码构建 MySQL Wsrep 5.7.44 镜像
- 一、构建环境
- 二、构建脚本
- [三、使用 docker 启动容器](#三、使用 docker 启动容器)
- [四、使用 docker-compose 运行 Galera 集群](#四、使用 docker-compose 运行 Galera 集群)
-
- 4.1、创建持久化目录和相关文件
-
- [4.1.1 mysql1.cnf](#4.1.1 mysql1.cnf)
- [4.1.2 mysql2.cnf](#4.1.2 mysql2.cnf)
- [4.1.3 mysql3.cnf](#4.1.3 mysql3.cnf)
- 4.2、galera-docker-compose.yml
- 4.3、运行命令
- 五、环境变量说明
一、构建环境



二、构建脚本
创建一个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,否则从节点会初始化失败