早些年装环境简直是我的噩梦。那时候项目要用 Redis、Nginx、Node、MySQL,我老老实实一个个去官网下安装包,然后配环境变量、改配置文件、处理端口占用,最崩溃的是卸载不干净,重装系统都试过两回。后来接触了 Docker,才意识到过去那些折腾大部分都是在做无用功,真正该装的是一个"能装环境的容器"。
这篇是 Docker 系列的第 3 篇,前两篇聊了容器技术的背景和基本概念,这篇落到实操:Docker 到底是什么、为什么值得装,以及 Windows、macOS、Linux 三个平台从零到跑通hello-world的完整安装过程。文里所有步骤都是我在不同机器上实测过的,也包括那些"装完之后才发现"的坑。无论你是刚接触 Docker 的开发者,还是被各种环境折腾到怀疑人生的运维新人,这篇都适用。
1. 先搞清楚 Docker 到底解决了什么问题
聊安装之前,必须先把一个底层问题说透:Docker 究竟在解决什么?如果你不理解这个,装完之后大概率只是多了一个吃内存的桌面软件,然后继续用老办法装环境。
1.1 环境隔离:把"污染"挡在门外
传统装环境的路子是直接在操作系统上装软件。今天装个 Python,明天装个 Redis,后天发现版本冲突,然后开始漫长的"卸载—清理—重装"循环。更麻烦的是,很多软件的依赖是全局共享的,你在一个项目里升级了 OpenSSL,另一个老项目可能就起不来了。
Docker 的思路完全不同。它不修改你的宿主机,而是提供一个隔离的运行环境。每个应用跑在一个独立的容器里,容器里有自己的文件系统、网络接口、进程空间。应用以为自己在独享一台机器,但实际上只是寄居在你的电脑里。这意味着你可以同时跑三个不同版本的 MySQL,互不干扰;你可以在容器里随便折腾,坏了就删掉重建,宿主机依然干干净净。
1.2 一致性:"我这能跑,你那儿不行"从此消失
另一个痛点就是环境差异。"在我机器上能跑啊"——这句话大概每个开发者都听过、也说过。你的机器是 Windows,服务器是 CentOS;你本地是 Python 3.9,线上是 3.6;你缓存了某些依赖,线上没有。
Docker 把应用连同它的运行环境一起打包成镜像。镜像里什么都带好了,包括操作系统层、运行时、依赖库、应用代码。你本地怎么跑,服务器上就怎么跑,因为运行的就是同一个镜像。部署的时候只需要拉取镜像、启动容器,不再需要在目标机器上重复一遍"装环境"的过程。
1.3 秒级启动和快速销毁
虚拟机也能做隔离,但虚拟机要装完整操作系统,启动要按分钟计算,占用的资源也大。容器共享宿主机的内核,启动本质上是创建一个进程,所以启动速度是秒级的。我本机跑一个 Nginx 容器,从命令执行到能访问,大约 0.5 秒。开发时频繁创建、销毁环境,这个体验差距是决定性的。
这也回答了题图那个热搜词"docker是干什么的"——它本质上是一套标准化、可移植的软件打包与运行机制。你把它理解为"一个装软件的规范盒子"也行,但记住:盒子里不仅是软件,还包括软件运行所需要的全部环境,这才是它最厉害的地方。
2. Docker 的三个核心概念:镜像、容器、仓库
很多人一上来就敲命令,敲了半天还是晕,就是因为没先搞懂三个基本概念。这里用生活化的说法把它们理顺。
2.1 镜像(Image):只读的"安装包模板"
镜像是一个只读的、分层构建的文件系统模板。你可以把它理解成一个"软件快照",里面包含了完整的运行环境和配置。docker pull nginx拉取的就是一个 Nginx 镜像。
镜像有个很重要的特性:分层。比如你写一个 Dockerfile,基础镜像用ubuntu:22.04,然后安装 Node 18,再复制项目代码。这三步会形成三个层,每一层都是基于上一层的增量。拉取的时候,如果本地已经有ubuntu:22.04的层,重复的层就不用再下载。这也是为什么 Docker 镜像传输比整机快得多,因为很多层可以直接复用。
2.2 容器(Container):镜像的运行实例
容器就是镜像运行起来之后的"活体"。简单说,镜像类似一个类,容器就是类实例化的对象。同一个镜像可以创建多个容器,每个容器都是独立运行的,应用互不干扰。
容器有一个特点:可写层。容器运行时文件系统由两层组成,底层是只读的镜像层,上层是容器自己可写的。你在容器里修改的文件都存在可写层里,一旦容器被删除,这个可写层也一起消失。所以容器内产生的数据默认不持久化,需要持久化的数据要用数据卷挂载。
2.3 仓库(Registry):存放镜像的"云盘"
仓库就是集中存放镜像的地方。最常用的是 Docker Hub,它是一个公共的镜像仓库,里面有官方维护的各类镜像。nginx、redis、mysql都有官方版本,直接拉取即可。
仓库的完整路径格式是[仓库地址]/[命名空间]/[镜像名]:[标签]。比如docker.io/library/nginx:latest,其中docker.io是默认仓库地址,library是官方镜像的命名空间。实际使用中经常省略默认地址和标签,nginx默认等价于docker.io/library/nginx:latest。
这三点连起来,Docker 的核心流程就很清晰了:从仓库拉取镜像,用镜像创建容器,容器里跑应用,数据落到数据卷。后续所有的命令,都是在围绕这四个动作展开。
3. 环境安装前的准备:先检查你的机器够不够格
安装 Docker Desktop 之前,有两件事必须先确认:一个是操作系统版本和 CPU 架构,另一个是虚拟化支持。别觉得这是小事,很多"装不上"和"装完启动失败"的问题,根源就在这里。
3.1 操作系统和架构检查
先看你的系统类型和 CPU 架构。打开终端执行:
# Linux / macOS uname -m # Windows PowerShell echo $env:PROCESSOR_ARCHITECTURE常见的输出有x86_64(对应 Intel/AMD 的 64 位 CPU)、aarch64(对应 ARM64 CPU,比如 Apple M1/M2/M3 芯片)。这个信息决定了你要装哪一个版本的 Docker。
macOS 用户要特别注意:M 系列芯片和 Intel 芯片的镜像架构不一样。Apple Silicon 的 Mac 上,Docker Desktop 默认会模拟 x86 环境,但性能有损耗。新版 Docker Desktop 支持arm64原生镜像,像mysql、redis这些官方镜像都有 arm64 版本,拉取的时候 Docker 会自动选择匹配的架构。
3.2 Windows 必须开虚拟化:先跑一个自检命令
Windows 上 Docker Desktop 依赖虚拟化技术,底层要么是 Hyper-V,要么是 WSL2。没开启虚拟化的话,安装完会出现一个最经典的错误提示:
Docker Desktop failed to start because virtualization support wasn't detected.
这个提示就是说你的电脑没有开启虚拟化功能。排查步骤如下:
先在 PowerShell 里输入:
systeminfo输出信息里找到 "Hyper-V Requirements" 部分。重点看两项:"Virtualization Enabled In Firmware"(固件中已启用虚拟化)和 "A hypervisor has been detected"(已检测到虚拟机监控程序)。如果第一项显示No,说明 BIOS/UEFI 里的虚拟化开关没打开。
解决方案是重启电脑,进入 BIOS 设置界面(不同品牌进入方式不同,大多数是开机按 F2 或 Del)。在 BIOS 里找到Intel Virtualization Technology(Intel)或SVM Mode(AMD),把它设为Enabled,保存退出。如果重启后systeminfo还是显示No,可能是主板固件更新问题,更新 BIOS 后再试。
3.3 WSL2 检查:Docker Desktop 的新依赖
现在的 Docker Desktop 默认使用 WSL2 后端,因为它的性能全面优于旧版 Hyper-V。装 Docker 之前建议先确认 WSL 是否可用:
wsl --status如果没有输出,或者提示没有安装 WSL,可以顺手装上。安装 Docker Desktop 的时候其实可以顺便处理,但提前装好更省事。
安装 WSL2 要分两步:先启用 Windows 功能,再设置默认版本。
# 启用 Windows 子系统 Linux 和虚拟机平台功能 dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart执行完后重启电脑。重启后在 PowerShell 里设置默认版本:
wsl --set-default-version 2这里有一个容易忽略的坑:WSL 内核必须更新。如果系统比较旧,执行上面命令可能会提示需要更新 WSL 内核。解决办法是去微软官网下载最新的 WSL2 Linux 内核更新包,安装后重新执行wsl --set-default-version 2。
3.4 macOS 的准备工作相对简单
macOS 上 Docker Desktop 的要求是:macOS 12 或更高版本,至少 4GB 内存。如果是 Apple Silicon 芯片,系统版本要求会更高一点。没有额外的虚拟化开关需要打开,因为 macOS 自带的 Hypervisor.framework 已经提供了支持。
唯一的槽点是:老款 Intel Mac 跑 Docker Desktop 普遍存在资源占用高的问题,4GB 内存会比较紧张,建议 8GB 起步。
4. 三个平台的 Docker Desktop 安装实操
准备做完了,下面进入正题。我按 Windows、macOS、Linux 三条线来走一遍,同时会讲清楚每一步是"为什么"这么做,而不只是给一串命令。
4.1 Windows:下载、安装、切换 WSL2 后端
Docker Desktop 的安装包要从 Docker 官网下载(搜索 "docker desktop download",认准docker.com后缀)。拿到.exe安装包后,双击运行,基本就是一路 Next。
安装过程中有个复选框,会问你是否使用 WSL2 作为后端。建议勾选 WSL2,因为现在 Docker Desktop 已经不对 Hyper-V 做重点优化了。装完之后的配置要分两处确认:
第一处是 Docker Desktop 的 Settings → Resources → Advanced,确认 WSL Integration 已经启用;第二处是在 PowerShell 里确认默认 WSL 版本是 2:
wsl --list --verbose如果默认版本不是 2,用下面命令指定:
wsl --set-default-version 2这里给一个非常容易踩的坑:如果你之前装过 WSL1 的发行版,WSL2 可能不会自动生效。当时我遇到的情况是,wsl --list --verbose显示我的 Ubuntu 还是 VERSION 1,Docker Desktop 启动时直接提示无法连接到引擎。解决方法是:
# 停掉旧的 WSL 实例 wsl --shutdown # 把发行版迁移为 WSL2 wsl --set-version Ubuntu 2迁移完成后重新验证,Docker Desktop 就能正常启动了。
4.2 macOS:Intel 与 Apple Silicon 的差异
macOS 上安装 Docker Desktop 最简单:下载.dmg文件,双击打开,把 Docker 图标拖到 Applications 文件夹,然后再双击运行。首次启动会要求授权安装辅助工具并输入密码,这是 Docker Desktop 在配置权限,属于正常现象。
接下来一个关键选项:Apple Silicon 和 Intel 版本的 Docker Desktop 是不同的安装包。官网下载页面会自动识别你的芯片,但如果你手动寻找版本,一定不要下错。
对于 Apple Silicon,还有一个小优化:Docker Desktop 默认会为容器分配一部分内存。M 系列芯片的内存是统一的(统一内存架构),所以分配给你的"可用内存"直接影响 Docker 内应用的体验。我建议在 Settings → Resources 里把内存调到 4GB 以上,如果只是跑轻量应用 2GB 也够,但跑多个容器会明显卡顿。
4.3 Linux:优先用发行版软件源,但版本会老
Linux 上没有 Docker Desktop 那样的一体化安装包(虽然现在也有了,但体验一般),更常用的是安装 Docker Engine。以 Ubuntu 为例,我推荐用官方 apt 源安装,因为这样能保证跟着 Docker 的发布节奏升级。
安装前的准备是卸载可能存在的旧版本:
sudo apt remove docker docker-engine docker.io containerd runc然后添加 Docker 官方 GPG key 和软件源:
# 安装 curl 和 ca-certificates sudo apt update sudo apt install -y ca-certificates curl # 下载并安装 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 a+r /etc/apt/keyrings/docker.asc # 添加 docker 软件源 echo \ "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接下来更新并安装:
sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这里有三个细节提醒:
第一,在docker-ce这个包名里,ce是 Community Edition(社区版)。如果你用的不是 Ubuntu 而是 Debian,系统代号要相应调整,Debian 12 对应的是bookworm。
第二,添加源那一段的$(. /etc/os-release && echo "$VERSION_CODENAME")会自动取系统代号,但如果你的 Ubuntu 是 LTS 版本,这个变量不会准确对应 Docker 支持的源名称。比如 Ubuntu 24.04 的代号是noble,Docker 官方源在某个阶段可能还没有noble目录,这时候要手动把源改成jammy(22.04 代号)来兼容。
第三,也是很多新手挂在半路的问题:下载速度极慢。Docker 官方源在一些网络环境下访问很慢,apt update 可能要等好几分钟。这时候可以用阿里云、清华等镜像站替换官方源,速度快很多。具体做法是把download.docker.com换成对应镜像站的地址,GPG key 也可以从镜像站获取。
4.4 Linux 下"免 sudo"配置:最值得做的一步
装完之后,你会发现每次执行docker命令都要加sudo,很烦。这个问题的根源是 Docker 的守护进程(dockerd)需要 root 权限,而docker命令要与守护进程通信,默认需要走/var/run/docker.sock这个 socket 文件,它属于 root 用户和 docker 组。
解决办法是把当前用户加入 docker 组:
sudo groupadd docker || true sudo usermod -aG docker $USER然后重新登录一次或者执行newgrp docker让用户组生效。之后就能直接用了:
docker version这一步有个安全提示需要注意:加入 docker 组的用户相当于获得了 root 权限,因为 Docker 可以挂载宿主机目录进容器。所以只对你信任的账号做这个操作,不要随手把所有用户都加进去。
5. 安装完成后的验证与关键配置
环境装好之后,下一个动作是验证、配置镜像加速、处理权限问题。这一节我按"从验证到踩坑"的顺序来展开。
5.1 验证 Docker:hello-world 是怎么工作的
先跑一个最简单但也最经典的测试:
docker run hello-world这条命令背后发生了四个动作:检查本地有没有hello-world镜像(没有就拉取),创建容器,运行容器,退出容器。如果输出里出现 "Hello from Docker!",说明安装成功。
这里有个小知识:hello-world镜像本身只有几 KB,它什么都不复杂,只打印一段欢迎语。它的价值在于验证 Docker 的拉取、创建、运行整条链路是否正常。如果你能看到那段欢迎语,说明 Docker 引擎、网络、CLI 都没问题。
如果拉取镜像时报错,常见的提示有:
Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection
这个基本就是网络问题。Docker Hub 在国内访问不稳定,解决办法是配置镜像加速器。
5.2 配置国内镜像加速:解决下载慢
镜像加速的作用是代理拉取 Docker Hub 上的镜像。配置方式在 Docker Desktop 和 Docker Engine 里不一样。
Docker Desktop 用户:打开 Settings → Docker Engine,在registry-mirrors字段里填入加速地址,然后点 Apply & Restart。
{ "registry-mirrors": [ "https://docker.mirrors.ustc.edu.cn", "https://hub-mirror.c.163.com" ] }Docker Engine(Linux)用户:编辑/etc/docker/daemon.json文件,如果没有就新建:
sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<EOF { "registry-mirrors": [ "https://docker.mirrors.ustc.edu.cn", "https://hub-mirror.c.163.com" ] } EOF然后重启 Docker:
sudo systemctl daemon-reload sudo systemctl restart docker验证生效的方式是执行docker info,看看输出里的Registry Mirrors一栏是否列出了你配置的地址。
需要提醒的是:镜像加速并不能加速所有镜像。不少镜像(尤其是需要从 GitHub Releases 下载内容的自定义镜像)在构建时才真正拉取外部文件,加速只覆盖了 Docker Hub 相关的层。如果你拉某个镜像仍然很慢,可以先看看是不是这个镜像本身在构建时依赖外部网络。
5.3 启动失败的经典错误:virtualization support wasn't detected
前面在准备阶段提过一次,但这个错误出现频率实在太高,值得单独列出完整的排查路径。症状是:双击 Docker Desktop 后弹窗提示启动失败,错误信息包含virtualization support wasn't detected。
第一步:确认 Hyper-V 功能和虚拟机平台是否启用。在管理员身份的 PowerShell 里执行:
Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V Get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform如果 State 不是 Enabled,就启用。
第二步:如果功能已启用但还是报错,问题可能出在 BIOS 里虚拟化开关没开。上面说过了,进 BIOS 找到 Intel VT-x 或 AMD SVM 开启。
第三步:如果 BIOS 也开了,错误依旧,还有一个隐藏原因——有旧版 Hyper-V 与 WSL2 冲突。这种情况下打开 "Windows 功能" 面板,找到 Hyper-V 和 Windows 虚拟机监控程序,把两个都关掉,重启,再尝试启用 WSL2。
我处理过一台老笔记本,就是最后这个原因。它的 BIOS 虚拟化开关是一开始就开着的,但 Docker Desktop 还是报错,排查最后发现是 Windows 上残留的旧 Hyper-V 组件和 WSL2 打架。关掉旧组件、只保留 WSL2 之后,问题才彻底解决。
5.4 权限报错:permission denied while trying to connect to the Docker API
我在 Linux 上第一次跑docker ps的时候就被这个报错糊了一脸:
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json": dial unix /var/run/docker.sock: connect: permission denied
这个报错有两种解法。第一种是把用户加入 docker 组,上一节已经写了。第二种是临时授权,适合只想跑一条命令的情况:
sudo docker ps但我强烈不建议一直用sudo docker,理由不只是麻烦,还有安全性的问题:sudo会让 root 用户直接操作 socket,日志会记录异常权限;更关键的是,如果哪个脚本里混了一条docker run命令,而它恰好是恶意的,那这个恶意容器默认挂载的是 root 权限的目录。
正确解法始终是把普通用户加入 docker 组,之后平时操作再也不用 sudo。这个报错的根因其实不是"权限设置错误",而是"用户不属于 Docker 运行所需的权限组"。理解这一点,以后换机器也不会慌。
6. 跑通 hello-world 之后:第一次实战的避坑清单
环境装好了、验证通过了,接下来才是 Docker 真正开始发挥作用的地方。这里集中整理我在初学阶段踩过的、以及不少读者反馈过的坑,按"从安装到日常使用"的顺序列出来,你可以直接当参考手册用。
6.1 启动容器时的"端口占用"问题
第一次跑 Nginx 容器,很多人会这样操作:
docker run -d -p 8080:80 nginx然后发现宿主机上访问http://localhost:8080没反应。排查思路:先确认容器状态:
docker ps发现容器状态是 Exited,再去查日志:
docker logs <容器ID>过半的情况是 80 端口在容器内已经占用,或者宿主机的 8080 端口被别的进程占用了。解决方法是换个宿主端口,比如-p 8081:80。还有一个隐蔽的坑:如果宿主机上有什么服务已经在监听 80 端口,而你把容器端口映射也写成了80:80,启动会报端口冲突。这个在 Windows 上尤其常见,因为 IIS 默认占用 80。
6.2 容器内文件修改不生效:数据卷的概念
初用 Docker 时我改了一份容器里的配置文件,然后docker stop+docker start,发现改动全没了。原因是容器停止不丢数据,但删除容器时,可写层被一并移除。如果你在容器里直接修改文件,而这些文件本身来自镜像层,那它们只存在于容器的可写层,容器一删,改动就没了。
正确的做法是用数据卷(Volume)把宿主机目录"挂"进容器:
docker run -d -p 8080:80 -v /home/me/nginx/html:/usr/share/nginx/html nginx这样/home/me/nginx/html里的文件就直接映射到容器的目录里,你在宿主机改什么,容器内立刻生效,容器删除后数据也不会丢。
6.3 网络问题:容器之间怎么通信
还有一个常见困惑:为什么两个容器之间无法通过 localhost 访问?原因是每个容器默认有自己独立的网络栈,localhost只代表容器本身。想要容器间通信,需要把他们放在同一个自定义网络里:
docker network create my-net docker run -d --network my-net --name app1 nginx docker run -d --network my-net --name app2 redis这样容器之间可以通过容器名互相访问,比如在 app1 里访问app2:6379。这个机制叫 Docker 自定义网络,以后你做多容器应用(比如前后端 + 数据库)时一定会用到。
热词里出现过的"docker网络不通",多数时候就是容器不在同一个网络里,或者宿主端口映射没映射到正确的容器端口。排查命令先把网络列表看一遍:
docker network ls docker network inspect my-net把容器放进同一个网络后,通信问题通常立刻消失。
最后再分享一个实际体会
跑通 hello-world 那一刻,只是 Docker 入门的第一步,真正的收获在之后:当你用 Docker 把开发环境的数据库、缓存、队列全部容器化,然后换一台新电脑时执行几条命令,一分钟内重建出完全一致的环境,那种"终于不用再折腾了"的感觉,值得你花这几十分钟把基础打好。
如果这篇文章帮你装好了 Docker,下一步我建议做两件事:第一,把常用的数据库镜像(比如mysql:8.0)拉下来跑一个带数据卷的实例,感受一下数据持久化;第二,尝试用 Docker Compose 编排一个简单的 Nginx + Redis 服务组。这两个场景覆盖了 Docker 日常使用的大部分需求,掌握了它们,你已经比大部分"装完就吃灰"的用户走得远多了。