VMware虚拟机中安装Ubuntu并配置Docker的完整指南与避坑手册 1. 为什么我推荐在虚拟机里装Docker而不是在Windows上硬啃Docker Desktop如果你正在Windows上折腾Docker Desktop被那个virtualization support not detected的报错折磨得想把电脑扔出窗外那这篇文章就是给你的。我把话先说在前面与其在宿主机上跟虚拟机监视程序、WSL2、Hyper-V这几个东西较劲不如直接在VMware里装一台Ubuntu虚拟机然后在Ubuntu里装Docker。这套方案我用了好几年省心、干净、出问题还能随时回滚。1.1 Docker Desktop反复报错的那台Windows机器我见过太多人卡在同一个地方Docker Desktop在Windows上启动失败报错提示Virtualization support not detected或者VMware Workstation cannot connect to the virtual machine。这个问题的根源在于Docker Desktop的Windows版依赖两块底层能力——WSL2或者Hyper-V。你一旦在Windows功能里启用了虚拟机监控程序平台或Hyper-VVMware Workstation会和它抢占同一套虚拟化资源结果就是Docker Desktop没起来VMware里的虚拟机也连不上了。我去年在某台笔记本上实测过开Hyper-V之后VMware直接报错无法连接到虚拟机。请确保您有权运行该程序最后只能关掉Hyper-V才恢复正常。你要知道Windows上跑Docker不是不可以但它牵扯太多系统层改动BIOS里开启VT-x、Windows功能里勾选WSL2、还要处理和VMware/VirtualBox的共存冲突。对于只想学Docker、想尽快把容器跑起来的人来说这条路实在太绕了。1.2 虚拟机方案真正的价值在哪用虚拟机装Ubuntu再装Docker好处有三点第一环境干净Linux下跑Docker是原生支持不需要任何翻译层第二快照功能太好用装坏了直接恢复快照几秒钟回到之前的状态比在物理机上折腾安全得多第三和真实服务器环境对齐你以后往云服务器上部署操作方式一模一样都是Linux命令行都是systemd管理服务不会有Windows上可以Linux上不行的割裂感。特别适合这个方案的人群有三类刚接触Linux和Docker的新手、需要在干净环境里做各种实验的开发者、以及想在一台Windows电脑里模拟多台服务器做联调测试的运维学习者。这篇文章后面就按我的实操路线走一遍从虚拟机配置到Docker落地每一步都附带为什么这样做的解释。2. 装虚拟机之前的配置镜像版本、虚拟化开关、资源分配别急着开机装系统先把虚拟机平台的配置调好后面能省掉很多麻烦。特别是VMware里那个虚拟化引擎的选项不少人都没注意到它。2.1 Ubuntu镜像选服务器版还是桌面版Ubuntu的长期支持版本现在主流是22.04 LTS和24.04 LTS选哪个都行LTS版本有五年维护周期不会用着用着仓库源就失效。版本不用追新22.04和24.04对Docker来说完全没有体验差距。关键分歧在于选桌面版还是服务器版。我的建议如果你主要是学Docker选Ubuntu Server版没有图形界面资源占用更少而且命令行的操作习惯和真实服务器完全一致。如果你对Linux完全陌生离开图形界面心里发慌那选桌面版也行反正现在Ubuntu桌面版装了Docker之后一切照常只是多占几百MB内存。从官网下载iso镜像时注意文件名里的版本标识arm架构的机器就下arm64的包x86就下amd64别下错。2.2 VMware里被忽略的虚拟化引擎选项创建虚拟机时在处理器配置页面里有一个虚拟化引擎区域里面有一个选项写着虚拟化Intel VT-x/EPT或AMD-V/RVI。这个选项很多教程根本不会提但对你后面在虚拟机里跑Docker很重要。勾上它的含义是把宿主机的CPU虚拟化能力再透传给虚拟机让虚拟机里的系统也认为自己运行在支持虚拟化的硬件上。Docker本身不强制要求嵌套虚拟化因为它只是利用Linux内核的namespace和cgroup但如果你后面想在Docker里再跑需要虚拟化支持的软件或者想在虚拟机里用带硬件加速的工具这个选项没勾会导致一系列莫名其妙的问题。我的做法很直接只要是在虚拟机里跑Linux这个选项永远勾上无一例外。还有一个经常出现的坑如果你在创建虚拟机时提示此主机支持Intel VT-x但Intel VT-x处于禁用状态那是BIOS/固件层面的虚拟化开关没打开。重启电脑进BIOS找到Intel Virtualization Technology之类字样改成Enabled再进系统就好了。搜vmware无法启用虚拟机平台能搜到一堆求助帖基本都是这个问题。2.3 磁盘和内存怎么分配不后悔内存方面Docker本身的守护进程占不了多少资源但容器镜像多起来、跑起来之后消耗就上来了。虚拟机我建议至少给4GB8GB更好。如果你的宿主机内存够大给Ubuntu虚拟机分8GB同时运行两三个容器不会卡。我有一个跑着Nginx加MySQL加Redis的开发环境虚拟机内存峰值也就3.5GB左右所以4GB是底线。磁盘分配要稍微放开一点。一套完整的Docker环境光镜像就可能占掉5GB以上mysql、nginx、redis、python这些常用镜像每个几百MB。加上虚拟机系统本身和日常日志增长60GB是起步建议给80GB也不嫌多。VMware的磁盘默认用动态扩展模式实际占用按需增长所以提到80GB不会真的马上占用你宿主机80GB放心给。网络模式这一项新手先不用纠结太多默认NAT模式就行后面我专门有一节讲它的坑在哪。现在创建虚拟机、挂载Ubuntu iso、装系统一路默认操作装完系统后先执行一遍sudo apt update sudo apt upgrade -y把系统基础包更新到最新然后就可以进入Docker的安装环节了。3. Docker安装官方源、apt源、自动脚本我选了哪条路Ubuntu下装Docker的方式大致有三条直接用apt装docker.io、配置Docker官方仓库后装docker-ce、或者用Docker官方的一键安装脚本。三条路我全都实际走过下面直接说结论。3.1 三种安装方式的对比先看一张表把三种方式的特点列清楚安装方式版本新鲜度与系统包管理的关系升级维护适合场景apt install docker.io滞后跟随Ubuntu发布节奏完全走系统apt体系随系统升级只想随便试试不关心新特性官方仓库安装docker-ce最新Docker发布后同步更新需要额外配置源和GPG密钥独立升级稳定可控学习、开发、生产都推荐get.docker.com一键脚本最新自动配置源并完成安装升级仍需处理源快速初始化新机器很多人会直接执行sudo apt install docker.io图省事。这个命令确实能装上Docker但装出来的是Ubuntu仓库里打包的版本版本号往往比官方版本落后不少。这里特别叮嘱一下生产环境或者长期学习环境别用docker.io这个包。有一次我在一台Ubuntu 22.04上贪图省事用了docker.io之后想用docker compose的新语法发现Compose插件要么不存在要么版本太老只能卸载重装折腾的功夫比一开始配官方源多好几倍。3.2 官方仓库安装的完整步骤在Ubuntu上配置Docker官方仓库核心是添加GPG密钥和软件源描述文件。我按下面这个顺序执行每一步都验证过# 第一步安装依赖工具 sudo apt update sudo apt install -y ca-certificates curl gnupg # 第二步创建密钥管理目录新版Debian/Ubuntu的规范做法 sudo install -m 0755 -d /etc/apt/keyrings # 第三步下载Docker官方GPG密钥并转为apt使用的格式 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg # 第四步写入软件源配置 echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release echo $VERSION_CODENAME) 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-buildx-plugin docker-compose-plugin这里解释几个容易困惑的点。第二步创建/etc/apt/keyrings目录是为了把第三方软件源的GPG密钥统一存放Ubuntu 22.04之后的版本都建议用这个路径。第三步里的gpg --dearmor作用是把下载的GPG密钥从ASCII格式转换为二进制格式apt只认这种格式。第四步里的$(dpkg --print-architecture)会自动输出当前系统架构比如amd64$(. /etc/os-release echo $VERSION_CODENAME)会自动获取系统版本代号像是jammy或noble这样就不用手动改文件内容了。安装的这几个包各有用处docker-ce是Docker守护进程本体docker-ce-cli是命令行工具containerd.io是容器运行时Docker依赖它来管理容器生命周期docker-buildx-plugin是构建镜像用的插件docker-compose-plugin就是现在官方推荐的新版Compose命令是docker compose中间有个空格不是老版本的docker-compose。3.3 安装完成后根据系统状态做验证装完先别急着跑容器确认一下守护进程的状态。# 查看docker服务状态 sudo systemctl status docker # 如果没启动手动启动并设置开机自启 sudo systemctl enable --now docker # 查看版本信息 sudo docker versionsystemctl status docker如果显示active (running)说明守护进程已经起来了。enable --now这条命令把开机自启动和立即启动一步做完虚拟机关机再开机后Docker会自动运行不需要每次手动启动。docker version会同时显示客户端和服务端版本如果只显示Client没显示Server多半是守护进程没起来先回去看status。如果你实在不想一步步配置源可以用一行脚本安装curl -fsSL https://get.docker.com | bash这个脚本会自动识别Ubuntu版本配置Docker官方源并完成安装。我建议初次学习还是手动走一遍源配置流程因为你能搞清楚每一层在干什么出问题也知道去哪排查。脚本方式适合你已经懂了原理、但想在新环境快速重建的时候用。4. 安装只算完成一半镜像加速、免sudo、基础验证Docker装好了如果直接开始拉镜像大概率会遇到镜像下载慢或者超时的问题。这也是为什么我说安装只算一半——后面这三件事不做你的体验会非常劝退。4.1 镜像加速源安装后第一件事Docker默认从官方仓库拉取镜像在国内网络环境下速度很不稳定。解决方法是配置镜像加速源。编辑如果没有就新建/etc/docker/daemon.json文件sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json -EOF { registry-mirrors: [ https://docker.m.daocloud.io ] } EOF sudo systemctl daemon-reload sudo systemctl restart docker配置完成后docker pull就会走加速源拉取镜像。你也可以在阿里云容器镜像服务控制台里申请一个专属加速地址格式类似https://你的ID.mirror.aliyuncs.com把自己的地址填进registry-mirrors数组里也行。这个文件是JSON格式多个加速地址用逗号分隔格式错了Docker守护进程会启动失败所以每次改完都要执行systemctl daemon-reload再restart docker。4.2 sudo不用每次都敲用户组和权限默认情况下执行docker ps、docker run这些命令需要root权限每次都要敲sudo很烦。原因是Docker的守护进程监听在/var/run/docker.sock这个Unix套接字上默认只有root和docker组的用户才能访问。把自己加入docker组重新登录后就不用再敲sudo了sudo usermod -aG docker $USER # 关键需要重新登录或执行newgrp命令才能生效 newgrp docker这里有个安全细节要提醒你加入docker组等于拥有了和root等同的权限因为你可以通过docker run -v /:/host ...把宿主机目录挂载进容器从而以root身份读写宿主机文件。这在个人学习虚拟机里无所谓但如果你在真实的多用户服务器上给用户分配docker组权限要慎重。我是建议在虚拟机里直接加组图省事因为这台机器本身就是你的试验场。4.3 开机自启和基础验证再确认一遍开机自启已经设置好sudo systemctl enable docker前面已经执行过enable --now的话这里会提示已经存在不用管。接下来用最经典的镜像做验证docker run hello-world正常情况下会拉取镜像并打印一段欢迎信息告诉你Docker已经能正常工作。如果这一步卡住很久多数还是镜像加速的问题回到4.1检查daemon.json配置和网络连通性。如果提示permission denied说明当前用户不在docker组回到4.2重新登录再试。做完这一步一套能用的Docker环境就算彻底就绪了。但虚拟机环境里跑容器后面还有几个高频坑等着你踩。5. 虚拟机里跑容器最常踩的三个坑端口映射、挂载权限、防火墙这一节的内容是我在虚拟机里跑容器反复踩过、花了大量时间排查才搞明白的地方。提前看完你至少能少走两天弯路。5.1 NAT模式下端口映射外部访问不到容器的解法VMware默认的网络模式是NAT虚拟机通过共享宿主机IP来访问外网但反过来从宿主机或者局域网其他机器访问虚拟机里的服务就没那么顺畅了。场景是这样的你在Ubuntu虚拟机的Docker里跑了一个Nginx容器做了-p 8080:80端口映射。在虚拟机里用curl http://localhost:8080访问完全正常但在宿主机的Windows浏览器里输入http://localhost:8080却连不上。原因在于NAT模式下宿主机访问虚拟机IP需要经过VMware的虚拟网卡转发而容器端口映射到的是虚拟机的网络命名空间外面的流量进不来。解决办法有三个一是把虚拟机网络改成桥接模式让虚拟机拿到和宿主机同一网段的独立IP然后在Windows浏览器直接访问那个IP加端口二是在VMware的虚拟网络编辑器里给NAT模式添加端口转发规则把宿主机的某个端口转发到虚拟机的对应端口三是用SSH端口转发ssh -L 8080:localhost:8080 user虚拟机IP把宿主机端口映射到虚拟机本地端口。我最常用的还是桥接模式。改桥接只需要在虚拟机设置里把网络连接从NAT改成桥接然后进入Ubuntu重新获取IP即可。桥接模式的好处不仅是端口访问方便虚拟机之间的通信也走真实局域网更接近真实服务器环境。要注意的是桥接模式下虚拟机的IP是局域网动态分配的如果用固定IP习惯建议在Ubuntu的网络配置里绑定静态IP免得重启后IP变了还要到处找。5.2 文件挂载权限错乱宿主机目录和容器目录的所有权冲突挂载目录是Docker的高频操作出现问题也最高频。最常见的是你把宿主机的某个目录挂载进容器后容器里读写这个目录时权限错乱。举个例子我在虚拟机里做了这样的挂载docker run -d -p 8080:80 \ -v /home/ubuntu/nginx-html:/usr/share/nginx/html \ nginx然后在宿主机里往nginx-html目录放网页文件发现Nginx显示403 Forbidden。排查后发现宿主机的nginx-html目录属主是ubuntu用户uid1000但容器里的Nginx以root身份运行读取文件没问题写入会有冲突反过来如果容器里创建的文件的属主是rootuid0在宿主机上就会显示成root所有普通用户想改文件还得加sudo。解决办法是保持宿主机的目录权限和容器内进程的用户一致。Nginx容器提供了环境变量NGINX_UID和NGINX_GID可以直接指定运行用户不过我更喜欢简单粗暴的chownsudo chown -R 1000:1000 /home/ubuntu/nginx-html这样容器和宿主机都用uid 1000的文件属主冲突就没了。所以每次做目录挂载前先想想容器内进程以什么用户运行宿主机的目录权限要让谁方便读写想清楚这两点权限问题基本不会出现。5.3 防火墙和Docker的私人恩怨在Ubuntu虚拟机里跑容器如果发现端口映射怎么都不生效检查一下是不是开启了防火墙。Docker的端口映射原理是直接操作iptables规则把宿主机端口转发到容器的IP。但如果你在Ubuntu里启用了ufwUncomplicated Firewallufw默认策略会挡住转发链的流量Docker发布的端口就会从外部无法访问。这个问题在物理机和云服务器上很常见在虚拟机里同样会遇到。我的建议是在虚拟机学习环境里直接不要开启ufwsudo systemctl disable --now ufw如果你确实需要防火墙那就得理解Docker和iptables的协作方式不盲目禁用但这属于进阶话题新手阶段建议先把防火墙关掉把环境跑通再考虑安全加固。这里特别提示不要随便在daemon.json里加iptables: false那会导致容器网络彻底乱掉我试过一次最后重启Docker才恢复。还有一个容易忽略的坑是容器时区。默认容器是UTC时间date命令显示的时间和宿主机差了8小时。解决方式很简单在docker run时加环境变量docker run -d -e TZAsia/Shanghai nginx或者用docker-compose时在服务配置里写environment: - TZAsia/Shanghai这些坑看着不大但在实际使用中每一个都能让人排查半天。6. 用docker compose把Nginx和MySQL跑起来一次完整实践光谈理论没意思最后我用一个实际项目来收尾——用docker compose一次性启动Nginx和MySQL这也是热搜词里出现频率很高的组合。6.1 定义compose文件在工作目录创建docker-compose.ymlservices: nginx: image: nginx:1.26 container_name: web-nginx ports: - 80:80 volumes: - ./html:/usr/share/nginx/html - ./nginx/conf.d:/etc/nginx/conf.d environment: - TZAsia/Shanghai restart: unless-stopped mysql: image: mysql:8.0 container_name: web-mysql ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: root123456 TZ: Asia/Shanghai volumes: - mysql-data:/var/lib/mysql restart: unless-stopped volumes: mysql-data:这个文件有几个值得说明的设计。nginx服务的./html目录挂载进去意味着你在宿主机上放网页文件容器里立即生效。./nginx/conf.d目录挂载进去可以在宿主机上添加站点配置文件实现多站点自定义域名配置的效果这正好对应热搜词里本地虚拟机多端口Nginx开发环境多站点自定义域名配置的场景。mysql的数据卷用命名卷mysql-data数据持久化在虚拟机磁盘上容器删了重建数据还在。restart: unless-stopped的意思是容器意外退出时自动重启但手动stop掉的不会强行拉起。这个策略最适合开发环境。6.2 启动、验证、清理执行启动命令docker compose up -d第一次启动会拉取Nginx和MySQL镜像等待时间取决于网络和镜像加速效果。启动完成后docker compose ps docker ps看到两个容器状态为Up就说明跑起来了。验证服务curl http://localhost curl http://localhost:3306第一个命令会返回Nginx的默认页面内容。第二个命令如果端口通会收到MySQL协议的错误信息这也算验证通过。需要看容器日志排查问题就用docker logs web-nginx docker logs web-mysqlMySQL容器第一次启动时日志里有Temporary password is generated之类的提示。另外要提醒一句MySQL 8.0在容器里的初始化和认证方式有些细节和5.7不同如果遇到客户端连接不上多半是认证插件问题在创建用户时用mysql_native_password插件可以解决。用完可以随时docker compose down容器停了但数据卷还在连数据一起清理才用docker compose down -v这个命令会删掉mysql-data卷里所有数据执行前一定确认没有重要数据。最后说一个我一直沿用的实操心得在虚拟机上装Docker最值钱的功能就是快照和克隆。每次配置好一套干净环境后拍一个快照再继续折腾一旦把系统搞挂恢复快照分分钟回到配置完美的状态。你可以把装上Docker的这台虚拟机保存为一个模板以后想新开环境时直接克隆几分钟就得到一台新机器比从零安装Ubuntu再装Docker快得多。这套虚拟机加Docker的开发方式我在本地用了很久它最大的优势是无论你怎么折腾宿主机Windows永远安全而且错了随时推倒重来。