
1. 先把 Docker 这件事讲清楚它到底解决了谁的痛点我最早接触 Docker 是在一台 Windows 笔记本上那时候刚配好一个后端项目数据库、缓存、消息队列全都装在本地换了台电脑部署的时候光是环境不一致的问题就折腾了整整两天。后来用了 Docker 才发现之前那种我这能跑你那跑不了的痛苦本质上不是代码的问题而是环境的问题。Docker 的核心价值就在这儿——它把应用和它所需要的运行环境打包成一个标准化的单元走到哪儿都能跑出一样的结果。用一句话类比Docker 就像集装箱。货物本身的形状千奇百怪但装进标准尺寸的集装箱之后吊车、货轮、卡车都能用同一套流程来处理它。容器镜像就是那个集装箱Docker 引擎就是那套搬运系统。你不需要关心货物是什么只要它是标准集装箱就能被这套系统无障碍地运输。Docker 主要解决三类问题。第一类是环境一致性问题开发、测试、生产三套环境的依赖版本、系统库、配置路径任何一处不一致都会导致诡异 bug。第二类是交付效率问题传统的虚拟机镜像动辄几个 G启动要几分钟而容器镜像往往只有几十到几百兆启动是秒级。第三类是资源利用率问题一台物理机塞十几个虚拟机就基本满了换成容器可以塞几十上百个因为它们共享宿主机的内核不重复加载操作系统。那 Docker 适合谁如果你在做后端开发、运维部署、CI/CD 流水线搭建或者你只是想在本机快速跑起一个 MySQL、Redis 来验证点东西Docker 都值得花时间学。反过来说如果你做的是纯前端静态页面或者只是偶尔跑一下单文件脚本Docker 带来的收益可能还抵不过学习成本。判断标准很简单你有没有遇到依赖关系复杂、环境难以复现、部署步骤冗长这三类问题中的任意一类。有就学没有先放着。很多人对 Docker 有个常见误解觉得它就是轻量级虚拟机。这个说法不完全错但它会误导你对架构的理解。虚拟机是在硬件层面做虚拟化每个虚拟机里跑一套完整的操作系统内核容器是在操作系统层面做隔离所有容器共享宿主机的内核靠命名空间做资源隔离、靠 cgroups 做资源限制。这就解释了为什么容器镜像那么小、启动那么快——它根本不需要启动一个操作系统。理解这一点后面遇到很多为什么容器里不能用 systemd为什么容器里的时间跟宿主机一样之类的疑问就都能自己想明白了。2. Windows 上装 Docker Desktop从下载到跑通第一个容器2.1 为什么 Windows 用户的第一选择是 Docker DesktopWindows 上装 Docker 有几种路径Docker Desktop、Docker Toolbox已废弃、WSL2 里直接装 Docker 引擎、或者用远程 Docker 主机。对于绝大多数个人开发者和小团队Docker Desktop 是最省心的选择因为它把 Docker 引擎、命令行客户端、GUI 管理界面、Kubernetes 单节点集群全部打包好了装完就能用不需要自己折腾守护进程的配置。不过 Docker Desktop 在 Windows 上有个前置依赖它需要一个 Linux 内核来跑容器。早期靠 Hyper-V 虚拟化一个 Linux 虚拟机现在官方主推 WSL2。WSL2 是 Windows 的 Linux 子系统第二代它自带一个真正的 Linux 内核资源占用比 Hyper-V 方案低启动也更快文件系统性能在某些场景下更好。这就是为什么很多人在装 Docker Desktop 时会被提示需要启用 WSL2。选择 Docker Desktop 还是纯 WSL2 原生 Docker 引擎我的建议是日常开发用 Docker Desktop因为 GUI 里看容器日志、管理镜像、调整资源限制都直观如果你是个重度命令行用户且不想让 Docker Desktop 常驻内存那 WSL2 里直接装引擎也完全可行代价是每次都要手动sudo service docker start。两种方式我都长期用过个人倾向于 Docker Desktop 起步等熟悉了再考虑轻量化。2.2 安装前的系统检查清单装之前有几件事必须先确认不然装到一半报错会更麻烦。按下面这张表逐项过一遍检查项要求如何确认Windows 版本Windows 10 64位 21H2 及以上或 Windows 11winver命令查看CPU 虚拟化BIOS/UEFI 中已开启 VT-x / AMD-V任务管理器 → 性能 → CPU → 虚拟化内存建议 8GB 以上Docker Desktop 至少分 2GB任务管理器磁盘空间至少留 20GB 可用资源管理器WSL2已安装并启用wsl --statusHyper-V家庭版没有专业版需确认systeminfo查看虚拟化没开是 Windows 上装 Docker 最常见的拦路虎。很多人跑到 Docker Desktop 启动时看到virtualization support not detected / Virtualisation support wasnt detected之类的提示第一反应是软件坏了其实是 BIOS 里的虚拟化开关没打开。这个开关的名字因主板厂商而异Intel 平台通常叫 Intel VT-x、Intel Virtualization Technology 或 VT-dAMD 平台叫 SVM Mode 或 AMD-V。进 BIOS 通常在开机按 Del、F2 或 F10找到 CPU 相关配置项把虚拟化打开保存重启即可。还有个容易忽略的点如果你开了 Hyper-V 或者装过某些安卓模拟器、沙盒类软件它们可能抢占了虚拟化资源导致 Docker Desktop 无法启动 WSL2 后端。这种情况可以关掉冲突软件或者在 Docker Desktop 设置里切换到 Hyper-V 后端试试。2.3 一步步安装 Docker Desktop第一步去 Docker 官网下载 Docker Desktop for Windows 的安装包。如果官网下载慢可以用国内的一些镜像加速站点获取安装包或者用离线安装包的方式官网也提供直链。第二步双击安装包安装向导里有一个关键选项Use WSL 2 instead of Hyper-V (recommended)。保持勾选 WSL2 方案除非你有特殊理由必须用 Hyper-V。安装过程会请求管理员权限同意即可。第三步安装完成后重启电脑。这一步不能省很多人的 Docker 起不来就是没重启系统组件没完全加载。第四步首次启动 Docker Desktop它会让你接受服务条款然后可能提示注册或登录账号个人使用可以跳过登录直接进入主界面。等待左下角的小鲸鱼图标变成绿色说明引擎已经正常运行。第五步打开 PowerShell 或 CMD执行下面这条命令docker version如果能看到 Client 和 Server 两部分的版本信息说明安装成功。如果只显示 Client 不显示 Server通常是引擎还没启动完成或者 WSL2 后端有问题可以看 Docker Desktop 的日志排查。再来一条验证命令docker run hello-world这条命令会从镜像仓库拉取一个极小的镜像并运行输出一段欢迎信息。这是判断 Docker 是否真正可用最直接的方式如果它跑通了说明拉取、存储、运行整条链路都正常。2.4 安装后必做的两件配置第一件是配置镜像加速源。默认从官方仓库拉镜像在国内网络环境下会非常慢甚至超时。打开 Docker Desktop → Settings → Docker Engine在 JSON 配置里加入镜像源地址{ registry-mirrors: [ https://your-mirror-1.example.com, https://your-mirror-2.example.com ] }具体可用的镜像源地址经常变动建议去相关社区查找当前可用的地址。配好之后点 Apply Restart再拉镜像速度会有明显提升。注意镜像源只是加速代理不改变镜像本身的内容安全性上要选择可信来源。第二件是调整资源限制。Windows 上的 Docker Desktop 默认可能只给容器分配 2GB 内存和 2 个 CPU跑个 MySQL 加 Redis 就捉襟见肘了。在 Settings → Resources 里根据你机器的实际情况调高比如给 4GB 内存、4 个 CPU。别把宿主机资源全分给 DockerWindows 本身和你的 IDE 也需要内存一般给宿主机留一半比较稳妥。还有Docker Desktop 默认会把镜像和数据存在 C 盘用久了 C 盘会爆。在 Settings → Resources → Disk image location 里可以改到其他盘建议一开始就改掉别等 C 盘红了再处理。3. 日常高频操作镜像、容器、仓库这三大件的使用逻辑3.1 镜像与容器的关系用一个比喻讲透很多人分不清镜像Image和容器Container。镜像是只读的模板容器是镜像运行起来的实例。类比一下镜像就是类容器就是对象镜像就是安装光盘容器就是装好并正在运行的系统。一个镜像可以启动出无数个互不干扰的容器就像一张光盘可以装无数台机器。容器的可写层也很关键。镜像本身是只读的当你启动一个容器并修改了里面的文件这些改动是写在镜像之上的一层可写层里不会污染原镜像。容器删掉这层也就没了。这也是为什么容器里改的文件重启就没了——除非你用数据卷Volume把数据挂载到宿主机上持久化。理解了可写层的概念数据持久化的必要性就顺理成章了。3.2 镜像相关的高频命令拉取镜像是用的最多的操作docker pull mysql:8.0 docker pull redis:7-alpine标签tag很重要不写标签默认是latest而latest并不代表最新稳定版只是一个默认标签。生产环境必须锁定明确版本否则今天拉到的 latest 和明天拉到的可能完全不同会导致不可复现的问题。alpine结尾的镜像是基于 Alpine Linux 的体积小得惊人比如 redis:7-alpine 只有几十兆代价是它用的不是 glibc 而是 musl libc某些依赖 glibc 的二进制可能不兼容。选镜像时这个权衡要心里有数。查看本地镜像docker images删除镜像docker rmi image_id如果镜像被容器占用需要先删容器或者用-f强制删除。强制删除时要小心如果有容器在引用它可能会出问题。3.3 容器生命周期的管理启动并运行容器docker run -d --name mymysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 mysql:8.0这条命令拆开看-d是后台运行--name给容器起名字-p 3306:3306把宿主机 3306 端口映射到容器 3306-e设置环境变量最后是镜像名。端口映射的格式是宿主机端口:容器端口顺序不能反。这个映射关系最容易搞错一旦写反了你会发现自己连接不上还会莫名其妙。查看运行中的容器docker ps docker ps -a # 包括已停止的进入正在运行的容器内部docker exec -it mymysql bash如果容器里没有 bash比如 Alpine 镜像可以换成sh。-it是-i交互加-t分配终端两个一起用才能得到一个可交互的 shell。停止、启动、删除容器docker stop mymysql docker start mymysql docker rm mymysql docker rm -f mymysql # 强制删除运行中的容器查看容器日志docker logs -f mymysql-f是 follow类似tail -f实时看日志。排查容器启动失败时docker logs和docker inspect是两把最常用的钥匙。3.4 一条命令起一个 MySQL 8.0 并带数据持久化上面那条docker run有个致命问题容器一删数据库数据全没了。正确姿势是挂载数据卷docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD你的密码 \ -e TZAsia/Shanghai \ -v mysql_data:/var/lib/mysql \ --restart unless-stopped \ mysql:8.0几个要点值得说。-v mysql_data:/var/lib/mysql用的是命名卷Docker 会在宿主机某个目录下管理这块数据容器删了数据卷还在下次用同样名字的卷启动数据就回来了。-e TZAsia/Shanghai设置时区不设的话容器默认 UTC你插入的时间会差 8 小时这是血泪教训我第一次用 Docker 跑 MySQL 时就被这个坑到过。--restart unless-stopped让容器在宿主机重启后自动拉起除了手动停止的之外这是个很实用的生产配置。启动后连接验证docker exec -it mysql8 mysql -uroot -p输入密码能进 MySQL 命令行就说明成了。3.5 用 Docker Compose 管多容器应用单容器用docker run还行一旦涉及 MySQL Redis 应用三个容器命令会越来越长管理也会乱。Compose 就是用来解决多容器编排的用一份 YAML 文件描述所有服务一条命令全部拉起。version: 3.8 services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: 你的密码 TZ: Asia/Shanghai volumes: - mysql_data:/var/lib/mysql ports: - 3306:3306 restart: unless-stopped redis: image: redis:7-alpine command: redis-server --appendonly yes volumes: - redis_data:/data ports: - 6379:6379 restart: unless-stopped volumes: mysql_data: redis_data:对应命令docker compose up -d # 启动 docker compose ps # 查看状态 docker compose logs -f # 看日志 docker compose down # 停止并删除容器数据卷保留 docker compose down -v # 连数据卷一起删慎用Compose 里服务之间可以通过服务名互相访问比如应用容器里连 MySQL 直接用主机名mysql就行不用管 IP。这比手动维护docker run的链接关系清爽太多了。注意version字段在新版 Compose 里已经非必需但写上兼容性更好。4. 实战常见故障从启动失败到权限错误的排查链路4.1 Docker Desktop 起不来虚拟化与 WSL2 两大元凶前文提到的virtualization support not detected处理思路是三步走。先确认 CPU 虚拟化在 BIOS 里是否开启再用任务管理器性能页核对如果显示已禁用就去 BIOS 开启。开启后重启Docker Desktop 一般就能起来。如果虚拟化开了还是起不来多半是 WSL2 的问题。执行wsl --status wsl --update如果 WSL 内核版本过旧wsl --update会升级内核。还可以执行wsl --set-default-version 2确保新安装的发行版走 WSL2。有时候 WSL 的某个发行版卡死了可以wsl --shutdown强制关掉所有 WSL 实例再重启 Docker Desktop。另一个高频报错是连接不到 Docker API提示类似 failed to connect to the docker API at npipe:////./pipe/dockerDesktopLinuxEngine。这个错误的本质是客户端找不到引擎的通信管道通常意味着引擎没在运行。处理顺序确认 Docker Desktop 进程在运行且图标是绿的重启 Docker Desktop如果还不行退出 Docker Desktop 后任务管理器里把相关进程全部结束再重新启动最后考虑重置 Docker Desktop 到出厂设置Settings → Troubleshoot → Reset to factory defaults注意这会清掉所有镜像和容器。4.2 权限错误permission denied怎么解在 Linux 上跑docker命令报Got permission denied while trying to connect to the Docker daemon socket是因为当前用户不在 docker 组里。默认情况下只有 root 和 docker 组成员能访问 Docker 守护进程的 socket。解决办法sudo usermod -aG docker $USER执行完必须重新登录或者重启终端组权限变更才会生效。这一点很多人会漏改完就以为好了结果还是报错其实只是当前会话没刷新。新会话里执行docker ps不报错就说明好了。Windows 的 Docker Desktop 上一般不会遇到这个权限问题因为 Desktop 会做好用户映射。但如果你在 WSL2 里装了原生 Docker 引擎就会遇到同样的组权限问题处理方式一致。4.3 镜像下载慢或拉不下来先说结论这个问题九成是网络到官方仓库的链路问题不是 Docker 的问题。处理方向有这几个。第一配置镜像加速源前文讲过。第二用国内厂商提供的镜像仓库很多 Docker 镜像都有官方的国内同步版本把docker pull mysql:8.0换成对应的国内地址格式即可。第三如果某个特定镜像在公共仓库找不到可以搜索其他维护者上传的同名镜像。配置完镜像源要记得重启 Docker 引擎不然不生效。另外如果加速源本身挂了也会导致拉取失败多配几个备用的会更稳。4.4 容器启动就退出如何用日志定位容器起来瞬间就退出是新手最常见的困惑。一个容器能持续运行的前提是它有一个前台进程在跑。如果你docker run一个镜像它内部的主进程执行完就结束了容器自然就退出了。排查第一步docker ps -a docker logs container_iddocker ps -a能看到退出码docker logs能看到退出前的输出。退出码 0 通常是进程正常结束非 0 就要看日志找原因。比如 MySQL 因为密码没配报错、Nginx 因为配置文件语法错误退出都会在日志里写得明明白白。如果日志看不出东西用docker inspect container_id看State字段和Config字段能拿到退出码、启动命令、环境变量等完整信息。4.5 端口冲突与数据卷踩坑启动容器时报port is already allocated说明宿主机上那个端口被占了。先查是谁占了# Linux/macOS lsof -i :3306 # Windows netstat -ano | findstr 3306要么停掉占用端口的服务要么换个宿主机端口比如-p 13306:3306。数据卷的坑主要有两个。一是挂载目录权限在 Linux 上挂载宿主机目录进容器时容器内进程可能因为 UID/GID 不匹配导致无法写入表现为数据库启动失败。解决方式是调整宿主机目录的属主属组或者用命名卷而不是绑定挂载。二是挂载路径写错-v的冒号前后顺序是宿主机路径:容器路径写反了会得到一个空目录或者报错。4.6 磁盘空间被 Docker 吃光Docker 用久了本地会堆积大量无用镜像、停止的容器、悬空卷和构建缓存。一条命令查看占用情况docker system df清理docker system prune # 清理停止的容器、悬空镜像、悬空网络 docker system prune -a # 连同未被使用的镜像一起清 docker volume prune # 清理悬空数据卷-a和volume prune要谨慎确认没有你需要的数据卷再执行尤其是数据库的数据卷删了就没法恢复。我自己的习惯是每周跑一次docker system df占用超过 30GB 就清理一下别等到磁盘满。5. 生产环境部署时我踩过的坑与经验5.1 数据持久化是底线别拿容器当存储用 Docker 跑数据库数据持久化是绝对底线。我见过有人docker run起了 MySQL数据全在容器可写层某天手滑docker rm了容器数据就没了。命名卷是最省心的方式绑定挂载把宿主机具体目录挂进去更透明能看到文件在哪便于直接备份。生产环境我更倾向绑定挂载配合宿主机上的备份脚本把数据目录定期打包出问题能快速恢复。5.2 网络模式的选择bridge、host 与自定义网络Docker 默认用 bridge 网络容器通过虚拟网桥和宿主机通信外部访问需要端口映射。host 模式让容器直接用宿主机网络栈省掉了映射性能略好但端口容易冲突Linux 上常用Windows 和 macOS 上支持有限。自定义网络则适合多容器互通的场景docker network create mynet把相关容器都接进这个网络它们就能用容器名互相解析。Compose 默认就会为每个项目创建独立网络这也是它比手动docker run方便的原因之一。5.3 镜像体积优化多阶段构建的实际收益如果自己构建镜像多阶段构建是性价比最高的优化手段。核心思路是用一个体积大的构建镜像来编译代码然后把产物复制到一个精简的运行镜像里最终镜像只包含运行所需的最小依赖。FROM node:18 AS builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build FROM nginx:alpine COPY --frombuilder /app/dist /usr/share/nginx/html一个典型的前端项目单阶段构建出来的镜像可能几百兆多阶段构建后能压到几十兆。镜像小了传输快、启动快、攻击面也小。这是我在实际项目中感受最明显的优化之一。5.4 数据库与缓存容器化的注意事项MySQL 容器化要特别注意三个点字符集默认可能是 latin1中文会乱码启动参数里加--character-set-serverutf8mb4、时区前文说过、数据卷。Redis 则要注意持久化配置和内存上限redis-server --appendonly yes开启 AOF 能保证数据不怎么丢但性能有代价要按场景权衡。生产环境跑 Redis 还要设置maxmemory和淘汰策略避免内存被吃爆。这些参数都不是默认就合理需要按场景显式配置。5.5 学习路径建议从单容器到编排我给身边朋友的建议路径是这样的。先跑通hello-world和单个 MySQL、Redis 容器熟悉run、exec、logs、ps这套高频命令。然后学 Compose把手上项目的依赖堆成一个 compose 文件一键拉起。再往后是 Dockerfile 自己构建镜像理解分层和缓存机制。最后才碰 Kubernetes 这类编排系统。别一上来就冲 K8s容器基础没打牢K8s 学起来只会更痛苦。这个顺序是我自己踩过弯路之后的总结先把单机玩熟再考虑集群。还有一个长期受益的习惯把你常用的docker run和 Compose 文件都整理成模板放在自己的笔记或者代码仓库里。用的时候改改参数就行不用每次重新查命令。我到现在都留着一份常用容器模板跑 MySQL、Redis、Nginx、各类中间件全是复制改一改效率高得多。Docker 这东西真正难的从来不是命令本身而是把常见场景的方案沉淀成自己的资产下次遇到类似需求直接复用。