ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Docker部署MySQL全攻略:从安装配置到容器内执行SQL

Docker部署MySQL全攻略:从安装配置到容器内执行SQL 1. 环境准备与整体思路先说结论如果你还在本地直接装MySQL真的要试试Docker这条路。我最早也是老老实实下载MySQL安装包一路Next结果因为版本不对、环境变量没配、端口被占、卸载不干净这些问题折腾了好几天。后来切到Docker整个过程压缩到十分钟以内而且换版本、迁移、删掉重来都特别干净宿主机上一点垃圾都不留。这篇内容围绕的就是我在纯新手阶段摸索出来的完整路径Docker下MySQL的安装、启动、配置、进入容器执行SQL。适合完全没接触过Docker的初学者也适合已经在用Docker但每次都被MySQL容器细节卡住的朋友。只要你按着步骤走基本能做到一遍跑通。1.1 为什么推荐用Docker跑MySQL一句话解释Docker它把MySQL和它所依赖的运行环境一起打包成一个“容器”容器里的东西跟你的Windows或Linux系统是隔离的但通过端口可以对外提供服务。你可以把容器想象成一套精装修的小房间里面什么都配好了你只需要把门打开一个口子让外面的人能进来访问就行。用Docker跑MySQL有四个很实际的收益安装快一条命令拉镜像不用跑安装向导。环境隔离不会污染宿主机的依赖卸载时直接删容器删镜像干净利落。版本随意切换同一台机器上同时跑MySQL 5.7和8.0都没有问题容器之间互不干扰。迁移方便数据目录用数据卷挂载出来后整个数据跟着走备份还原很直观。当然也有代价Docker本身需要额外安装内存占用会比裸装MySQL略高而且初学阶段需要理解几个新概念比如镜像、容器、数据卷、端口映射。但这一点点学习成本换来的是一劳永逸的开发环境管理体验绝对值回票价。1.2 安装Docker DesktopWindows和macOS的通用做法如果你用的是Windows 10/11或macOS最强的选择就是装Docker Desktop。这是Docker官方出的图形化客户端自带Docker Engine装好后可以在界面上直接管理容器、镜像、数据卷对新手特别友好。去Docker官网下载对应版本的Docker Desktop安装包双击安装。Windows下需要注意几点务必开启Windows的WSL 2功能具体是在PowerShell里执行wsl --install装完重启电脑。Docker Desktop现在默认依赖WSL 2来跑Linux容器这一步不做后面启动会报错。安装过程中如果提示需要Hyper-V按提示启用后重启。部分老版本Docker Desktop依赖Hyper-V新一代版本主要依赖WSL 2。安装完成后打开Docker Desktop第一次启动会花一两分钟初始化看到左下角出现“Engine running”的绿色状态就说明Docker核心已经跑起来了。macOS用户更简单把安装包拖进Applications就行Docker Desktop会自动处理好虚拟化相关的问题。1.3 Linux环境下直接装Docker Engine如果你用的是Ubuntu/CentOS这类Linux服务器不需要装Docker Desktop直接安装Docker Engine。这里以Ubuntu为例一条条命令来sudo apt update sudo apt install docker.io -y sudo systemctl enable docker sudo systemctl start dockerCentOS则是sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install docker-ce docker-ce-cli containerd.io -y sudo systemctl start docker sudo systemctl enable docker启动后验证一下Docker是否正常运行下面这条命令能打印出版本信息就说明装好了docker versionClient: Docker Engine - Community Version: 27.x.x ... Server: Docker Engine - Community Engine: Version: 27.x.x这里有个关键点如果docker version只显示了Client部分Server部分报错或卡住大概率是Docker服务没起来回看上一节的systemctl start docker是否执行成功。2. 拉取镜像与容器创建安装完Docker只是万里长征第一步接下来要让MySQL跑起来。很多新手在这里容易踩坑核心是没搞清楚镜像和容器的区别。镜像可以理解成MySQL的“安装包模板”而容器是模板跑起来后的“实例”。你拉取镜像一次之后可以基于它创建无数个容器每个容器都是独立的MySQL实例。2.1 MySQL版本怎么选8.0还是5.7这个选择直接影响你后续的配置方式和兼容性。我的建议是新项目直接上MySQL 8.0老项目需要兼容老代码再用5.7。MySQL 8.0默认的认证插件是caching_sha2_password比5.7的mysql_native_password安全性更高但老版本的客户端和部分老JDBC驱动连接时会报认证错误。如果你用Navicat旧版本或者老项目里的驱动连接8.0需要额外改认证方式这个我在后面常见问题里会详细说。拉取镜像的命令很简单docker pull mysql:8.0如果想用5.7就执行docker pull mysql:5.7执行docker images可以查看本地已有的镜像确认拉取成功。2.2 启动第一个MySQL容器拉好镜像后用docker run命令启动容器。这里直接给出一个生产环境可用的最小命令附带每个参数的解释docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDroot123456 \ -e TZAsia/Shanghai \ -v mysql_data:/var/lib/mysql \ mysql:8.0逐个拆解-d后台运行容器终端不会卡住。--name mysql8给容器起个名字后续操作全靠这个名字定位。-p 3306:3306端口映射。宿主机3306端口接收到的请求会转发到容器内的3306端口。这样本地Navicat连接时填localhost:3306就能访问容器里的MySQL。-e MYSQL_ROOT_PASSWORDroot123456设置MySQL root用户的初始密码。这是官方镜像提供的最直接的设置方式第一次初始化数据库时会自动生效。-e TZAsia/Shanghai把容器时区设置成东八区否则数据库里的NOW()返回的是UTC时间跟北京时间差8小时。-v mysql_data:/var/lib/mysql数据卷挂载。这是整个命令里最重要的一项。MySQL的数据文件默认存在容器内部的/var/lib/mysql目录容器一旦删除数据就没了。挂载到宿主机的一个名为mysql_data的Docker数据卷后即使容器删了重建数据依然还在。如果是在开发机上想快速验证不想挂载数据卷也可以但我的建议是从一开始就养成挂载的好习惯后面做数据备份、迁移都方便。2.3 验证容器启动状态执行下面的命令查看容器运行状态docker ps如果看到类似下面这行说明容器已经正常运行CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a1b2c3d4e5f6 mysql:8.0 docker-entrypoint.s… 5 seconds ago Up 4 seconds 0.0.0.0:3306-3306/tcp, 33060/tcp mysql8如果docker ps里没有这个容器说明启动失败了。这时候先用docker ps -a看所有容器然后查看日志定位原因docker logs mysql8常见的失败原因我这里先提前说一下后面还会展开3306端口被宿主机上已有的MySQL占用、磁盘空间不足、内存不够。尤其是端口占用本地如果已经装过MySQLdocker run会立刻报端口冲突。注意本地已有MySQL服务时要么把本地MySQL停掉要么把端口映射改成3307:3306两种方式选一个就行。3. 核心配置容器外的关键操作容器启动只是第一步真正好不好用还得看配置。这一节的内容重点在于MySQL在容器里但配置的主战场在宿主机上。3.1 MySQL的配置文件怎么挂载进去官方镜像默认的配置是够用的但实际开发中经常要调整字符集、排序规则、最大连接数等参数。修改容器内配置有三种思路第一种进入容器直接改配置文件。先执行docker exec -it mysql8 bash进入容器然后编辑/etc/my.cnf。缺点是容器一旦重建配置就丢了而且容器里默认没有vi/vim还得先安装麻烦。第二种启动时通过命令参数指定。比如加--character-set-serverutf8mb4 --collation-serverutf8mb4_unicode_ci。这种方式适合临时调整命令会变得很长不便于管理。第三种推荐把宿主机上的配置文件挂载进容器。先在宿主机建一个目录比如~/docker/mysql/conf在里面新建my.cnf然后启动容器时加参数-v ~/docker/mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf。这样以后要改配置直接改宿主机文件重启容器就生效。以我最常用的开发配置为例一段实用的my.cnf[mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci max_connections500 default-time-zone08:00配置里的三块重点说一下character-set-serverutf8mb4让数据库默认使用utf8mb4字符集。utf8mb4是utf8的超集能完整存储emoji和四字节生僻字。我见过太多项目因为默认utf8而存表情符号失败所以创建表的时候务必确认字符集。collation-serverutf8mb4_unicode_ci排序规则影响查询时的排序和比较行为。unicode_ci精度较高实际项目中用得最多。default-time-zone08:00跟前面-e TZAsia/Shanghai的效果类似双重保险确保SQL里的NOW()返回北京时间。配置挂载后重启容器让配置生效docker restart mysql83.2 首次连接与修改root密码容器初始化完成后用之前设置的MYSQL_ROOT_PASSWORD就能登录。但出于安全习惯第一次启动后建议把密码换掉。进入容器执行SQL来修改docker exec -it mysql8 mysql -uroot -p输入密码后进入MySQL命令行然后执行ALTER USER root% IDENTIFIED WITH mysql_native_password BY 新密码; FLUSH PRIVILEGES;这里有两个细节root%表示任意主机都能连接。官方镜像默认只允许root从localhost访问如果你后面想用Navicat从宿主机连接就必须允许%。IDENTIFIED WITH mysql_native_password是为了兼容老客户端。MySQL 8.0默认认证插件是caching_sha2_password老版本Navicat和旧驱动连接时会提示Authentication plugin caching_sha2_password cannot be loaded改成mysql_native_password是最快的解法。如果你是5.7镜像同样的语句也适用只是5.7默认就是mysql_native_password不需要刻意指定。3.3 创建业务库和专用账号永远不要用root账号跑业务。这是我在项目里踩过坑之后养成的习惯。之前图省事Java项目里直接配root连接结果有一次测试环境被人扫到弱口令数据库被删了一堆表教训深刻。正确的做法是创建一个业务专用账号只给它业务库的权限。在MySQL命令行执行CREATE DATABASE demo_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER demo_user% IDENTIFIED BY demo_pass123; GRANT ALL PRIVILEGES ON demo_db.* TO demo_user%; FLUSH PRIVILEGES;这段SQL的执行逻辑很清楚先建库再建账号然后授权最后刷新权限表。这里的demo_db.*意思是对demo_db库下所有表开放权限但不会影响其他库风险被控制在一个库范围内。4. 进入容器执行SQL的完整实操这个标题的核心诉求其实就是这一节怎么进入容器执行SQL。很多人第一次操作时不明白“执行SQL”还有好几种方式每一种的使用场景都不同。4.1 交互式进入容器与MySQL命令最直接的方式是两步先进入容器的bash再进入MySQL命令行。docker exec -it mysql8 bash进入容器后你会看到命令行提示符变成类似root容器ID:/#说明你已经到了容器内部。此时执行mysql -uroot -p输入密码后看到mysql提示符就成功进入了MySQL命令行。这之后的所有操作比如SHOW DATABASES;、SELECT NOW();、CREATE TABLE ...都是在容器内的MySQL实例上执行的。需要留意的是docker exec -it后面的-it是两个参数-i表示交互式输入-t分配一个伪终端。少了-t你可能看不到彩色的命令行输出少了-i你输入的命令不会传给容器。所以这两个字母务必一起用。我写了一个最常用的完整流程帮你建立体感[roothost ~]# docker exec -it mysql8 bash roota1b2c3d4e5f6:/# mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. ... mysql SHOW DATABASES; -------------------- | Database | -------------------- | information_schema | | demo_db | | mysql | | performance_schema | | sys | -------------------- 5 rows in set (0.00 sec) mysql USE demo_db; Database changed mysql SELECT NOW(); --------------------- | NOW() | --------------------- | 2025-01-15 14:30:22 | ---------------------4.2 不进入容器直接执行单条SQL交互式操作适合调试但如果你想在脚本或CI/CD流水线里执行SQL不能每次都手动进入容器。这时可以直接用docker exec在宿主机上执行docker exec -i mysql8 mysql -uroot -p123456 -e SHOW DATABASES;注意我这里用的是-i没有-t因为是非交互式执行不需要伪终端。-p123456的写法是把密码直接跟在-p后面中间没有空格这是MySQL命令的固定语法要求。再举一个实用例子直接查询业务数据docker exec -i mysql8 mysql -uroot -p123456 demo_db -e SELECT id, name, create_time FROM users WHERE status1 ORDER BY create_time DESC LIMIT 10;这里在容器名后跟了一个demo_db表示默认使用这个数据库不需要在SQL里写USE demo_db;。命令会在宿主机控制台直接打印查询结果非常适合快速验证数据状态。4.3 在容器内执行SQL脚本文件实际开发中初始化表结构、导入测试数据动辄成百上千行SQL不可能手动一条条敲。这时候需要执行SQL脚本文件。有两种场景对应两种做法。场景一脚本已经在宿主机上。用重定向方式导入docker exec -i mysql8 mysql -uroot -p123456 demo_db /path/to/init.sql关键点在-i参数它让容器保持标准输入打开宿主机上的文件内容通过管道流进容器里的MySQL进程。少了-i这个导入会静默失败SQL文件不生效还不报错是很多人的第一个坑。场景二脚本在容器内部。先进入容器再执行sourcedocker exec -it mysql8 bash mysql -uroot -p123456 demo_db source /tmp/init.sql;source是MySQL客户端的内部命令它会逐条读取并执行sql文件中的语句。相比重定向方式它会把执行结果打印在屏幕上方便观察每条语句是否出错。如果你在开发Java后端项目用MyBatis-Plus这类框架时通常会根据实体类自动生成建表SQL然后把这些SQL存成脚本在Docker容器里执行。做法就是上面这两条要么重定向要么source没有第三条路。别想着把sql文件往容器里拖最简单的办法是用命令直接拷贝进去docker cp /path/to/init.sql mysql8:/tmp/init.sql然后进入容器执行source /tmp/init.sql;完事之后记得清理docker exec mysql8 rm -f /tmp/init.sql4.4 用图形化客户端连容器里的MySQL数据库操作光靠命令行还是不够直观日常我主要用Navicat或DBeaver这类图形客户端。连接参数就四个主机地址填127.0.0.1端口填3306用户名填root或业务账号密码填对应密码。这里有个非常容易出问题的点客户端连不上时先检查容器端口是否真的映射出来了。执行docker ps看PORTS列如果显示的是0.0.0.0:3306-3306/tcp说明宿主机3306端口已经在监听。再用telnet 127.0.0.1 3306测试宿主机端口是否通。如果宿主机能通但Navicat连不上多数是账号权限或认证插件问题。还有一个小细节Navicat连接MySQL 8.0时如果报Authentication plugin caching_sha2_password cannot be loaded是因为Navicat老版本不支持新认证插件。解法有两个把Navicat升级到新版本或者按前文所述把账号的认证插件改成mysql_native_password。ALTER USER demo_user% IDENTIFIED WITH mysql_native_password BY demo_pass123; FLUSH PRIVILEGES;5. 常见问题与排查技巧实录这部分是我最想跟你分享的。Docker跑MySQL出错时报错信息有时候很抽象新手容易一头雾水。我把实际工作中高频踩过的坑按症状整理出来每一条都给出排查思路和解决方法。5.1 容器启动失败排查清单症状一端口冲突报错信息长这样docker: Error response from daemon: driver failed programming external connectivity on endpoint mysql8: Bind for 0.0.0.0:3306 failed: port is already allocated.这几乎可以肯定是宿主机3306端口被占用了。排查方法netstat -tlnp | grep 3306在Windows上则是netstat -ano | findstr 3306看到占用进程后要么停掉占用进程要么改端口名映射。改端口是最省事的docker run -d --name mysql8 -p 3307:3306 -e MYSQL_ROOT_PASSWORDroot123456 mysql:8.0改完之后客户端连接的端口也相应改成3307。症状二镜像启动后立即退出docker ps -a看到容器状态是Exited (1)先用日志定位docker logs mysql8如果日志里出现Cant start server: Bind on unix socket: No such file or directory或[ERROR] InnoDB: Unable to lock ./ibdata1通常是因为挂载的数据卷权限不对。宿主机目录和数据卷的权限必须让容器内mysql用户可写。最简单粗暴的解法删掉旧的匿名数据卷全新重建后用具名数据卷。症状三内存不足MySQL 8.0镜像默认会吃掉几百MB内存。如果你是在低配服务器或Windows上跑容器可能因为OOM被杀掉。日志里会有Killed字样。解法是限制容器内存docker run -d --name mysql8 -p 3306:3306 -m 512m --memory-swap 512m -e MYSQL_ROOT_PASSWORDroot123456 mysql:8.0-m 512m表示容器最多使用512MB内存。不过要注意MySQL内存需求跟连接数和buffer pool正相关限制太低可能反过来影响性能。5.2 连接不上容器的MySQL现象很常见容器明明是Up状态但Navicat或Java应用连不上。我按排查路径列一下顺序第一步确认MySQL进程在容器内正常监听。进入容器看端口docker exec mysql8 netstat -tlnp | grep 3306第二步确认账号是否允许远程连接。如果mysql.user表里没有root%记录任何外部连接都会被拒绝。查看方式docker exec mysql8 mysql -uroot -p -e SELECT user, host FROM mysql.user;如果只有rootlocalhost执行前面说过的ALTER USER就好。第三步确认防火墙没有拦截。Linux服务器上要放行3306端口很多云服务器还有安全组规则默认只放行了22、80、443这几个端口3306得单独去控制台加白名单。第四步确认application.yml里的连接串写对了。SpringBoot项目里常见的错误是时区没写或字符集不对正确的连接串参考spring: datasource: url: jdbc:mysql://127.0.0.1:3306/demo_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueallowPublicKeyRetrievaltrue这里特别提一句。MySQL 8.0使用caching_sha2_password认证时客户端需要获取服务器公钥JDBC驱动默认不允许明文传输公钥连不上时会报Public Key Retrieval is not allowed加这个参数就能解决。5.3 慢SQL与MySQL 8.0配置优化很多人在Docker里跑MySQL发现同样一份数据比本地的慢。这通常是默认配置没有针对你的工作负载调优。看几个关键指标最大连接数。默认只有151如果你的Java应用连接池配了50个连接再加上管理端和报表系统很容易打满。启动容器时加参数或在挂载的my.cnf里加max_connections500缓冲池大小。InnoDB的buffer pool默认是128MB生产机建议调到物理内存的50%-70%。在my.cnf里设置innodb_buffer_pool_size1G设置后可以在MySQL命令行验证SHOW VARIABLES LIKE innodb_buffer_pool_size; SHOW VARIABLES LIKE max_connections;慢SQL日志也建议开发阶段就打开提前发现问题。my.cnf加slow_query_log1 slow_query_log_file/tmp/mysql-slow.log long_query_time1long_query_time1表示超过1秒的查询都记录到慢日志里。定位问题时执行SHOW FULL PROCESSLIST;能实时看到正在执行的SQL再配合EXPLAIN分析执行计划基本能解决95%的慢SQL问题。5.4 容器删除与数据保留最后一个高频疑问容器删了数据还在吗答案取决于你启动时有没有做数据卷挂载。如果你用了-v mysql_data:/var/lib/mysql这种具名数据卷即使执行docker rm -f mysql8删掉整个容器数据卷里的数据依然完好。重新用同样的命令创建容器后数据自动回来。这也是我强烈建议用具名数据卷的原因。如果你没用数据卷数据就在容器内部删除容器等于数据全没。想保留数据的话要先把容器里的数据复制出来docker cp mysql8:/var/lib/mysql ./mysql_bak然后销毁容器就可以在宿主机目录mysql_bak里拿到所有数据文件。数据卷相关的常用操作也很简单# 查看所有数据卷 docker volume ls # 查看数据卷详情 docker volume inspect mysql_data # 手动删除无用数据卷 docker volume prune6. 我的实操建议与后续扩展最后再说几个从实战中沉淀下来的细节希望能帮你少走弯路。第一个建议把常用的Docker命令整理成一份速查笔记。刚开始记不住很正常我自己也是边查边用用多了自然就记住了。但切记一条原则凡是docker run创建容器一定要把所有配置都写在命令里别等启动完了再手动进容器改配置那样容器重建后容易忘。第二个建议如果你只是想在本地快速搭一个MySQL学习环境容器未必要设置太复杂。最简单的命令就是docker run -d --name mysql8 -p 3306:3306 -e MYSQL_ROOT_PASSWORDroot123456 mysql:8.0能用就行等真正做项目再补数据卷和配置挂载。第三个建议容器内执行SQL脚本时养成用-i重定向的习惯。因为脚本导入是自动化部署的一部分纯交互式source方式虽然直观但没法在脚本里复用。我把常用命令贴在这里直接抄作业# 单条SQL查询 docker exec -i mysql8 mysql -uroot -p123456 -e SHOW DATABASES; # 执行SQL脚本文件 docker exec -i mysql8 mysql -uroot -p123456 demo_db init.sql # 进入容器交互操作 docker exec -it mysql8 bash还有一个不算太技术、但非常影响体验的建议如果你的Docker Desktop启动很慢或者经常启动失败可以先检查WSL 2是否正常再看看是否开启了Windows的虚拟化功能。这类环境问题最磨人排查思路就一句话先保证Docker本体能跑再去折腾容器内部的东西。关于后续扩展我个人的使用习惯是在Docker里同时部署MySQL和Redis用Docker Compose把它们编排在一起。一条docker compose up -d命令整套开发环境直接拉起来比一个个容器手动启动省心太多。等MySQL玩熟了非常建议往这个方向走。Docker下跑MySQL本质上就是用一套标准化的流程把数据库环境管理这件事变得可复现、可迁移。从头安装、启动、配置到进入容器执行SQL流程不复杂但每一步都有对应的细节。只要把这篇里的思路吃透再遇到数据库部署你大概率不会再想去装那个几GB的本地安装包了。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进