news 2026/9/18 13:25:59

Docker 容器化 MySQL 8.0 GTID 主从复制实战与排错

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Docker 容器化 MySQL 8.0 GTID 主从复制实战与排错

1. 我为什么坚持用 Docker 跑 MySQL 主从而不是装两台虚拟机

前阵子帮同事在测试环境里搭一套 MySQL 主从复制,他原本的计划是开两台虚拟机,各自装一遍 MySQL 8.0,再手动改配置文件、开防火墙端口、配账号。我看了眼他那台 16G 内存的开发机,直接把他拦下来了。用一个自定义 Docker 网络加两个容器,一主一从的复制链路跑起来只花了十几分钟,删掉也只需要两条docker rm -f

这就是我用 Docker 做 MySQL 主从复制最直接的理由:环境的创建和销毁成本被压到了极低。主从复制本身涉及的东西不少——server-id、binlog 格式、复制账号、初始数据快照、位点或者 GTID,任何一个环节出错都会导致Replica_IO_Running显示No。如果每次排查都要重装一遍数据库,光是等初始化就够喝一壶的。容器化之后,配置全部落在宿主机目录的my.cnf里,数据落在挂载卷里,重建实例就是删容器再起容器,配置文件不动,思路清晰得多。

这套方案适合谁?我的判断是这样:正在学 MySQL 复制原理、想动手验证binlogrelay log到底怎么流转的同学;需要给项目搭一套本地读写分离验证环境的后端开发;还有准备面试、想把主从延迟、GTID、半同步这些概念从纸面拉到实操层面的人。如果你是要上生产环境,容器跑数据库这件事需要更谨慎地评估存储性能和运维体系,但作为学习、测试、预发验证,Docker 这条路我走过很多次,靠谱。

接下来的内容我会把整套流程拆开讲:为什么这么选参数、每一步在干什么、报错了怎么查。所有命令和配置都是我在实际机器上跑通过的,你可以直接抄,但建议先看完原理那一段再动手。

2. 动手前的环境准备与镜像选型

2.1 Docker 环境检查与那几个经典的启动失败

第一步永远是确认 Docker 本身是活的。Linux 上执行docker versiondocker info,能看到 Server 段的信息就说明守护进程在跑。Windows 和 macOS 上用的是 Docker Desktop,这里有个特别高频的坑:启动时报 virtualization support not detected,一堆人卡在这一步以为要重装。这个报错的意思是宿主机的硬件虚拟化没打开,进 BIOS 或 UEFI 把 Intel VT-x 或者 AMD-V 打开就行。还有一种情况是 Windows 上 Hyper-V、WSL2 和某些虚拟机软件抢虚拟化层,关掉冲突的那一方再重启。

另外提醒一句,Windows 家庭版早期是不支持 Hyper-V 的,现在走 WSL2 后端基本都能用,但 BIOS 那一步躲不掉。确认环境没问题后,建议把 Docker 的镜像存储位置调到大一点的盘,两个 MySQL 8.0 容器加上数据卷,几个 G 是起步的量。

需要开放的端口也要提前想好。我一般把主库映射成3307,从库映射成3308,避开宿主机上可能已经存在的3306。容器之间通信走 Docker 内部网络,用的是容器自己的3306,所以外网映射成什么端口完全不影响复制配置。这个区分很重要,后面配CHANGE REPLICATION SOURCE TO时,SOURCE_HOST要填容器名或者内网 IP,不是127.0.0.1:3307

2.2 MySQL 镜像版本到底该挑哪个

镜像 tag 的选择直接决定了配置文件里哪些参数能用、哪些会报错。我把常见的几个版本列一下,方便你按需选:

镜像 tag特点适合场景
mysql:8.0长期支持,caching_sha2_password默认认证插件,生态成熟大多数学习和测试场景,我的首选
mysql:8.4LTS 版本,默认移除mysql_native_password插件想提前踩新版本坑的人
mysql:5.7老项目兼容,utf8mb4排序规则不同复现遗留系统问题
mysql:latest指向当前最新,可能带来意外的行为变化我个人不建议在复现环境用

为什么不建议用latest?因为主从复制对版本一致性有要求,从库版本低于主库基本没法玩,而latest会在你不知情的时候变成一个新的大版本。你可能今天搭好的环境,明天docker pull一下就变成另一个东西了。我通常写死mysql:8.0,需要升级时显式改 tag,这样出问题时可回溯。

还有个细节:MySQL 8.0 默认的认证插件是caching_sha2_password。用位点方式复制时,从库连主库如果没配置 TLS,可能报认证相关的错误。解决办法有两个,一是复制账号显式指定IDENTIFIED WITH mysql_native_password,二是在CHANGE REPLICATION SOURCE TO里加上SOURCE_SSL=1GET_SOURCE_PUBLIC_KEY=1。我在测试环境习惯用第一种,简单直接;生产环境更倾向走 TLS。

2.3 目录规划与端口分配

我习惯在宿主机上建一个统一的工作目录,结构大致是这样:

mkdir -p /opt/mysql-cluster/{master/{conf,data,logs},slave/{conf,data,logs}}

分成masterslave两棵树,各自的confmy.cnfdata挂载到容器的/var/lib/mysqllogs放错误日志方便排查。这样做的核心考虑是:数据和配置必须落在容器外部。如果只写在镜像层或者容器可写层里,容器一删数据就没了;而且容器重建之后server-idlog-bin这些配置如果靠docker exec进去临时改,下次重建又要再来一遍,非常难受。

权限问题这里要特别注意。MySQL 容器内以mysql用户运行,UID 通常是 999。宿主机上data目录如果归属 root,容器启动时会报权限错误然后退出,日志里能看到Permission denied。解决办法是把宿主机的data目录chown 999:999,或者干脆用命名卷让 Docker 自己管。我偏向 bind mount 加改权限,因为想随时ls一下数据文件看看情况。

端口分配上,主库对外用3307,从库用3308,两个容器内部都是3306。容器之间在自定义网络里可以直接用容器名互相解析,这是后面配复制主机的关键,能绕开容器 IP 变化的问题。

3. 主从复制原理先啃透,配置才不是背口诀

3.1 binlog、relay log 和那三个线程的流转过程

很多人配主从就是背命令:主库开 binlog,从库 change master,start slave。能跑通,但一报错就懵。我想先把流转链路讲清楚,因为后面所有报错几乎都能对应到这条链路上的某个环节。

主库上任何一次数据变更,会被写进binlog(二进制日志)。这个写入是在事务提交阶段完成的,所以顺序很关键。主库同时会维护一个dump 线程,从库连上来之后,主库为每个从库启动一个 dump 线程,负责把 binlog 事件推给从库。从库这边有两个线程:IO 线程负责接收主库推过来的事件,写进本地的relay log(中继日志);SQL 线程负责读 relay log,把事件在从库上重放一遍,最终让从库的数据和主库一致。

所以从库本质上是个"重放机"。它不关心主库现在有什么数据,它只关心"从某个起点开始,主库发生的每一条变更,我按顺序执行一遍"。这也是为什么搭建时必须解决两个问题:起点在哪(位点或 GTID),起点之前的历史数据怎么补齐(初始快照)。搞不清这两点,就会出现"从库连上了但数据对不上"的情况。

还有一个常被忽略的点:log_replica_updates(老版本叫log_slave_updates)这个参数。从库默认要不要把自己重放产生的事件再写进自己的 binlog?如果你的架构是级联复制(从库再挂从库),必须打开;如果是纯粹的一主一从,打开与否不影响当前链路,但打开之后从库也能当其他实例的数据源,做后续扩展会方便。我在从库配置里一律打开。

3.2 位点复制和 GTID 复制,新手到底选哪个

两种复制方式的差别,用一句话概括:位点复制靠"文件名 + 偏移量"定位,GTID 复制靠全局事务编号定位

位点复制是这样的:你在主库上执行SHOW MASTER STATUS,得到类似mysql-bin.000003157这样的结果,然后告诉从库"从 mysql-bin.000003 的第 157 字节开始追"。这种方式直观,但脆得很。主库重启、日志切割、做了日志清理,位点就可能失效或者对不上。更麻烦的是主从切换场景,你手动去算位点,很容易算错。

GTID(全局事务标识符)是给每个事务发一个唯一编号,形如3f9b8e5a-...-0000000000a:1-25。从库只需要告诉主库"我要所有我还没执行过的事务",主库自动算出该从哪里开始发。主从切换、故障恢复时,不用再纠结位点,直接SOURCE_AUTO_POSITION=1就行。代价是配置上多两个参数:gtid_mode=ONenforce_gtid_consistency=ON,而且 GTID 模式下不支持某些语句(比如事务里混用临时表和 InnoDB 表的老写法)。

我的建议是:新搭环境直接用 GTID。它把最容易出错的一块交给了数据库自己处理,学习成本略高一点,但省下来的排查时间远超这点投入。本文后面的实操也走 GTID 路线,同时我会说明位点方式怎么写,方便你看老文档时对照。

3.3 server-id、binlog 格式这些参数为什么非改不可

server-id是复制拓扑里每个实例的唯一身份证。主库、从库的 server-id 绝对不能相同,相同的话从库连上来会直接被拒绝,错误日志里会明确说这是同一个 server id。我一般用 1、2、3 这样递增规划,一眼能看出角色。

binlog_format我选ROW。三种格式里,STATEMENT记录的是 SQL 语句原文,遇到NOW()UUID()RAND()这类非确定性函数,主从执行结果可能不一致;MIXED是在两者之间自动切换,但切换逻辑对使用者是个黑盒;ROW记录的是每一行数据变更前后的镜像,结果确定,绝大多数场景下更安全。配合binlog_row_image=FULL,更新时把整行前后镜像都记下来,方便排查,代价是日志体积大一些。

还有一组关于持久化的参数:sync_binlog=1表示每次事务提交都把 binlog 刷盘,innodb_flush_log_at_trx_commit=1表示每次提交都把 redo log 刷盘。两个都设成 1 就是常说的"双 1 配置",数据安全性最高,性能有一定损耗。学习环境我建议双 1 开着,先把数据一致性这件事保证住;如果做压测,可以调整后再观察差异,这种对比本身也是有价值的经验。

字符集统一设成utf8mb4,排序规则utf8mb4_0900_ai_ci(8.0 的默认)。字符集不一致是复制报错的一个隐蔽来源,尤其是中文和 emoji 场景,主库能写进去从库写不进去,Replica_SQL_Running就会变成No

4. 一个字符一个字符敲出主库和从库

4.1 主库的 my.cnf 与启动配置

主库配置文件放在/opt/mysql-cluster/master/conf/my.cnf,内容如下:

[mysqld] server-id=1 log-bin=mysql-bin binlog_format=ROW binlog_row_image=FULL gtid_mode=ON enforce_gtid_consistency=ON log_replica_updates=ON binlog_expire_logs_seconds=604800 max_binlog_size=512M sync_binlog=1 innodb_flush_log_at_trx_commit=1 character-set-server=utf8mb4 collation-server=utf8mb4_0900_ai_ci default_authentication_plugin=mysql_native_password max_connections=500 innodb_buffer_pool_size=1G [client] default-character-set=utf8mb4

逐个说下我为什么这么写。binlog_expire_logs_seconds=604800是 7 天,8.0 之后推荐用这个参数而不是老的expire_logs_days,单位是秒,604800 = 7 * 24 * 3600。日志不清理会一直吃磁盘,测试环境尤其容易堆到几十个 G,所以我默认设了 7 天。max_binlog_size=512M控制单个 binlog 文件大小,到这个值就滚动新文件。

innodb_buffer_pool_size=1G这个值不是拍脑袋来的。经验做法是给到物理内存的 50% 到 70%,但容器里要留够给系统和其他进程的空间。宿主机 8G 内存、跑两个容器的情况下,各给 1G 是比较稳妥的。如果你机器内存宽裕,可以往上加,这个参数对读性能影响最大。

default_authentication_plugin=mysql_native_password是给复制账号铺路,避免caching_sha2_password带来的认证麻烦。注意这个参数在 8.4 已被移除,用 8.4 的话得改用mysql_native_password=ON之类的写法,这也是我前面建议写死mysql:8.0的原因之一。

启动主库容器:

docker run -d \ --name mysql-master \ --network mysql-cluster-net \ -p 3307:3306 \ -v /opt/mysql-cluster/master/conf/my.cnf:/etc/mysql/conf.d/my.cnf \ -v /opt/mysql-cluster/master/data:/var/lib/mysql \ -v /opt/mysql-cluster/master/logs:/var/log/mysql \ -e MYSQL_ROOT_PASSWORD=Master@2024 \ mysql:8.0

注意网络这里,需要先建一个自定义网络docker network create mysql-cluster-net。用自定义网络的好处是容器之间可以通过容器名互访,从库配SOURCE_HOST=mysql-master就能解析到,不用管 IP 变成多少。这一点后面讲容器重启问题时会再强调。

4.2 从库配置的差异点在哪

从库的my.cnf大部分和主库一致,差异集中在几行:

[mysqld] server-id=2 relay_log=relay-bin log_replica_updates=ON read_only=ON super_read_only=ON gtid_mode=ON enforce_gtid_consistency=ON

server-id=2不重复,这是硬性要求。relay_log指定中继日志的前缀,方便在数据目录里辨认。read_only=ON让普通用户无法在从库写入,防止误操作破坏一致性;super_read_only=ON更进一步,连有 SUPER 权限的账号也写不了。这两个一起开,能挡住绝大多数"手滑在从库改数据导致主从冲突"的事故。

但要提醒一句:super_read_only=ON会把复制线程之外的写入全部锁死,包括你用 root 账号做维护操作。搭环境阶段如果需要临时导入初始数据,得先把它关掉,导完再打开。我有次忘了这一步,mysqldump导入一直报The MySQL server is running with the --super-read-only option,排查了十分钟才反应过来。

从库容器的启动命令和主库类似,改一下名字、端口和挂载路径即可:

docker run -d \ --name mysql-slave \ --network mysql-cluster-net \ -p 3308:3306 \ -v /opt/mysql-cluster/slave/conf/my.cnf:/etc/mysql/conf.d/my.cnf \ -v /opt/mysql-cluster/slave/data:/var/lib/mysql \ -v /opt/mysql-cluster/slave/logs:/var/log/mysql \ -e MYSQL_ROOT_PASSWORD=Slave@2024 \ mysql:8.0

4.3 启动之后先验证连通性再往下走

两个容器起来后,先看状态:

docker ps --filter "name=mysql-"

如果某个容器状态是Restarting或者直接退出了,马上看日志,别急着往下配:

docker logs mysql-master --tail 100

最常见的两种失败:一是配置文件语法错误,MySQL 直接起不来,日志里会明确指出是my.cnf的第几行;二是数据目录权限问题,日志里是Permission denied。前者改配置,后者执行chown -R 999:999 /opt/mysql-cluster/master/data

容器都正常后,验证从库能不能访问主库。进从库容器:

docker exec -it mysql-slave bash mysql -uroot -pSlave@2024 -h mysql-master -P 3306 -e "SELECT 1;"

这一步能通,说明网络和账号都没问题,可以进入下一阶段。如果卡住或者报Unknown MySQL server host,说明两个容器不在同一个网络,或者名字写错了。这里先解决掉,别等到配复制时才发现,因为那时的报错信息会绕一层,排查成本更高。

5. 建立复制链路并验证数据真的同步了

5.1 主库创建复制账号并导出初始快照

复制账号只干一件事:让从库能连上来读 binlog。权限给REPLICATION SLAVE就够了,不要图省事给ALL PRIVILEGES,账号权限越大,泄露后的风险越高。

CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'Repl@2024'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%'; FLUSH PRIVILEGES;

'repl'@'%'里的%表示允许任意主机连接。在容器网络里,从库的 IP 是不固定的,所以用%比较省事。如果是生产环境,建议限定具体网段。

接下来做初始数据快照。GTID 模式下,我推荐用mysqldump--single-transaction的方式,它在 InnoDB 上能拿到一致性快照又不锁表:

docker exec mysql-master mysqldump \ -uroot -pMaster@2024 \ --single-transaction \ --master-data=2 \ --flush-logs \ --all-databases \ --set-gtid-purged=ON \ > /opt/mysql-cluster/init.sql

几个参数的意思:--single-transaction开启可重复读事务,保证备份期间数据一致;--master-data=2会把当时的 GTID 信息以注释形式写进备份文件,方便对照;--flush-logs在备份开始时切一个新 binlog,让位点更清晰;--set-gtid-purged=ON会把 GTID 信息写进备份,导入从库后能正确对应复制起点。这个参数很关键,漏了它导入后可能报 GTID 相关错误。

备份文件如果很大,注意宿主机的磁盘余量。默认导出路径选在挂载目录里,容器删了文件还在,这点比导到容器内部好。

5.2 从库导入数据并开启复制

把备份导入从库。前面提到super_read_only会挡住写入,先临时关掉:

SET GLOBAL super_read_only = 0; SET GLOBAL read_only = 0;

然后导入:

docker exec -i mysql-slave mysql -uroot -pSlave@2024 < /opt/mysql-cluster/init.sql

导入完成后,在从库上执行复制配置。GTID 模式下命令很简洁:

CHANGE REPLICATION SOURCE TO SOURCE_HOST='mysql-master', SOURCE_PORT=3306, SOURCE_USER='repl', SOURCE_PASSWORD='Repl@2024', SOURCE_AUTO_POSITION=1, GET_SOURCE_PUBLIC_KEY=1; START REPLICA;

SOURCE_AUTO_POSITION=1就是 GTID 模式的核心,从库不再需要手算位点。GET_SOURCE_PUBLIC_KEY=1是为了兼容caching_sha2_password认证,加了它即使账号不是mysql_native_password也能连上,属于兜底。

如果是位点模式,写法是这样:

CHANGE REPLICATION SOURCE TO SOURCE_HOST='mysql-master', SOURCE_PORT=3306, SOURCE_USER='repl', SOURCE_PASSWORD='Repl@2024', SOURCE_LOG_FILE='mysql-bin.000003', SOURCE_LOG_POS=157;

日志文件名和偏移量来自SHOW MASTER STATUS的输出,必须在锁表或者一致性快照那一刻获取,否则会漏数据。这也是位点模式更麻烦的地方。

导入之后记得把只读打开回来:

SET GLOBAL read_only = 1; SET GLOBAL super_read_only = 1;

5.3 状态怎么看,字段怎么读

执行:

SHOW REPLICA STATUS\G

在 8.0.22 之前这个命令叫SHOW SLAVE STATUS,两个都能用,但新命令是趋势。输出字段很多,重点看这几个:

字段正常值含义
Replica_IO_RunningYesIO 线程正常,能从主库拉事件
Replica_SQL_RunningYesSQL 线程正常,能重放事件
Seconds_Behind_Source0或接近 0复制延迟,空闲时通常是 0
Last_IO_Error有内容就是 IO 环节出问题了
Last_SQL_Error有内容就是重放环节出问题了
Retrieved_Gtid_Set一串 GTID 区间已经从主库拉到的 GTID
Executed_Gtid_Set和上面一致或更全已经在从库执行完的 GTID

Replica_IO_RunningReplica_SQL_Running有一个不是Yes,复制就是断的。这两个字段的组合能快速定位问题层次:IO 挂了是连接层面的问题,SQL 挂了是数据或语句层面的问题。Seconds_Behind_Source这个值在空闲时可能显示 0,但主库持续写入时它反映的是真实延迟,压测时可以观察它是否飙升。

验证同步最直接的办法是造数据:

-- 在主库执行 CREATE DATABASE IF NOT EXISTS demo_db; CREATE TABLE demo_db.t_user (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50)); INSERT INTO demo_db.t_user (name) VALUES ('alice'), ('bob');

然后在从库查:

SELECT * FROM demo_db.t_user;

能看到两条记录就说明链路通了。再回从库SHOW REPLICA STATUSExecuted_Gtid_Set有没有变化,两边对照着看,心里更有底。

6. 我踩过的坑和排查思路实录

6.1 连接失败的三类报错怎么区分

复制配置完,SHOW REPLICA STATUS一看Last_IO_Error有内容,先从错误码入手。下面这张表是我遇到频率最高的几种:

错误码报错内容关键词常见原因处理方式
1045Access denied账号密码错,或主机名不匹配检查'repl'@'%'是否存在,密码是否一致
2003Can't connect网络不通,容器名解析失败确认两个容器在同一自定义网络
1236Could not find first log file位点失效、日志被清理GTID 模式改用AUTO_POSITION=1
1062Duplicate entry从库已有数据,重放冲突重新做快照,或按 GTID 跳过
1032Can't find record从库数据被误删或缺失确认从库只读是否被绕过

1045 这个最坑的地方在于:账号密码都对,但报错依旧。原因往往是主库上存在多个同名账号,比如'repl'@'localhost''repl'@'%',从库连过来匹配到的是权限更小的那个。用SELECT user, host FROM mysql.user WHERE user='repl';看一眼就清楚了。

6.2 数据不一致了,怎么安全地恢复

主从数据不一致的表现通常是Replica_SQL_Running=NoLast_SQL_Error报主键冲突或者找不到记录。原因可能是有人在从库手动写了数据,也可能是主从切换时的边界问题。

处理冲突最省事的做法是重建:停掉复制,重新在主库做一次一致性快照,导入从库覆盖,再重新配置复制。数据量小的时候十分钟搞定,比一点点修数据可靠得多。

如果数据量很大、重做代价高,可以考虑跳过出问题的事务。GTID 模式下这样操作:

STOP REPLICA; SET GTID_NEXT='出问题事务的GTID'; BEGIN; COMMIT; SET GTID_NEXT='AUTOMATIC'; START REPLICA;

这里的 GTID 从Last_SQL_Error或者Retrieved_Gtid_Set里找。必须强调:跳过事务意味着从库永远缺了这条变更,只在确认这条变更对数据完整性无影响时使用,比如重复插入一条已经存在的配置记录。跳过之后要做数据校验,别跳完就当没事了。

6.3 容器重启后 IP 变了,复制直接断

这是我早期用 Docker 搭主从最容易翻车的地方。刚开始图省事,配置里写的是主库容器的内网 IP,类似SOURCE_HOST='172.18.0.2'。容器重启一次,IP 可能变成172.18.0.3,复制立刻报 2003 连不上。而且这个错很难第一时间想到,因为昨天还好好的。

解决办法就是用自定义网络加容器名。创建网络:

docker network create mysql-cluster-net

启动容器时加--network mysql-cluster-net,配置里主机名写mysql-master。Docker 内置的 DNS 会把容器名解析成当前 IP,IP 怎么变都不影响。这个改动看起来小,但省下的排查时间非常多。

另一种情况是容器被删了重建,数据目录挂载还在,但server-id变了。比如重建从库时网络配置没问题,但配置文件挂载路径写错,容器用了默认的server-id=1,和主库撞车,复制直接拒绝。所以每次重建之后,进容器SELECT @@server_id;确认一下,是个成本极低的习惯。

6.4 日常该盯哪些指标

搭好只是开始,长期跑起来要盯几个点。我一般看两类东西。

第一类是复制状态本身。写个小脚本定时跑SHOW REPLICA STATUS,把Replica_IO_RunningReplica_SQL_RunningSeconds_Behind_Source三个值抓出来,任一异常就告警。这么简单的检查能挡住 90% 以上的复制中断事故。

第二类是资源水位。SHOW BINARY LOGS;看 binlog 占了多少,SHOW REPLICA STATUS里看 relay log 有没有堆积。测试环境磁盘被日志吃满是常事,尤其有人跑压测写了几百万行,几天就几十个 G。前面配的binlog_expire_logs_seconds就是干这个的,但也要确认清理策略真的生效了。

还有一条经验:不要在主库上跑FLUSH LOGS之后马上做位点操作。这个命令会滚动 binlog,如果你用的不是 GTID 模式,正在复制的从库可能正好在一个日志切换的边界上,出现短暂的状态异常。GTID 模式下这个问题基本不存在,这也是我推荐 GTID 的原因之一。

最后分享一个我经常用的小技巧。搭这套东西之前,我会先写一个简单的docker-compose.yml,把两个服务、网络、卷、端口全写进去。这样每次重建就是docker compose down && docker compose up -d,比手动敲两条docker run更省事,也更容易分享给同事。配置如下,可以直接拿走用:

services: master: image: mysql:8.0 container_name: mysql-master networks: - mysql-net ports: - "3307:3306" volumes: - ./master/conf/my.cnf:/etc/mysql/conf.d/my.cnf - ./master/data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: Master@2024 slave: image: mysql:8.0 container_name: mysql-slave networks: - mysql-net ports: - "3308:3306" volumes: - ./slave/conf/my.cnf:/etc/mysql/conf.d/my.cnf - ./slave/data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: Slave@2024 networks: mysql-net: driver: bridge

compose的时候要注意,卷路径是相对docker-compose.yml所在目录的,所以文件得放在/opt/mysql-cluster/下。另外container_name在同一台宿主机上不能重复,这点和用docker run是一样的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 13:24:24

Download gradle超时

Android Studio经常会出现一直在Download gradle&#xff0c;可能是无法找到资源&#xff0c;可以按照如下方法离线下载 进入https://mirrors.cloud.tencent.com/gradle/下载gradle-wrapper.properties文件中所需要版本压缩包复制到C:\Users\用户名.gradle\wrapper\dists\gradl…

作者头像 李华
网站建设 2026/9/18 13:23:46

基于Unity与C#的瓯绣3D虚拟展馆漫游系统实现

前几年接手过几个地方非遗文化数字化的活儿&#xff0c;说实话&#xff0c;一开始我是拒绝的。因为这类项目十有八九最后做成了一个"能点的电子画册"——几张高清图加一段文字说明&#xff0c;再配点背景音乐&#xff0c;交差完事。但瓯绣这个题材不太一样&#xff0…

作者头像 李华
网站建设 2026/9/18 13:23:29

龙虾AI台式机批量作业,OpenClaw 的 Base URL 改到 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 13:21:11

Mac上Python环境搭建指南:Homebrew+虚拟环境+编辑器配置

1. 让 Mac 的 Python 环境不再"裸奔"你有没有遇到过这种场景&#xff1a;满怀期待地打开 Mac 终端&#xff0c;敲下python --version&#xff0c;屏幕上却跳出个 2.7.16 这种上世纪的老古董&#xff1f;或者明明天天喊"Python 很好上手"&#xff0c;结果光…

作者头像 李华
网站建设 2026/9/18 13:17:40

把 MCP 挂进 docmd,AI 助手调用走 TaoToken 记账

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 13:17:35

Redis哨兵集群高可用架构:一主两从三哨兵部署与故障转移实战解析

搭建一套高可用的Redis哨兵集群&#xff0c;是很多团队从单机Redis迈向生产环境的必经之路。网上关于哨兵的教程不少&#xff0c;但多数要么只讲概念不讲落地&#xff0c;要么给了一堆命令但没解释为什么要这么配。这篇文章我基于实际部署经验&#xff0c;把一主两从三哨兵的完…

作者头像 李华