Docker 部署 MySQL:持久化数据库、账号与 5.x/8.x 版本迁移
MySQL 适合用 Docker Compose 作为长期运行的单实例数据库:镜像负责 MySQL Server,命名卷保存 /var/lib/mysql,.env 提供初始化密码,健康检查为依赖服务提供可观察的就绪信号。本文主路径使用官方 mysql:8.4.11 镜像,不把 latest 当作可回滚版本。
MySQL 5.7 仍可能出现在旧系统中,但新部署不应继续选择 5.7。5.7 与 8.x 在默认认证插件、默认排序规则、数据字典和升级流程上存在实际差异;文章后半部分给出迁移检查和兼容处理,不能把 5.7 的配置文件直接套进 8.4。
部署结论
| 项目 | 本文采用的值 |
|---|---|
| 项目地址 | MySQL 官方 Docker 镜像 |
| 官方镜像 | mysql |
| 项目版本 | 8.4.11 |
| 部署日期 | 2026-09-28 |
| 主部署方法 | 官方镜像 Docker Compose 单实例 |
| 备选方法 | 官方镜像 docker run,适合临时验证 |
| 对外端口 | 默认只绑定 127.0.0.1:3306 |
| 持久化位置 | Docker 命名卷 mysql84_data:/var/lib/mysql |
| 依赖服务 | 无;应用通过 Compose 网络访问 mysql:3306 |
| 支持架构 | 官方资料列出 amd64、arm64v8 |
| 初始化账号 | root 管理账号和 appuser 业务账号 |
主路径固定到 mysql:8.4.11,以便记录升级前后的镜像版本。官方镜像的 MYSQL_ROOT_PASSWORD、MYSQL_DATABASE、MYSQL_USER 和 MYSQL_PASSWORD 只在空数据目录首次初始化时生效;已有数据卷再次启动时不会重置密码、重建数据库或重跑初始化脚本。
5.x 和 8.x 的部署差异
| 项目 | MySQL 5.7 | MySQL 8.0/8.4 | 部署影响 |
|---|---|---|---|
| 新部署建议 | 已停止主流维护,适合遗留兼容 | 8.4 是当前 LTS 线 | 新环境选择 8.4;5.7 只为迁移或旧客户端保留 |
| 默认认证 | 常见为 mysql_native_password | 默认使用 caching_sha2_password | 旧驱动可能在 8.x 登录失败,应升级驱动 |
| 8.4 的旧认证插件 | 可直接使用 | mysql_native_password 默认禁用 | 只有临时兼容旧客户端时才显式启用,完成迁移后关闭 |
| 默认排序规则 | 不要依赖发行版默认值 | 8.x 常见 utf8mb4_0900_ai_ci | Compose 中显式设置字符集和排序规则 |
| 初始化脚本 | 空目录首次执行 | 空目录首次执行 | 更换镜像不会让旧卷重新执行 init/ |
| 数据字典 | 旧版数据字典 | 8.0 起使用事务性数据字典 | 5.7 直接挂卷给 8.x 不是升级方案 |
| 升级方式 | 需要检查 5.7 到 8.x 路径 | 8.0 到 8.4 也要做升级检查 | 先备份和检查,再换镜像;失败时恢复备份 |
认证插件差异
MySQL 8.0 默认认证插件是 caching_sha2_password。MySQL 5.7 客户端或旧版驱动可能只支持 mysql_native_password,常见表现是认证插件不支持或错误 2059。优先升级应用驱动、连接器和客户端,不要为了迁就旧驱动把整个 8.x 实例降级。
MySQL 8.4 仍提供 mysql_native_password,但服务端默认禁用。临时兼容时,可以在独立配置文件中加入 mysql_native_password=ON,重启后为确需兼容的账号执行 ALTER USER ... IDENTIFIED WITH mysql_native_password BY ...;迁移客户端后删除该配置并改回 caching_sha2_password。不要在公开 Compose 文件中写真实密码。
字符集和排序规则差异
业务系统应统一使用 utf8mb4,不要依赖不同大版本的默认字符集。8.x 可以使用 utf8mb4_0900_ai_ci;5.7 不识别该排序规则,兼容配置应使用 utf8mb4_unicode_ci。因此,5.7 和 8.x 不应共用一份未经检查的 .cnf 文件。
升级路径差异
MySQL 5.7 数据目录不能通过简单替换镜像标签直接变成 8.4 数据目录。迁移前要用目标版本对应的升级检查工具检查保留字、表定义、触发器、视图、字符集和已移除选项,使用逻辑备份或官方支持的升级路径迁移,并在隔离环境恢复验证。8.0 到 8.4 也可能涉及保留字、SQL 模式和已移除选项变化;不要把回滚镜像当成不可逆数据字典变更的恢复手段。
架构说明
正在准备渲染...
查看源码
flowchart LR
App[应用容器] -->|mysql:3306| Net[Compose 网络]
Net --> DB[mysql:8.4.11]
DB --> Data[(mysql84_data /var/lib/mysql)]
Operator[运维主机] -->|127.0.0.1:3306| DB
Backup[备份目录] -->|mysqldump 导入导出| DB应用容器使用服务名 mysql 和容器端口 3306,不应使用宿主机映射端口连接同一 Compose 网络中的数据库。宿主机端口仅用于本机运维或受控隧道,默认绑定回环地址,避免把 MySQL 直接暴露到公网。
前置条件
- Docker Engine 与 Docker Compose v2 可用。
- 宿主机
3306未被其他服务占用;如果已占用,只修改宿主机端口一侧,例如127.0.0.1:13306:3306。 - 至少为 MySQL 数据卷预留足够磁盘空间,并为备份目录准备独立空间。
- 目标架构为官方镜像支持的
amd64或arm64。 - 应用使用与 MySQL 8.x 兼容的驱动;从 5.7 迁移时先升级驱动,再切换服务端。
docker version
docker compose version
docker info --format '{{.Architecture}}'预期能看到 Docker Engine、Compose v2 和 amd64 或 aarch64。如果宿主机已经运行 MySQL,先确认端口、数据目录和备份归属,不要让两个 Compose 项目共用同一个数据卷。
Docker Compose 正式部署
准备目录、环境变量和备份目录
sudo mkdir -p /opt/mysql84/init /opt/mysql84/backups
sudo chown -R "$USER":"$USER" /opt/mysql84
cd /opt/mysql84
touch .env
chmod 600 .env在 /opt/mysql84/.env 写入安装实例自己的值。下面的密码只是格式示例,必须替换为随机生成的值,不要复制到生产环境:
MYSQL_ROOT_PASSWORD=<set-a-random-root-password-locally>
MYSQL_DATABASE=appdb
MYSQL_USER=appuser
MYSQL_PASSWORD=<set-a-random-app-password-locally>密码应通过密码管理器生成和保存。.env 含有 root 和业务账号密码,不要提交到 Git、镜像或工单;需要更严格的密钥管理时,改用 Docker secrets 的 _FILE 变量方式。
写入 Compose 配置
创建 /opt/mysql84/compose.yaml:
services:
mysql:
image: mysql:8.4.11
container_name: mysql84
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?请在 .env 中设置 MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE:-appdb}
MYSQL_USER: ${MYSQL_USER:-appuser}
MYSQL_PASSWORD: ${MYSQL_PASSWORD:?请在 .env 中设置 MYSQL_PASSWORD}
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_0900_ai_ci
- --skip-name-resolve
ports:
- "127.0.0.1:3306:3306"
volumes:
- mysql84_data:/var/lib/mysql
- ./init:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p\"$${MYSQL_ROOT_PASSWORD}\" --silent"]
interval: 10s
timeout: 5s
retries: 12
start_period: 30s
volumes:
mysql84_data:这份配置的关键决策如下:
mysql:8.4.11固定到可审计版本,不使用latest。mysql84_data只挂载到/var/lib/mysql,容器删除不会删除数据库文件。./init只读挂载到官方入口目录,脚本只在全新数据目录执行。utf8mb4和utf8mb4_0900_ai_ci明确写入 8.4 配置,避免依赖默认值。--skip-name-resolve避免依赖 DNS 反向解析;使用该选项时授权主机应使用 IP、localhost或%等明确规则。- 健康检查只检查 MySQL 是否接受本地连接;健康不等于业务 SQL、备份和恢复已经验证。
如果需要同时维护 MySQL 5.7,必须复制为单独项目目录和卷,使用独立服务名、端口和配置。5.7 配置不能把 utf8mb4_0900_ai_ci 或 8.4 专用选项直接带过去。
可选:初始化业务表
如果需要在空库创建最小验证表,创建 /opt/mysql84/init/001-app.sql:
USE appdb;
CREATE TABLE IF NOT EXISTS deployment_probe (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
note VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;脚本中的 appdb 必须与 MYSQL_DATABASE 一致。若使用其他数据库名,要同步修改 SQL。已有 mysql84_data 时,新增或修改这个文件不会自动执行;应通过 SQL 客户端显式迁移。
检查配置并启动
docker compose config --quiet
docker compose config --images
docker compose pull mysql
docker compose up -d mysql
docker compose ps预期 config --quiet 返回状态码 0,镜像列表只包含 mysql:8.4.11,服务最终显示 healthy。首次初始化可能需要几十秒,期间 MySQL 不接受连接是正常现象;不要因为短暂的连接失败就删除容器或数据卷。
查看初始化日志:
docker compose logs --tail=160 mysql日志中不应持续出现权限、数据字典或 InnoDB 错误。首次启动完成后,日志通常会出现 ready for connections;日志文本受镜像基础系统影响,以健康检查和 SQL 验收为准。
备选:官方镜像 docker run
单容器体验可以使用官方镜像的直接运行方式。长期维护、备份和升级应保留 Compose 文件:
docker volume create mysql84_data
docker run --name mysql84 \
--restart unless-stopped \
-e MYSQL_ROOT_PASSWORD='<set-a-random-root-password-locally>' \
-e MYSQL_DATABASE=appdb \
-e MYSQL_USER=appuser \
-e MYSQL_PASSWORD='<set-a-random-app-password-locally>' \
-p 127.0.0.1:3306:3306 \
-v mysql84_data:/var/lib/mysql \
mysql:8.4.11 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_0900_ai_ci命令行中的密码只用于展示参数位置,不能直接执行。生产环境应改为 Compose .env 或 Docker secrets,并避免密码进入 Shell 历史。docker run 路径没有本文 Compose 的健康检查和初始化目录挂载,适合短期验证,不适合作为长期运维记录。
验证部署结果
配置层
docker compose config --quiet
docker compose config --volumes
docker compose config --images预期能看到 mysql84_data、mysql:8.4.11 和成功的配置解析。若 Compose 展开后仍有未设置变量,先修复 .env,不要用空密码启动。
容器状态层
docker compose ps
docker inspect --format '{{.State.Status}} {{if .State.Health}}{{.State.Health.Status}}{{end}} {{.State.ExitCode}}' mysql84预期输出包含 running healthy 0。若状态为 starting,等待初始化完成;若为 unhealthy 或 exited,先看日志和磁盘空间,不要先删除 mysql84_data。
SQL 协议层
通过容器内客户端检查版本、字符集、认证插件和数据库账号:
docker compose exec -T mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -NBe "SELECT VERSION(), @@character_set_server, @@collation_server;"'
docker compose exec -T mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -NBe "SELECT user, host, plugin FROM mysql.user;"'
docker compose exec -T mysql sh -c 'exec mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -D"$MYSQL_DATABASE" -NBe "SELECT DATABASE(), CURRENT_USER();"'预期版本以 8.4.11 开头,服务端字符集为 utf8mb4,排序规则为 utf8mb4_0900_ai_ci;业务账号能连接 appdb,但不应拥有全局 root 权限。不要把命令输出中的密码或完整用户授权信息粘贴到公开工单。
初始化表与日志层
如果创建了 init/001-app.sql,检查探针表和日志:
docker compose exec -T mysql sh -c 'exec mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -D"$MYSQL_DATABASE" -NBe "SHOW TABLES LIKE \"deployment_probe\";"'
docker compose logs --tail=160 mysql预期能看到 deployment_probe,日志没有持续的 InnoDB、权限或认证错误。初始化脚本只对空数据目录有效,不能把“文件存在”误判为每次启动都会执行。
持久化重建层
写入一条无害记录后重建容器,确认数据仍在命名卷中:
docker compose exec -T mysql sh -c 'exec mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -D"$MYSQL_DATABASE" -e "INSERT INTO deployment_probe(note) VALUES (\"persistence-check\");"'
docker compose up -d --force-recreate mysql
docker compose ps
docker compose exec -T mysql sh -c 'exec mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -D"$MYSQL_DATABASE" -NBe "SELECT note FROM deployment_probe ORDER BY id DESC LIMIT 1;"'预期最后一条命令返回 persistence-check。不要执行 docker compose down -v 作为普通重建,该命令会删除命名卷并使数据库文件不可恢复。
首次初始化与账号使用
首次启动空卷时,官方入口脚本按以下顺序处理:创建 root、创建 MYSQL_DATABASE、创建 MYSQL_USER 并授予该数据库权限,然后按文件名顺序执行 /docker-entrypoint-initdb.d/ 中的 .sh、.sql 和压缩 SQL 文件。初始化完成前,应用必须实现重试或等待健康状态。
应用连接 MySQL 8.4 时使用:
host=mysql
port=3306
database=appdb
user=appuser
password=<.env 中的 MYSQL_PASSWORD>同一 Compose 网络内使用 mysql:3306,宿主机运维使用 127.0.0.1:3306。生产应用不要使用 root;appuser 只获得 appdb 的权限。需要更细粒度权限时,在初始化脚本或独立迁移中显式执行 GRANT,并验收实际权限。
日常运维
cd /opt/mysql84
docker compose ps
docker compose logs --tail=160 mysql
docker compose exec -T mysql sh -c 'exec mysqladmin -uroot -p"$MYSQL_ROOT_PASSWORD" status'日常记录至少包括镜像 tag、镜像 ID、数据卷名、.env 变更、备份文件位置、最近一次恢复演练时间和应用驱动版本。容器可重建,数据卷、备份和密钥不能只保留在容器可写层。
备份与恢复
MySQL 数据不能只复制容器目录。使用 mysqldump 或与业务规模匹配的 MySQL 官方备份工具,并单独备份 Compose 文件、.env(加密保存)和应用迁移记录。下面的逻辑备份适合中小规模实例,持续写入的大库应评估 MySQL Enterprise Backup、物理快照或复制方案。
逻辑备份
cd /opt/mysql84
mkdir -p backups
docker compose exec -T mysql sh -c 'exec mysqldump --single-transaction --routines --events --triggers --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > "backups/mysql-all-$(date +%F).sql"
test -s "backups/mysql-all-$(date +%F).sql"--single-transaction 主要适用于 InnoDB;如果实例包含非事务表、DDL 频繁执行或需要严格跨库一致性,要先停止写入或采用项目级备份方案。备份文件可能包含账号、业务数据和结构,必须限制权限、加密存储并保存异机副本。
隔离恢复演练
恢复前停止应用写入,并确认目标卷、目标数据库和备份日期。不要在唯一生产实例上直接覆盖恢复:
docker compose exec -T mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "CREATE DATABASE IF NOT EXISTS restore_check;"'
sed 's/`appdb`/`restore_check`/g' backups/mysql-all-YYYY-MM-DD.sql > /tmp/mysql-restore-check.sql
docker compose exec -T mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD"' < /tmp/mysql-restore-check.sql
docker compose exec -T mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -NBe "SELECT COUNT(*) FROM restore_check.deployment_probe;"'
rm -f /tmp/mysql-restore-check.sql上面的 sed 只适合演练中结构简单、数据库名明确的导出文件;真实生产恢复应按 dump 内容和目标库逐项检查,避免误替换字符串。验收至少包括数据库可连接、关键表数量、关键记录、字符集、触发器、存储过程和应用读写。确认恢复成功前保留原备份和原数据卷。
通用卷归档、权限和异机备份方法见 Docker 备份与恢复数据教程。MySQL 专有 dump 与升级边界仍以本节为准。
升级与回滚
8.4 小版本升级
小版本升级也要先备份、检查空间、记录镜像 ID 和执行 SQL 验收。将 Compose 的 image 改为目标固定 tag 后:
docker compose exec -T mysql sh -c 'exec mysqldump --single-transaction --routines --events --triggers --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > "backups/before-upgrade-$(date +%F).sql"
docker image inspect mysql:8.4.11 --format '{{.Id}}'
docker compose pull mysql
docker compose up -d mysql
docker compose ps
docker compose logs --tail=200 mysql
docker compose exec -T mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -NBe "SELECT VERSION();"'升级后重新检查应用连接、迁移、字符集、关键查询和备份。不要把 docker compose pull 与未审查的 latest 结合使用。
5.7 到 8.x 迁移
迁移顺序应是:
- 在 5.7 上完成全量逻辑备份和恢复演练,记录字符集、排序规则、账号认证插件、触发器、事件、存储过程和 SQL 模式。
- 升级应用驱动和运维客户端,使连接器支持
caching_sha2_password。 - 使用 MySQL 8.x 的升级检查工具或
mysqlcheck --check-upgrade检查保留字、表定义、视图、触发器、字符集和已移除选项。 - 在隔离目录用
mysql:8.4.11创建空实例,把逻辑备份导入新实例,执行应用迁移和分层验收。 - 确认应用读写、后台任务和备份恢复后,再安排停写窗口切换连接地址。
不要把 5.7 的 /var/lib/mysql 直接挂载给 8.4 作为升级操作;不要跨过官方支持的中间版本路径;不要在 8.4 已启动并修改数据字典后只换回 5.7 镜像。发生失败时,停止服务并恢复升级前的 Compose、镜像和完整数据备份。
回滚限制
如果只是镜像拉取失败且数据字典未改变,可以把 image 改回上一个固定 tag。若新版本已经执行升级、修改系统表或完成不可逆迁移,镜像回滚通常不够,必须恢复升级前备份到隔离卷,再切换服务。回滚完成后重新执行版本、账号、关键表和应用连接验收。
生产环境安全加固
- 宿主机端口默认绑定
127.0.0.1:3306,公网应用通过私有网络、VPN 或受控入口访问;不要把 MySQL 端口直接暴露到公网。 - 应用使用最小权限业务账号,root 仅用于运维;定期轮换
.env或 Docker secret 中的密码。 - 8.x 优先使用
caching_sha2_password;只为迁移旧客户端临时启用mysql_native_password,并记录关闭计划。 .env、SQL dump、Docker volume 归档和日志可能包含敏感数据,应加密保存、限制权限并放置异机副本。- 固定镜像 tag,发布前检查镜像来源、架构、漏洞扫描和升级说明;不要在生产环境使用
latest。 - 不要把
/var/run/docker.sock、宿主机根目录或不必要的特权能力挂进 MySQL 容器。 - 持续监控磁盘空间、InnoDB 错误、连接数、慢查询和备份结果;健康检查不能代替数据库指标。
- 通用 Compose 安全基线见 Docker Compose 生产安全加固教程。
常见问题
容器一直是 starting 或 unhealthy
首次初始化需要创建系统表、数据库和账号,空卷启动会比已有卷慢。先看 docker compose logs --tail=200 mysql、宿主机磁盘空间和 docker inspect 的健康检查输出。不要在未确认数据卷归属前执行 down -v。
MYSQL_ROOT_PASSWORD 修改后密码没有变化
官方入口变量只在空数据目录首次初始化时生效。已有 mysql84_data 时,修改 .env 不会重置 root 密码。使用旧密码登录后执行 ALTER USER,或在经过备份和恢复演练的前提下完成账号变更。
应用报认证插件不支持或错误 2059
这是旧客户端不支持 caching_sha2_password 的常见表现。先升级连接器和客户端;不要立即启用 mysql_native_password。如果暂时无法升级,MySQL 8.4 需要在服务端显式启用该插件,再只为指定账号设置旧插件,完成客户端迁移后关闭。
5.7 的配置文件在 8.4 启动失败
检查是否使用了已删除的选项、旧 SQL 模式、旧排序规则或 mysql_native_password 相关配置。5.7 的 utf8mb4_unicode_ci 可以作为兼容排序规则;8.4 的 utf8mb4_0900_ai_ci 不能直接写入 5.7 配置。按目标版本文档逐项迁移,不要把整份 my.cnf 原样复制。
初始化 SQL 没有执行
确认数据卷是全新的、文件扩展名正确、挂载路径是 /docker-entrypoint-initdb.d,并检查日志。已有数据目录不会再次执行初始化目录;应把后续变更作为显式数据库迁移。
宿主机端口 3306 被占用
执行 docker ps 和宿主机端口检查,确认占用者。应用容器仍使用 mysql:3306,只有宿主机运维端口需要改成例如 127.0.0.1:13306:3306,不要修改容器内部端口。
直接挂载旧数据目录后 MySQL 无法启动
不要继续重启或运行修复命令覆盖现场。停止容器、保留原目录副本,确认源版本和目标版本,优先从逻辑备份或物理备份恢复到新卷,再按升级路径处理。5.7 数据目录不能作为 8.4 的直接启动卷。
总结
新部署使用 mysql:8.4.11、Compose 命名卷 mysql84_data、127.0.0.1:3306 和 .env 密钥即可得到可维护的 MySQL 单实例。验收要覆盖 Compose 配置、容器健康、SQL 连接、账号插件、初始化表和重建后的数据持久化;备份使用 mysqldump 并在隔离环境恢复验证。
MySQL 5.7 只作为遗留兼容线处理。5.7 到 8.x 的升级必须先升级客户端、检查认证插件和字符集,使用逻辑备份或官方支持路径迁移,不能直接复用数据目录或把镜像回滚当作数据库恢复。