Docker 与容器是什么? 为什么需要? 如何快速开始使用?

2026年8月13日

💎 加入 E+ 會員方案 與超過千位工程師一同在社群成長,並獲得更多深度的軟體前後端學習資源

先前有读者在内容许愿中提到 E+ 中似乎没有关于 Docker 与容器 (container) 相关的内容,对此我们接下来会陆续有一系列的文章,从 Docker 与容器的基本介绍,到实战的运用。

在开头的第一篇文章中,我们会先介绍 Docker 与容器到底是什么? 为什么现在有那么多项目都会用? 具体来说 Docker 与容器解决了什么问题? 此外,我们也会通过一个最简单的示例,让过去还没接触过 Docker 的人能快速开始。

Docker 与容器是什么? 为什么要用 Docker?

相信多数人听过 Docker 是一套用来构建与执行容器化应用程序的平台与工具,不过容器究竟是什么? 事实上,容器这个概念与实体世界中会看到的集装箱很相似,都是像箱子一样 (在英文上,集装箱的英文跟容器的英文都是 container,所以很常看到实体集装箱会出现在 Docker 相关的图上)。但不同的地方在于,在软件世界的容器,是装着应用程序。

在这个箱子中,应用程序会有一套相对独立的执行环境,包含主机名称、IP 地址、硬盘等等。不过这些其实都是由 Docker 创建出来的虚拟资源;通过 Docker 的管理,这些资源能够被组合成一个让应用程序得以被执行的环境。

image

在一台电脑中,多个容器会使用同一个 CPU 与内存等硬件资源,并共享底层的操作系统内核。不过,每个容器仍有自己的进程、文件系统与网络环境,因此容器中的应用程序可以彼此隔离。

image

Docker 解决了什么问题?

在看完上面 Docker 实际做的事情后,可能还没办法很直观感受到其价值所在,或者 Docker 这项技术解决了什么问题,让我们在这个段落进一步说明。

在上面对 Docker 的介绍中,有提到容器提供一个让应用程序得以被执行的环境,这边的环境是个关键。在做软件开发时,写程序只是其中一环,要让代码跑起来,会需要有相对应的执行环境。由于使用的机器、安装包的版本,以及各种因素的不同,很可能某个项目能在你同事用的电脑跑起来,但是在你的电脑上跑不起来。

过去在没有使用 Docker 的情况下,有些人入职新团队后,光是要让项目能够跑起来,就得花很多时间。在安装环境时遇到问题,去找同事协助解决时,很常会听到类似以下的话「这个项目的 Node.js 版本要 18,但你装到 Node.js 22 所以跑不起来」,或是「这个的 Postgres 版本不同,才导致行为的不一致」。

通过容器,我们能把一个应用程序需要的执行环境包起来,让它可以用相对一致的方式,在不同机器上跑。因此,在使用 Docker 的情况下,当团队有新成员加入,要构建、部署、管理项目时,不管用哪一台机器,机器里的某个依赖是哪一个版本,只要通过 Docker,新成员拉下源码后,执行一个指令,就能在本机把所有东西构建并跑起来,从此不再担心要花很多时间构建环境 (备注:前提是团队已经妥善准备好 Dockerfile、Compose 设置与必要的初始化流程,这些概念我们在后面的文章会谈)。

因为这个特性,让现代的应用程序,可以很轻易地跑在云端、跑在数据中心,甚至是无服务器函数 (serverless function) 中。不管应用背后选择什么架构或技术栈,都能够通过 Docker 来构建、部署与管理。

为什么 Docker 出现前的解决方案会被 Docker 取代?

Docker 容器创建出的执行环境,让多个应用程序即使背后用的依赖版本不同,也不会有冲突、能同时运行,不会因为两个应用程序用的 Node.js 版本不同,就无法和平共处。这件事在 Docker 出现前,业界其实已经有技术在解决了。

在 Docker 之前比较广泛被使用的方法,是通过虚拟机 (VM)。在概念上,虚拟机跟容器其实很类似,都是提供一个箱子,让应用程序能在里头执行。不过两者的差异在于,虚拟机的箱子中包含操作系统,所以两个虚拟机彼此不会共享主机的操作系统。

image

由于一套操作系统可能就会占用好几 GB 的内存,也会消耗大量 CPU 时间,不共享操作系统就代表原本能给应用程序使用的资源,被操作系统占据。这就导致同样的资源,在使用虚拟机的情况下,能同时跑的应用程序数量下降。

对比之下,同样提供隔离环境,由于 Docker 容器会共享执行容器的机器上的操作系统,所以容器本身相对轻量。给定同样的硬件,能够执行的应用系统,会比用虚拟机多不少。

从一个最简单的示例开始用 Docker

如果要在本机当中使用 Docker,最简单的方式是先安装 Docker Desktop,或是社区中的替代方案。以我们自己的经验来说,因为是使用 Mac,OrbStack 这个替代方案,会是很不错的选择;除了更轻量更快、多数平常需要用到的功能都有覆盖外,个人版使用还是免费的。

在下载并安装完 Docker Desktop 或替代方案后,接着可以在终端中输入

docker --version

接着就会看到类似下面的这一段

Docker version xx.x.x, build xxxxx

要测试看看能跑起 Docker,可以在终端用以下指令

docker run hello-world

这时如果是第一次用 Docker,可能会看到这个信息

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world

下载完成后就会看到类似以下的信息

Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (arm64v8)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

最后我们可以执行 docker container ls --all,就会看到类似下方的信息

CONTAINER ID   IMAGE         COMMAND    CREATED         STATUS                     PORTS     NAMES
XXXXXXXXXXXX   hello-world   "/hello"   3 seconds ago   Exited (0) 2 seconds ago             goofy_ride

回顾一下刚刚做了什么

在上述的流程中,我们执行了 docker run hello-world 这个指令。我们可以把这个指令拆成三个部分来看。第一个是 docker 是 Docker 的命令行工具,也就是在终端里用来跟 Docker 沟通的入口,未来如果要请 Docker 做任何事,都可以用这个开头。

第二个是 runrun 会「建立并启动一个容器」。换句话说,这个指令不只是执行而已,如果本机没有需要的镜像 (image),就会先下载 (所以会看到 Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 这一段);接着根据镜像建立一个新的容器;最后启动这个容器里面默认要执行的程序。

而第三个 hello-world 则是镜像 (image) 的名称。镜像是在 Docker 中最重要的概念之一。我们可以把镜像理解成「容器的模板」,里面可能包含操作系统的基本文件 (但不包含独立操作系统内核)、程序语言环境、系统包、应用程序代码,以及默认要执行的命令。Docker 会根据这个模板,建立出一个可以执行的容器。

在实际执行完后,我们就会看到 Hello from Docker! This message shows that your installation appears to be working correctly. 开头的输出,这就代表你的 Docker 安装看起来可以正常运行。

在往下的信息可以看到背后 Docker 完成的几件事情

  • 第一步是 The Docker client contacted the Docker daemon.,也就是 Docker 的客户端 (docker 这个命令行工具),会联系 Docker 背后真正负责做事的常驻程序 (daemon)。
  • 第二步是 The Docker daemon pulled the "hello-world" image from the Docker Hub,也就是 Docker 的常驻程序会去 Docker Hub 抓取 hello-world 这个镜像。Docker Hub 是 Docker 官方提供的容器镜像注册服务,定位类似于 Docker 世界里的包中心 (就像 npm registry 之于 Node、RubyGems 之于 Ruby、crates.io 之于 Rust 的角色),在 Docker Hub 中有许多别人上传的镜像,可以抓取下来使用。
  • 第三步是 The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading.,这边就是前面有提到的,Docker 会根据镜像来创建容器。
  • 最后一步是 The Docker daemon streamed that output to the Docker client, which sent it to your terminal.。Docker 会把容器产生的输出结果,通过流的方式传给 Docker 的客户端。

要理解最后一步,会需要先认识 Docker CLI 与 Docker daemon 这两个组件。Docker daemon(dockerd)是在后台执行的常驻程序,负责建立和管理镜像、容器、网络等等。Docker CLI(docker)则是用户操作的命令行工具,它会通过 Docker API,把 docker run 等指令发送给 daemon 执行。

(备注:大家平常可能常听到的 Docker 引擎 (Docker Engine) 是包含 Docker daemon、Docker API 与 Docker CLI 的统称)。

镜像与容器的关系

在上面我们有提到镜像 (image) 这个概念,由于在 Docker 中,镜像与容器是最重要的概念,因此在这篇文章最后,我们会进一步讨论之间的关系。

前面提到可以把镜像理解成容器的模板;反过来说,容器则可以理解成从镜像跑起来的实例。如果要比喻,镜像像蛋糕模具,容器就是真的烤出来的蛋糕。我们可以用同一个镜像跑出很多个容器。

如果今天想要跑一个有 Node.js 20 版本的执行环境,我们可以用 docker run node:20 这个指令。如果在终端输入这个指令,Docker 就会去下载 node:20 的镜像,并启动该容器。

因此举例来说,大家可以在终端中输入

 docker run -it node:20

就能够在终端开启一个 Node.js 20 版本的执行环境,然后就能直接在终端使用 Node.js。例如下面这个截图,在输入完 docker run -it node:20 后,会得到 Welcome to Node.js v20.20.2,接着就能在终端写 JavaScript 程序。

!CleanShot 2026-06-20 at [email protected]

这边我们加上了 -it,对 node 这个官方镜像来说,如果今天没有加上 -it,容器会启动一下后就马上结束。因为如果没有一个可以互动的终端环境,从容器的角度来看就会觉得「没有输入、也不是互动模式,那我没事做」于是就会结束 (备注:这并不代表所有容器都必须加上 -it。例如执行网站服务器或数据库的容器,只要主要程序仍在运行,容器就会继续执行)。

这个 -it 是由以下两个参数组合而成

  • i 是 interactive,意思是保持标准输入开启,让用户可以跟容器互动。换句话说,如果想要在容器里输入东西,就需要加上这个参数。
  • t 是 tty,意思是配置一个 pseudo-TTY。可以理解成让这个容器里的命令行环境比较像正常终端,可以显示提示符、支持互动操作。

所以 docker run -it node:20 会等同于 docker run --interactive --tty node:20

前面谈到,镜像像是模板,可以用镜像建立多个同样环境的容器。所以假如开启多个终端,每个都输入 docker run -it node:20,就能够在本机起多个彼此独立的 Node.js 20 版执行环境。

进一步说,我们可以开一个终端分页输入 docker run -it node:20,然后另一个终端分页输入 docker run -it node:18,然后第三个终端分页输入 docker run -it node:22,就能在本机起三个不同版本的 Node.js 执行环境,彼此独立不会互相干扰。

还记得最开头提到,过去很多工程团队有新成员加入,在安装开发环境时会遇到版本问题,所以导致环境装不好,或者遇到冲突。但通过 Docker 起不同的容器,即使本机是 Node.js 18 版,也完全不用担心项目要求 Node.js 20 版会有冲突的问题。

总结

在这篇文章我们初步介绍了为什么需要 Docker,以及用一个最简单的 hello-world 示例,介绍了如何在本机中使用 Docker,也了解了包含镜像、容器等不同概念彼此之间的关系。

上面有提到假如跑 docker run node:20,Docker 引擎会去 Docker Hub 这个官方提供的公开镜像仓库抓取对应的镜像。但 Docker 的镜像不只是存在于 Docker Hub,假如自己想要定制某个开发环境,也可以自己构建镜像。在这个系列的下一篇文章,我们将会接着讨论如何做这件事。


加入 E+ 会员方案

如果你觉得这篇分享有帮助,想阅读更深入的内容,欢迎加入 E+ 会员。除了有会员专属的深度文与影片,探讨前后端开发、AI 工程、职业发展,同时有 Discord 社群一同交流与成长。

许多 E+ 会员通过所任职公司的教育训练补助 (E+ 会开立有统编的发票),不用花自己的钱,也能每周通过深度主题文与社群交流,拓展技术视野、加速职业成长。

🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們