Skip to content

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.7MySQL 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_ciCompose 中显式设置字符集和排序规则
初始化脚本空目录首次执行空目录首次执行更换镜像不会让旧卷重新执行 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 模式和已移除选项变化;不要把回滚镜像当成不可逆数据字典变更的恢复手段。

架构说明 ​

Mermaid 流程图
查看源码
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 迁移时先升级驱动,再切换服务端。
bash
docker version
docker compose version
docker info --format '{{.Architecture}}'

预期能看到 Docker Engine、Compose v2 和 amd64 或 aarch64。如果宿主机已经运行 MySQL,先确认端口、数据目录和备份归属,不要让两个 Compose 项目共用同一个数据卷。

Docker Compose 正式部署 ​

准备目录、环境变量和备份目录 ​

bash
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 写入安装实例自己的值。下面的密码只是格式示例,必须替换为随机生成的值,不要复制到生产环境:

dotenv
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:

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:

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 客户端显式迁移。

检查配置并启动 ​

bash
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 不接受连接是正常现象;不要因为短暂的连接失败就删除容器或数据卷。

查看初始化日志:

bash
docker compose logs --tail=160 mysql

日志中不应持续出现权限、数据字典或 InnoDB 错误。首次启动完成后,日志通常会出现 ready for connections;日志文本受镜像基础系统影响,以健康检查和 SQL 验收为准。

备选:官方镜像 docker run ​

单容器体验可以使用官方镜像的直接运行方式。长期维护、备份和升级应保留 Compose 文件:

bash
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 的健康检查和初始化目录挂载,适合短期验证,不适合作为长期运维记录。

验证部署结果 ​

配置层 ​

bash
docker compose config --quiet
docker compose config --volumes
docker compose config --images

预期能看到 mysql84_data、mysql:8.4.11 和成功的配置解析。若 Compose 展开后仍有未设置变量,先修复 .env,不要用空密码启动。

容器状态层 ​

bash
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 协议层 ​

通过容器内客户端检查版本、字符集、认证插件和数据库账号:

bash
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,检查探针表和日志:

bash
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、权限或认证错误。初始化脚本只对空数据目录有效,不能把“文件存在”误判为每次启动都会执行。

持久化重建层 ​

写入一条无害记录后重建容器,确认数据仍在命名卷中:

bash
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 时使用:

text
host=mysql
port=3306
database=appdb
user=appuser
password=<.env 中的 MYSQL_PASSWORD>

同一 Compose 网络内使用 mysql:3306,宿主机运维使用 127.0.0.1:3306。生产应用不要使用 root;appuser 只获得 appdb 的权限。需要更细粒度权限时,在初始化脚本或独立迁移中显式执行 GRANT,并验收实际权限。

日常运维 ​

bash
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、物理快照或复制方案。

逻辑备份 ​

bash
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 频繁执行或需要严格跨库一致性,要先停止写入或采用项目级备份方案。备份文件可能包含账号、业务数据和结构,必须限制权限、加密存储并保存异机副本。

隔离恢复演练 ​

恢复前停止应用写入,并确认目标卷、目标数据库和备份日期。不要在唯一生产实例上直接覆盖恢复:

bash
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 后:

bash
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 迁移 ​

迁移顺序应是:

  1. 在 5.7 上完成全量逻辑备份和恢复演练,记录字符集、排序规则、账号认证插件、触发器、事件、存储过程和 SQL 模式。
  2. 升级应用驱动和运维客户端,使连接器支持 caching_sha2_password。
  3. 使用 MySQL 8.x 的升级检查工具或 mysqlcheck --check-upgrade 检查保留字、表定义、视图、触发器、字符集和已移除选项。
  4. 在隔离目录用 mysql:8.4.11 创建空实例,把逻辑备份导入新实例,执行应用迁移和分层验收。
  5. 确认应用读写、后台任务和备份恢复后,再安排停写窗口切换连接地址。

不要把 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 的升级必须先升级客户端、检查认证插件和字符集,使用逻辑备份或官方支持路径迁移,不能直接复用数据目录或把镜像回滚当作数据库恢复。

参考资料 ​

全部公开文章由同一个站点构建和发布。