
1. Docker到底是什么为什么大家都在吹它1.1 一句话讲清Docker的定位如果你是第一次接触Docker很可能已经被“容器化”“镜像”“编排”“沙箱”这些词绕晕了。我先用最接地气的方式说清楚Docker干的事情是把你的应用和它依赖的一整套运行环境打包成一个标准化的箱子。不管这个箱子到了哪台机器上只要那台机器装了Docker都能以几乎一致的方式把应用跑起来。“在我电脑上是好的”这句话是程序员之间最大的谎言。环境不一致导致的问题比如JDK版本不同、数据库编码不对、某个底层库没装占了开发联调和线上事故的一大半。Docker解决的就是这个痛点——你把应用连同运行环境一起交付别人拉下来就能跑环境差异被关在箱子外面。它能做什么简单说三件事一是环境隔离同一个机器上可以同时跑不同版本的MySQL、Redis、Node.js互不干扰二是交付标准化开发、测试、生产用同一套镜像三是资源利用率高和虚拟机比容器不模拟整套操作系统启动耗的是毫秒级单机跑的实例数可以多很多。这篇文章适合谁主要是刚接触Docker、准备在Windows或Mac上用Docker Desktop做开发的人。我会尽量避开教科书式的理论轰炸把“怎么装、怎么用、踩了坑怎么排”讲透。你已经用过一段时间Docker、只想查某个报错可以直接跳到第7章的排查表。1.2 镜像和容器先忘掉“虚拟机”这个概念很多新手最容易卡住的地方就是把Docker容器当成一台精简虚拟机。这么想不是完全错但会误导你后面很多操作。正确的心智模型是镜像是模板容器是运行实例。用生活里的例子镜像就像一道菜的菜谱容器就是按这份菜谱实际炒出来的那一盘菜。菜谱可以反复使用炒出来的每一盘菜都是独立的你吃掉了这一盘不会影响菜谱也不会影响之前用同一份菜谱炒出的其他菜。镜像本身是只读的你基于镜像启动一个容器Docker会在镜像上面加一个可写层容器运行时的所有文件改动都发生在这一层。所以这里有个非常关键、几乎每个新手都会犯的错容器被删除之后你在容器里改的文件、写入的数据全部跟着消失。镜像还是那个镜像干干净净。等会儿第4章我们会专门验证这个特性到时候你会对“为什么要用数据卷”有切肤之痛的理解。镜像还有一个特点它是分层的。拉镜像的时候你会看到“Pulling fs layer”“Waiting”这些输出其实就是Docker在按层下载。每一层对应Dockerfile里的一条指令比如装某个依赖、拷贝某个文件。这种分层的设计让多个镜像可以共享底层比如你基于同一个基础镜像构建的多个应用底层只需要存一份磁盘占用和网络传输都能省不少。但刚开始用不需要深入理解分层知道有这么回事就行。1.3 Docker Desktop在Windows/Mac上扮演什么角色Docker本身的老家是Linux。容器技术依赖Linux内核的namespace、cgroup等能力Windows和Mac并不能直接跑Linux容器不考虑Windows容器那种另类场景。那么你在Windows上装的“Docker Desktop”到底是个什么东西它其实是一个包含了Docker引擎、命令行工具、图形化界面、底层虚拟机的一套全家桶。在Windows上Docker Desktop默认借助WSL2Windows Subsystem for Linux 2运行一个轻量级Linux虚拟机Docker引擎就跑在这个虚拟机里。换句话说你以为自己在Windows里用Docker实际Docker跑在一个看不见的Linux环境里Docker Desktop只是帮你把这一层管理好了。我在刚入门时没搞清这层关系导致遇到问题不知道从哪查起。比如后来遇到“Failed to connect to the Docker API at npipe”大概率就是Docker Desktop后台没起来或者WSL2的子系统卡死了。理解了Docker Desktop和WSL2之间的依赖很多报错思路就清晰了。Docker Desktop自带的图形界面也不是摆设。启动后你可以在Dashboard里直观看到当前有哪些容器在跑、状态如何、日志输出长什么样还能直接点按钮停止、重启、删除容器。对新手来说图形界面是很好的辅助学习工具但我还是建议命令行为主图形界面为辅毕竟后面上服务器操作可没有图形界面给你点。2. 安装Docker Desktop从下载到跑起第一个容器2.1 安装前准备系统要求和必须开的开关先说硬件和系统门槛。如果你用的是Windows 10 64位专业版/企业版/教育版或者Windows 11基本都满足条件。内存建议至少8GB4GB虽然文档说勉强能用但实际跑一两个容器就捉襟见肘了。另外CPU需要支持虚拟化技术Intel叫VT-xAMD叫AMD-V并且要在BIOS里开启。这一步是很多新手栽跟头的地方我后面单独用一节讲。在Windows上安装Docker Desktop强烈建议先搞定WSL2。本质上Docker Desktop安装包会帮你处理一部分WSL的安装但提前手动装好能少踩很多坑。以管理员身份打开PowerShell运行wsl --install这个命令会默认安装WSL2并启用需要的Windows功能装完按提示重启。如果之前装过WSL1想升级到WSL2用wsl --set-version 发行版名称 2 wsl --set-default-version 2查看当前WSL版本wsl --status wsl -l -v看到版本号是2说明OK。很多教程会漏掉一个细节Docker Desktop文档要求开启“虚拟机平台”这个Windows功能。在“启用或关闭Windows功能”里把“虚拟机平台”和“适用于Linux的Windows子系统”两个复选框打上勾。如果你用Hyper-V也建议一起勾上虽然WSL2并不强制依赖Hyper-V图形界面但Docker Desktop两种后端都用得上。最后检查一下虚拟化是否真的开了。打开任务管理器→性能→CPU右下角能看到“虚拟化已启用”。如果显示“已禁用”先去BIOS里找虚拟化开关。2.2 Docker Desktop安装过程的关键选项去Docker官网下载Docker Desktop for Windows安装包下载完成后双击运行。安装过程中有一步会问你要不要勾选“Use WSL 2 instead of Hyper-V”建议勾上。WSL2的启动速度比Hyper-V后端快内存占用也更小这是当前Windows上跑Docker的主流方式。安装完成后一般需要注销当前用户或重启系统系统会提示你。重启完打开Docker Desktop它会自动初始化首次启动可能需要一两分钟托盘区域会出现一个鲸鱼图标。点开鲸鱼图标看到绿色状态就说明引擎已经在跑了。这里有个虽然记录在案但依然让很多人困惑的问题安装时没有跳出“选择安装目录”的选项。Docker Desktop默认安装在C盘而且WSL2的虚拟磁盘文件默认也在C盘后面越用越大。所以如果你的C盘空间比较紧张建议先规划好或者等装完后再迁移虚拟磁盘位置。这个我放到第7章排查表里细说。装完别急着用。先验证命令行能用在PowerShell或CMD里执行docker version docker info能看到Client和Server两段信息说明Docker引擎和CLI已经打通。如果docker version只显示Client、没有Server说明引擎没起来或者你连的是不存在的API。这个问题很典型后面排查表里也会提到。2.3 首次启动的常见坑虚拟化支持未检测到怎么办Docker Desktop启动失败弹窗提示“Docker Desktop failed to start because virtualisation support wasnt detected”应该是Windows用户遇到频率最高的报错之一搜索量常年下不去。先说结论这个报错说明Docker Desktop的启动前置检查没过。最常见的三个原因一是BIOS里的虚拟化开关没开二是Windows的虚拟化相关功能没启用三是Windows自带的安全中心内核隔离拦截了。排查顺序建议是这样的第一步先按CtrlShiftEsc打开任务管理器切到“性能→CPU”看“虚拟化”后面写的是“已启用”还是“已禁用”。如果显示“已禁用”那就进入BIOS找到Intel Virtualization Technology或SVM Mode之类选项改成Enabled保存重启。不同品牌主板的BIOS界面差异很大找不到就搜“主板型号VT-x”。第二步如果任务管理器显示“已启用”但还是报错然后去“启用或关闭Windows功能”里确认“虚拟机平台”“适用于Linux的Windows子系统”“Hyper-V”三项都正常开启了。改完重启。第三步如果前面都没问题检查Windows安全中心。打开“设备安全性→内核隔离”如果你的电脑开了“内存完整性”功能部分老机器上Docker Desktop的WSL2后端确实会被它影响可以尝试暂时关闭再启动Docker试试。还有一个小众但真实存在的情况你开了第三方安全软件或旧版的沙盒工具它们提前占用了虚拟化相关能力。这种时候把第三方软件退出再启动Docker Desktop试试。如果还不行把报错信息里的关键字原样搜一遍多半能找到和你配置相近的案例。2.4 WSL2和Docker Desktop的关系弄懂这个少走弯路WSL2到底和Docker Desktop是什么关系我尽量讲得通俗一些。WSL2是Windows自带的Linux兼容层它背后是一个轻量级虚拟机完整运行了一个Linux内核。Docker Desktop正是看中了这个内核直接把Docker引擎放在WSL2里跑不需要再维护一套额外的虚拟机。安装完Docker Desktop后你可以在WSL里看到两个额外的发行版一个叫docker-desktop一个叫docker-desktop-data。这两个不是给你玩的普通发行版而是Docker Desktop内部的“工作机”不要手动去删删了Docker Desktop就起不来了。这里要纠正一个常见误区不是说你在Windows的CMD里能敲docker命令就万事大吉了。真正推荐的做法是在WSL发行版里安装Docker Desktop的CLI集成这样你在自己常用的Ubuntu终端里也能直接使用docker命令。Docker Desktop的Settings里Resources→WSL Integration可以勾选要让哪些发行版“感知”Docker。勾上之后在WSL里敲docker命令走的是和Docker Desktop同一个引擎。把这一层关系理解成“寄宿”比较贴切Docker引擎寄宿在WSL2的Linux内核上Windows上的命令行工具只是一个客户端。所以当你以后排查问题发现Windows侧docker命令能用、WSL里docker命令报“docker: command not found”大概率只是没有安装或配置CLI集成而不是Docker坏了。3. 核心概念和常用命令镜像、容器、数据卷3.1 镜像从哪来、怎么拉、怎么删镜像的官方来源是Docker Hub一个公共的镜像仓库类似应用市场。你在里面能找到大量官方维护的镜像比如nginx、mysql、redis、ubuntu、python等等。拉取镜像是很直白的操作docker pull nginx:latest docker pull mysql:8.0冒号后面跟的是标签也就是镜像的版本。日常使用强烈建议不要只靠latest或者干脆不写标签。latest是一个浮动的指针今天拉下来是1.25过半年再拉可能已经变成1.27了你的环境突然就变了这不是一件值得惊喜的事。线上或者开发环境统一用固定版本号比如nginx:1.25.3、mysql:8.0.36出了问题能复现升级也是主动的。查看本地有哪些镜像docker images这个命令会列出镜像仓库名、标签、镜像ID、创建时间和大小。删除镜像是docker rmi nginx:1.25.3如果镜像被某个容器使用着直接删除会报错需要先删除对应容器或者用-f强制删除。新手有个常见困惑docker pull明明拉了镜像为什么docker images里看不到多半是用了别的平台比如在Mac的ARM架构和Linux的AMD64架构下镜像仓库名和标签一样但镜像ID不同。先确认你当前Docker的平台架构再决定拉哪个镜像。镜像还有一种来源就是你自己构建。用一个叫做Dockerfile的文本文件描述构建步骤然后docker build -t my-app:1.0 .这条命令会按照Dockerfile里的指令一层层构建出你的自定义镜像。构建的时候注意上下文目录最后的那个点代表使用当前目录作为构建上下文Docker会把该目录内容发送给引擎。目录里如果有什么敏感文件或不需要的玩意记得用.dockerignore排除掉。3.2 容器生命周期启动、查看、停止、删除有了镜像就可以创建容器了。最简单的运行命令docker run --name my-nginx -p 8080:80 -d nginx:1.25.3这条命令的含义后文会详细拆解这里先把生命周期讲完。启动后查看运行中的容器docker ps加一个-a参数查看所有容器包含已经停止的:docker ps -a停止容器、启动容器、重启容器docker stop my-nginx docker start my-nginx docker restart my-nginx删除容器docker rm my-nginx容器正在运行的话直接rm会报错需要先stop或者用rm -f强制删除。还有一个很有用的操作是进入正在运行的容器内部docker exec -it my-nginx bash-i是保持标准输入打开-t分配一个伪终端结合起来你就能像SSH进了一台机器一样在容器里敲命令。你会发现自己其实在一个精简的Linux环境里但很多工具可能没装因为镜像讲究最小化。如果容器里没有bash就换成sh或者有的镜像连sh都没有那就需要换思路下一章用MySQL实例演示。还有一个大家经常用到的技巧把容器里的文件拷贝出来docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf反向也一样把本地文件拷进容器docker cp ./my.conf my-nginx:/etc/nginx/conf.d/my.confdocker cp是排查问题和临时改配置的利器但注意它不适合做数据持久化的正途因为容器一删拷进去的东西也全没了。3.3 日志容器里到底发生了什么容器运行异常时最直接的排查手段就是看日志docker logs my-nginx加-f参数可以跟随输出类似Linux里的tail -f看着滚动日志开发调试非常高效docker logs -f my-nginx只看最后200行docker logs --tail 200 my-nginx容器日志在Docker里默认是标准输出stdout/stderr。所以如果你在自己的应用代码里用的是log4j、logback这类日志框架需要把日志输出到标准输出Docker才能抓到否则docker logs里什么都看不到。这一点经常被初次部署项目的人忽略明明应用在日志文件里写了大量信息docker logs却一片空白查了半天才发现是输出位置不对。还有一种常见情况是容器启动后立刻退出docker ps看不到它在运行docker ps -a看到状态是Exited。这时候先用docker logs看一下退出前的输出绝大多数情况下真正原因就写在最后几行日志里。比如端口被占用、配置文件语法错误、应用启动失败都会在日志里体现。3.4 数据卷容器没了数据不能丢容器生命周期短、还不留痕“删了重来”是它的优点但对数据来说就是灾难。你往容器里写的任何文件都存在刚才说的可写层里容器删除可写层也会被销毁。所以Docker提供了一种叫数据卷Volume的持久化存储机制。我建议新手刚开始就要养成这个意识数据库、上传文件、配置文件这类需要长期保存的数据一定要放在数据卷或者绑定挂载目录中别直接裸写进容器。数据卷就是Docker专门管理的一块存储区域它独立于容器存在容器删了重建只要挂载同一个数据卷数据还在。创建一个数据卷docker volume create mysql_data查看数据卷列表docker volume ls运行容器时挂载数据卷docker run -d --name mysql8 -v mysql_data:/var/lib/mysql mysql:8.0这里-v参数的含义冒号左边是宿主机上的数据卷名或目录路径右边是容器内的路径。把容器内MySQL的数据目录/var/lib/mysql和宿主机数据卷mysql_data关联起来MySQL写文件时实际上写进了数据卷。除了命名数据卷还有两种常见的挂载方式一种是绑定挂载直接指定宿主机的一个绝对路径比如-v /home/user/mysql-data:/var/lib/mysql好处是你能直接用编辑器查看和修改宿主机上的这些文件另一种是匿名卷只写容器内路径不写宿主路径比如-v /var/lib/mysqlDocker会自动生成一个随机卷名。匿名卷在删除容器时会留下一个游离数据卷容易造成磁盘垃圾不建议新手多用。这里要插一个很多人会迷糊的点为什么要设计这么多种挂载方式简单说绑定挂载适合开发和调试场景你想改完代码立刻在容器里生效命名数据卷适合部署和生产场景你不想去关心宿主机上的具体目录交给Docker管理更清爽。实际项目里两种经常混用比如代码用绑定挂载数据库数据用命名数据卷。4. 从实操看Docker跑一个MySQL 8.0容器4.1 拉镜像前的三件事版本、端口、数据目录光讲概念太抽象我带你把一个MySQL 8.0容器完整跑起来。MySQL是大家最熟悉也最常用的基础组件用它做例子你能直观理解端口映射、环境变量、数据卷这些东西。动手之前先想三件事。第一版本选什么。Docker Hub上MySQL镜像有很多标签常见的有latest、8.0、5.7、8.4等。latest是当前默认推荐版本但实际生产环境不会直接用latest因为版本漂移风险太大。这篇文章就以mysql:8.0为例这个标签是8.0大版本下的最新补丁版比较稳定。至于为什么不用5.7它虽然还有很多存量用户但官方早已宣布5.7生命周期结束新项目不建议再碰。第二端口怎么规划。MySQL默认监听3306端口。你要确认宿主机上3306端口没被占用Windows下可以用netstat -ano | findstr :3306查看。如果被占用了可以把宿主机的端口换成别的比如33306后面连接时用33306。端口映射的原则是右边是容器内的端口一般保持默认不要动左边是宿主机上的对外端口可以根据需要修改。第三数据放哪里。这是最容易踩的坑。如果直接docker run mysql而没挂载数据卷那你的数据库就是一次性用品。容器删了、升级镜像、重建容器数据全没了。所以从一开始就要把数据目录挂出来。MySQL在容器内的数据目录是/var/lib/mysql我在这里使用一个命名卷mysql8_data来承接。4.2 docker run命令逐段拆解准备工作做完执行启动命令docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD123456 \ -v mysql8_data:/var/lib/mysql \ mysql:8.0我强烈建议新手把一条docker run命令拆成五行写每一行一个参数这样才清楚每一段是干嘛的。逐段说-d表示后台运行容器Docker返回一个容器ID后命令就结束了容器在后台跑。--name mysql8给容器起的名字后面docker stop mysql8、docker logs mysql8都用这个名字。不指定的话Docker会随机生成一个名字比如zealous_ptolemy记起来麻烦。-p 3306:3306端口映射。左边是宿主机端口右边是容器端口。宿主机上访问localhost:3306流量会被转发到容器的3306端口也就是MySQL的监听端口。-e MYSQL_ROOT_PASSWORD123456环境变量。MySQL镜像要求使用MYSQL_ROOT_PASSWORD指定root初始密码。环境变量是容器向内部程序传参的标准方式很多镜像都是这么设计的。-v mysql8_data:/var/lib/mysql数据卷挂载。如果mysql8_data这个卷不存在docker run会自动创建。MySQL在容器内把数据写入/var/lib/mysql实际上写进了这个数据卷。mysql:8.0是镜像名加标签。执行完之后先用docker ps看看容器是否在运行。如果状态是Up再用docker logs mysql8看看启动日志。第一次启动MySQL需要初始化数据目录日志里会刷很多提示看到“ready for connections”字样说明初始化完成。这个初始化过程需要一点时间等30秒左右再连接比较稳。注意MYSQL_ROOT_PASSWORD这种明文密码只适合本地开发环境。如果要部署到测试或生产环境更安全的做法是用MYSQL_ROOT_PASSWORD_FILE从文件读取密码或者结合密文配置统一管理不要直接在启动命令里暴露密码。4.3 实操连接MySQL、执行SQL、验证数据持久化容器起来了怎么验证两条路径。第一条进入容器内部用MySQL自带的客户端连接docker exec -it mysql8 mysql -uroot -p输入密码后就能进MySQL命令行。这是我们之前说的docker exec典型用法——在容器内执行命令。不过注意这里执行的不是bash而是直接执行mysql客户端命令。如果你想先进入容器再看可以docker exec -it mysql8 bash mysql -uroot -pMySQL 8.0的官方镜像是基于Debian的里面装了bash所以这条路走得通。有些精简镜像没有bash只有sh用docker exec -it 容器名 sh即可。第二条从宿主机上用Navicat、DBeaver这类图形工具连接。连接信息填localhost端口3306用户名root密码用上面设置的root密码。如果连接失败最常见的报错是Authentication plugin caching_sha2_password cannot be loaded这是MySQL 8.0默认认证插件变了导致的。解决方式是在启动MySQL时加上参数指定使用mysql_native_password插件docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD123456 \ -e MYSQL_ROOT_HOST% \ -v mysql8_data:/var/lib/mysql \ mysql:8.0 \ --default-authentication-pluginmysql_native_passwordMYSQL_ROOT_HOST%表示允许root从任意主机连接本地开发时比较省事。如果你用老版本Navicat连不上MySQL 8.0多半就是认证插件问题这个方法基本能解。现在做一次持久化验证这是本节的精髓。在MySQL命令行里执行CREATE DATABASE demo; USE demo; CREATE TABLE t_user (id INT PRIMARY KEY, name VARCHAR(50)); INSERT INTO t_user VALUES (1, docker);然后退出容器停止并删除这个容器docker stop mysql8 docker rm mysql8删完容器用全新容器重新挂载同一个数据卷docker run -d \ --name mysql8_new \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD123456 \ -v mysql8_data:/var/lib/mysql \ mysql:8.0 docker exec -it mysql8_new mysql -uroot -p进入MySQL后执行USE demo; SELECT * FROM t_user;不出意外的话id1、namedocker那行数据还在。这就是数据卷的威力——容器就像一次性的临时工数据卷才是真正存家底的金库。4.4 备份和导出数据别等出事才着急容器虽然删了重建代价不大但万一宿主机磁盘坏了数据卷也跟着完蛋。所以备份是必修课。最朴素的做法是用mysqldump定时导出SQL文件docker exec mysql8 mysqldump -uroot -p123456 demo backup.sql把容器里的MySQL数据导出到宿主机当前目录的backup.sql中。注意mysqldump是在容器内执行的重定向符是在宿主机shell里生效的所以输出文件会落在宿主机。恢复时反向操作docker exec -i mysql8 mysql -uroot -p123456 backup.sql前面带-i参数让宿主机标准输入可以传进容器。另一种备份思路是直接备份整个数据卷目录。先查看数据卷的挂载点docker volume inspect mysql8_data输出结果里有一个Mountpoint字段在WSL2环境中这个路径通常位于WSL虚拟磁盘内你在Windows资源管理器里直接看不到。真要文件级别备份推荐用容器把数据目录打包docker run --rm -v mysql8_data:/data -v $(pwd):/backup alpine tar czf /backup/mysql8_data.tar.gz -C /data .这里用了alpine镜像它会以只读方式挂载数据卷然后把整个数据目录打包到当前目录。备份到本地后还可以上传到对象存储等远端位置这里就不展开了。备份这件事平时觉得是小题大做真遇到磁盘故障或误删数据时才追悔莫及。我的习惯是本地开发环境至少保证“数据卷定期mysqldump”双保险线上环境更严格必须有异地备份和定时任务。5. Docker Compose一个文件搞定复杂环境5.1 什么时候需要Compose一个项目通常不是一个容器就能搞定的。典型Web应用至少需要应用容器、MySQL容器、Redis容器可能还要Nginx容器做反向代理。你用docker run一个个启动当然也可以但问题马上就来了每条docker run命令都是一长串参数拷贝粘贴容易出错容器之间启动有先后依赖MySQL没起来应用连不上换一台机器要重新敲一堆命令团队协作时每个人敲的命令还不完全一样。这种时候就需要编排工具把容器清单、网络、依赖关系、配置项统一写在一个文件里一键启动、一键停止。Docker Compose就是解决这个问题的。它的核心是一个YAML格式的配置文件默认叫docker-compose.yml或compose.yaml。你在文件里描述我需要哪些服务、每个服务用什么镜像、映射哪些端口、挂载哪些数据卷、设置什么环境变量然后执行docker compose up -d一套环境就齐活了。我要多说一句很多新手学了docker run之后觉得Compose不过是把命令变了个写法懒得用。但等你真正部署一个带多个依赖的项目你会发现Compose的价值远超“少打几行命令”它本质上是把环境定义为代码能够版本化、评审、复制。5.2 写第一个docker-compose.yml继续用MySQL做例子再拉上Redis组成一个开发环境。在项目根目录新建docker-compose.yml写入services: mysql: image: mysql:8.0 container_name: dev-mysql restart: unless-stopped ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: 123456 MYSQL_DATABASE: app_db TZ: Asia/Shanghai volumes: - mysql_data:/var/lib/mysql command: --default-authentication-pluginmysql_native_password redis: image: redis:7.2 container_name: dev-redis restart: unless-stopped ports: - 6379:6379 volumes: - redis_data:/data command: redis-server --appendonly yes volumes: mysql_data: redis_data:这个文件值得逐行细看理解了它你基本就掌握了Compose的核心语法。services下面定义了两个服务mysql和redis。每个服务对应一个容器。container_name可以不写不写的话Compose会自动用“项目名-服务名-序号”的方式命名写的话好处是固定好记。restart: unless-stopped表示容器意外退出时自动重启除非你手动停止这个策略很适合开发环境服务挂了能自己拉起来。ports字段把容器端口映射到宿主机写法是宿主机端口:容器端口。注意YAML里这个值建议用引号包裹否则像3306这样的值会被解析成数字没问题但如果是6379:6379里带冒号YAML解析就可能出问题所以统一加引号更稳妥。environment是环境变量列表。MYSQL_DATABASEapp_db表示MySQL首次初始化时自动创建app_db这个数据库省去手动建库。TZ设置时区虽然MySQL镜像本身可以工作但时区不设置的话NOW()之类的函数返回的时间和宿主机时间可能差几个小时开发联调时容易把人坑哭。volumes里的mysql_data:/var/lib/mysql和redis_data:/data都是命名数据卷。注意这里我没在卷名前面加路径Compose会创建由Docker管理的命名卷。文件末尾的volumes段用来申明卷申明过后服务里才可以使用。command字段覆盖镜像默认的启动命令。redis容器默认不带持久化配置重启后数据会丢所以这里redis-server --appendonly yes开启了AOF持久化。MySQL那行command是延续上一章解决问题的思路把认证插件切回兼容模式。5.3 Compose常用命令和常见错误在docker-compose.yml所在目录执行docker compose up -d-d表示后台运行。首次执行会自动拉取镜像、创建网络和卷、启动容器。用docker compose ps查看服务状态docker compose ps查看某个服务日志docker compose logs -f mysql进入某个服务容器docker compose exec mysql bash停止所有服务但保留容器和卷docker compose stop停止并删除所有服务容器和网络但数据卷默认保留docker compose down如果想连数据卷一起删掉加-v参数。这步操作要特别小心加了-v之后以后想找回数据就很难了docker compose down -v新手最常见的Compose报错是“no configuration file provided”或者“cant find a suitable configuration file”。解决方式很简单确认当前目录下有docker-compose.yml文件名别写错有个点是docker-compose.yml不是docker-compose.yaml两个都支持或者用-f参数指定文件路径docker compose -f /path/to/docker-compose.yml up -d还有一个坑改了compose文件里的端口或卷配置执行docker compose up -dCompose会检测到配置变化并重新创建相关容器。这个机制很智能但也意味着如果你只想重启容器而不是改配置不要随便改文件否则会触发不必要重建。需要纯重启某个服务时用docker compose restart mysql。6. 镜像源配置解决docker pull慢吞吞的问题6.1 为什么docker pull这么慢在国内网络环境里从Docker Hub官方仓库拉取镜像速度慢、超时、失败几乎是每个新手的必经之路。原因也不复杂Docker Hub的服务器在国外跨大洋传输几百MB的镜像层速度自然上不去。那怎么解决最常见、也最稳妥的思路是配置镜像加速器。它的原理也不神秘提供加速服务的服务商会在自己的服务器上提前缓存你常用的镜像你拉取镜像时实际是从加速器服务器下载而不是直接连Docker Hub。这里我要强调一个注意事项所谓的“加速器”不是你做了什么违规定义的事而是官方和各家云服务商都支持的常规配置手段。这是国内开发者日常使用的正当技术方案把自己配置文件里的registry-mirrors字段填好就能显著提升拉取速度。还有一个容易误解的点镜像加速器拉下来后docker images显示的仓库名仍然是原来的名字比如nginx:1.25.3。因为加速器只是在传输层帮你代理镜像本身的元数据和标签不会改变。6.2 在Docker Desktop里配置镜像加速器Docker Desktop提供了图形界面配置入口。打开Docker Desktop进入Settings设置→Docker Engine你会看到一段JSON配置默认大概是这样的{ builder: { gc: { defaultKeepStorage: 20GB, enabled: true } }, experimental: false, registry-mirrors: [] }把registry-mirrors字段填上加速器地址{ builder: { gc: { defaultKeepStorage: 20GB, enabled: true } }, experimental: false, registry-mirrors: [ https://你的专属加速器地址 ] }填好之后点击右下角的“Apply Restart”按钮Docker Desktop会重新启动引擎配置立刻生效。然后再执行docker pull你会发现镜像下载速度有明显提升。加速器地址去哪找国内主流的云服务厂商比如阿里云、腾讯云、华为云都提供容器镜像服务注册之后每个用户会分配一个专属加速地址。这类地址通常和你的账号关联直接写死到配置文件即可。还有一些高校和开源组织维护的公共加速器虽然方便但稳定性时好时坏需要自己实测。如果你是命令行优先的用户也可以直接修改Docker引擎配置文件。在Windows上路径是C:\Users\你的用户名\.docker\daemon.json没有这个文件就手动创建内容和上面JSON一致。改完重启Docker Desktop。其实通过图形界面改最终也是写入这个文件效果相同。验证配置是否生效可以执行docker info输出会有非常长的一段信息找到Registry Mirrors那一栏能看到你配置的加速器地址说明已经生效。6.3 镜像源失效了怎么办配置好之后拉取速度通常会稳定一段时间但“加速器失效”这个问题迟早会碰到。最明显的症状是某个镜像之前拉得好好的某一天docker pull直接报错或者一直卡在Waiting。这时候可以按下面顺序排查。首先确认网络本身没毛病。ping一下目标镜像仓库域名排除DNS解析问题。如果网络正常那大概率是加速器服务本身出问题了要么是服务商调整策略要么是公共加速器被限流关停。换一个加速器地址是最直接的解法。可以在网上搜一下“容器镜像加速器”会找到不少可用的公共地址但需要注意时效性有些地址今天能用明天就失效了。如果条件允许直接用云服务商提供的专属地址是最稳定的毕竟服务商有SLA承诺不会无缘无故突然关停。还有一个思路是把Docker Hub的镜像同步到自己的私有仓库。比如你在阿里云上开通容器镜像服务后可以创建个人命名空间从Docker Hub同步常用镜像到自己的仓库之后docker pull全部从自己仓库拉。这样拉取速度有保障也不怕公共加速器失效但需要一定的云服务使用经验。最后万一某个镜像无论如何都拉不下来了别硬刚。试试换一个版本标签比如把latest换成具体的8.0.36因为不同层的大小和热度可能差异很大。也试试docker pull先拉一个小镜像比如alpine看基础网络通不通。如果连alpine都拉不动那问题可能出在Docker网络配置上回到前面的排查思路。7. 常见问题排查速查表7.1 Docker Desktop启动失败/虚拟化报错这个我在第2章已经展开讲过这里以速查表形式呈现方便你直接对照处理。报错信息可能原因处理方式Docker Desktop failed to start because virtualisation support wasnt detectedBIOS虚拟化未开启重启进BIOS开启VT-x或AMD-VDocker Desktop failed to start because virtualisation support wasnt detectedWindows虚拟化功能未启用启用“虚拟机平台”“适用于Linux的Windows子系统”Docker Desktop failed to start because virtualisation support wasnt detected安全软件干扰暂时退出安全软件或关闭内核隔离“内存完整性”报错码0x80370102WSL2虚拟机创建失败检查BIOS虚拟化开关执行wsl --updateDocker Desktop一直停在StartingWSL2子系统卡死重启Docker Desktop必要时在PowerShell执行wsl --shutdown实战里我发现很多人BIOS虚拟化其实已经开了问题出在Windows功能勾选不全。所以第一步先检查Windows功能面板第二步再进BIOS顺序不要搞反。7.2 WSL is unresponsive / Failed to connect to Docker API这个报错的完整形态一般是“Failed to connect to the Docker API at npipe:////./pipe/dockerDesktopLinuxEngine - WSL is unresponsive”。拆开看连接地址是Windows命名的管道也就是Docker Desktop和WSL2后端之间的通信通道WSL没有响应。这个问题大多出现在Docker Desktop引擎还没完全启动完成或者WSL2子系统卡住了。先试试最基础的右键托盘鲸鱼图标选择Restart。如果不行在PowerShell里wsl --shutdown强制关闭所有WSL子系统然后重新打开Docker Desktop它会自动拉起新的WSL后端。这个操作基本能解决大半的“WSL is unresponsive”问题。还不行的话检查WSL状态wsl -l -v如果你看到某个发行版状态是Stopped可以先手动启动一下wsl等待WSL进入命令行后再打开Docker Desktop。有些时候是WSL发行版本身的文件系统异常导致启动慢或卡住可以执行wsl --update更新WSL内核后再试。极少数情况下WSL的虚拟磁盘文件损坏需要重置发行版但那属于重装级操作建议放在最后才试并且操作前一定要备份数据。7.3 容器启动后秒退这是新手第二个高频问题docker run执行后docker ps看不到容器docker ps -a看到Exited状态。我见过太多人卡在这一步。一般分两类。第一类是镜像本身启动后没有持续前台进程。Linux容器必须有PID 1进程在跑如果这个进程退出了容器也就退出了。比如你用nginx镜像它自带启动命令会一直跑但如果你用ubuntu镜像跑个bashbash交互结束、容器就退了。这类容器需要加上-ti参数保持终端打开docker run -it ubuntu:22.04 bash第二类是应用启动时直接失败。这种用docker logs看退出前日志最直接。比如MySQL端口被占用比如应用缺少某个环境变量报错信息都会在日志里留下痕迹。看到日志后针对性修改配置重新起一个容器。有一个习惯值得分享拿任何镜像做尝试时先用一条docker run -it... bash看看容器能不能进入、基础环境是什么样的再决定后续操作。这比直接梭哈一个长命令碰运气高效得多。7.4 端口冲突和磁盘占用端口冲突的报错通常表现为“port is already allocated”或“bind: address already in use”。当你同时启动多个容器或者宿主机上已经有程序占用端口就会出现这个问题。排查方法Windows下用netstat查看端口被谁占用netstat -ano | findstr :3306最后一列是PID再到任务管理器里找到对应进程。确认占用后可选择关掉进程或者给容器换一个宿主机端口。注意换端口只改-p参数左边的值就行容器内端口不变。磁盘占用是另一个长期隐患尤其Docker Desktop默认把虚拟磁盘放在C盘。容器、镜像、数据卷、构建缓存都会占磁盘空间。查看占用docker system df清理不再使用的镜像、容器、网络docker system prune这个命令加-a会连没有在用的镜像一起清掉加--volumes会清理游离数据卷。用之前先确认你不需要这些数据docker system prune -a --volumes如果发现是构建缓存占用太多单独清理一下docker builder prune如果发现连Docker数据文件本身都占了几十GB而且你的C盘确实容不下可以考虑把WSL2的虚拟磁盘迁移到其他盘。Docker Desktop的Settings→Resources→Advanced里有一个Disk image location选项直接把目录改到D盘或其他分区即可。这是官方提供的功能迁移前先停止所有容器迁移过程中不要关机。7.5 Docker Desktop只能装C盘吗“Docker Desktop只能装在C盘吗”这个问题搜索量很大说明困扰了不少人。答案分两层。第一层Docker Desktop程序本体安装时确实没有图形化的目录选择界面。但严格说你可以用命令行安装时指定路径或者在安装完成后使用mklink这类符号链接手段把它“搬”到别的盘只是不推荐这么搞毕竟系统盘装应用软件本身就是Windows的常规用法程序本体也就一两个GB。第二层真正占磁盘大头的是WSL2的虚拟磁盘里面存放了镜像、容器、数据卷。这个位置的迁移在Docker Desktop设置里就支持就是上面说的Disk image location改到D盘后数据文件会迁移过去C盘空间瞬间就宽裕了。迁移前记得先看看当前虚拟磁盘实际占用多少。方法打开Docker Desktop进入Settings→Resources→Advanced界面会显示当前磁盘镜像位置和大小。确认目标磁盘有足够空间然后修改路径点击Apply Restart。Docker会负责把虚拟磁盘文件搬过去。迁移过程可能较长别中途关机。还有一个容易被忽略的细节每次docker pull拉新镜像、docker build构建新镜像、docker compose up启动服务都会往虚拟磁盘里写数据。也就是说即使你容器数据放在数据卷里、而且数据卷指向别的盘其实WSL2虚拟磁盘是一个整体磁盘占用仍然会只增不减。定期docker system prune清理缓存是维护磁盘空间的好习惯。写在最后我的一些体会这套内容是我自己从零折腾Docker过来的完整心路。当年我在Windows上第一次启动Docker Desktop同样遇到了virtualisation support报错折腾了一整个下午才弄明白是BIOS里VT-x没开。后面又把MySQL容器删了发现数据全没了才老老实实研究数据卷。这些坑都不是某本教材能一次性教会你的只有亲手跌进去一次印象才深刻。如果你也是刚开始接触Docker我的建议很简单别急着去背命令先跑起来一个容器然后故意把它删掉再重建亲手验证一下数据卷的作用。把第3章的概念和第4章的MySQL实操走一遍你对Docker的理解会比看十篇文章都扎实。遇到问题不要慌记住一个核心排查思路——先看日志再查配置最后考虑重建容器。容器本来就是用来“随手扔”的大胆试错不会搞坏什么。往前再走一步等你用顺了Docker Desktop可以试试在空白的Linux服务器上用命令行的方式安装Docker并部署同样的项目到那时你会发现Docker带来的最大价值不是省去安装的麻烦而是让“同样的环境复现”这件事变成了一个标准动作。