很多人学 Docker,卡住的地方往往不是命令,而是脑子里缺一张完整的地图。尤其当你打算走“虚拟机 + Docker”这条路时,Windows、Linux、容器三层关系叠在一起,很容易绕晕。这篇文章我想把整条路梳理成一条线:为什么在 Windows 上折腾 Docker 首选虚拟机方式、每一步在干什么、哪些坑我替你踩过了。适用对象是 Windows 为主力系统、又不想长期租云服务器,也不想被 Docker Desktop 各种启动报错折磨的新手。
我在本地环境里反复装过很多次这套组合,踩过“虚拟化支持检测不到”“虚拟机没有网络适配器”“容器一删数据全没”这类问题。下面这些内容不涉及高深理论,全部是可以直接落地的操作和思路,你照着走一遍,基本就能把这套组合跑通。
1. 先定方向:为什么我建议用“虚拟机 + Docker”而不是 Docker Desktop
先说结论:在 Windows 上跑 Docker 容器,本质上是在 Linux 内核上运行进程。Docker 依赖 Linux 内核里的 namespace、cgroups 等机制做隔离和资源限制,Windows 自己不具备这套内核能力,所以必须有一个 Linux 环境垫底。
Windows 用户面前有两条主流路线:
| 方案 | 底层实现 | 适合场景 |
|---|---|---|
| Docker Desktop | 基于 WSL2 或 Hyper-V | 常规开发,追求简单 |
| 虚拟机 + Linux + Docker | 基于 VMware/VirtualBox 等完整虚拟化 | 想学 Linux、需要和生产环境一致、不想被 Hyper-V 绑定 |
Docker Desktop 的安装体验确实好,但它的报错也出了名的多。最典型的就是你打开软件后看到一句:
Docker Desktop failed to start because virtualisation support wasn't detected这句话的直译是“检测不到虚拟化支持”,实际原因大概有这么几类:
- BIOS/UEFI 里没开启 Intel VT-x 或 AMD-V
- Windows 的“虚拟机监控程序平台”或 Hyper-V 功能没有启用
- 电脑装了 VMware,VMware 和 Hyper-V 同时抢虚拟化资源,导致 Docker Desktop 起不来
- 老 CPU 本身不支持嵌套虚拟化
我还遇到过另一种情况:公司电脑有安全策略,禁用 Hyper-V 和 WSL,你根本没有权限去“启用或关闭 Windows 功能”。这种环境下,Docker Desktop 基本就是废的,但 VMware 是用户态软件,不需要改系统组件,绕开了限制。
所以我的判断是:如果你已经在用 VMware,或者电脑配置一般、系统是 Windows 10/11 家庭版、想顺手把 Linux 命令也学了,那“虚拟机 + Linux + Docker”这条路更适合你。它多了一层性能开销,但换来的是干净、可控、和服务器完全一致的环境。容器的运行机制、端口映射、数据卷这些概念,在虚拟机里理解起来反而更直观,因为你一眼就能看到“容器在 Linux 内部”、“Linux 在虚拟机内部”这个物理边界。
虚拟机方式还有一个杀手级优势:快照。你在干净的 Linux 系统上打个快照,后面 Docker 配置搞乱了、软件装坏了,直接回滚到快照,一分钟还原,不用重装系统。
2. 三层架构一次看清:宿主机、虚拟机、容器的协作链路
这一节是整个思路的核心,我建议你花十分钟把这张图在脑子里刻下来。
整个环境分三层:
- 宿主机:你的 Windows 电脑,负责提供 CPU、内存、磁盘这些硬件资源
- 虚拟机:VMware 里跑起来的 Linux 系统,是一台完整的“假电脑”,有自己的内核、文件系统、网络栈
- 容器:Docker 在 Linux 里创建的隔离进程,共享 Linux 内核,但有自己的文件系统、进程空间、网络空间
用一个生活化的类比:Windows 是小区,虚拟机是一栋楼,楼里每个房间是一个容器。房间(容器)住着不同的人(应用),大家共享整栋楼的供水供电(Linux 内核),但每个房间有自己的锁和门(隔离机制)。你在房间门口装了一个传菜口(端口映射),外面的人按门铃点菜,菜就从传菜口递进去。
Docker 为什么比传统虚拟机轻量?关键就在“共享内核”这四个字上。传统虚拟机(比如 VMware 里的 Linux)需要模拟出一整套硬件,然后再起一个完整的操作系统,开机要一两分钟。而容器没有自己的内核,Docker 引擎直接调用宿主 Linux 内核的能力,启动一个容器通常只要几秒钟。
下面重点说网络这条链路,这是新手最容易懵的地方。
我在 VMware 里创建的虚拟机通常用 NAT 模式上网。在这个模式下,虚拟机和 Windows 处于一个私有网段,Windows 能访问虚拟机,虚拟机也能访问 Windows。假设虚拟机的 IP 是192.168.110.128,你在虚拟机里跑了 Docker 容器,容器内部启动了一个 Web 服务监听端口80。这时候你要访问它,需要分两步:
- 先做端口映射,比如
-p 8080:80,意思是将虚拟机的 8080 端口转发到容器的 80 端口 - 然后在 Windows 浏览器里访问
http://192.168.110.128:8080
很多人的困惑是:我明明在 Linux 上能访问localhost:80,为什么 Windows 上访问不了?因为你还没有明白端口映射这层关系。没有-p参数,容器里的端口对外界是完全不可见的,它只存在于容器的网络空间里。
数据也是这样。容器可以随时被删除、重建,如果数据存在容器内部,一删就没了。所以我们在运行容器时,用-v参数把 Linux 上某个目录挂载进容器,数据写到宿主机(这里是 Linux 虚拟机)上,容器销毁后数据还在。三层架构里,数据最终落在 Linux 虚拟机的磁盘上,而 Linux 虚拟机的磁盘又是一个文件存在于 Windows 上,所以从某种意义上说,数据是“穿透”了两层虚拟化,落在最底下的物理磁盘里的。
我用一张表总结三层各自的职责:
| 层级 | 职责 | 类比 |
|---|---|---|
| Windows 宿主机 | 提供硬件资源给 VMware | 小区物业管理 |
| Linux 虚拟机 | 提供 Linux 内核给 Docker | 整栋楼的基础设施 |
| Docker 容器 | 运行具体应用进程 | 楼里的房间 |
理解这层关系后,后面所有命令、报错、配置都有了解释模板:容器内部是独立的,容器之间是隔离的,要访问就得开端口,要持久化就得挂数据卷。
3. 从 VMware 到 Linux:环境准备里那些“当时没注意、后来全是坑”的选项
环境准备阶段看起来简单,其实细节很多,我按顺序走一遍,把容易踩的坑标出来。
3.1 先确认 CPU 虚拟化已经开启
不管你是 Intel 还是 AMD,在装 VMware 之前,先进 BIOS/UEFI 确认 CPU 虚拟化已经打开。
- Intel 平台:找
Intel VT-x或Virtualization Technology,设为Enabled - AMD 平台:找
SVM Mode,设为Enabled
判断是否已开启,最简单的办法是打开 Windows 的任务管理器,切到“性能”标签,看“虚拟化”那一行是否显示“已启用”。
这一步没做好的后果,轻则 VMware 提示“无法运行虚拟机,请确认 BIOS 中开启了虚拟化”,重则安装 Linux 的过程中直接蓝屏。我在贴吧里看到很多人问“虚拟机安装Linux蓝屏”,十有八九是这一步没搞定。
3.2 VMware 版本与 Linux 发行版选择
VMware Workstation 现在个人使用是免费的,直接去官网下载最新版,装 17 的 Pro 版本就行。安装过程一路下一步,注意取消勾选“安装后自动检查更新”这一类选项,能省不少心。
Linux 发行版我推荐新手用 Ubuntu Server 的长期支持版本,目前稳定可选 22.04 LTS 或 24.04 LTS。原因很简单:社区资料最多,Google 和各类论坛里搜任何报错,几乎都能找到 Ubuntu 的答案。CentOS Stream 和 Rocky Linux 也完全能用,但教程资料相对少一些,如果遇到问题排查成本会高。
在虚拟机设置里,我建议给的配置是:
| 配置项 | 推荐值 | 说明 |
|---|---|---|
| CPU | 2 核以上 | 少于 2 核,Docker 构建镜像时很难受 |
| 内存 | 4 GB 起步,建议 8 GB | 后面要跑 GitLab 这类吃内存的应用,4 GB 是底线 |
| 磁盘 | 40 GB 以上,动态分配 | 镜像、容器日志、数据卷都会占用空间 |
| 网络 | NAT 模式 | 最简单,不需要额外配置 |
3.3 Linux 安装过程中的关键选择
用 Ubuntu Server 的 ISO 启动后,安装界面有几个地方要特别注意。
第一是网络配置。安装程序的默认 DHCP 会给你一个动态 IP,但 DHCP 分配的地址可能在重启后变化。后面你打包镜像、访问 Web 服务都要用 IP,地址变了会很烦。建议在安装时选择手动配置静态 IP,或者装完后用 netplan 改。
第二是“SSH server”这个组件一定要勾选。装完后你用 Windows 上的 MobaXterm、Xshell 或者终端直接 SSH 连进虚拟机操作,比在 VMware 窗口里敲命令舒服得多,复制粘贴也方便。
第三是安装过程中会问要不要装“Ubuntu Pro”插件、要不要加入环境统计之类的,全部跳过即可。
装完之后建议先把 SSH 服务启起来,然后在外面的终端里连一下:
sudo apt update sudo apt install -y openssh-server sudo systemctl enable --now ssh如果你发现虚拟机没有网络,或者 VMware 的 NAT 服务异常,优先检查系统的两个服务:VMware NAT Service和VMware DHCP Service。Windows 下按Win + R输入services.msc,找到这两个服务,确认是“正在运行”,如果不是,右键启动。这也是“虚拟机没有网络适配器”这个问题最常见的解法。
3.4 快照:装机之后的第一件事
系统装好、SSH 能连接、apt update 能跑通之后,我建议你立刻做一件事:关闭虚拟机,然后在 VMware 里“拍摄快照”。
快照相当于给整个虚拟机拍了一张照片,之后无论你在系统里怎么折腾,快照都能带你回到这个干净状态。我后面装 Docker、跑容器、改配置,每次遇到不可逆的修改前都会先打个快照。这个习惯帮我省了无数次重装系统的时间。
4. Docker 引擎安装与镜像加速:跑通 docker run 之前的必修课
Linux 系统就绪后,下一步就是在里面装 Docker 引擎。
4.1 安装方式选择
Docker 官方提供了一个一键安装脚本,但国内网络访问官方源经常很慢,甚至直接超时。我建议用国内镜像源来装,稳妥可靠。
方法一:使用阿里云镜像源安装。
先装依赖:
sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release添加阿里云的 Docker 源:
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null然后安装:
sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin方法二:如果你相信自己网络够快,也可以用官方脚本:
curl -fsSL https://get.docker.com | bash脚本会顺便把docker compose插件也装好,省去手动安装的麻烦。但脚本在国内环境经常卡住,如果你看到半天没反应,果断 Ctrl+C,换回镜像源方案。
4.2 配置镜像加速器
Docker 安装好后,默认从 Docker Hub 拉取镜像,国内访问速度堪忧。打开配置文件:
sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://docker.m.daocloud.io"] } EOF镜像加速地址有很多免费的,比如阿里云需要去控制台申请专属地址,DaoCloud 这个公共地址实测可用性也不错。如果你有阿里云账号,建议用阿里云的镜像加速器,在容器镜像服务控制台就能看到专属地址。
配置完后重启 Docker:
sudo systemctl daemon-reload sudo systemctl restart docker4.3 用户组配置与验证
默认情况下,执行 docker 命令需要 sudo,非常别扭。把当前用户加入 docker 组,重新登录后就能免 sudo 了:
sudo usermod -aG docker $USER然后重新登录(或者直接newgrp docker让组立即生效)。
验证是否装好,先看客户端和服务端状态:
docker version关键看Server段有没有正常显示版本号。如果只显示 Client 没有 Server,大概率是服务没起来,执行:
sudo systemctl enable --now docker然后再跑一个测试容器:
docker run hello-world能看到一段“Hello from Docker!”的说明文字,就算正式跑通了。
4.4 一个容易忽略的点:dockerd 启动失败
Docker 服务起不来,最常见的原因是daemon.json里写入了非法的 JSON。比如镜像加速地址写错、端口号带引号、逗号放错位置。排查思路是看日志:
sudo journalctl -u docker --no-pager | tail -50如果日志指向daemon.json配置解析错误,把文件改回来,重启服务即可。这种错误很隐蔽,因为docker version的 Client 部分照常显示,你会有一种“Docker 装好了”的错觉,直到运行容器才发现服务端根本没起来。
5. 镜像、容器、数据卷、端口映射:先在脑子里建好这份心智模型
前面几节已经把环境和引擎都装好了,但不少同学到这一步仍然觉得 Docker 很抽象。这一节我用最直白的方式,把四个最核心的概念讲透。
5.1 镜像和容器:模板与实例的关系
镜像(Image)是静态的、只读的模板,容器(Container)是镜像运行起来后的实例。
你可以把镜像理解成一个游戏光盘,容器就是用这张光盘启动的游戏会话。同一个镜像可以启动多个容器,互不干扰。但要注意:容器是可变的,你在容器里安装了软件、修改了文件,这些改动只存在于这个容器里,镜像本身不会被改变。如果用这个镜像再启动一个新容器,新容器还是原来的初始状态。
日常操作命令:
| 命令 | 用途 | 说明 |
|---|---|---|
docker pull nginx:1.25 | 拉取镜像 | nginx是镜像名,1.25是标签(版本) |
docker images | 查看本地镜像 | 列出镜像名、标签、大小 |
docker run | 创建并启动容器 | 最核心的命令,带一堆参数 |
docker ps | 查看运行中的容器 | 加-a可以看到包括已停止的全部容器 |
docker exec -it 容器名 bash | 进入容器内部 | 容器名可以用容器 ID 前几位替代 |
docker logs -f 容器名 | 查看容器日志 | -f表示持续跟踪输出 |
docker stop/start/restart | 停止/启动/重启容器 | 不删除容器 |
docker rm 容器名 | 删除容器 | 加-f强制删除运行中的 |
docker rmi 镜像名 | 删除镜像 | 先删除依赖该镜像的容器 |
一个完整的实战示例,用 Nginx 跑一个网站:
docker run -d \ --name my-web \ -p 8080:80 \ -v /data/web:/usr/share/nginx/html \ nginx:1.25这个命令是接下来所有容器的样板,拆开看每一步:
-d:后台运行--name my-web:给容器起个名字,方便后续操作-p 8080:80:端口映射,Windows 访问虚拟机IP:8080时,流量进入容器的 80 端口-v /data/web:/usr/share/nginx/html:数据卷挂载,Linux 的/data/web目录对应容器里的网站目录
启动后,你在 Linux 里往/data/web放一个index.html,Windows 浏览器访问http://虚拟机IP:8080就能看到内容。这个例子同时涉及了端口映射和数据卷,是理解 Docker 工作方式最好的入门案例。
5.2 端口映射:冒号两边的顺序不能反
-p参数的格式是 `宿主机端口:容器端口。前半部分是你在宿主机(Linux虚拟机)上要监听的端口,后半部分是容器内服务实际监听的端口。
比如 Nginx 容器内部默认监听 80 端口,你希望外部通过 8080 访问,所以写成-p 8080:80。如果你写成-p 80:8080,意思就变成了访问虚拟机 80 端口转发到容器 8080,Nginx 没在 8080 上监听,结果当然是访问不了。
我在排查问题的时候,第一件事就是看docker ps的PORTS那一列。如果显示0.0.0.0:8080->80/tcp,说明映射正确;如果什么都没显示,说明容器启动时没加-p参数。
5.3 数据卷:容器可以随便删,数据不能丢
容器的文件系统是临时的,每次容器重建后,里面所有改动都会消失。这是新手最常踩的坑:跑了几天数据库,某天手一抖把容器删了,数据全没了。
解决方法是把数据放到宿主机上。常见的两种挂载方式:
- 绑定挂载(bind mount):
-v /data/mysql:/var/lib/mysql,把宿主机物理目录挂进去,最直观,方便备份 - 命名卷(named volume):
-v mysql-data:/var/lib/mysql,由 Docker 管理目录,数据在/var/lib/docker/volumes/下
我建议新手优先用绑定挂载,因为你知道数据确切放在哪里,备份、迁移都方便。
数据卷还有一个好处:容器升级版本时,新容器直接复用旧数据卷,相当于“无损升级”。比如 MySQL 从 8.0 升级到 8.1,你只需要拉新镜像、用同一个数据卷启动新容器。
5.4 容器网络:容器之间怎么通信
默认情况下,每个容器有独立的 IP,属于 Docker 的默认 bridge 网络。容器之间可以互相访问,但地址是动态分配的,不固定。
更推荐的做法是创建一个自定义网络,然后让容器通过容器名互相访问:
docker network create my-net docker run -d --name mysql8 --network my-net -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0 docker run -d --name adminer --network my-net -p 8080:8080 adminer在同一个自定义网络里,mysql8这个容器名会解析成对应的 IP,其他容器直接通过名字访问就可以了。这就避免了手动查 IP 的麻烦。
6. 四个典型落地方案:MySQL 8.0、Redis 主从、GitLab、青龙依赖管理
概念讲完,我来跑四个实际的方案。这几个案例是我自己装过很多遍的,覆盖了数据库、缓存、协作平台、定时任务等常见需求,照着做就能跑起来。
6.1 MySQL 8.0:数据库的持久化与远程访问
MySQL 8.0 的启动命令:
docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=你的密码 \ -v /data/mysql:/var/lib/mysql \ -v /data/mysql-conf:/etc/mysql/conf.d \ --restart=always \ mysql:8.0几个参数分别解释:
MYSQL_ROOT_PASSWORD:设置 root 密码。首次启动容器时读取,之后修改不影响已有密码。/var/lib/mysql是 MySQL 存储数据的目录,挂载出来防止容器删除丢数据。--restart=always:机器重启后容器自动启动,这个参数对长期运行的服务非常重要。
MySQL 8.0 与旧版有一处明显差异:默认的认证插件改成了caching_sha2_password。老版本的 Navicat、MySQL Workbench 连接时可能报“Authentication plugin 'caching_sha2_password' cannot be loaded”,解决办法是进入容器改认证方式:
docker exec -it mysql8 mysql -uroot -pALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '你的密码'; FLUSH PRIVILEGES;还有一个高频问题:容器里的 MySQL 默认 root 只允许 localhost 登录,你在 Windows 上用客户端远程连接时会提示权限被拒绝。需要创建一个允许任意主机访问的用户:
CREATE USER 'app'@'%' IDENTIFIED BY '密码'; GRANT ALL PRIVILEGES ON *.* TO 'app'@'%'; FLUSH PRIVILEGES;中文乱码的预防,建议在挂在出来的conf.d目录里加一个配置文件utf8mb4.cnf:
[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci然后重启容器:docker restart mysql8。
6.2 Redis 主从:最简洁的高可用方案
Redis 主从可以直观地体现“一套镜像起多个容器”的思路。先创建一个自定义网络让主从节点可以互访:
docker network create redis-net启动主节点:
docker run -d \ --name redis-master \ --network redis-net \ -p 6379:6379 \ -v /data/redis-master:/data \ redis:7 redis-server --appendonly yesredis-server --appendonly yes是容器启动时要执行的命令参数,开启 AOF 持久化,数据默认写在容器的/data目录,也就是我们挂载出来的/data/redis-master。
启动从节点:
docker run -d \ --name redis-slave \ --network redis-net \ -p 6380:6379 \ -v /data/redis-slave:/data \ redis:7 redis-server --appendonly yes --replicaof redis-master 6379关键就俩单词:--replicaof redis-master 6379,让这个从节点复制redis-master这个主节点。因为两个容器在同一个自定义网络里,直接用容器名就能找到对方。
验证主从状态:
docker exec -it redis-slave redis-cli info replication重点看role:slave和master_link_status:up这两行。如果显示up,说明主从已经通了。主从验证完,顺手给它设个密码更稳妥,--requirepass 你的密码加到主节点配置里,从节点则要加--masterauth 主节点密码。
6.3 GitLab:内存大户和端口冲突
GitLab 是我用 Docker 装过的应用里最重的一个,它内置了 PostgreSQL、Redis、Nginx 等一堆组件,单机内存建议至少 4 GB。如果你虚拟机只分了 2 GB,启动会非常卡,甚至直接启动失败。
启动命令:
docker run -d \ --name gitlab \ -p 8443:443 \ -p 8081:80 \ -p 8022:22 \ -v /data/gitlab/etc:/etc/gitlab \ -v /data/gitlab/log:/var/log/gitlab \ -v /data/gitlab/data:/var/opt/gitlab \ --restart=always \ gitlab/gitlab-ce:latest这里特别注意端口映射的选择。GitLab 内部默认用 80 端口提供 Web 服务,但很多环境里 80 被系统进程占用,所以我把宿主机端口改成了 8081。启动后你在 Windows 浏览器里访问http://虚拟机IP:8081。
GitLab 第一次启动很慢,可能要等两三分钟才能打开页面,这个阶段别急着下结论说它坏了,可以用docker logs -f gitlab看启动进度。首次访问时,root 用户的初始密码在容器里:
docker exec -it gitlab cat /etc/gitlab/initial_root_password这个密码文件只在第一次启动时生成,一定要趁早保存。
6.4 青龙面板:一次“依赖管理”引发的隔离性教学
青龙面板是一个定时任务管理平台,轻量、界面友好,很多人在虚拟机上装它就是想让定时脚本有个统一的运行环境。它的安装命令:
docker run -d \ --name qinglong \ -p 5700:5700 \ -v /data/ql/config:/ql/config \ -v /data/ql/log:/ql/log \ -v /data/ql/db:/ql/db \ -v /data/ql/scripts:/ql/scripts \ --restart=always \ whyour/qinglong:latest装完后访问http://虚拟机IP:5700就能看到登录页面。
这里我想重点说“依赖管理”这个事,因为它是我见过新手最容易卡住的地方。很多人把脚本放进青龙后,执行时报错提示找不到模块,比如Cannot find module 'axios'或者python: command not found,第一反应是去 Linux 宿主机上装依赖,但装完发现还是没用。
这个现象的背后,正是 Docker 的隔离性在起作用。脚本是在容器内部运行的,容器有自己的文件系统,你看不到也访问不到宿主机上装的东西。所以在青龙后台,正确做法是在“依赖管理”功能里添加需要安装的依赖,或者在容器内部执行安装命令:
docker exec -it qinglong bash # 进入容器后,根据运行语言执行: # 如果跑的是 Node.js 脚本: npm install axios -g # 如果跑的是 Python 脚本: pip3 install requests当你理解了“容器内部和宿主机是两套环境”这个原理,这类问题就再也不会困扰你了。这也解释了为什么我们用-v把配置文件、脚本目录挂载出来:因为容器随时会重建,只有把数据放在宿主机上才能长久保存。
7. 虚拟机方式最常见的坑与排查链路:从报错到根因
最后把我在虚拟机上跑 Docker 遇到的高频问题汇总一下,每个问题都给一个完整的排查思路,而不是只给一个答案。
7.1 常见问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| Docker Desktop 报 virtualisation support wasn't detected | BIOS 未开启 VT-x/AMD-V、Hyper-V 未启用 | 进 BIOS 开启虚拟化;控制面板启用“虚拟机监控程序平台” |
| VMware 虚拟机没有网络适配器 | VMware NAT/DHCP 服务停止 | services.msc 中找到 VMware NAT Service、VMnetDHCP 并启动 |
| 虚拟机安装 Linux 蓝屏 | CPU 虚拟化未开启、VMware 版本过老 | BIOS 开启虚拟化,更新 VMware 到 17 版本 |
| docker 命令提示权限不足 | 用户不在 docker 组 | sudo usermod -aG docker $USER后重新登录 |
| 容器能启动但网页打不开 | 端口映射不正确、容器内服务监听地址不对 | docker ps看 PORTS;docker exec进容器确认服务监听 0.0.0.0 |
| MySQL 容器删了再启,数据没了 | 没挂载数据卷 | 启动时加-v /data/mysql:/var/lib/mysql |
| 磁盘被占满 | 容器日志、无用的镜像和构建缓存堆积 | docker system prune -a一键清理 |
| 容器重启后自动启动不了 | 没加--restart=always | 更新容器:docker update --restart=always 容器名 |
7.2 完整排查链路一:Windows 访问不了虚拟机里的网页
这是最高频的问题,我给你走一遍我的排查顺序。
第一步,确认服务真的起了。在 Linux 虚拟机里执行:
curl http://localhost:容器端口如果 curl 能拿到 HTML 响应,说明容器和 Docker 都没问题,问题出在链路的下游。如果 curl 失败,先看docker ps确认容器在运行,再看docker logs 容器名找应用报错。
第二步,确认端口映射正确。
docker ps | grep 容器名看 PORTS 列是不是0.0.0.0:8080->80/tcp这样的格式。如果没有->,说明容器启动时忘了加-p,需要重建容器。
第三步,确认 Linux 的防火墙没有拦截。Ubuntu 默认可能是ufw开启状态:
sudo ufw status如果Status: active,就把对应端口放开:
sudo ufw allow 8080/tcp第四步,确认 VMware 网络模式。如果虚拟机是 NAT 模式,Windows 访问虚拟机 IP 没问题;如果你改成了桥接模式,虚拟机的 IP 可能和 Windows 不在同一个网段,需要先确认 IP。
这个“从内往外、逐层排查”的思路,比任何一键修复工具都可靠。
7.3 完整排查链路二:Docker 服务起不来
另一个高频问题是执行任意 docker 命令都报“Cannot connect to the Docker daemon”。
第一反应:
sudo systemctl status docker如果显示Active: failed,看日志:
sudo journalctl -u docker --no-pager | tail -50日志里最常出现的是:
failed to load listeners: listen tcp 0.0.0.0:2375: bind: address already in use这说明 2375 端口被其他进程占了,常见的原因是之前手动启动过 dockerd,或者有残留进程。处理办法:
sudo pkill dockerd sudo systemctl start docker如果日志提示:
unable to configure the Docker daemon with file /etc/docker/daemon.json那就是daemon.json写坏了,检查 JSON 格式,重点看是不是多了或少了一个逗号、引号是不是成对的。修复后再重启:
sudo systemctl daemon-reload sudo systemctl restart docker7.4 磁盘空间:虚悬镜像与构建缓存
跑一段时间 Docker 后,磁盘空间会很紧张。/var/lib/docker目录只增不减。执行:
docker system df会看到镜像、容器、构建缓存各占多少空间。清理套路分三步:
docker system prune -a:清理所有未使用的镜像、停止的容器、悬空数据卷和构建缓存。注意-a会删除所有没被容器引用的镜像,如果这些镜像你还需要,别加-a,只执行docker system prune。docker builder prune -f:单独清理构建缓存,这个通常能释放好几个 GB。如果你启了带日志的应用,日志文件也可能很大。看看:
du -sh /var/lib/docker/containers/*找到大文件后,可以限制容器日志大小。最佳做法是在/etc/docker/daemon.json里加上:
{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }然后重启 Docker,新容器就会自动遵守日志上限。这个配置我建议你从第一天就加上,省得以后手动清日志。
最后说一个我个人的使用习惯。我会在虚拟机里建一个/data目录,所有容器的数据卷都挂在/data/应用名下面,这样备份、迁移、清理非常直观。每次新建容器前,先在脑子里过一遍:这个容器的数据放哪里、端口怎么映射、要不要随系统启动、和哪些容器要通信。想清楚再敲启动命令,比反复尝试改配置高效得多。
一开始你可能觉得这些流程繁琐,但等你按这套思路跑通 MySQL、Redis、GitLab 这些应用之后,再回头看 Docker 的文档就会感觉顺眼很多——因为你已经知道它的运行逻辑,剩下的都只是参数细节而已。