十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Docker部署MySQL 8.0开发环境:从镜像到连接的全流程指南

Docker部署MySQL 8.0开发环境:从镜像到连接的全流程指南 最近在搭开发环境时又把 MySQL 8.0 和 Docker 这套组合重新梳理了一遍。如果你也正在折腾“用 Docker 跑 MySQL 8.0.42 作为开发库”这件事这篇手册应该是你正需要的。它覆盖了从镜像选择、容器启动参数、数据持久化、远程连接配置到日常开发中高频踩坑的排查方法全程用我实测过的命令和配置来讲保证你在自己机器上能一步步复现。这套方案特别适合这几类朋友刚接触 Docker 想拿 MySQL 练手的前端或后端开发、需要在本地快速起多个 MySQL 实例做测试的工程师以及想彻底摆脱“在本机装 MySQL 装到崩溃”的同学们。整个手册思路很简单用官方镜像、挂载数据卷、通过环境变量完成初始化然后通过宿主机的端口去访问它。我在写的时候刻意避开了那些“复制粘贴就能跑但坏了一脸懵”的碎片化资料把所有关键参数、为什么这么配、出了问题怎么查都拆开讲明白。1. 为什么选择 Docker 来部署 MySQL 开发环境1.1 传统安装方式的痛点以前在 Windows 或 macOS 上装 MySQL 开发库要经历下载安装包、配置环境变量、初始化 data 目录、注册系统服务、设置 root 密码等一系列步骤。中间任何一个环节出错排查起来都相当痛苦。而且一旦电脑上有多个项目需要不同版本的 MySQL比如老项目用 5.7、新项目用 8.0传统安装方式几乎没法做到“一键切换”最后只能在虚拟机里折腾或者在多个端口间反复横跳。我第一次在 Windows 上装 MySQL 8.0 时就遇到过服务启动后立即停止的问题查日志发现是 data 目录权限不对后来干脆卸载重装才解决。这种问题在 Docker 里基本不会出现因为容器环境是隔离的不会跟宿主机其他软件抢资源、抢端口、抢系统服务。1.2 Docker 方案的核心优势用 Docker 跑 MySQL 开发库最大的优势在于“隔离”和“可复制”。你的 MySQL 实例就像一个小盒子盒子里的操作系统、依赖库、配置文件都是独立的一套。你不需要关心 MySQL 是怎么被安装到系统里的只需要挂载好自己的配置文件和数据目录随时可以销毁旧容器、创建新容器而数据不会丢。另外一个特别重要的点是版本切换。Docker Hub 上 mysql 官方镜像的 tag 特别全8.0.42、8.0.41、5.7.44、8.4 这些版本都存在。你可以在同一台机器上同时跑两个不同版本的 MySQL只要宿主机端口不冲突就行。这一点在验证“某些 SQL 在 8.0 和 5.7 下行为差异”时真是救命级的方便。1.3 版本选择为什么是 8.0.42 而不是 8.4 或 9.xMySQL 8.0 是当前公认最稳的长期支持版本8.0.42 是 8.0 分支生命周期末期的维护版本修掉了大量历史 bug也包含所有 8.0 系列的安全补丁。如果你要跑的是生产环境的模拟场景8.0.42 是最贴近生产实际的选择因为大量存量系统都停留在 8.0 系列。相比之下8.4 是创新版本后续的 9.x 变化更大一些老的 JDBC 驱动、ORM 框架未必能在短时间内兼容。开发环境下如果没必要尝鲜稳定压倒一切。我之前在一个同事的机器上帮他排查问题他装的是 MySQL 9.0结果 MyBatis Plus 的某些分页插件行为表现跟 8.0 有明显差异排查了半天才发现是版本问题。所以在 Docker 镜像里我始终坚持用 8.0.42 这种成熟小版本。2. 部署前的准备工作2.1 Docker 环境安装与 Virtualization 报错排查在 Windows 上大部分人选择 Docker Desktop。装好之后最常见的坑就是启动时报错Docker Desktop failed to start because virtualisation support wasnt detected。这类问题基本都是因为 BIOS/UEFI 里的虚拟化功能没开启或者 Windows 的 Hyper-V、WSL 2 功能没有启用。检查步骤我整理了一下打开任务管理器切到“性能”标签看 CPU 部分是否显示“虚拟化已启用”。如果显示“已禁用”需要进 BIOS 开启 Intel VT-x 或 AMD SVM不同主板品牌的路径不一样但关键词就是 Virtualization Technology。在“启用或关闭 Windows 功能”中勾选“适用于 Linux 的 Windows 子系统”和“虚拟机平台”然后重启。以管理员身份打开 PowerShell执行wsl --set-default-version 2确保 WSL 默认走 2 代版本。如果你的 Windows 版本比较老Docker Desktop 会提示 incompatible version of Windows那就需要升级系统到受支持的版本或者改用 Docker Toolbox 加 VirtualBox 的旧方案但那个方案已经过时强烈不建议。如果你用的是 Linux 服务器安装 Docker Engine 就简单多了。Ubuntu 系发行版直接用官方 apt 源安装装好后把当前用户加入 docker 组避免每次敲命令都要 sudo。CentOS 7 的话注意它的默认内核版本较低建议先升级内核再装新版 Docker否则可能遇到存储驱动不兼容的问题。2.2 配置镜像加速源在国内网络环境下直接从 Docker Hub 拉取 mysql:8.0.42 镜像可能会很慢甚至超时。Docker Desktop 的设置界面里可以配置 Registry mirrors填入可用的加速地址。Linux 环境下则改/etc/docker/daemon.json添加registry-mirrors字段然后重启 docker 服务。这里我多说一句网上很多镜像加速地址会不定期失效建议你在配置前先 curl 试一下连通性。如果某个地址长期拉不动果断换掉。测试也很简单curl -I https://你的加速地址/v2/如果返回 200说明可用。配置完成后执行docker info在输出的内容里找到 Registry Mirrors 部分确认配置生效。2.3 拉取 mysql:8.0.42 镜像并验证准备工作做完后拉取镜像本身很简单docker pull mysql:8.0.42拉取完成后可以顺手确认一下镜像信息docker images | grep mysql你会看到类似mysql 8.0.42 xxxxxxxxxxxx xxxMB的输出。国内网络如果比较慢也可以在加速源配置好之后再拉。这里有个小的判断点官方 mysql 镜像大概 500MB 左右按架构略有差异如果显示只有一两百兆那大概率拉取不完整建议docker rmi后重新拉。这一步拉取时间取决于网络情况趁这个时间可以先把后面要用到的目录结构建好。3. 容器创建与核心参数解析3.1 挂载目录规划在启动容器之前我建议先在宿主机上规划好数据目录和配置目录。不要图省事直接跑一个不带挂载的容器否则哪天容器被删除数据跟着一起没了那种滋味体验过一次就再也不想体验了。我习惯的工作目录结构是这样~/docker/mysql/ ├── conf/ # 存放自定义配置文件 ├── data/ # MySQL 数据文件 ├── logs/ # 错误日志与慢查询日志 └── backup/ # 手动备份的 SQL 文件进入这个目录再执行后面的 docker run 命令路径会比较清晰。注意如果 data 目录已经存在并且非空MySQL 容器初始化时不会重复执行初始化脚本这个机制后面会用到。3.2 docker run 命令逐参数拆解这是整个部署过程的核心命令我直接给出一个适合开发环境的完整示例docker run -d \ --name mysql-8.0.42 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDRoot123456 \ -e MYSQL_DATABASEdevdb \ -e MYSQL_USERdev \ -e MYSQL_PASSWORDDev123456 \ -v /your/path/mysql/conf:/etc/mysql/conf.d \ -v /your/path/mysql/data:/var/lib/mysql \ -v /your/path/mysql/logs:/var/log/mysql \ --restart unless-stopped \ mysql:8.0.42我们逐个看参数的含义-d后台运行容器不加的话容器在前台运行关掉终端容器就停了。--name mysql-8.0.42给容器起个名字后续docker exec -it mysql-8.0.42 bash进容器就靠它。-p 3306:3306宿主机 3306 端口映射到容器内 3306。注意如果你的宿主机已经装了 MySQL 或其他服务占用 3306 端口这里要改成其他映射比如-p 3307:3306。-e MYSQL_ROOT_PASSWORDroot 用户的初始密码。-e MYSQL_DATABASE容器首次初始化时自动创建的数据库名。-e MYSQL_USER和-e MYSQL_PASSWORD额外创建的一个普通用户这个用户对MYSQL_DATABASE指定的库拥有全部权限。-v把宿主机的目录挂载进容器实现配置、数据、日志在宿主机上直接可见。--restart unless-stopped容器异常退出或 Docker 服务重启后自动拉起容器对开发机来说特别实用。3.3 为什么需要自定义配置目录MySQL 官方镜像的默认配置适合通用场景但开发环境常常需要针对自己的机器调整参数。比如你的电脑只有 8GB 内存而 MySQL 8.0 默认的innodb_buffer_pool_size是 128MB虽然是默认值但有些联合查询情况下确实偏保守反过来如果你在做性能测试又希望调大这个值。更常见的情况是修改sql_mode、字符集、时区、慢查询日志等。把conf挂载到容器内的/etc/mysql/conf.d之后你只需要在宿主机上新建一个my.cnf文件写出你想覆盖的配置项然后重启容器即可生效。例如[mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_0900_ai_ci default-time-zone08:00 slow_query_logON long_query_time2注意default-time-zone08:00是开发环境中一个容易被忽略的点。如果你不设置MySQL 容器默认使用 UTC 时间程序里NOW()返回的时间和本地时间对不上排查起来会一脸懵。3.4 容器启动后的基础验证容器启动后先看一眼状态docker ps | grep mysql-8.0.42STATUS 列显示 Up 几秒或几分钟基本就是正常了。如果发现容器反复重启用docker logs mysql-8.0.42看日志绝大多数问题在日志里都有明确提示。确认容器处于 Up 状态后进入容器用 mysql 客户端连一下docker exec -it mysql-8.0.42 mysql -uroot -p输入之前设置的密码能进入 MySQL 命令行说明基础部署成功。接着可以顺手执行一句SELECT VERSION();如果返回8.0.42那就是我们部署的目标版本。4. 开发期必备用户权限与客户端连接4.1 root 用户的本地限制用 Docker 方式部署的 MySQL容器内 root 用户默认只允许从 localhost 连接。你在宿主机上用 Navicat、MySQL Workbench 或 DataGrip 去连的时候如果直接拿 root 账号去试大概率会报Access denied for user rootlocalhost。这就是为什么我强烈推荐在启动容器时用MYSQL_USER和MYSQL_PASSWORD创建一个普通开发账号。如果你已经错过了这个参数也可以在容器启动后手动创建用户CREATE USER dev% IDENTIFIED BY Dev123456; GRANT ALL PRIVILEGES ON devdb.* TO dev%; FLUSH PRIVILEGES;这里dev%中的%表示该用户可以从任意主机连接开发环境这么配是没问题的。生产环境千万别这么干一定限制 IP 段。4.2 客户端无法连接的常见原因当你用客户端工具连接localhost:3306时如果连接失败优先排查这几个点第一防火墙是否拦截了 3306 端口。Windows 上第一次运行 Docker Desktop 时通常会自动放行但如果你用的是 Linux 服务器可能需要手动开放端口比如firewall-cmd --add-port3306/tcp。第二连接地址是否用了127.0.0.1。在 Docker Desktop 的默认网络模式下容器通过端口映射暴露服务宿主机访问127.0.0.1:3306是没问题的。第三密码是否正确。MySQL 8.0 的默认认证插件是caching_sha2_password如果你用 Navicat 旧版本可能提示Client does not support authentication protocol requested by server。这个问题有两种解法一是升级 Navicat 到 16.x 以上版本二是新建用户时指定mysql_native_passwordCREATE USER dev% IDENTIFIED WITH mysql_native_password BY Dev123456;不过说实话如果你的客户端版本能升级尽量升级客户端因为mysql_native_password在 MySQL 8.0 里已经被标记为废弃未来版本中可能会被移除。4.3 MySQL Workbench 和命令行工具的使用MySQL Workbench 在开发环境里还是很有用的特别是看 ER 图、跑查询分析、管理数据导出导入。连接时填写主机127.0.0.1、端口3306、用户名dev、密码基本上一次就能连通。如果是命令行连接宿主机上装了 mysql-client 的话可以mysql -h 127.0.0.1 -P 3306 -u dev -p注意如果你在 Windows 的 CMD 里执行-p和密码之间不要有空格否则会提示输入密码。5. 数据初始化与开发库常用操作5.1 首次启动时的自动初始化脚本MySQL Docker 镜像有个特性当容器第一次启动且数据目录为空时会自动执行/docker-entrypoint-initdb.d目录下的所有.sql、.sh和.sql.gz脚本。这个机制特别适合初始化表结构、插入基础数据。你可以在宿主机上创建/your/path/mysql/init目录然后把 SQL 文件放进去启动容器时再挂载到这个目录-v /your/path/mysql/init:/docker-entrypoint-initdb.d容器首次初始化时就会自动按文件名顺序执行这些脚本。注意这个机制只在数据目录为空时生效如果 data 目录已经初始化过了新加的脚本不会自动执行。5.2 手动导入导出 SQL 数据开发过程中经常需要导入别人给的 SQL 文件或者导出当前库做备份。在 Docker 环境下最方便的方式是直接通过 mysql 命令行重定向docker exec -i mysql-8.0.42 mysql -uroot -pRoot123456 devdb backup.sql-i参数保证标准输入可以从宿主机重定向到容器内。导出操作类似docker exec mysql-8.0.42 mysqldump -uroot -pRoot123456 devdb backup.sql如果你用的是新版本 mysqldump8.0 默认会把SET GLOBAL等语句也导出导入时如果权限不足可能报错可以加--single-transaction --set-gtid-purgedOFF来规避。5.3 UPDATE 语法和 ORDER BY 常见问题开发中写 UPDATE 语句时有个常见的误解就是 UPDATE 和 ORDER BY 的组合。MySQL 是支持这种语法的尤其是分页更新场景UPDATE employees SET status inactive ORDER BY last_login ASC LIMIT 100;这个语句会按 last_login 升序排列后更新最早未登录的 100 个用户。这在批量处理场景里非常有用但要注意 LIMIT 不能用于多表 UPDATE否则语法报错。另外 MySQL 8.0 的排序行为有一些细节值得注意。默认排序是utf8mb4_0900_ai_ci大小写不敏感所以ORDER BY name会把apple和Apple排在一起。如果你需要大小写敏感排序可以在字段级别指定COLLATE utf8mb4_bin或者在查询中显式指定SELECT * FROM employees ORDER BY name COLLATE utf8mb4_bin;这类问题在开发环境可能注意不到但一旦数据量变大、分词场景变多排序规则的坑就会暴露出来。5.4 存储过程的调试与注意事项MySQL 8.0 的存储过程开发和调试也是开发工作中的一部分。创建存储过程时要注意DELIMITER的命令差异DELIMITER $$ CREATE PROCEDURE get_employee_count(IN dept_id INT, OUT total INT) BEGIN SELECT COUNT(*) INTO total FROM employees WHERE department_id dept_id; END$$ DELIMITER ;在命令行客户端或 Workbench 中执行时DELIMITER是必需的因为默认的分号会导致 CREATE PROCEDURE 提前结束。但在 Docker 的 mysql 客户端交互模式下也可以直接写多行语句Enter 后它不会执行直到遇到完整的分号结构。存储过程的调试比普通 SQL 麻烦一些如果过程不复杂我建议先把它改成普通 SELECT 验证逻辑确认结果正确后再包进过程里。如果确实要步骤化调试可以在过程中加临时表或使用 SELECT 标记 的方式确认执行到哪一步这是最朴素的调试方法。5.5 MySQL 中 INT 5 的含义互联网上关于“mysql 中 int5”的搜索量一直不小这个问题其实是在问 MySQL 中整型字段与常量相加时的类型转换行为。比如字段类型是 INT执行SELECT id 5 FROM tableMySQL 会自动以数值方式计算但如果字段是 VARCHAR 且内容不是纯数字会触发隐式转换非数字部分按 0 处理。开发中这种隐式转换会隐藏 bug建议在 SQL 中显式使用CAST(id AS SIGNED)来保证类型安全SELECT CAST(user_id AS SIGNED) 5 FROM user_table;这个知识点虽然简单但在实际开发中踩的人很多。特别是在 ORM 框架拼接动态 SQL 的时候字符串和数字混在一起查出来的结果经常对不上。6. 常见问题排查实录6.1 容器反复重启排查第一步永远是看日志。假设你的容器一直在 Restarting用 docker logs 看最后几行docker logs --tail 50 mysql-8.0.42最常见的错误是配置目录挂载了非法的my.cnf文件导致 MySQL 解析失败。我遇到过的情况是写配置时把bind-address写成了0.0.0.0.0多了一个 0MySQL 直接拒绝启动。这种错误在日志里会明确提示配置文件第几行有问题按提示修改即可。另一个常见问题是 data 目录权限。如果你把 data 目录挂载到了 SELinux 启用的 Linux 系统上容器内的 mysql 用户可能没有写权限。解决方案要么是关闭 SELinux不建议要么执行chcon -Rt svirt_sandbox_file_t /your/path/mysql/data6.2 3306 端口被占用如果启动容器时报Bind for 0.0.0.0:3306 failed: port is already allocated说明宿主机已经有程序占用 3306。你可以用netstat -ano | findstr 3306Windows或lsof -i:3306Linux/macOS查看占用进程也可以直接修改映射端口为 3307、3308 等。这里有个经验开发环境下项目配置里数据库端口都是写死的改映射端口意味着要同步改项目配置。如果你不想改项目文件就把占用 3306 的旧服务停掉把端口让给 Docker 容器。6.3 数据丢失的情况有三个原因会导致容器删除后数据丢失第一没挂载数据卷。这是最惨痛的教训。容器删了数据跟着走了。第二挂载了数据卷但路径不对。比如你把宿主机的/your/path/data挂载到了容器的/var/lib/mysql但实际容器启动后 data 目录用的是另一个路径看日志才发现写到了容器层。第三误删了挂载目录。这种情况我只能建议定期做 SQL dump 备份或者在 Docker 之外再用工具做定时备份。6.4 Firedac 连接时认证协议问题搜索热词里有一个很具体的问题Firedac 连接 MySQL 时报[MySQL] Client does not support authentication protocol requested by server。这个问题我上面提到过本质是 MySQL 8.0 默认的caching_sha2_password认证插件和旧客户端的兼容性问题。Firedac 是 Delphi/C Builder 生态的数据库访问库如果版本较老就可能不支持新的认证方式。解决思路有两个方向升级 Firedac 或 MySQL 驱动版本使客户端支持caching_sha2_password。将 MySQL 用户的认证插件改为mysql_native_password但注意这个插件在 MySQL 8.0 中已标记为废弃未来版本可能不再支持。从长远角度看想办法升级驱动才是正路改数据库侧认证插件只是在老项目上的临时方案。这种问题在开发环境里还不明显一旦项目上线部署到使用新客户端的生产环境反而更麻烦。7. 开发环境的其他扩展场景7.1 同时部署多个 MySQL 版本Docker 最爽的地方是可以并行跑多个 MySQL 实例。假设你要跑一个 5.7 的旧项目和一个 8.0.42 的新项目只需要docker run -d --name mysql-5.7 -p 3307:3306 -e MYSQL_ROOT_PASSWORDRoot123456 mysql:5.7 docker run -d --name mysql-8.0.42 -p 3306:3306 -e MYSQL_ROOT_PASSWORDRoot123456 mysql:8.0.42两个容器互不干扰数据也各自独立切换项目时只需要改项目里的数据库连接端口。这个操作在传统安装方式下简直不敢想象。7.2 数据备份与恢复策略开发环境的备份不用搞太复杂的方案但定时 dump 还是必要的。可以参考这样的脚本逻辑每天凌晨用mysqldump导出所有数据库按日期命名压缩保留一周。如果项目里有 Redis也可以参考同样的思路用 Docker 搭一套 Redis 主从或单机开发环境。毕竟用 Docker 管理中间件最大的优势就是基础设施即代码一条 docker run 命令就能把整套环境描述清楚换电脑或者新人入职时照着命令跑一遍就齐活了。7.3 Docker 青龙依赖管理的一个小对比搜索热词里出现了“docker 青龙 依赖管理”这里稍微提一下也能说明 Docker 的通用性。青龙面板这类工具本身就是一个 Docker 容器它的依赖管理也是在容器内部完成的。这种方式和 MySQL 容器一样宿主机只负责运行容器所有环境问题都在容器内解决不需要污染宿主机的全局环境。理解了这个模式你在用 Docker 跑任何中间件时思路都会清晰很多。8. 常用命令速查和避坑清单8.1 开发中高频使用的 Docker MySQL 命令# 进入容器 bash docker exec -it mysql-8.0.42 bash # 直接执行 SQL docker exec -it mysql-8.0.42 mysql -uroot -p -e SHOW DATABASES; # 查看容器日志 docker logs -f mysql-8.0.42 # 重启容器 docker restart mysql-8.0.42 # 停止容器 docker stop mysql-8.0.42 # 删除容器数据保留在挂载目录 docker rm mysql-8.0.42这些命令在开发过程中基本每天都会用到。docker exec -it进入容器的操作特别多因为有时候需要看容器内的配置文件、日志文件或者在容器里执行 mysql 命令。8.2 避坑清单踩坑点解决方案容器删了数据丢了必须挂载 /var/lib/mysql 数据卷远程连接不了检查端口映射、防火墙、用户 host 是否为 %认证协议报错升级客户端或临时改 mysql_native_password时区不对挂载 my.cnf 设置 default-time-zone08:00容器反复重启先看 docker logs 定位原因一般是配置或权限问题中文乱码字符集统一配置为 utf8mb4连接串加 characterEncodingutf8这张表是我在实际使用中踩过坑之后总结出来的每一条背后都有一段“查了半天资料才发现是这么回事”的经历。如果你是新手建议收藏这张表遇到问题先对照一遍。8.3 最后再分享一个小技巧我个人习惯是把启动命令写成一个 shell 脚本或者 docker-compose.yml 存到项目仓库里。比如 docker-compose 的方式services: mysql: image: mysql:8.0.42 container_name: mysql-8.0.42 ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: Root123456 MYSQL_DATABASE: devdb MYSQL_USER: dev MYSQL_PASSWORD: Dev123456 volumes: - ./conf:/etc/mysql/conf.d - ./data:/var/lib/mysql - ./logs:/var/log/mysql restart: unless-stopped这样不管是换电脑还是组里新同事加入只需要把仓库拉下来执行docker compose up -d一套完全相同的开发环境就起来了。这种“复制一个文件就能复现环境”的体验才是 Docker 真正解决开发效率问题的价值所在。以上内容是基于我本人在 Docker 和 MySQL 8.0.42 开发部署过程中积累的经验整理出来的。不同系统环境可能会遇到一些细节差异关键是抓住核心思路数据卷必须挂、日志多看、版本选对、客户端别太旧。把这几个点把握住你在开发环境里基本就不会再被 MySQL 部署折腾了。
返回列表