首页 / 资讯中心 / 文章详情

Docker 启动 MySQL 实战:从环境准备到备份恢复踩坑指南

Docker 启动 MySQL 实战:从环境准备到备份恢复踩坑指南 ★ FEATURED ARTICLE
最近被不少朋友问到“用docker启动mysql步骤”到底怎么走其实我在本地环境和生产环境里用 Docker 跑 MySQL 已经三年多了踩过的坑确实不少。很多人的困惑点其实不是 Docker 本身而是端口、数据卷、权限、SSL 这些概念和数据习惯之间对不上导致命令东拼西凑报错了也不知道去哪查。这篇文章我会按自己实际操作的顺序从环境准备、拉镜像、启动容器、客户端连接一直讲到备份恢复把每条命令和参数背后的原因一起交代清楚。如果你是第一次在 Docker 里跑 MySQL或者已经跑起来但连不上、老报 SSL 错误、重启后数据丢了这篇应该能帮你把整条链路的坑都填平。我见过太多人照着网上的命令一行行复制粘贴最后卡在Error response from daemon: Conflict、Cant connect to MySQL server (10061)这类报错上然后开始怀疑人生。其实多数问题都不是因为难而是我们太习惯“双击安装包”的思维忽略了容器世界里端口、网络、持久化这几个概念。下面我按真实使用场景来写命令可以直接抄但更重要的是搞清楚每个参数为什么这么写。1. 为什么是 Docker 而不是本机安装 MySQL1.1 版本隔离是最大的理由我最早在 Windows 上开发 Java 项目时本机装的是 MySQL 5.7后来另一个项目要求 MySQL 8.0于是开始了一场灾难版本冲突、端口冲突、配置文件乱掉、卸载卸不干净。Docker 解决的就是这类环境隔离问题你可以在同一台机器上同时跑 MySQL 5.7、8.0、8.4甚至一个测试库一个线上库用相同版本但不同配置文件只要端口和数据卷分开就行。对团队来说也是好事。新同事入职不用再花半天装数据库、改my.cnf、配环境变量。直接给一份docker-compose.yml跑一条docker compose up -d数据库环境就起来了。这对前端、测试、运维来说都很友好因为他们不需要懂 MySQL 的安装细节。1.2 什么时候不建议用 Docker 跑 MySQL但我也得说句实话Docker 不是万能的。如果是以下场景我建议你谨慎高并发、极高 I/O 的生产环境。容器本身性能损耗不大但数据卷挂载方式、日志采集、网络延迟都要额外调优裸金属或云数据库会更省心。团队没有容器运维经验。虽然 MySQL 镜像很方便但数据安全、容器崩溃后的自愈、备份恢复策略都需要有人负责否则出了问题比本机更难看。宿主机资源非常紧张。Docker 本身也要占内存尤其 Docker Desktop 在 Windows/Mac 上会跑一个轻量虚拟机如果你只有 8G 内存再起一个 MySQL 容器会明显卡顿。1.3 我整理的一个“先想清楚再动手”的清单开始敲命令之前先花两分钟回答下面几个问题检查项思考方向为什么重要端口是否被占用本机 3306 有没有被其他服务占用端口映射失败会导致连接不上数据放哪里容器删除后数据是否要保留不挂载数据卷容器删了就全没了密码怎么管理是否使用强密码文件而非明文命令命令行记录、日志泄露风险字符集和时区是否需要 utf8mb4、Asia/Shanghai中文乱码和日志时间错乱都很麻烦网络方案是否多个容器需要互联需要用 Docker 自定义网络而不是靠 IP 碰运气备份策略容器挂了怎么恢复数据至少要知道 mysqldump 怎么用这些问题不一定要在第一次跑通时全部解决但脑子里要有这根弦。后面所有操作其实都是围绕这份清单展开的。2. 环境准备先把 Docker Desktop 跑起来再谈 MySQL2.1 Windows 下最容易卡住的虚拟化检测问题如果你用的是 Windows最常遇到的一个拦路虎就是 Docker Desktop 启动时报Docker Desktop failed to start because virtualisation support wasnt detected翻译过来就是“虚拟化支持没检测到”。这不是 Docker Desktop 的锅而是宿主机的虚拟化环境没准备好。解决办法按顺序排查确认 CPU 虚拟化有没有开启。打开任务管理器 - 性能 - CPU看右下角“虚拟化”是否显示“已启用”。如果是“已禁用”需要进 BIOS 开启 Intel VT-x 或 AMD-V。不同主板按键不一样通常是开机时按 Del、F2、F10 或 ESC。开启 Windows 必要功能。进入“控制面板 - 程序 - 启用或关闭 Windows 功能”勾选“适用于 Linux 的 Windows 子系统”和“虚拟机平台”。如果你已经装了 Hyper-V也要保证 Hyper-V 功能没有被整体关闭。设置 WSL 默认版本为 2。打开 PowerShell执行wsl --set-default-version 2如果提示需要更新 WSL 内核就按提示下载更新包安装。完成后可以执行wsl --status看默认版本。重新启动 Docker Desktop。有时它检测虚拟化能力有缓存重启一次会比反复卸载安装更有效。注意如果你在虚拟机里再装 Docker Desktop需要在虚拟化软件里启用“嵌套虚拟化”这个很多新手不知道导致在虚拟机里怎么也起不来。2.2 Docker Desktop 启动失败后的排查顺序遇到启动失败我建议按下面这个顺序排查而不是上来就重装# 查看 Windows 系统信息中的 Hyper-V 支持 systeminfo # 查看 WSL 状态 wsl --status # 查看 WSL 发行版列表 wsl --list --verbose如果systeminfo中显示“已检测到 Hyper-V。将使用 Hyper-V 加载虚拟机”这类信息说明虚拟化层没问题。如果显示“不支持 Hyper-V”那基本就是 BIOS 或 Windows 功能问题。还有一个被很多人忽略的坑Windows 上开启了“内核隔离”或“内存完整性”有时会和 Docker Desktop 的虚拟化冲突。你可以在“Windows 安全中心 - 设备安全性 - 内核隔离”里临时关掉试试但也别说关就一直关保持系统安全更重要。2.3 拉取镜像前的镜像加速配置Docker Desktop 装好之后建议先配置镜像加速否则docker pull mysql:8.0可能慢到让你怀疑网络。国内有不少公共镜像加速器比如阿里云、网易、中科大等。核心思路是给 Docker Engine 加registry-mirrors。在 Docker Desktop 里打开 Settings - Docker Engine修改 JSON 配置{ registry-mirrors: [ https://docker.mirrors.ustc.edu.cn, https://hub-mirror.c.163.com ] }改完后点 Apply Restart。注意这类公共加速器的可用性有时会调整如果拉取还是失败可以多换几个源试试或者直接用企业内网私服。配置完成后执行docker pull mysql:8.0这里我强烈建议指定版本不要用latest。latest在 MySQL 这个镜像上往往指向当前最新主版本前后升级可能导致配置不兼容。生产环境更是要锁定版本号比如mysql:8.0.36这样行为可预期。3. 用 docker run 启动 MySQL 8.0逐参数拆解3.1 最简单的启动命令先给你一条最简命令能跑但别在生产用docker run -d --name mysql8 -e MYSQL_ROOT_PASSWORD123456 mysql:8.0这条命令干了三件事后台运行一个叫mysql8的容器设置 root 的密码为123456使用mysql:8.0镜像。你会看到容器状态是 Up日志也显示 MySQL 已就绪。但这有一个致命问题没有映射端口。容器内部的 3306 只在 Docker 网络中可用你在宿主机上用 Navicat 连127.0.0.1:3306是连不上的因为容器和宿主机是隔离的。我们需要把容器的 3306 映射到宿主机的一个端口上。3.2 端口、密码、卷、字符集和时区每个参数都不能少推荐至少使用下面这条命令docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDYourPass123 \ -e TZAsia/Shanghai \ -v mysql8_data:/var/lib/mysql \ -v $PWD/mysql-conf:/etc/mysql/conf.d \ --restart always \ mysql:8.0 \ --character-set-serverutf8mb4 \ --collation-serverutf8mb4_unicode_ci逐个解释-p 3306:3306左边是宿主机端口右边是容器端口。如果宿主机已有 MySQL 占着 3306可以改成3307:3306客户端连接时端口就用 3307。-e MYSQL_ROOT_PASSWORD设置 root 密码。你也可以用MYSQL_DATABASE、MYSQL_USER、MYSQL_PASSWORD在首次初始化时创建业务库和业务用户。例如-e MYSQL_DATABASEappdb -e MYSQL_USERapp -e MYSQL_PASSWORDAppPass123-e TZAsia/Shanghai设置容器时区。如果不加容器默认是 UTC 时间和北京时间差 8 小时查询NOW()时往往跟你本地时间对不上。-v mysql8_data:/var/lib/mysql数据持久化。mysql8_data是 Docker 命名卷数据会存放在 Docker 管理目录中不受容器生命周期影响。容器删了重建数据还在。-v $PWD/mysql-conf:/etc/mysql/conf.d将宿主机当前目录下的mysql-conf目录挂载到容器配置目录。你可以放进去一个my.cnf容器启动时自动读取。--restart alwaysDocker 守护进程启动时自动拉起容器服务器重启后 MySQL 也能自动恢复。--character-set-serverutf8mb4 --collation-serverutf8mb4_unicode_ci在命令行后面传给容器内的 mysqld设置默认字符集和排序规则。如果你用挂载的my.cnf也一样二选一即可。提示使用命名卷而不是直接挂宿主机目录最大的好处是避免权限问题。直接挂宿主机目录时镜像初始化过程可能需要修改/var/lib/mysql的属主但宿主机目录的属主和容器内 mysql 用户uid 999不一致就会报类似chown: changing ownership of /var/lib/mysql: Permission denied的错误。3.3 容器启动后怎么确认 MySQL 真的能用了启动完成后别急着打开客户端先看日志确认初始化状态docker logs mysql8日志末尾如果出现[System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections.说明 MySQL 已就绪。接着进入容器内验证docker exec -it mysql8 mysql -uroot -p输入密码后执行SELECT VERSION(); SELECT character_set_server, collation_server; SELECT time_zone;VERSION()会显示类似8.0.36字符集和时区也应该符合预期。如果你配置了MYSQL_DATABASEappdb可以执行SHOW DATABASES;看到它已经建好。如果在这一步就发现端口映射不对可以用docker port mysql8查看实际映射关系docker port mysql8 # 输出类似 3306/tcp - 0.0.0.0:33064. 生产一点的做法用 docker compose 管理 MySQL4.1 compose 文件怎么组织用docker run可以跑通但配置一多、环境变量一长就不好维护。我的习惯是一切容器化服务都用docker compose管理。它可以把端口、卷、环境变量、健康检查、重启策略都写在一个文件里还能用docker compose up -d一条命令拉起整个服务栈。下面是我常用的一份docker-compose.yml可以直接保存使用services: mysql8: image: mysql:8.0.36 container_name: mysql8 restart: always environment: MYSQL_ROOT_PASSWORD: RootPass123 MYSQL_DATABASE: appdb MYSQL_USER: app MYSQL_PASSWORD: AppPass123 TZ: Asia/Shanghai ports: - 3306:3306 volumes: - ./mysql-data:/var/lib/mysql - ./mysql-conf:/etc/mysql/conf.d - ./mysql-init:/docker-entrypoint-initdb.d command: - --character-set-serverutf8mb4 - --collation-serverutf8mb4_unicode_ci - --default-time-zone08:00 healthcheck: test: [CMD, mysqladmin, ping, -h, 127.0.0.1, -u, root, -p$${MYSQL_ROOT_PASSWORD}] interval: 10s timeout: 5s retries: 5在这里我用./mysql-data:/var/lib/mysql这种相对路径挂载方便团队里任何人 clone 项目后直接跑。但生产环境建议改成命名卷或者使用固定的宿主机绝对路径别让数据散落在项目目录里。4.2 配置文件、初始化脚本、健康检查一个都不能少我在./mysql-conf/my.cnf里通常会写[mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci default-time-zone08:00 max_connections500 max_connect_errors1000 slow_query_log1 slow_query_log_file/var/log/mysql/slow.log long_query_time2这些参数放在配置文件里比放在 command 里更清晰。注意max_connections500不要随手写太大Docker 容器如果没设内存限制连接数太高会直接把宿主机的内存耗光。至于./mysql-init目录MariaDB 风格的官方镜像支持放.sql、.sh、.sql.gz文件容器首次初始化时自动按文件名顺序执行。这对初始化表结构、导入种子数据非常方便。注意只在数据目录为空时执行也就是说如果 MySQL 已经初始化过了往这个目录放文件不会重复执行。如果你需要“强行重新初始化”要么删掉数据卷要么用新的数据目录。健康检查我用了mysqladmin ping能更真实地反映 MySQL 是否可连接。$${MYSQL_ROOT_PASSWORD}的写法是让 compose 把环境变量传给容器内部避免明文写死在命令里这也是一个小细节。4.3 compose 和命令行日常管理命令对照操作docker run 方式docker compose 方式启动docker run ...docker compose up -d停止docker stop mysql8docker compose stop启动已存在容器docker start mysql8docker compose start查看日志docker logs -f mysql8docker compose logs -f mysql8进入容器docker exec -it mysql8 bashdocker compose exec mysql8 bash删除容器docker rm -f mysql8docker compose down删除容器数据卷手动删卷docker compose down -vdocker compose down -v会连数据卷一起删掉操作风险极高我建议在任何环境都默认把它列入“慎用命令”。5. 客户端连接不了的排错手册2003、1045、SSL 错误5.1 Navicat 报 SSL 连接错误的根因和处理这是我被问得最多的一个问题容器起来了日志正常但 Navicat 连接 MySQL 8 时报SSL connection error: error:1425F102:SSL routines:ssl_choose_client_version:unsupported protocol原因在于 MySQL 8.0 默认启用 SSL而部分旧版客户端或当前客户端在连接时默认尝试 SSL但使用的 TLS 版本和 MySQL 不支持/不匹配。Navicat 从某个版本开始默认“使用 SSL”是开启的跟 MySQL 8 的默认配置一碰就容易出问题。解决办法很简单在 Navicat 连接配置的“高级”标签页中把“使用 SSL”改为“不使用”重新测试连接。如果你用命令行客户端mysql -h127.0.0.1 -P3306 -uroot -p --ssl-modeDISABLED如果你的是 MySQL 5.7可能也会报 SSL 相关警告解决办法一样。如果项目方要求必须加密连接那就不要关 SSL正确做法是在容器里配置证书并强制要求 SSL同时更新客户端 CA 证书。这个话题可以单独写一篇这里先不展开。提醒如果启动 MySQL 时加了--skip-ssl或--ssl0虽然也能解决报错但你的连接是明文的生产环境不要这么干。客户端主动关闭 SSL 比服务器端全局关闭要安全得多。5.2 root 远程登录 1045 的权限配置默认情况下容器内的 MySQLroot用户只允许从本地localhost登录root%是不存在的。所以用 Navicat 连远程 IP 的 root 时经常报Access denied for user roothost (using password: YES)这时进入容器docker exec -it mysql8 mysql -uroot -p然后执行CREATE USER root% IDENTIFIED BY 你的强密码; GRANT ALL PRIVILEGES ON *.* TO root% WITH GRANT OPTION; FLUSH PRIVILEGES;注意 MySQL 8 里不能再用老式的GRANT ALL PRIVILEGES ... IDENTIFIED BY一次性创建用户和授权必须先用CREATE USER。如果你只想开应用账号不给它全局权限可以这样CREATE USER app% IDENTIFIED BY AppPass123; GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO app%; FLUSH PRIVILEGES;安全性优先不要给应用账号ALL PRIVILEGES ON *.*。哪怕只是在测试环境习惯养好后到生产才不会手抖。5.3 端口和防火墙本机能连局域网不能连怎么查如果你本机127.0.0.1:3306能连但局域网其他电脑连不上按下面这个顺序排查现象检查命令可能的解决方式docker port mysql8无输出docker ps -a端口映射没加重新创建容器宿主机telnet 127.0.0.1 3306不通netstat -ano可能有其他进程占端口调整-p映射只绑定了 127.0.0.1docker port mysql8显示127.0.0.1:3306-3306/tcp创建容器时用-p 0.0.0.0:3306:3306绑定所有网卡外部 telnet 不通Windows 防火墙入站规则 / Linux firewalld放行 3306 端口云服务器安全组也要放行多容器互通失败docker network ls创建自定义网络使用服务名互访另外有个常见误解Docker 端口映射里的127.0.0.1:3306:3306是“只允许本机访问”很多没仔细看的人以为这就是公网安全的意思。实际上如果只在本机开发用127.0.0.1没问题但如果要跨机器访问就要改成0.0.0.0:3306:3306同时配合防火墙限制来源 IP别直接暴露到公网。6. 数据备份、恢复和容器迁移实操6.1 用 mysqldump 备份要绕过容器层的坑有些人习惯在宿主机直接敲mysqldump结果发现没安装或者版本不匹配。正确姿势是在容器内部执行docker exec mysql8 sh -c exec mysqldump -uroot -p$MYSQL_ROOT_PASSWORD --all-databases --single-transaction --quick --default-character-setutf8mb4 backup.sql这种方式把$MYSQL_ROOT_PASSWORD环境变量传进容器避免密码出现在宿主机命令历史里。但如果你没设置过这个环境变量那就直接docker exec -it mysql8 mysqldump -uroot -p --all-databases --single-transaction --default-character-setutf8mb4 backup.sql输入密码后重定向到宿主机文件。--single-transaction对 InnoDB 表很友好不会长时间锁表适合在线备份。备份完检查一下文件大小和开头内容head -20 backup.sql wc -l backup.sql如果文件只有几百字节大概率是报错信息被重定向了打开看看。6.2 通过 Docker 卷迁移数据如果要把整个 MySQL 数据目录迁移到另一台机器mysqldump 是最低风险的方式但数据量大时比较慢。另一个办法是直接打包数据卷。假设你用了命名卷mysql8_data先在旧机器上打包# 容器停止避免写数据导致不一致 docker stop mysql8 docker run --rm -v mysql8_data:/data -v $PWD:/backup alpine tar czf /backup/mysql8-data.tar.gz -C /data .把生成的mysql8-data.tar.gz拷到新机器创建新卷并解压docker run --rm -v mysql8_new_data:/data -v $PWD:/backup alpine tar xzf /backup/mysql8-data.tar.gz -C /data然后用新卷启动一个新的 MySQL 容器。注意两点新容器使用的镜像最好和旧容器版本一致至少大版本一致否则可能存在数据文件不兼容的情况恢复前先备份旧的空卷不要覆盖已有数据目录。6.3 日常维护日志、进入容器、资源限制最后说说日常看得最勤的几个操作。查看日志尤其是启动失败时docker logs --tail 50 mysql8如果日志一闪而过加上-f实时跟踪docker logs -f mysql8进入容器看配置文件和环境变量docker exec -it mysql8 bash cat /etc/mysql/conf.d/*.cnf env | grep MYSQL容器里可能没有 vi/vim所以改配置文件建议在宿主机挂载目录改然后重启容器docker compose restart mysql8资源限制Docker 默认不限制容器能用的 CPU 和内存一个失控的查询可能会拖垮宿主机。建议运行时指定docker run ... --memory1g --cpus1 mysql:8.0compose 里对应deploy: resources: limits: cpus: 1 memory: 1G查看容器退出状态码docker inspect --format{{.State.ExitCode}} mysql8通常退出码 0 是正常退出非 0 需要结合日志分析。这一招在排查“容器启动了 3 秒就退出”的场景很管用。最后分享几个我实际踩过的坑。第一不要在生产环境直接挂宿主机目录到/var/lib/mysql如果不小心改了目录权限整库都可能读不了推荐用命名卷或至少用 Docker 管理的卷路径。第二MySQL 8.0 默认的排序规则是utf8mb4_0900_ai_ci如果项目里有从 5.7 导出的数据建议初始化时指定collation-serverutf8mb4_unicode_ci否则一些字符排序和索引对比行为会和老库不一致。第三Docker Desktop 在 Windows 上的数据文件默认放在系统盘的 VHDX 里我见过不少人 C 盘被 Docker 的镜像和数据卷塞满最好的办法是在 Docker Desktop 设置里把 Disk image location 改到大分区。第四所有密码不要直接写在命令行历史里用--env-file或 compose 的env_file管理这比把密码贴在命令里安全得多。希望你也能少走几条弯路。
阅读完成 · 觉得有帮助?
咨询建站