当前位置: 首页 > news >正文

MySQL容器化生产实践:Docker部署、配置与持久化全指南

1. 为什么今天还要花时间学 MySQL + Docker?这根本不是“玩具组合”

MySQL 占据全球关系型数据库市场超 40% 的份额,这个数字背后不是偶然——它代表了数百万开发者、DBA 和运维工程师在真实生产环境里反复验证过的稳定性、可预测性和上手成本。而 Docker,早已不是当年那个只在开发机上跑 demo 的轻量工具。现在,从初创公司的微服务后端,到中大型企业内部的数据分析平台,MySQL 容器化部署已是默认选项,不是加分项,而是入场券。

我带过三支不同规模的技术团队,做过七次以上核心数据库架构迁移。最深的体会是:用裸机或虚拟机装 MySQL,你管的是“一台服务器”;用 Docker 跑 MySQL,你管的是“一个可声明、可版本化、可漂移的服务单元”。这个认知差,直接决定了你后续做备份、扩容、灰度发布、多环境一致性时,是写一页纸的 SOP,还是敲三行命令搞定。

关键词里没写,但实际落地时绕不开的四个硬需求,我先摊开说清楚:

  • 数据不丢:容器删了,库还在。这不是功能,是底线。见过太多人第一次docker rm -f后对着空目录发呆。
  • 配置不乱my.cnf改一次,所有环境自动同步。而不是开发改了本地配置,测试环境忘同步,上线直接报错。
  • 连接不懵:本地 IDE、Python 脚本、另一个 Node.js 容器,都能用同一套逻辑连上,不用记一堆127.0.0.1:3307mysql-db:3306host.docker.internal:3306切来切去。
  • 升级不慌:从 8.0.33 升到 8.2.0,不是重装系统,而是换一行image:,加个-v挂载旧数据卷,启动即用。中间零停机窗口,这才是现代运维该有的样子。

这篇文章不讲 Docker 基础语法,也不教 SQL 建表语句。它是一份我压箱底的实操手册——里面每一个命令、每一处参数、每一条docker exec的路径,都来自我亲手部署过 127 个 MySQL 容器的真实记录。你不需要背原理,照着做,就能搭出一个能进测试环境、敢接小流量、经得起同事拷问的 MySQL 容器实例。下面开始,我们从最底层的“镜像选择”说起。

2. 镜像选型:别无脑latest,8.0 和 8.2 的坑我替你踩过了

很多人一上来就docker pull mysql:latest,觉得省事。我试过三次,每次都在交付前夜翻车。latest是个移动靶,Docker Hub 上它指向的版本随时在变。上周拉下来是 8.2.0,这周可能就是 8.3.0 的 RC 版,而后者默认启用了caching_sha2_password认证插件——你的老 Java 应用连不上,报错Public Key Retrieval is not allowed,查文档要半小时,改代码要两小时。

所以,镜像选择的第一铁律:永远用明确的、带小版本号的 tag,且优先选 GA(General Availability)稳定版。我现在所有项目统一用mysql:8.0.33mysql:8.2.0,原因很实在:

  • 8.0.x系列是当前企业级部署的“黄金标准”。Oracle 官方对它的长期支持(LTS)已明确到 2026 年,社区补丁、监控插件、备份工具链最成熟。我维护的一个金融类风控系统,三年没动过 MySQL 大版本,全靠8.0.33的稳定输出。
  • 8.2.x是新锐力量,性能提升明显。实测同样硬件下,innodb_buffer_pool_size相同,8.2.0处理 1000 万行订单表的聚合查询,比8.0.33快 18%。但它对客户端驱动有要求:Java 需mysql-connector-j 8.2.0+,Python 需PyMySQL 1.1.0+mysqlclient 2.2.0+。如果你的项目技术栈较新,8.2.0是更优解。

提示:怎么查哪个版本是 GA?别信博客,直接看 MySQL 官方发布日历 。页面里标着 “General Availability” 的日期,就是你该锁死的版本。比如8.0.33的 GA 日期是 2023-04-18,8.2.0是 2023-10-16。

拉镜像的命令,必须带版本号:

# 推荐:明确指定 8.0.33,稳定压倒一切 docker pull mysql:8.0.33 # 推荐:明确指定 8.2.0,追求新特性与性能 docker pull mysql:8.2.0 # 绝对禁止:用 latest,自找麻烦 # docker pull mysql:latest

拉完之后,立刻执行docker images,确认镜像 ID 和 CREATED 时间:

$ docker images | grep mysql mysql 8.0.33 5a1e0b9d7c8f 2 months ago 587MB mysql 8.2.0 9f2c1d4e6b7a 3 weeks ago 612MB

看到CREATED是“2 months ago”或“3 weeks ago”,说明你拉的是官方构建的正式版,不是某个人随手docker build推上去的野鸡镜像。镜像大小(587MB/612MB)也符合预期——这是官方精简过的基础镜像,不含调试工具,安全又轻量。

这里有个容易被忽略的细节:镜像名mysql其实是library/mysql的缩写。Docker 默认会从 Docker Hub 的library组织拉取。如果你在内网环境,公司私有 Registry 地址是registry.internal.company.com,那正确写法是:

docker pull registry.internal.company.com/mysql:8.0.33

漏掉组织名,Docker 会默认去公网拉,内网机器直接超时。我在一家券商做驻场时,就因为这个细节,让整个 CI 流水线卡了 40 分钟,教训深刻。

3. 启动容器:docker run的 7 个参数,每个都决定生死

docker run是 MySQL 容器的生命起点。网上很多教程只给一行命令,却不说清每个参数背后的“为什么”。我把它拆成 7 个关键参数,逐个解释它们在真实场景中的分量。

3.1--name:给容器起个“身份证号”,不是昵称

--name mysql-prod-order

test-mysql这种名字只适合五分钟实验。在真实项目里,容器名必须体现环境 + 业务 + 用途mysql-prod-order表示:这是生产环境(prod)、服务于订单系统(order)的 MySQL 主库。好处有三:

  • docker ps一眼识别角色,不用docker inspect查环境变量;
  • Docker Compose 里服务发现依赖这个名字(mysql-prod-order就是其他容器的 hostname);
  • 监控告警规则按名字打标签,container_name=mysql-prod-order这条规则,比image=mysql精准十倍。

注意:名字不能含下划线_,Docker 内部 DNS 解析会失败。必须用短横线-mysql_prod_order是错的,mysql-prod-order才对。

3.2-e MYSQL_ROOT_PASSWORD=xxx:密码不是字符串,是密钥

这是最常被轻视的参数。很多人写:

-e MYSQL_ROOT_PASSWORD=123456

大错特错。123456这种弱密码,在docker ps输出里明文可见,任何有docker权限的人都能ps aux | grep MYSQL_ROOT_PASSWORD看到。更糟的是,如果容器崩溃,docker inspect会把所有环境变量原样吐出来。

正确姿势是:用 Docker Secrets(推荐)或挂载文件。但 Secrets 需 Swarm 模式,对单机开发不友好。我的折中方案是:密码存在本地文件,用-e读取文件内容:

# 先创建密码文件(权限设为 600,仅 owner 可读) echo "StrongP@ssw0rd_2024!" > /etc/docker/secrets/mysql-root-password chmod 600 /etc/docker/secrets/mysql-root-password # 启动时用 $(cat ...) 注入 docker run --name mysql-prod-order \ -e MYSQL_ROOT_PASSWORD=$(cat /etc/docker/secrets/mysql-root-password) \ ...

这样docker inspect看到的只是MYSQL_ROOT_PASSWORD=StrongP@ssw0rd_2024!,但密码文件本身受系统权限保护,比明文写在命令里安全得多。

3.3-p 3307:3306:端口映射不是“转发”,是“网络缝合”

-p 3307:3306的含义常被误解为“把容器 3306 端口映射到宿主机 3307”。其实更准确的说法是:Docker 在宿主机的docker0网桥上,开了一个监听0.0.0.0:3307的 socket,所有发向这个 socket 的 TCP 包,被内核 NAT 规则重写目标 IP 为容器 IP,并转发到容器的3306端口。

这意味着:

  • 如果宿主机已有进程占了3307docker run会直接报错Bind for 0.0.0.0:3307 failed: port is already allocated,不会自动换端口;
  • 0.0.0.0:3307表示监听所有网卡,包括192.168.1.100:3307。如果只想让本机访问(更安全),应写成-p 127.0.0.1:3307:3306
  • 容器内应用看到的源 IP 是172.17.0.1(docker0 网关),不是你的笔记本真实 IP。这点在配置bind-address时至关重要。

3.4--restart=unless-stopped:容器不是“运行一次”,是“永生服务”

-d让容器后台运行,但没解决“它挂了怎么办”。默认--restartno,容器一崩就静默退出。线上服务必须加:

--restart=unless-stopped

含义:只要不是你手动docker stop,容器崩溃、宿主机重启、内核 panic 后,Docker Daemon 都会自动拉起它。这是生产环境的保命参数。我见过太多人忘了加这个,凌晨三点数据库挂了,微信告警响成一片,而容器根本没起来。

3.5--memory=2g --cpus=2:资源限制不是“建议”,是“契约”

MySQL 是内存大户。不加限制,一个SELECT * FROM huge_table就能把宿主机内存吃光,触发 OOM Killer 杀掉其他进程。必须明确划界:

--memory=2g --cpus=2
  • --memory=2g:容器内 MySQL 进程最多用 2GB 内存。超过则被 Linux cgroup OOM kill;
  • --cpus=2:MySQL 最多占用 2 个 CPU 核心的计算时间(不是绑定到特定核)。避免它抢光 CPU,影响宿主机上其他服务。

实测心得:innodb_buffer_pool_size应设为--memory值的 70%-75%。比如--memory=2g,则my.cnf里配innodb_buffer_pool_size = 1536M。留出空间给 OS 缓存、连接线程栈等。

3.6--network=myapp-net:网络不是“默认桥接”,是“专属通道”

docker run默认用bridge网络,所有容器在172.17.0.0/16网段。问题在于:IP 地址随机分配,mysql容器今天是172.17.0.2,明天重启变成172.17.0.3,其他容器连不上。

解决方案:创建自定义网络,用容器名当 DNS 名:

docker network create myapp-net docker run --name mysql-prod-order --network=myapp-net ... docker run --name app-backend --network=myapp-net ...

此时,app-backend容器里ping mysql-prod-order能通,mysql -h mysql-prod-order -u root -p能连。Docker 内置 DNS 会把mysql-prod-order解析成其当前 IP。这才是微服务间通信的正道。

3.7--health-cmd="mysqladmin ping -h 127.0.0.1 -u root -p\$\$MYSQL_ROOT_PASSWORD" --health-interval=30s:健康检查不是“可选”,是“心跳”

没有健康检查,Kubernetes 或 Docker Swarm 无法判断 MySQL 是否真活。--health-cmd是一个 shell 命令,Docker 每隔--health-interval执行一次,返回 0 为健康,非 0 为不健康。

关键点:

  • 命令里$MYSQL_ROOT_PASSWORD$必须转义为\$,否则 shell 会在宿主机解析,而宿主机没有这个变量;
  • mysqladmin pingnc -z localhost 3306更可靠——后者只检查端口通,前者真正连上 MySQL Server 并发了个PING包;
  • --health-start-period=30s很重要:MySQL 启动要时间,刚run完立即检查必失败。设 30 秒,让它冷启动完成。

4. 配置持久化:my.cnf不是文本文件,是数据库的“基因图谱”

MySQL 容器的/etc/mysql/my.cnf是它的灵魂。默认配置(mysql:8.0.33)只为“能跑”,不是为“跑好”。不改配置,你永远得不到生产级性能和安全性。但直接进容器vi /etc/mysql/my.cnf?那是自杀行为——容器一删,配置全丢。

4.1 挂载策略:conf.d目录才是你的主战场

官方 MySQL 镜像的启动逻辑是:加载/etc/mysql/my.cnf→ 加载/etc/mysql/conf.d/*.cnf→ 加载/etc/mysql/mysql.conf.d/*.cnf。后两者是“追加覆盖”模式,.cnf文件里同名参数,后面的会覆盖前面的。

所以,永远不要碰/etc/mysql/my.cnf,只往/etc/mysql/conf.d/挂载你自己的custom.cnf这样做的好处:

  • 一个文件管所有自定义配置,清晰不混乱;
  • custom.cnf存在宿主机,docker rm不影响它;
  • 多个环境(dev/staging/prod)可以共用同一个镜像,只换custom.cnf文件。

创建宿主机配置目录和文件:

sudo mkdir -p /etc/docker/mysql-prod-order/conf.d sudo tee /etc/docker/mysql-prod-order/conf.d/custom.cnf << 'EOF' [mysqld] # 性能调优 innodb_buffer_pool_size = 1536M innodb_log_file_size = 256M max_connections = 200 # 安全加固 bind-address = 0.0.0.0 skip-name-resolve validate_password.policy = STRONG # 字符集统一 character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci # 日志与备份 log-bin = mysql-bin binlog-format = ROW expire_logs_days = 7 EOF

注意<< 'EOF'的单引号:防止 shell 解析$符号,确保utf8mb4这种字面量原样写入。

4.2 关键参数详解:为什么这些值是我选的?

innodb_buffer_pool_size = 1536M

InnoDB 缓冲池是 MySQL 的“内存硬盘”。它缓存数据页和索引页。值太小(如默认 128M),大量磁盘 IO,慢得像蜗牛;太大(如 3G),OS 内存不足,频繁 swap,更慢。我的公式:--memory=2g的容器,设为2g * 0.75 = 1.5g。实测SELECT COUNT(*) FROM orders WHERE status='paid'查询,缓冲池命中率从 42% 提升到 98%,响应时间从 1200ms 降到 86ms。

innodb_log_file_size = 256M

InnoDB 重做日志(redo log)的大小。它影响崩溃恢复速度和写入吞吐。默认 48M,对于高并发写入(如秒杀下单),日志频繁切换,产生 IO 瓶颈。256M是经验值:足够撑住 500 TPS 的写入压力,又不会让崩溃恢复时间过长(恢复时间 ≈ 日志总大小 / 恢复速度)。

bind-address = 0.0.0.0

容器内 MySQL 默认bind-address = 127.0.0.1,只接受本地连接。但 Docker 网络模型下,“本地”指容器内部 loopback,外部容器(如app-backend)连不上。设为0.0.0.0,表示监听所有网络接口。配合--network=myapp-net,只有同网络容器能访问,比127.0.0.1更安全。

validate_password.policy = STRONG

强制密码强度。STRONG要求:长度 ≥ 8,含大小写字母、数字、特殊字符各一。避免password123这种弱口令。这是validate_password插件的功能,MySQL 5.7+ 默认启用。

character-set-server = utf8mb4

utf8是 MySQL 的历史遗留坑,它只支持 BMP 字符(3 字节),不支持 emoji 和部分生僻汉字。utf8mb4是真正的 UTF-8(4 字节),兼容所有 Unicode 字符。不设这个,你的用户昵称👨‍💻存进去会变??

4.3 挂载命令:-v的路径必须精确到“目录”,不是“文件”

挂载custom.cnf的命令是:

-v /etc/docker/mysql-prod-order/conf.d:/etc/mysql/conf.d

注意:左边是宿主机目录,右边是容器内目录。不是-v /etc/docker/.../custom.cnf:/etc/mysql/conf.d/custom.cnf!因为:

  • Docker 挂载是以目录为单位的。挂载文件会覆盖整个目标目录;
  • /etc/mysql/conf.d/是 MySQL 启动时扫描配置的固定路径,必须挂载整个目录,让custom.cnf成为其中一员;
  • 如果挂载单个文件,/etc/mysql/conf.d/下原有的docker.cnf(官方镜像自带)会被覆盖,导致 MySQL 启动失败。

完整启动命令(整合前 7 参数):

docker run --name mysql-prod-order \ --restart=unless-stopped \ --memory=2g --cpus=2 \ --network=myapp-net \ --health-cmd="mysqladmin ping -h 127.0.0.1 -u root -p\$\$MYSQL_ROOT_PASSWORD" \ --health-interval=30s --health-start-period=30s \ -e MYSQL_ROOT_PASSWORD=$(cat /etc/docker/secrets/mysql-root-password) \ -v /etc/docker/mysql-prod-order/conf.d:/etc/mysql/conf.d \ -v mysql-prod-order-data:/var/lib/mysql \ -p 127.0.0.1:3307:3306 \ -d mysql:8.0.33

5. 数据持久化:/var/lib/mysql不是文件夹,是数据库的“心脏”

容器删了,/var/lib/mysql里的数据就没了。这是 Docker 新手最大的认知盲区。-v mysql-prod-order-data:/var/lib/mysql这行命令,本质是把 MySQL 的“心脏”——数据文件、日志文件、表结构定义——从容器的临时文件系统,移到宿主机的持久化卷里。

5.1 为什么必须用docker volume,而不是-v /host/path:/var/lib/mysql

两种方式都能持久化,但docker volume是官方推荐,原因有三:

对比项docker volume(推荐)-v /host/path(不推荐)
路径管理Docker 自动创建/var/lib/docker/volumes/xxx/_data,路径抽象,不暴露宿主机结构你必须自己mkdir -p /data/mysql-prod-order,路径硬编码,迁移困难
权限安全Volume 由 Docker 管理,自动设置chown 999:999(MySQL 用户 UID/GID),容器内 MySQL 进程能正常读写宿主机目录权限常为root:root,容器内 MySQL 进程(UID 999)无权写入,启动失败报Can't start server : Bind on unix socket
备份迁移docker volume lsdocker volume inspect xxxtar -cf backup.tar /var/lib/docker/volumes/xxx/_data,一键打包tar -cf backup.tar /data/mysql-prod-order,但路径散落在各处,易遗漏

创建并使用 volume:

# 创建 volume(Docker 自动处理权限) docker volume create mysql-prod-order-data # 启动时挂载(注意:左边是 volume 名,右边是容器内路径) -v mysql-prod-order-data:/var/lib/mysql

5.2 初始化数据:如何让容器启动时自动建库、建表、导入初始数据?

官方镜像支持docker-entrypoint-initdb.d机制。在挂载的/docker-entrypoint-initdb.d/目录下放.sql.sh文件,MySQL 第一次启动(检测到/var/lib/mysql为空)时,会自动执行它们。

步骤:

  1. 创建初始化脚本目录:
sudo mkdir -p /etc/docker/mysql-prod-order/initdb.d
  1. 写建库建表 SQL(01-create-db.sql):
-- /etc/docker/mysql-prod-order/initdb.d/01-create-db.sql CREATE DATABASE IF NOT EXISTS order_db CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; CREATE USER 'order_app'@'%' IDENTIFIED BY 'AppP@ss_2024'; GRANT ALL PRIVILEGES ON order_db.* TO 'order_app'@'%'; FLUSH PRIVILEGES;
  1. 写数据导入脚本(02-load-data.sql),或放 CSV 文件用LOAD DATA INFILE
  2. 启动时挂载:
-v /etc/docker/mysql-prod-order/initdb.d:/docker-entrypoint-initdb.d

注意:文件名按字母序执行(01-先于02-),SQL 文件必须以.sql结尾。

5.3 备份与恢复:mysqldump不是命令,是“数据保险单”

备份必须自动化,不能靠人想起来。我的方案:宿主机上写一个backup-mysql.sh,用cron每天凌晨 2 点执行。

#!/bin/bash # /usr/local/bin/backup-mysql.sh VOLUME_NAME="mysql-prod-order-data" BACKUP_DIR="/backup/mysql" DATE=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="${BACKUP_DIR}/mysql-prod-order_${DATE}.sql.gz" # 确保备份目录存在 mkdir -p ${BACKUP_DIR} # 使用 docker exec 进容器执行 mysqldump docker exec mysql-prod-order \ mysqldump -u root -p$(cat /etc/docker/secrets/mysql-root-password) \ --all-databases --single-transaction --routines --triggers \ | gzip > ${BACKUP_FILE} # 只保留最近 7 天备份 find ${BACKUP_DIR} -name "mysql-prod-order_*.sql.gz" -mtime +7 -delete echo "Backup completed: ${BACKUP_FILE}"

添加到 crontab:

# 每天凌晨 2:00 执行 0 2 * * * /usr/local/bin/backup-mysql.sh >> /var/log/backup-mysql.log 2>&1

恢复时,把.sql.gz解压,用docker exec导入:

gunzip < /backup/mysql/mysql-prod-order_20240501_020000.sql.gz | \ docker exec -i mysql-prod-order mysql -u root -p$(cat /etc/docker/secrets/mysql-root-password)

6. Docker Compose:不是“高级技巧”,是“多人协作的基础设施”

当项目从单体走向微服务,MySQL 不再是孤岛。它要和 API 网关、用户服务、订单服务一起启动、一起扩缩、一起升级。docker run一行行敲?那是单打独斗。docker-compose.yml是团队协作的基石。

6.1 一份生产可用的docker-compose.yml

以下是我在线上环境使用的精简版,已去掉注释,可直接复制:

version: '3.9' services: mysql: image: mysql:8.0.33 container_name: mysql-prod-order restart: unless-stopped environment: MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password MYSQL_DATABASE: order_db MYSQL_USER: order_app MYSQL_PASSWORD_FILE: /run/secrets/mysql_app_password secrets: - mysql_root_password - mysql_app_password command: --default-authentication-plugin=mysql_native_password healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "--password-file=/run/secrets/mysql_root_password"] interval: 30s timeout: 10s retries: 3 start_period: 40s volumes: - mysql-prod-order-conf:/etc/mysql/conf.d:ro - mysql-prod-order-data:/var/lib/mysql - ./initdb.d:/docker-entrypoint-initdb.d:ro ports: - "127.0.0.1:3307:3306" networks: - myapp-net deploy: resources: limits: memory: 2G cpus: '2.0' app-backend: image: mycompany/app-backend:v1.2.0 depends_on: mysql: condition: service_healthy environment: DB_HOST: mysql-prod-order DB_PORT: 3306 DB_NAME: order_db DB_USER: order_app DB_PASSWORD_FILE: /run/secrets/mysql_app_password secrets: - mysql_app_password networks: - myapp-net secrets: mysql_root_password: file: ./secrets/mysql-root-password mysql_app_password: file: ./secrets/mysql-app-password volumes: mysql-prod-order-conf: mysql-prod-order-data: networks: myapp-net: driver: bridge

6.2 关键设计解析:为什么这样写?

  • secrets替代environment:密码不再明文出现在docker-compose.ymldocker inspect输出里。secrets会以文件形式挂载到/run/secrets/,权限为0400,只有容器内 root 可读。
  • command: --default-authentication-plugin=mysql_native_password8.0+默认用caching_sha2_password,老客户端不兼容。此参数强制回退到旧插件,兼容性第一。
  • depends_on+condition: service_healthyapp-backend启动前,必须等mysqlhealthcheck返回健康。避免应用启动时连不上数据库,疯狂重试。
  • volumes声明在底部:这是 Compose v3 的规范。mysql-prod-order-confmysql-prod-order-data是命名卷,Docker 自动管理,无需docker volume create预创建。
  • deploy.resources:在 Swarm 模式下生效,但即使单机,docker-compose up也会应用limits,效果等同于--memory --cpus

6.3 日常运维命令:从启动到排障

  • 启动全套服务
    docker-compose up -d
  • 查看服务状态与日志
    # 看整体状态 docker-compose ps # 看 MySQL 日志(实时) docker-compose logs -f mysql # 看 MySQL 健康状态 docker-compose ps | grep mysql # 输出:mysql-prod-order ... Up (healthy)
  • 进入 MySQL 容器执行 SQL
    docker-compose exec mysql mysql -u root -p$(cat ./secrets/mysql-root-password)
  • 备份数据(Composed 方式)
    docker-compose exec mysql mysqldump -u root -p$(cat ./secrets/mysql-root-password) order_db | gzip > backup.sql.gz
  • 紧急停服
    docker-compose down # 停所有服务,但保留 volume docker-compose down -v # 停服务 + 删除 volume(慎用!)

7. 常见问题排查:那些让我凌晨三点爬起来的错误

再完美的配置,上线后也会遇到问题。我把最常遇到的 7 类故障,按发生频率排序,附上根因和一招毙命的解决命令。

7.1 故障:容器启动后立即退出,docker logs mysql-prod-order显示mysqld: Can't read dir of '/etc/mysql/conf.d/'

根因:挂载的/etc/mysql/conf.d目录权限不对。宿主机目录属主是root:root,但容器内 MySQL 进程以 UID 999 运行,无权读取。

解决

# 查看宿主机目录权限 ls -ld /etc/docker/mysql-prod-order/conf.d # 如果是 root:root,改为 999:999 sudo chown -R 999:999 /etc/docker/mysql-prod-order/conf.d # 或更安全:用 docker volume 管理配置(推荐) docker volume create mysql-prod-order-conf docker run -v mysql-prod-order-conf:/etc/mysql/conf.d ...

7.2 故障:docker exec -it mysql-prod-order bash进不去,报OCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found in $PATH

根因:官方 MySQL 镜像基于debian:slimalpine,精简掉了bash,只留sh

解决:用sh代替bash

docker exec -it mysql-prod-order sh # 进去后,可以用 mysql 客户端 mysql -u root -p

7.3 故障:本地mysql -h 127.0.0.1 -P 3307 -u root -p连不上,报ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1' (111)

排查三步

  1. 确认容器在运行
    docker ps | grep mysql-prod-order # 如果没输出,容器已退出,看日志 docker logs mysql-prod-order
  2. 确认端口映射正确
    docker port mysql-prod-order # 正确输出:3306/tcp -> 127.0.0.1:3307 # 如果是 0.0.0.0:3307,说明监听所有网卡,但防火墙可能拦截
  3. 确认 MySQL 配置允许远程连接
    docker exec mysql-prod-order cat /etc/mysql/conf.d/custom.cnf | grep bind-address # 必须是 bind-address = 0.0.0.0

7.4 故障:app-backend容器里ping mysql-prod-order通,但mysql -h mysql-prod-order -u root -pHost '172.20.0.3' is not allowed to connect to this MySQL server

根因:MySQL 用户权限只授权了'root'@'localhost',没授权 `'

http://www.jsqmd.com/news/1134366/

相关文章:

  • Python双下划线方法:12个核心Dunder方法实战指南
  • Django技术向善:用开源框架驱动社会价值落地
  • Opus 4.7代码审查实战:穿透屎山的三层静态分析能力
  • Git分支重命名的完整协作流程与风险防控
  • 医疗营销五大核心挑战:合规、语言、数据、渠道与归因实战解法
  • ZFS快照在Plone发布中的秒级回滚实践
  • Plone生产部署实战:从Unified Installer到云原生交付
  • STM32G474RE与M24C04-R的I2C通信与EEPROM存储方案
  • Claude全自动开发流水线:从Git触发到CI/CD部署的工程化实践
  • macOS Apple Silicon下Anaconda安装避坑指南:架构、校验与环境隔离
  • OpenShift企业级云原生操作系统:Operator驱动的混合云治理实践
  • KeymouseGo:告别重复劳动,让鼠标键盘自动化成为你的数字助手
  • 打卡信奥刷题(3430)用C++实现信奥题 P10236 [yLCPC2024] D. 排卡
  • Excel数据透视表条件格式两大实战方案
  • 你的老旧电视也能流畅看直播:15MB轻量级电视直播应用全解析
  • C++笔记之std::list和std::vector的区别
  • Kafka 消费幂等:重复消息不可怕,重复副作用才可怕
  • PloneFormGen邮件适配器配置:性能优化与安全实践指南
  • Plone零代码交互:用Web Components封装JS行为
  • Python用户组实战:Grinder分布式压测与本地技术社区价值
  • 第二十一届智能车竞赛硬件盲盒任务参赛介绍
  • AI认知革命:从推箱子任务看世界模型与规划算法的技术演进
  • MAX22000与MSP432P401R高精度信号采集系统设计
  • OpenShift企业级落地:混合云编排与安全策略即代码实战
  • Django 集成 Okta 的最佳实践:从 OIDC 协议到生产级部署
  • AI Agent 部署与配置实战:从 Hermes 安装到跨平台自动化
  • Shell脚本工程化:从能跑到稳跑的7大核心原则
  • STM32F373RC与SLO2016高精度隔离数据采集方案
  • 前后端分离医院信管系统系统|SpringBoot+Vue+MyBatis+MySQL完整源码+部署教程
  • STM32与XTR116实现4-20mA工业信号传输设计