一段时间里我和同事反复争论过“虚拟机里到底能不能正经跑 Docker”这件事。支持的一方觉得虚拟机快照方便、环境干净反对的一方则总拿性能损耗和网络复杂性说事。直到我真的拿一台全新的 Ubuntu 虚拟机从零开始完整装了一遍 Docker才发现真正麻烦的从来不是 Docker 本身而是那些你没预料到的前置条件网络通不通、软件源健不健康、磁盘够不够、权限对不对。本文就是那份完整实操记录照着我这套流程走能帮你省掉不少试错时间。本教程以 Ubuntu 22.04 LTS 为例从空白虚拟机到 Docker 正常拉镜像、跑容器、做排错整个过程会尽量讲清每一步为什么这么做以及哪些坑是我亲自踩过之后才填平的。1. 装之前先想明白虚拟机里跑 Docker 到底图什么很多人一上来就敲sudo apt install docker.io装完之后才发现问题是连环的。虚拟机里跑 Docker和物理机上跑 Docker看起来命令一模一样实际上环境差异会直接影响踩坑概率。先说结论虚拟机里跑 Docker 完全是可行方案尤其适合学习、测试、验证配置这类场景。我平时做实验时最喜欢用的方式就是保留一个干净的 Ubuntu 虚拟机快照比如刚装完系统、网络通畅、软件源已经换好的状态。之后不管怎么折腾容器只要恢复快照几分钟内就能回到“起点”。这种试错成本是物理机给不了的。但虚拟机也有天然短板。最明显的是嵌套虚拟化导致的性能折损。Docker 本身不依赖虚拟机内核它用的是宿主机的 Linux 内核能力所以虚拟机里跑容器时核心损耗不在于“容器里跑业务”而在于虚拟化层对磁盘 I/O 和网络吞吐的额外开销。对普通实验项目影响很小但如果你的目标是压测高并发服务我建议还是用物理机或者云实例。还有一个容易被忽略的点你是用什么软件创建的虚拟机。不同虚拟化产品对网络模式的处理不完全一样最常见的是 NAT 模式。这个模式下虚拟机可以主动访问外部网络但外部设备主动访问虚拟机内的容器端口会比较曲折。桥接模式则更像一台独立主机容器端口映射后局域网内其他机器也能直接访问。如果你后续想体验更真实的服务发布流程尽量把网络模式选成桥接。1.1 虚拟机选型必须先定Ubuntu 系统本身又分成桌面版和服务器版。装 Docker 这件事本身没有任何区别但我更推荐服务器版。桌面版默认带图形界面占内存和磁盘而且图形界面相关的后台进程会在虚拟机上白白消耗资源。服务器版干净、轻量SSH 连上去操作就行和线上服务器环境也更接近。如果手里只有 Windows 主机比较常见的虚拟化工具是 VMware Workstation 或者 VirtualBox两者对 Ubuntu 的支持都很成熟选哪个看个人习惯。我常用 VMware主要心得是它的“快照”恢复速度更快而且支持把虚拟机的磁盘一次性扩容不用额外创建新磁盘。VirtualBox 胜在免费开源适合预算敏感或者完全自学的场景。安装 Ubuntu 时记得分给系统足够的磁盘空间虽然 20GB 也能跑但 Docker 镜像和容器日志会持续增长我踩过磁盘爆满的坑这是后话。1.2 硬件底线的三个硬指标给虚拟机分配多少资源直接决定了 Docker 体验。我这边测试机分配了 2 核 CPU、4GB 内存、50GB 磁盘同时跑 Nginx、Redis、MySQL 三个容器仍有余量。如果是纯练习最低建议如下资源项最低建议推荐配置说明CPU1 核2 核单核跑多个容器时调度争抢明显内存3GB4GB 以上容器并开会吃内存Swap 不能当主内存用磁盘30GB50GB 以上镜像分层加容器日志增长快务必预留空间内存这块要着重说。Docker 本身占用的内存不大真正吃内存的是你用 Docker 跑的镜像和应用。比如 MySQL 随便就是 500MB 起Java 应用堆内存 1GB 很正常。所以分内存时宁可多给也不要让虚拟机动不动就触发 OOM。如果只有一个 8GB 的宿主机你强行分 6GB 给虚拟机那宿主机自己也会很吃力。另外确认一下虚拟机是否开启了硬件虚拟化辅助功能。现在的虚拟化软件一般默认开启但如果你在安装 Ubuntu 时报错或者机器启动明显缓慢可以检查虚拟机设置里的 VT-x/AMD-V 是否开启。它虽然不是 Docker 运行的必要条件但 Ubuntu 内核和一些嵌套场景会受益。2. 安装前的系统准备我见过太多人跳过准备工作直接装 Docker最后在软件源、依赖包这些环节反复折腾。这一节花不了五分钟但能帮你把大半的安装问题摁死在萌芽阶段。2.1 先更新软件源与系统组件新装好的 Ubuntu 系统软件源索引一般停留在安装镜像出厂状态。直接apt install报“软件包未找到”的几率不低因为索引里根本没有你要的包。所以第一步必须刷新索引sudo apt update这个命令会从软件源拉取最新的软件包列表。执行时如果看到很多Err或者长时间卡住说明网络或者软件源配置有问题。先别急着继续优先解决源的问题。接着做一次全量升级。这一步可做可不做但我个人强烈建议做。因为 Docker 依赖的内核模块和 iptables 相关组件在旧内核版本上偶尔会出现兼容性问题。把系统包升级到当前源里的最新状态可以降低后续异常概率sudo apt upgrade -y如果系统打印需要重启先重启虚拟机再继续。带着一个没更新完内核的系统去装 Docker后续出问题排查起来会非常痛苦。2.2 补齐依赖工具装 Docker 官方源之前系统里必须先有几个基础工具。Ubuntu 最小化安装默认不会全带sudo apt install -y ca-certificates curlca-certificates是 CA 证书集合所有 HTTPS 请求都要靠它来验证服务器身份。curl用来下载密钥文件和测试网络。这两个包几乎是 Linux 服务器的标配装了不吃亏。某些教程还会让你装apt-transport-https、software-properties-common、gnupg这些包。实际在较新的 Ubuntu 上apt-transport-https已经集成进 apt 本身了不装也没关系。gnupg用于校验 GPG 密钥如果是全新系统可以顺手装上避免后续导入密钥时报“gpg 命令不存在”sudo apt install -y gnupg这块建议一次装齐别偷懒sudo apt install -y ca-certificates curl gnupg lsb-releaselsb-release可以用来查看当前系统的发行版代号后续配置软件源时会用到装一个也不多余。2.3 网络连通性检查这一步是很多人忽略的重灾区。虚拟机网络不通或者 DNS 解析异常会导致后续下载 Docker 密钥、拉取镜像全部失败而且报错信息多种多样极其容易迷惑人。在继续之前先确认虚拟机能不能访问外网。最简单的做法是 ping 一个公共 DNS 地址ping -c 4 8.8.8.8能收到回包说明物理链路通。再检查 DNS 解析getent hosts ubuntu.com如果返回了 IP 地址说明 DNS 正常。两个检查都通过才能真正放心去下载外部软件包。提示如果物理链路通但 DNS 解析失败优先检查虚拟机的网络配置和软件源里的地址是否可用。不要一上来就重装系统。校验一下当前系统的版本代号因为后面配置 Docker 源时要用lsb_release -a记住输出里的 Codename比如 22.04 LTS 对应的是 jammy。后续软件源地址里会用到这个代号。3. 安装 Docker 的完整过程系统准备就绪之后就可以进入正题了。这里说的安装方式走的是 Docker 官方 apt 仓库而不是直接apt install docker.io。两者最大的区别在于官方仓库里的docker-ce是社区版最新引擎更新节奏快系统自带的docker.io通常是老版本功能偏旧。既然要学直接用官方源才是正确姿势。3.1 添加 Docker 官方软件源先创建用于存放官方 GPG 密钥的目录并下载密钥。GPG 密钥的作用是验证软件包来源防止软件源被篡改sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod ar /etc/apt/keyrings/docker.asc然后把 Docker 官方仓库写入 apt 源列表。注意这里要用上一步查到的发行版代号我这边是 jammyecho deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release echo $VERSION_CODENAME) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null这行命令看起来长其实拆开理解很简单arch$(dpkg --print-architecture)自动识别当前 CPU 架构signed-by指定刚才下载的密钥文件后面的$VERSION_CODENAME动态读取系统版本代号。这样配置兼容性最好不需要每次手动改。看到这里有人会问为什么不直接用add-apt-repository那个命令确实能加源但它生成的源文件格式在某些场景下不太标准而且需要额外安装software-properties-common。直接写源文件更透明、可控排查问题也方便。添加完成后再刷新一次软件源索引sudo apt update如果这一步没有报错说明 Docker 官方源已经成功接入。可以顺手检查一下能不能看到 Docker 相关的候选版本apt-cache policy docker-ce正常会显示一个候选版本号即便还没安装。这一步能印证源配置是否正确比直接蒙头安装靠谱得多。3.2 安装 Docker 引擎接下来安装 Docker 本体。现代 Docker 引擎拆成了若干组件最简单的方式是一次装全家桶sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin各组件作用如下组件作用docker-ceDocker 守护进程及核心引擎docker-ce-cli命令行客户端也就是docker命令本身containerd.io容器运行时管理组件负责真正拉起容器进程docker-buildx-plugin增强版镜像构建插件支持多平台构建docker-compose-pluginCompose 插件用 YAML 编排多个容器如果不想一次装这么多只装前三个也能跑普通容器。但 buildx 和 compose 都是日常高频工具反正迟早要装不如一步到位。安装过程如果有交互提示选默认项即可。看到Setting up docker-ce ...之类的输出说明组件都安装完成了。3.3 启动服务并设置开机自启安装完成后Docker 服务不会自动启动需要手动开启sudo systemctl start docker sudo systemctl enable dockersystemctl start是立刻启动systemctl enable是设置开机自启。两个命令建议连着执行不然虚拟机重启后 Docker 又停在那里需要手动敲命令的体验太差了。确认服务状态sudo systemctl status docker输出里有Active: active (running)就说明守护进程已经正常运行。如果你看到failed大概率是前面某一步没做完先回头检查依赖工具是否装齐。此时可以看一下 Docker 版本确认安装成功docker version这个命令会分别显示客户端和服务端的版本。如果服务端信息都没打出来说明守护进程有问题需要排查。3.4 免 sudo 使用 Docker默认情况下执行docker命令需要 root 权限否则会报permission denied或者提示连不上 socket。老老实实每次加sudo虽然能用但总敲显得麻烦而且某些脚本里不能频繁用 sudo。解决办法是把当前用户加入 docker 用户组sudo usermod -aG docker $USER注意这一步执行完之后当前终端会话不会立即生效必须重新登录或者执行以下命令刷新组权限newgrp docker更稳妥的做法是退出 SSH 重新连接一次。之后再直接敲docker ps就不会再遇到权限问题了。注意把用户加入 docker 组意味着这个用户拥有等同于 root 的容器控制权。网上有人提醒过这个安全边界如果是多人共用的虚拟机要慎重决定是否开放。4. 验证安装与容器基本操作装完之后先别急着部署业务容器先做两个小实验确认整套环境能跑通。这两步都很便宜失败了也不心疼。4.1 用 hello-world 做首次验证Docker 官方提供了一个最简镜像hello-world只有几百 KB专门用来验证安装是否正常docker run hello-world首次运行会先从远程仓库拉取这个镜像然后启动一个临时容器打印欢迎信息。如果看到一段英文提示说明整个链路已经通了。注意观察拉取镜像的输出速度如果非常慢或者直接超时说明网络环节存在问题可以对照后面第 5 节的排查方法处理。看到Hello from Docker!那一段输出以后再用一条命令确认镜像确实已经落到本地docker images列表里出现hello-world就说明本地镜像仓库正常。这一步很简单但它是区分“安装成功”和“真的能用”的分水岭。4.2 真正跑一个可交互容器hello-world只是验证它跑完就退出了。我们再跑一个能保持运行、能交互的容器比如 Ubuntu 容器docker run -it --name test-ubuntu ubuntu bash这行命令会拉取完整的 Ubuntu 镜像然后进入容器内部的 bash 交互界面。你可以试试在里面执行cat /etc/os-release看看当前容器的系统版本同样来自 Ubuntu但和宿主机内核无关。退出交互界面时在容器里直接敲exit即可。此时容器已经停止运行但并没有删除。查一下状态docker ps -a能看到刚才的容器状态是Exited (0)。这就是 Docker 的一个基本逻辑容器默认不是用完即焚的除非加--rm参数。所以日常练习时建议习惯性加--rm避免堆积一堆停止状态的容器占用磁盘。4.3 常用命令速查练手阶段需要记住的命令其实不多我整理了一份高频速查表命令作用docker ps查看正在运行的容器docker ps -a查看所有容器含已停止docker images查看本地镜像列表docker pull 镜像名手动拉取镜像docker run 镜像名创建并启动容器docker exec -it 容器ID bash进入运行中容器的交互终端docker logs 容器ID查看容器日志docker stop 容器ID停止容器docker rm 容器ID删除容器docker rmi 镜像名删除镜像docker system df查看 Docker 占用的磁盘空间这些命令用熟了日常基本的容器操作就够。更复杂的编排任务再交给 Compose 处理后面会提到。5. 常见问题排查与避坑实录这一节是我最想写的内容。Docker 安装失败的报错千奇百怪但根因往往就那么几个。我把自己实际遇到的和身边人常踩的坑都整理成了速查表跟着对照排查会快很多。5.1 安装报错软件源 404 或候选版本为空apt update时如果 Docker 源报 404最常见的原因是系统版本代号和源地址不匹配。比如你实际装的是 24.04但命令里写死了 jammy。解决思路确认lsb_release -cs的实际输出并删掉写错的源文件重新生成。还有一种情况是apt-cache policy docker-ce看不到候选版本。排查顺序是先确认源文件内容正确然后确认密钥文件存在且可读。密钥文件权限不对apt 会静默忽略这个源表现就是查不到候选包。5.2 拉取镜像超时或卡住这个问题十有八九是网络链路或 DNS 的问题和 Docker 本身没关系。排查路径先ping一个公共地址确认外网通不通再用getent hosts registry-1.docker.io确认镜像仓库域名能否解析。如果解析失败修改虚拟机的 DNS 配置后重启网络。如果虚拟机在公司内网环境优先确认内网默认的镜像仓库地址把对应配置写进 Docker 配置文件即可。这里多说一句不要用来源不明的“一键加速脚本”很多只是往配置文件里塞了不维护的第三方地址出了问题反而更难排查。5.3 权限与 socket 错误执行docker命令报Cannot connect to the Docker daemon at unix:///var/run/docker.sock通常是两种可能Docker 服务没启动用sudo systemctl start docker解决。当前用户不在 docker 组里按 3.4 节操作后重新登录。这个报错信息很有误导性很多人第一反应是重装 Docker其实系统检查两步就能解决。5.4 磁盘空间被撑爆容器的日志文件、停止状态的容器、悬空镜像都会持续占用磁盘。虚拟机的磁盘一旦分小跑几天后就会遇到no space left on device。预防措施是把虚拟磁盘初始容量分大一点在此基础上定期清理docker system prune -a这个命令会清理停止的容器、未使用的网络、悬空镜像和构建缓存。执行前确认容器里没有需要保留的数据因为清理后不可恢复。如果清理后空间仍然紧张需要检查 Docker 的数据目录是否占了系统盘。桌面上非常经典的问题就是/挂载点只有 20GB而/var/lib/docker就在其中。扩容系统盘或者把 Docker 数据目录迁移到单独分区都可以解决。5.5 虚拟机时间不对导致 HTTPS 证书校验失败这个坑藏得比较深。虚拟机每次从挂起状态恢复后系统时间可能与真实时间存在偏差。拉取镜像报certificate signed by unknown authority或者类似证书错误时大多数人会怀疑证书配置实际可能是时间不对。先确认系统时间date如果时间明显偏早或偏晚安装并启用时间同步服务sudo apt install -y systemd-timesyncd sudo timedatectl set-ntp true sudo systemctl restart systemd-timesyncd同步完成后再拉镜像证书错误通常随之消失。这个坑在笔记本里频繁开关虚拟机时特别容易触发。6. 卸载、清理与后续扩展Docker 用久了或者配置改乱了想重来知道怎么干净卸载也很重要。很多人直接apt remove docker-ce留下一堆残留容器和配置下次重装还被干扰。6.1 彻底卸载 Docker 全家桶如果你确定要彻底清掉 Docker按以下顺序执行sudo apt purge -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginpurge会连同配置文件一起删除比remove更彻底。然后手动清理 Docker 的遗留数据目录sudo rm -rf /var/lib/docker sudo rm -rf /var/lib/containerd sudo rm -f /etc/apt/sources.list.d/docker.list sudo rm -f /etc/apt/keyrings/docker.asc清理前想清楚/var/lib/docker里包含全部镜像、容器、卷数据删除后不可恢复。如果你只是暂时不想用 Docker只想留数据那purge这步都不要执行。6.2 按需安装 Docker Compose 插件之前安装时已经带了docker-compose-plugin可以直接验证docker compose version注意命令是docker compose中间有空格这是新版插件用法。老式的docker-compose命令是独立二进制已经不太推荐了。Compose 的价值在于把多个容器用 YAML 文件组织起来比如“一个应用”同时依赖 Nginx、Redis、MySQL写一个docker-compose.yml一个docker compose up -d全搞定。虚拟机里练习多容器编排这是很合适的切入点。6.3 继续扩展的方向Docker 装好后可以做的试验方向很多。基础的可以试试给容器加资源限制比如docker run -m 512m限制内存用docker stats观察实时占用。进阶一点的可以尝试把某个 Web 应用打包成自定义镜像编写 Dockerfile 后构建运行。再往后就是 Docker Compose 编排一套完整的本地开发环境模拟生产拓扑。我自己使用下来的体会是虚拟机本身就自带隔离和快照能力和 Docker 形成了一种“双重保险”。练习时大胆折腾出问题就恢复快照这是实体机羡慕不来的便利。但也正因为虚拟机的便利很多人忽略了 Docker 的存储与网络基本功导致换到生产环境时手忙脚乱。所以练手时别只满足于docker run能跑多看容器日志、多理解镜像分层、多试着排查网络问题这些基本功最终都会转化为实打实的经验。最后分享一个小技巧在虚拟机里给 Docker 单独开一个数据目录或者至少把快照打在“刚装好 Docker 但还没跑业务镜像”的状态。这样一个快照就是纯净的开发环境后面想怎么折腾都行几分钟就能回到起点。这比任何安装教程都更省心也更能让你放开手多试多错。
阅读完成 · 觉得有帮助?