尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Docker 入门:镜像、容器、数据卷、网络与 Compose 核心概念
如果你也被“在我电脑上是好的”这句话折磨过那迟早会和 Docker 打上交道。容器技术解决的最核心问题不是“让服务器更轻”而是把整套运行环境——系统依赖、库文件、配置、代码——一起打包成一个标准件拿到哪台机器上结果都一样。这篇内容适合刚接触容器、还没系统理清概念的人我会把镜像、容器、数据卷、网络、Compose 这些绕不开的基础知识按照实际使用时的逻辑重新讲一遍也会分享一些踩过之后才知道的细节。1. 为什么 Docker 值得你花时间环境不一致的终极解法先不急着敲命令得先把“为什么要用”这件事想明白。否则你只会觉得 Docker 是个比虚拟机轻一点的工具用起来还得学一堆新概念性价比不高。实际上 Docker 能火起来核心原因是它把“部署应用”这件事从“在机器上装环境”变成了“把环境一起带走”。以前部署一个 Web 服务流程大概是买一台服务器装操作系统装语言运行时装数据库手动改配置文件再跑一堆初始化脚本。哪个环节版本不对、路径不对、权限不对都能让服务起不来。最麻烦的是这些问题往往不是立刻暴露而是等流量上来之后才出各种怪问题。后来有人尝试用虚拟机解决把整个系统快照下来到哪都能跑但虚拟机太重了动辄几个 GB启动要几分钟性能损耗也明显。Docker 换了个思路不虚拟整套硬件和操作系统而是共享宿主机的操作系统内核只把“应用运行时需要的文件、依赖、配置”打包进一个镜像。启动容器相当于直接跑一个进程秒级完成磁盘占用也只有几十到几百 MB。我见过很多团队从虚拟机迁到容器之后最直观的感受不是省了多少资源而是“新环境从搭建到跑通”的时间从两三天缩短到了几分钟。1.1 虚拟机 vs 容器两种“隔离”思路虚拟机和容器都叫“隔离”但隔离的东西不太一样。虚拟机隔离的是“整个系统”每台虚拟机里有完整的操作系统有自己的内核Hypervisor 负责分配硬件资源。容器隔离的是“进程和文件系统”所有容器共用宿主机的内核但在文件系统、进程、网络、用户等方面做了隔离容器里的进程以为自己在独立的系统里跑。可以用一个不太严谨但好懂的类比虚拟机像是你租了一整套房子里面水管、电线、家具全是你自己的和邻居完全隔开容器像是住在一栋统一管理的大楼里墙和公共管线是共用的但房间钥匙、内部装修是你的你想装成什么样就装成什么样。这个差异直接决定了两个特性。第一容器的启动速度远快于虚拟机因为它不需要引导操作系统只需要启动一个进程。第二容器的资源开销远小于虚拟机因为不需要为每个实例维护一整套操作系统内核和系统服务。代价是容器里的进程直接跑在宿主机内核上隔离强度不如虚拟机所以生产环境通常会在命名空间、权限、安全策略上再做加固。1.2 镜像分层与共享为什么拉取快、占用小很多人第一次用 Docker 时会有一个疑问我明明只拉了一个应用镜像为什么输出里会显示十几层“Pull complete”这些层就是镜像分层结构——每个镜像不是一大块数据而是由一系列只读层叠加而成每一层对应 Dockerfile 里一条指令产生的变更。这个设计的最大好处是共享。宿主机上存着多个镜像时相同的基础层只存一份。比如你同时拉了基于某个 Linux 发行版的多个应用镜像它们底层的系统库层是同一份数据不需要重复下载、重复占磁盘。实际使用中你会发现拉一个常见基础镜像可能只要几秒钟很多层是本机已经存在的。分层还带来另一个实用特性增量拉取。如果某个镜像更新了应用层但是底层基础镜像没变你重新拉取时只需要下载变化的那几层。这一点在镜像更新频繁、带宽又不太充裕的场景里特别管用。理解了这一点后面看 Dockerfile 时你就会明白为什么调整指令顺序会影响构建缓存命中率。2. 镜像和容器先分清这两个最容易混淆的概念Docker 里出现频率最高的两个词就是镜像和容器但新手往往把它们当成一个东西。我自己的经验是一旦把这两者的关系真正想明白后面学什么命令都很快。镜像Image是一个只读的、静态的文件包里面包含了运行某个应用所需的全部内容代码、运行时、系统库、配置、环境变量等。它本身不运行是“死”的。容器Container是镜像被加载到内存并启动进程之后形成的运行实例是“活”的。同一个镜像可以同时启动出多个容器它们彼此独立、互不影响。这和面向对象里的“类与实例”很像镜像是类定义好了模板容器是根据模板创建出来的对象每个对象有自己的状态。你改一个对象的状态不会影响类本身也不会影响其他对象。2.1 一个关于“可写层”的关键细节镜像本身是只读的那容器运行时产生的文件写到哪去了答案是在镜像之上叠加了一层“可写层”。镜像所有层都是只读的启动容器时Docker 在最上面添加一个临时可写层。容器里的所有写操作——创建文件、修改配置、安装软件——都发生在这个可写层里。容器被删除时这一层跟着消失而底层镜像完全不受影响。换句话说容器运行期间折腾出来的所有东西都是“临时”的。你可以在容器里安装一堆调试工具也能跑得很欢但容器一删这些改动统统没了。这正是很多人踩过的坑在容器里手动改完配置、装完数据以为一切保存好了结果容器一重启或一删除发现什么都没了。所以任何需要持久化的东西都不能只留在容器可写层里要么在镜像构建时就固化好要么挂载数据卷把数据写到宿主机上。这个我在后面的数据卷部分会详细展开。2.2 容器的生命周期怎么创建、怎么停止、怎么删除理解容器生命周期命令就很好记了。整个过程大概是创建容器、启动进程、运行中、停止、删除。docker run是核心入口它做的事可以拆成几步先去本地找镜像没有就自动从仓库拉取然后基于镜像创建一个新容器最后在该容器里启动指定的进程。所以docker run本质上等于“拉取 创建 启动”的组合。运行中的容器用docker ps查看加上-a参数可以连已停止的容器一起列出。停止容器用docker stop它会给容器主进程发送停止信号让进程优雅退出强制停止用docker kill信号发送过去之后基本是直接终止。删除容器用docker rm已经停止的容器才能干净删除如果你不想先停再删可以用docker rm -f直接强制删除。我建议新手记住一个原则容器是很容易重建的东西。真正宝贵的是镜像、数据卷和配置文件容器本身只是这些要素的“运行组合体”。所以不要心疼删容器多删几次你对编排的理解会深很多。3. 每天都要用的 Docker 命令清单照着敲就行基础命令不需要背多用几次自然就记住了。但有几个高频场景的命令我会放到一起说方便你快速查阅镜像管理、容器运行、进入容器与看日志、系统清理。3.1 镜像管理拉取、查看、删除从仓库拉取镜像用docker pull后面跟镜像名和标签docker pull nginx:latest标签tag用来区分同一个镜像的不同版本latest是最常见的默认标签但在生产环境里我建议尽量指定明确版本比如改用具体的版本号标签而不是依赖latest否则某天更新可能带来不兼容变化。查看本地已有的镜像docker images删除镜像用docker rmi可以跟镜像 ID 或镜像名加标签。如果这个镜像还有容器在使用删除会报错需要先停掉并删除相关容器或者用-f强制删除。清理无用的悬空镜像没有任何标签且没有被容器使用的镜像可以用docker image prune3.2 容器运行run 的参数到底该怎么给docker run的参数非常多刚开始不需要全记先把这几个高频的用好docker run -d -p 8080:80 --name web nginx:latest-d后台运行不占用当前终端。调试时可以去掉-d换成前台运行日志直接打在终端里。-p端口映射格式是“宿主机端口:容器端口”。这里把宿主机的 8080 端口映射到容器里的 80 端口访问宿主机 IP 的 8080 就能访问容器里的 Web 服务。--name给容器起个名字。不写的话 Docker 会随机生成一个名字虽然能用但在管理多个容器时非常痛苦。--rm容器停止后自动删除适合临时测试避免堆积垃圾容器。--restart设置重启策略比如unless-stopped表示除非手动停止否则容器退出后会自动重启。线上服务通常会设置这个策略。进入正在运行的容器最常用的是docker exec -it web /bin/bash-i保持标准输入打开-t分配一个终端。进入容器后你就可以像在一台精简版 Linux 机器里一样查看文件、跑命令。不过基础镜像通常很精简可能没有vim、ping这些工具需要先安装或直接通过日志排查。3.3 日志与系统清理排障和防止磁盘膨胀看容器日志docker logs -f web-f表示持续跟踪新日志退出按 Ctrl C。排障时这是第一利器90% 的“容器怎么起不来”问题都可以靠日志定位。系统清理方面Docker 用久了磁盘会越来越大尤其是日志文件和悬空镜像。查看占用情况docker system df一把清理所有未使用资源docker system prune -a这会删除所有未被使用的镜像、停止的容器、网络和构建缓存。注意它是把“没用到的”都清理掉如果某些镜像你有保留需求先确认它们正在被使用否则不要随便加-a。4. Dockerfile把“环境”固化成一段可读的配方命令行只是第一步真正让项目可复现的关键是 Dockerfile。它是一份声明式的环境配置文档规定了用什么基础镜像、拷贝哪些文件、安装什么依赖、启动什么命令。有了它任何一个人拿到项目之后执行一次构建得到的镜像基本是一致的。4.1 Dockerfile 的骨架FROM、WORKDIR、COPY、RUN、CMD一个最简单的 Node.js 服务 Dockerfile 大概长这样FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3000 CMD [node, server.js]逐条拆开讲讲为什么这么写。FROM指定基础镜像。选镜像的关键是“够用就好”不需要什么都装。node:20-alpine是基于 Alpine Linux 的精简镜像体积比完整版小很多适合装完依赖直接跑。WORKDIR设置工作目录。之后的命令都会在这个目录下执行如果目录不存在Docker 会自动创建。COPY把宿主机上的文件复制进镜像。注意这里有个顺序问题我先复制了package.json和package-lock.json再运行npm install最后才复制项目源码。这是有意为之。因为 Docker 构建时会缓存每一层的结果如果先把整个项目的文件都复制进去再执行npm install那么任何源码改动都会导致依赖安装那一层缓存失效每次构建都要重新下载依赖。先复制依赖清单、安装依赖、再复制源码源码改动时只有最后两层需要重建速度会快很多。CMD是容器启动时要执行的命令。这里用数组形式书写表示直接执行可执行文件没有经过 shell 包装。这个细节有讲究后面讲坑的时候会说。4.2 多阶段构建为什么镜像能瘦一大圈构建某些项目时编译阶段需要完整的编译工具链但运行阶段根本不需要。多阶段构建就是利用 Dockerfile 里多个FROM把构建和运行分开。看一个 Go 项目的经典例子FROM golang:1.22-alpine AS builder WORKDIR /src COPY . . RUN CGO_ENABLED0 go build -o app . FROM alpine:latest WORKDIR /app COPY --frombuilder /src/app /usr/local/bin/app CMD [app]第一阶段用带 Go 编译器的镜像编译出二进制文件第二阶段直接用精简的 Alpine 镜像把编译产物复制进去。最后得到的镜像里只有二进制文件不包含任何编译工具链。这种方式对很多语言项目都适用最终镜像体积能减小一个数量级推送和拉取都快很多。4.3 搭镜像的几条实用原则第一基础镜像尽量选官方镜像和精简版本不要什么都往里面装。镜像体积越大构建、拉取、启动的速度都受影响。第二把“不常变的内容”放在 Dockerfile 前面“频繁变的内容”放后面充分利用构建缓存。第三安全相关的更新放在基础镜像层面解决而不是每次在容器里手动打补丁。我现在写 Dockerfile 的习惯是先保证能跑通再考虑瘦身最后才加各种优化。不要一开始就追求最薄的镜像那会让你把大量时间花在折腾底层依赖上。5. 数据持久化容器删了数据还在前面提到容器可写层是临时的容器一删数据就没了。那数据库、上传文件、日志这类要长期保存的数据怎么办答案是数据卷Volume和挂载Mount。5.1 为什么不能直接往容器里写文件容器本质上是一堆可执行文件和运行配置的组合它应该是“可替换的”。今天跑一个容器明天因为镜像更新或者配置调整你可能需要删掉它再起一个新的。如果把数据放在容器可写层里更新版本就意味着丢数据。所以凡是需要长期保留的内容都应该写到宿主机文件系统上再通过挂载的方式“借给”容器使用。这样容器本身可以随时重建数据却保留在宿主机上。5.2 bind mount 和 named volume选哪种Docker 提供两种主要的持久化方式绑定挂载和命名卷。对比项绑定挂载bind mount命名卷named volume宿主机目录位置由你指定如/data/appDocker 统一管理不需要你指定路径适合场景本地开发调试代码改完立即生效生产环境数据持久化、多容器共享维护方式直接操作目录文件用docker volume命令管理可用docker volume prune清理数据一致性目录权限由宿主机决定Docker 创建卷时可以按镜像要求初始化目录权限绑定挂载的典型写法docker run -d -p 3000:3000 -v /home/user/app:/app your-app-image把宿主机/home/user/app目录挂载到容器里的/app两边的数据是实时同步的。开发时改代码不需要重新构建镜像容器里的进程运行着新代码前提是服务支持热加载或者你手动重启容器内进程。命名卷的典型写法docker volume create appdata docker run -d -v appdata:/var/lib/data your-app-image数据实际存在 Docker 管理的路径里但你不必关心具体在哪。备份时用docker run --rm -v appdata:/data alpine tar -czf /data-backup/data.tar.gz -C /data .这类方式处理。这里有个我见过很多次的误区以为-v后面写宿主机路径就一定是绑定挂载。实际上如果冒号左边写的是纯名称不含/Docker 会把它当成命名卷处理如果写的是以/开头的绝对路径才是绑定挂载。两者的管理方式差异很大很容易搞混建议刚开始用时多看几眼命令写法。6. 网络基础端口映射与容器互访容器之间、容器和宿主机之间怎么通信是另一个高频困惑点。默认情况下容器只有自己的网络命名空间外部并不能直接访问它的端口需要通过端口映射把容器的端口“暴露”到宿主机上。6.1 -p 到底做了什么-p 8080:80的含义是访问宿主机的 8080 端口时流量会被转发到容器的 80 端口。实际操作中你可以把宿主机端口换成任意没有被占用的端口比如-p 8080:80、-p 3000:3000。反向理解也很有用如果你发现某个端口访问不了检查两件事——容器里进程是否真的监听在容器端口上以及宿主机端口映射是否写对。很多时候问题是服务监听在容器内的127.0.0.1上只对容器内部可见外部映射完全进不去这时候要让进程监听0.0.0.0。6.2 自定义网络让容器用服务名互相访问当有多个容器需要协作时比如一个 Web 服务要连数据库容器新手最容易想到的办法是“用数据库容器的 IP 地址来访问”。这能用但容器重建后 IP 可能会变配置里 IP 就得跟着改非常脆弱。更好的做法是创建一个自定义 bridge 网络让容器加入同一个网络后直接用容器名互相访问。Docker 内置的 DNS 服务会自动把容器名解析成对应 IP。实际操作是docker network create mynet docker run -d --network mynet --name db mysql-image docker run -d --network mynet --name app your-app-image之后在app容器里直接用db这个主机名就能访问数据库容器比如数据库连接串里写db而不是 IP。容器重建多少次只要名字还叫db连接关系就不会断。这个模式在本地开发和单机多容器编排时尤其好用也是后面 Compose 能简化多容器管理的基础。7. Docker Compose一份 YAML 管理整套服务单机跑两三个容器时手动敲命令还能忍。一旦服务变多你会发现启动顺序、网络、数据卷、环境变量都要手动管理命令也越来越长。Docker Compose 就是用来解决这个问题的用一份 YAML 文件描述整套服务一条命令全部启动。7.1 为什么推荐 Compose一套环境一个文件搞定Compose 的核心价值是“把环境配置代码化”。项目里提交一份docker-compose.yml任何同事拉下来后执行docker compose up -d就能在当前机器上跑起完全一致的环境。这比在文档里写“先装这个、再改那个配置”要可靠得多。而且 Compose 会自动创建项目专属网络容器之间直接用服务名互访数据卷也在同一份文件里声明。整个环境的状态一目了然。7.2 一个可运行的 Compose 示例Web 服务加数据库以 Node.js 服务连 Redis 为例配置文件内容大致如下services: app: build: . ports: - 3000:3000 environment: - REDIS_HOSTredis depends_on: - redis redis: image: redis:7-alpineapp服务用当前目录的 Dockerfile 构建redis服务直接用公共仓库里的 Redis 镜像。app通过environment里的REDIS_HOSTredis拿到 Redis 服务名在代码里连接redis这个主机名就能访问数据库容器。depends_on表示启动顺序先启动redis再启动app省去不少手动等待的时间。7.3 常用的 Compose 命令docker compose up -d # 构建并后台启动所有服务 docker compose down # 停止并删除所有容器和默认网络 docker compose logs -f # 查看所有服务日志 docker compose exec app bash # 进入某个服务容器需要注意新旧版本命令差异早期版本命令是docker-compose现在主流是docker compose中间有空格。如果你用的是老版本记得把命令里的空格换成-。配置文件里有变更时重新执行docker compose up -d会按变更重建相关服务日常更新代码后基本靠这一个命令完成重新部署。8. 先用起来再慢慢深入几个绕不开的坑和我的建议最后聊几个新手最容易踩、但文档里不会专门强调的点。8.1 容器里的进程和日志藏着两个隐患第一个坑是容器主进程的退出信号问题。如果 Dockerfile 里写的是CMD [sh, -c, node server.js]那容器的主进程其实是sh而不是node。当你执行docker stop时Docker 给主进程发退出信号sh不一定会把信号转交给node结果容器迟迟不退出最后 Docker 只能等超时后强制杀。写 Dockerfile 时尽量让最终命令直接执行目标进程不要多包一层 shell。第二个坑是日志文件无限增长。容器里的进程如果一直往标准输出写日志Docker 会收集这些日志并写到宿主机上的日志文件里。日志文件越滚越大慢慢把磁盘占满。可以在 Docker 配置文件里加上日志轮转限制比如单个日志文件最大 10 MB、最多保留 3 个文件具体配置可以查你所用环境的 Docker 日志配置说明。8.2 镜像拉取慢先检查网络和加速配置很多新手第一次拉镜像就卡在下载阶段于是开始怀疑是不是哪里操作有问题。实际上公共镜像仓库的下载速度受网络环境影响不同网络环境下差异很大。如果你也遇到拉取特别慢的情况比较常见的解决办法是给 Docker 配置一个合适的镜像加速地址在 Docker 配置文件中添加registry-mirrors字段然后重启 Docker 服务。{ registry-mirrors: [https://registry.example.com] }地址需要填写你所在网络环境实际可用的加速服务。配置完成后再拉一次速度通常会有明显提升。另外拉取镜像时尽量选择精简基础镜像数据量小自然拉得快。8.3 一条更适合新手的路径不要试图一次把所有概念都学完。先只做一个容器跑一个简单的服务折腾明白docker run、docker ps、docker logs然后写一份 Dockerfile把项目容器化再学习数据卷把数据留在宿主机上最后才上 Compose 管理多个服务。每一步都在真实场景里用过之后再去研究镜像瘦身、存储驱动、容器编排这些进阶内容。我自己到现在还会偶尔用docker system df看看磁盘占用情况也会定期清理多年前的旧镜像。Docker 的学习曲线并不陡真正的门槛在于是否理解“镜像只读、容器临时、数据要挂载”这三个基本心智模型。想明白这三点你碰到的大部分问题都能自己推断出原因了。
RELATED

相关推荐

离线安装deb包:构建APT本地仓库与依赖解析实践

离线安装deb包:构建APT本地仓库与依赖解析实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/23 7:11:53
电机驱动器通讯中断导致失控的排查与根治方案

电机驱动器通讯中断导致失控的排查与根治方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/23 7:06:53
英雄联盟语音没声音排查:声卡驱动与音频服务修复

英雄联盟语音没声音排查:声卡驱动与音频服务修复

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/23 7:06:53
MORE NEWS

更多资讯

📰

基于 Spring Boot 的研究生导师信息管理系统设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

基于 Spring Boot 的学校浴室管理系统设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

ADK A2A 实战:从零跑通分布式智能体协作,把孤岛智能体连成网络

ADK A2A 实战:从零跑通分布式智能体协作,把孤岛智能体连成网络 【免费下载链接】adk-docs An open-source, code-first toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control. 项目地址: https://g…

📰

如何快速选对 Rime 输入方案:awesome-rime 完整指南

如何快速选对 Rime 输入方案:awesome-rime 完整指南 【免费下载链接】awesome-rime A curated list of Rime IME schemata and configs | Rime 輸入方案和配置列表 项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-rime 拼音候选翻不完&#xff0…

📰

平衡车FOC固件改造完全实战:hoverboard-firmware-hack-FOC 上手指南

平衡车FOC固件改造完全实战:hoverboard-firmware-hack-FOC 上手指南 【免费下载链接】hoverboard-firmware-hack-FOC With Field Oriented Control (FOC) 项目地址: https://gitcode.com/GitHub_Trending/ho/hoverboard-firmware-hack-FOC 平衡车FOC固件项目…

📰

Minecraft启动器PCL2:免费开源,5分钟跑起来还能自动装Java

Minecraft启动器PCL2:免费开源,5分钟跑起来还能自动装Java PCL2(Plain Craft Launcher 2)是一款免费开源的Minecraft启动器:自动匹配并下载Java、多版本并存、一键导入模组整合包,下载、启动、崩溃排查全包…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬