安装 Docker 这件事我一开始也是走了不少弯路。最早听到“容器”两个字觉得高深得不行什么隔离、虚拟化、镜像脑子里全是抽象名词。后来真正跑起来第一个容器才发现它没有想象中那么玄乎本质上就是一套“打包环境”的思路把应用以及它依赖的运行环境一起塞进一个独立的小盒子里想搬到哪台机器都能原样跑起来。这篇笔记就是写给完全零基础的朋友的我会用最直白的话把 Docker 的底层逻辑拆开再把安装、常用命令、Dockerfile 和实战排查都过一遍。学完之后你至少能自己拉镜像、跑容器、写一个简单的部署脚本遇到常见报错也知道从哪里入手查。先说清楚这篇文章的定位是“极速入门”不追求把所有底层原理都讲透而是先让你能上手、能跑通、能看懂常见的操作。我尽量按自己踩坑的经验来讲哪些命令是有用的哪些概念一开始不用太较真都会标出来。看完之后你会有一个完整的轮廓Docker 到底在解决什么问题日常开发里该怎么用它。1. 先搞清楚 Docker 到底是干嘛的1.1 一个小白视角的类比从“搬家”到“集装箱”我第一次理解 Docker是拿“搬家”来类比的。以前你在自己电脑上搭好了一个项目运行得好好的可一旦换一台电脑或者让同事在自己机器上跑就会出现各种奇怪问题环境变量没配、依赖版本不对、系统库缺失甚至操作系统不一样就直接跑不起来。这种“在我机器上好好的”现象本质就是运行环境不一致。Docker 的思路有点像货运行业里集装箱的发明。在没有集装箱的年代货物散装搬运不同港口、不同船型、不同货物类型装卸流程和标准都不一样效率很低。集装箱出现后所有货物按统一标准装进一个固定尺寸的箱子吊机、卡车、轮船都能直接用标准接口搬运跨地区流转就变得非常顺畅。Docker 里的镜像Image就是那个已经封装好的集装箱模板容器Container就是由镜像启动出来的一个个实例。镜像里不光有你的代码还包括操作系统的基础层、运行时、系统库、配置文件全部打成一个整体。这样无论底层主机是什么系统、什么版本只要这台机器能跑 Docker就能把容器启动起来而且行为几乎一致。1.2 为什么说 Docker 能解决“环境不一致”问题关键在于镜像的分层结构。Docker 镜像不是一个大而全的单文件而是由一层一层只读的文件系统叠加起来的。每一层对应 Dockerfile 里的一条指令比如装一个依赖、拷贝一份代码。因为层可以复用所以拉取一个新镜像时如果底层已经存在就只有差异部分需要下载这也是为什么很多镜像看着很大实际上跑起来占用的额外空间并不会特别夸张。容器在镜像之上加了一层可写层你在容器里写文件、改配置都发生在这层可写层里。一旦容器删除这一层也跟着没了原始镜像不受影响。这种机制带来的好处是你可以随意在容器里做实验玩坏了直接删掉容器再基于原镜像重新启动一个就能恢复干净状态跟新的一样。在实际项目里我经常用 Docker 来跑数据库、消息队列、缓存这些中间件。以前在本地装一个 MySQL光是对应系统版本的安装包、初始化配置、字符集设置就能折腾一下午。现在只需要一条命令就能拉镜像、启动容器用完了还可以直接删掉重来对开发环境的清理特别友好。1.3 学习 Docker 前要放下哪些心理负担很多小白一上来就去看 Docker 源码、研究容器底层原理结果一头扎进 namespaces、cgroups、overlayfs 这些概念里还没开始用就先被劝退了。说实话作为入门阶段这些底层机制完全可以先放一边。就像你用手机不需要先搞懂射频电路一样先会用、跑通流程等以后真遇到性能或安全问题时再回头深挖也不迟。入门阶段只需要掌握四样东西镜像是什么容器是什么怎么拉镜像跑容器怎么用 Dockerfile 描述一个应用。这四个点串起来你就已经能应对日常 80% 的需求了。至于网络、存储驱动、编排调度都是在基础之上延伸的进阶话题。2. 3分钟装好 Docker 环境Windows / macOS / Linux 通用思路2.1 安装前需要知道的三件事装 Docker 这件事本身不难但有几个前提要先确认清楚。第一Docker 的命令行工具需要和 Docker 引擎通信在 Windows 和 macOS 上最常见的方式是安装 Docker Desktop它会帮你把虚拟机、命令行、图形界面一次性装好。第二Linux 上通常直接安装 Docker Engine然后用命令行操作没有图形界面不过对日常使用没啥影响。第三不管哪个平台装完后都要确保 Docker 服务处于运行状态否则敲任何 docker 命令都会报“无法连接”的错误。另外Docker 官方镜像仓库里的镜像数量非常庞大但在某些网络环境下拉取镜像可能会比较慢甚至超时。这个问题后面我会专门讲怎么处理你现在只需要知道这属于常见现象不用慌。2.2 Windows / macOS 推荐方案Docker Desktop如果你用的是 Windows 或 macOS直接去 Docker 官网下载对应系统的 Docker Desktop 安装包双击安装一路默认配置就行。安装过程中可能会提示需要开启虚拟化功能比如 Windows 上的 Hyper-V 或者 WSL2 后端macOS 上则依赖系统自带的虚拟化框架。只要电脑不是太老一般都能满足要求。装完后打开 Docker Desktop你会发现它有一个图形界面可以看容器列表、镜像列表、日志输出还能一键清理资源。对我个人来说图形界面主要用来观察状态真正高频操作还是放在终端里因为命令行的灵活性和可脚本化程度是图形界面比不了的。安装完成后在终端里执行下面这条命令看到版本信息就说明安装成功了docker --version如果提示命令找不到多半是安装过程中没有把 Docker 的可执行文件加入 PATH重装一次或者手动配置一下环境变量就行。macOS 上如果用的是 Apple Silicon 芯片尽量下载对应架构的安装包性能会更好。2.3 Linux 服务器方案直接用包管理器安装Linux 上安装 Docker 其实比桌面端更简单直接。以最常见的 Ubuntu / Debian 系为例先用系统自带的包管理器更新索引然后安装几个必备的基础工具包再添加 Docker 官方的软件源最后安装 docker-ce 和 docker-ce-cli 就可以。sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release sudo install -m 0755 -d /etc/apt/keyrings 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 $(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如果嫌麻烦很多 Linux 发行版也直接提供了 docker.io 这个包一条sudo apt install docker.io就能装好版本可能略旧但入门完全够用。国内还有不少云厂商提供了内网源安装速度会快很多。装完先别急着用把当前用户加入 docker 用户组避免每次敲命令都要加 sudo。这一步很容易被忽略但不做的话后面非常难受sudo usermod -aG docker $USER newgrp docker退出终端重新登录一次再执行docker run hello-world能看到欢迎信息就说明整个链路已经通了。2.4 装完后必做的验证动作不管是桌面端还是服务器端装好后都要做一次完整的验证。第一步是查看版本确认客户端和服务端都在正常工作docker version docker info第二步是跑一个测试容器Docker 官方提供了一个极小的镜像 hello-world专门用来验证安装是否成功docker run hello-world这条命令如果输出了类似 “Hello from Docker!” 的提示就说明拉取镜像、创建容器、运行进程全流程都正常。我在帮别人排查环境问题时通常会让他们先跑这条命令因为只要它能过90% 的基础环境问题就排除了。3. 极速入门核心概念镜像、容器、仓库、数据卷3.1 镜像和容器的关系用“乐高图纸”来理解很多新手混淆镜像和容器觉得它们是一回事。其实可以这样理解镜像是“类”和“图纸”容器是根据图纸造出来的“实例”。同一个镜像可以启动多个容器每个容器都是互相隔离的独立运行环境互不干扰就像你用同一张图纸盖了好几栋一模一样的房子但每栋房子里的住户和生活状态互不影响。从操作上看镜像一旦构建好就是只读的不能直接改。想要修改一个镜像的内容通常不是去“编辑”它而是基于它启动容器在容器里改动然后把容器提交成新镜像或者更推荐的方式是修改 Dockerfile 重新构建。容器则是有状态的临时运行体你可以随意进出、安装软件、删改文件但想要把这些改动固化下来就需要对容器做提交操作。在入门阶段你不需要深入理解容器和虚拟机的区别到底有多大。只需要知道虚拟机是模拟了一整台电脑里面跑完整操作系统容器则是直接复用宿主机的操作系统内核只是把文件系统、进程、网络等做了隔离。所以容器启动非常快往往是毫秒级到秒级占用的额外资源也很少。3.2 仓库去哪里拉镜像镜像仓库是存放镜像的地方最常用的公共仓库是 Docker Hub。你可以把它理解成应用商店里面有各种官方镜像比如 Ubuntu、Nginx、Redis、MySQL也有个人或组织发布的第三方镜像。日常操作中docker pull就是从仓库拉取镜像到本地docker push则是把自己构建的镜像上传到仓库。常见仓库里的镜像名有一定规范。比如nginx:latest表示 Nginx 官方的 latest 标签redis:7.0表示 Redis 的 7.0 版本标签。标签是用来区分不同版本的如果不写标签默认就是latest。在生产环境里建议一定要指定明确版本号避免后续拉取时版本漂移导致行为不一致。docker pull nginx:1.25 docker pull mysql:8.0拉下来的镜像存在本地用docker images就可以查看。这个命令会列出仓库名、标签、镜像 ID、创建时间和大小。对于同一个仓库名和标签如果重新拉取时发现本地已经有相同的内容就不会重复下载。3.3 数据卷让容器里的数据不丢前面提到容器删除后可写层的数据也会没掉。那如果是一个 MySQL 容器数据库文件存在容器内部一删容器数据全没了这对实际使用来说是不能接受的。解决办法就是使用数据卷Volume或者绑定挂载Bind Mount。数据卷本质上是让容器里的某个目录脱离容器的生命周期直接存储在宿主机上。你可以把数据卷理解成一个外置硬盘换了电脑主机硬盘还在数据当然也还在。创建容器时通过-v参数指定挂载关系docker run -d --name mysql8 -v mysql_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD123456 mysql:8.0这里的mysql_data:/var/lib/mysql表示把名为 mysql_data 的数据卷挂载到容器内的/var/lib/mysql目录MySQL 的所有数据文件都会写到这个数据卷里。即使容器被删掉数据卷还在下次重新创建容器时再挂载同一个数据卷数据就都回来了。绑定挂载则更直接把宿主机的某个目录直接映射进容器比如把当前目录下的代码挂载到容器里的/app目录。这样你在宿主机上编辑代码容器里马上就能看到变化非常适合本地开发调试不用每次改代码都重新构建镜像。3.4 端口映射和环境变量让容器和外界沟通容器默认是一个相对封闭的网络环境宿主机之外的人访问不到容器里的服务。要让外部能够访问容器中的 Web 服务就需要做端口映射。冒号左边的端口是宿主机的端口右边是容器内的端口docker run -d --name web -p 8080:80 nginx:1.25启动之后访问本机的 8080 端口请求就会被转发到容器内的 80 端口也就是 Nginx 的服务端口。如果宿主机上已经占用了 8080启动时会直接报端口冲突换一个宿主机端口就行。环境变量则用来向容器传递配置信息。比如 MySQL 镜像要求通过环境变量指定 root 密码上面的命令里就用了MYSQL_ROOT_PASSWORD。还有一些镜像支持用环境变量指定时区、启动模式、连接地址等具体看对应镜像的文档。常见的数据库密码、服务端口这类敏感信息建议不要写死在命令里而是通过环境变量文件传入docker run -d --name web --env-file .env nginx:1.254. 小白最常用的 10 个 Docker 命令附实操演示4.1 镜像相关命令镜像操作是整个 Docker 使用的基础我用得最多的就是拉取、查看、删除这三个动作。先看拉取镜像docker pull nginx:1.25这条命令会从 Docker Hub 拉取 Nginx 1.25 版本镜像到本地。如果网络不太顺畅可以多试几次或者换用其他公共镜像源后面我会专门讲。查看本地已经存在的镜像docker images输出会有一个 IMAGE ID它是镜像的唯一标识删除镜像时可以直接用这个 ID也可以用“仓库名:标签”的形式。删除不再使用的镜像docker rmi nginx:1.25如果镜像正被某个容器使用直接删除会报错需要先停掉并删除相关容器。不要用蛮力去强删按照依赖顺序来处理就不会有问题。还有一个命令很少被提到但很实用查看镜像的详细配置docker inspect nginx:1.25它会输出一大段 JSON里面有镜像的架构、环境变量、暴露端口、数据卷定义等。排查问题时这个命令能提供非常多线索。4.2 容器相关命令容器的生命周期一般就是创建、启动、停止、删除。最常用的启动命令是docker run它其实等于“创建 启动”。举个例子启动一个 Nginx 容器并绑定端口docker run -d --name nginx-test -p 8080:80 nginx:1.25-d表示后台运行--name给容器起名-p做端口映射。跑起来之后查看当前正在运行的容器docker ps只看所有容器包括已经停止的加一个-a参数docker ps -a停止和启动容器分别用docker stop nginx-test docker start nginx-test删除容器用docker rm nginx-test容器在后台运行时不方便看报错信息可以用docker logs查看日志流docker logs nginx-test如果加了-f参数还能像tail -f一样持续实时打印日志排查问题的时候非常顺手。有时候需要进入一个正在运行的容器里看看内部状态用docker exec -it nginx-test bash-i是保持标准输入打开-t是分配一个伪终端这样才能模拟成你在容器里打开了一个终端窗口。进去之后你可以查看文件、检查进程、测试网络就像在普通 Linux 服务器上操作一样。4.3 一个清晰的完整示例启动 Nginx 并访问把上面这些命令串起来做一个完整的实际操作。第一步拉取镜像docker pull nginx:1.25第二步启动容器并做端口映射docker run -d --name my-site -p 8080:80 nginx:1.25第三步确认容器运行状态docker ps正常情况下状态下会显示 Up端口映射列会显示0.0.0.0:8080-80/tcp。第四步用浏览器访问http://localhost:8080看到 Nginx 的默认欢迎页就成功了。这个流程虽然简单但它把镜像、容器、端口映射三个核心知识点全部串起来了。之后你再接触任何镜像比如跑一个 Redis、跑一个后端服务套路基本都是一样的拉镜像、定参数、启动容器、验证访问。5. 从“跑起来”到“会部署”Dockerfile 和 docker compose5.1 手写一个最小可用的 DockerfileDockerfile 就是用来描述“如何构建一个镜像”的脚本。它由一系列指令组成每一条指令都会产生一个新层。对小白来说第一阶段只需要掌握四个基础指令FROM、WORKDIR、COPY、CMD。假设现在有一个非常简单的 Python Flask 应用文件结构大概是这样app/ app.py requirements.txt在项目根目录新建一个 Dockerfile内容如下FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 5000 CMD [python, app.py]逐行解释一下。FROM指定基础镜像这里用的是 Python 3.11 的精简版已经自带了 Python 环境。WORKDIR是切换工作目录后面所有命令都会在这个目录下执行。COPY requirements.txt .把依赖清单拷进镜像然后RUN pip install安装依赖。再把整个项目目录拷进去EXPOSE 5000声明容器监听的端口最后CMD指定容器启动时执行的命令。有了 Dockerfile 之后构建镜像的命令是docker build -t my-flask-app:v1 .最后的点表示构建上下文的路径是当前目录。构建过程会在终端里一层层输出每一层都是一个缓存单位。如果第二次构建时某些层没有变化Docker 会直接使用缓存速度会快非常多。启动这个自制镜像的容器docker run -d --name my-flask -p 5000:5000 my-flask-app:v1这里有个常见误区很多新手以为 Dockerfile 里写了 EXPOSE容器启动后外部就能访问了。其实 EXPOSE 只是“声明”并不会自动发布端口真正让外部能访问的还是-p参数做端口映射。5.2 用 Docker Compose 一键启动整套服务当服务变多以后一个个docker run命令会变得很啰嗦。比如一个 Web 应用需要有后端、数据库、缓存三个容器用三条命令分别启动还能接受但要加上网络配置、数据卷挂载、环境变量、重启策略命令行就会变得又长又难维护。Docker Compose 就是用来解决这个问题的。它通过一个 YAML 文件集中描述所有服务然后一条命令把所有容器都启动起来。我用docker-compose.yml来定义一个简单的全栈应用version: 3.8 services: db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: myapp volumes: - db_data:/var/lib/mysql ports: - 3306:3306 backend: build: ./backend depends_on: - db environment: DB_HOST: db DB_USER: root DB_PASSWORD: root123 ports: - 8080:8080 frontend: build: ./frontend depends_on: - backend ports: - 3000:3000 volumes: db_data:services下定义了几个容器服务build: ./backend表示从 backend 目录下的 Dockerfile 构建镜像depends_on控制启动顺序volumes声明数据卷。最后在项目根目录执行docker compose up -d所有服务就会在后台启动。查看状态用docker compose ps查看所有服务的日志用docker compose logs -f一键停止用docker compose down。Compose 最大的价值在于“可复现”。你在本地写好的整套环境配置提交到代码仓库之后同事只需要安装 Docker 和 Compose然后docker compose up就能把整套环境跑起来不再需要每个人手动装依赖。5.3 网络与日志基础排查容器之间的互通是实际使用中绕不开的问题。在 Compose 里服务名可以直接当作主机名来互相访问比如 backend 连接数据库时主机名填db就能连通因为 Compose 会自动创建默认网络并把所有服务加进去。如果你是手动docker run启动的多个容器要让它们互通就需要自己创建一个网络然后启动容器时指定加入该网络docker network create my-net docker run -d --name redis --network my-net redis:7 docker run -d --name app --network my-net my-flask-app:v1之后 app 容器里访问 Redis直接用redis:6379就可以了。容器名称在这里充当了主机名的角色这也意味着你不能在同一网络里给两个容器起相同的名字。日志排查方面docker logs是最直接的入口。看容器日志时重点观察启动阶段有没有报错、监听端口是否成功、连接依赖服务是否失败。比如一个容器报“连接被拒绝”通常说明它要访问的另一个容器没启动或者网络不在同一层这时候优先检查依赖服务的状态和网络配置。6. 常见问题与排查技巧实录6.1 拉取镜像超时的解决办法换镜像仓库地址这个应该是小白遇到最多的一个坑。执行docker pull时长时间没反应或者报网络超时多半是默认仓库地址在当前网络环境下连接不通畅。解决思路是让 Docker 使用其他可用的公共镜像仓库地址。在 Docker Desktop 里进入设置界面找到 Docker Engine 的配置项在 JSON 里添加registry-mirrors字段{ registry-mirrors: [ https://docker.m.daocloud.io ] }保存并重启 Docker 之后再拉镜像就会通过镜像地址去获取速度通常会快不少。Linux 上则需要编辑 Docker 的配置文件路径一般是/etc/docker/daemon.json没有就手动创建内容一样然后重启 Docker 服务。这里需要说明一下更换镜像仓库地址只是改变了镜像下载的来源渠道换来的是读取速度提升这个操作本身和网络服务类型无关。如果你在修改配置后还是超时优先检查配置文件格式是否符合 JSON 规范以及 Docker 服务是否已经重启成功。6.2 端口冲突的快速定位启动容器时报port is already allocated或者bind: address already in use说明宿主机的端口被占用了。先用下面的命令查看端口占用情况sudo lsof -i :8080或者用新版系统自带的sudo ss -tlnp | grep 8080找到占用进程之后要么停掉它要么换一个宿主机端口映射比如改成-p 8081:80。这个问题的本质是端口资源冲突和镜像本身没有关系。有时候docker ps -a能看到一些“死掉”的容器还占着端口资源把它们删掉就好。6.3 容器退出码和日志怎么看容器启动后立刻退出先不要急着重试第一步是看它的退出码。用docker ps -a可以看到 STATUS 列里类似Exited (1)的提示括号里的数字就是退出码。退出码 0 表示正常结束非 0 通常是程序执行过程中出了问题。比如退出码 1 一般是程序主动报错像 Python 脚本抛了异常退出码 137 表示进程被强制杀掉常见原因是内存超限被系统 OOM退出码 139 则是段错误多半和底层库不兼容有关。配合日志来看会更清晰docker logs 容器名如果日志量太大可以只看最后 100 行docker logs --tail 100 容器名看日志是排查入口但别只看日志末尾的报错往前翻一点启动流程中缺失的环境变量、文件路径问题往往才是真正原因。6.4 磁盘空间被 Docker 占满怎么清理使用 Docker 久了以后会发现磁盘占用越来越夸张。这主要是因为本地堆积了大量无用的镜像层、被删除但没清理的数据卷、以及已经停止运行的容器残留。查看占用情况可以用docker system df它会按镜像、容器、数据卷、构建缓存分别统计占用大小。一键清理所有未使用的资源docker system prune -a这个命令会删除所有没有被容器使用的镜像、所有停止状态的容器以及没有被容器引用的数据卷和构建缓存。执行之前它会列出将要删除的内容并让你确认不用担心误删。如果只想清理已停止的容器可以单独用docker container prune只想清理多余的镜像用docker image prune。我在实际运维中会每周跑一次docker system df看看有没有异常增大的资源再做针对性清理。这个习惯能让开发机保持清爽也能避免磁盘满了之后容器无法写日志、无法创建临时文件等连锁问题。我个人在实际操作中最大的体会是Docker 的学习曲线其实没有想象中陡峭。很多概念一开始不用追求彻底理解先照着命令跑起来遇到报错再一步步排查积累几次经验过后整个体系就会变得清晰起来。尤其是镜像和容器之间的关系、端口映射、数据卷挂载这三个点一旦你在实际项目里亲自动手配置过一两次就会真正内化。这篇笔记里的命令和配置我基本都是照着生产环境里的常见写法整理出来的直接复制到自己的项目里做调整就能用。如果你第一次跑容器遇到了问题记住一个基本原则先看日志再查退出码最后确认端口和网络配置绝大多数问题都能在这个流程里找到答案。