开发者 Docker 指南:镜像、容器与 Compose

DevOps2026-10-12TryQuickToolBox

你克隆了一个项目,按照安装说明操作,结果卡住了:缺少系统库、数据库无法启动,或者 Node 版本不匹配。Docker 的存在就是为了消除这类问题。与其直接在机器上安装各种服务,不如一次性描述它们,然后在隔离的容器中运行,无论在哪里行为都一致。

本指南讲解你真正需要的三个概念:镜像、容器和 Compose。内容注重实用,边读边跑命令即可。

镜像与容器:心智模型

镜像是一个只读模板,包含文件系统快照以及默认命令、暴露端口等元数据。容器则是该镜像的一个运行(或停止)实例,顶部带有一层薄薄的可写层。

如果你有面向对象编程背景,这个类比很贴切:镜像是类,容器是对象。你可以从一个镜像启动多个容器,而容器内的改动不会修改镜像本身。

概念是什么常用命令
镜像不可变模板docker build、docker pull
容器运行实例docker run、docker ps
卷持久化存储docker volume create
Compose多容器定义docker compose up

用 Dockerfile 构建第一个镜像

Dockerfile 是一份配方。每条指令创建一个层,Docker 会缓存这些层,所以当没有变化时重建非常快。下面是一个简单的 Node.js 示例:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

这里有两个重要习惯。第一,先复制依赖清单再复制其余源码,这样当只有应用代码变化时,npm ci 层仍能命中缓存。第二,优先使用像 alpine 这样的精简基础镜像,以减小体积和攻击面。

构建并运行:

docker build -t myapp:1.0 .
docker run --rm -p 3000:3000 myapp:1.0

-p 3000:3000 将宿主机端口 3000 映射到容器端口 3000。--rm 会在容器退出时删除它,开发时能保持机器整洁。

日常容器命令

以下命令覆盖了大部分日常工作:

当容器立即退出时,先看日志。最常见的原因是 CMD 错误、缺少环境变量,或者进程在容器内绑定到了 127.0.0.1 而不是 0.0.0.0。

Compose 为什么存在

真实应用通常需要多个进程:一个 API、一个数据库,可能还有一个缓存。用一长串参数运行三个 docker run 命令很容易出错。Docker Compose 让你在一个 YAML 文件中声明所有服务,并一起启动。

services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://app:secret@db:5432/app
    depends_on:
      - db
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: app
    volumes:
      - db_data:/var/lib/postgresql/data
volumes:
  db_data:

注意 API 连接的是 db,而不是 localhost。Compose 会创建一个私有网络,每个服务都可以通过其服务名访问。这是新手最常见的困惑来源。

用 docker compose up 启动所有服务,或加 -d 在后台运行。代码变更后用 docker compose up --build 重建。用 docker compose down 拆除,如果还想删除命名卷,加上 -v。

卷、绑定挂载与热重载

容器是临时的;里面的数据在删除容器后就会消失。有两种机制可以解决:

  1. 命名卷 — 由 Docker 管理,适合数据库文件。它们能在容器重启后保留,并在 volumes: 部分声明。
  2. 绑定挂载 — 将宿主机目录映射到容器内,适合开发时的源代码。
services:
  api:
    build: .
    volumes:
      - ./src:/app/src

使用绑定挂载后,在宿主机上编辑文件会反映到容器内。再配合文件监视工具(nodemon、uvicorn --reload、air),就能实现热重载而无需重建镜像。

实用的开发工作流

  1. 为应用编写 Dockerfile,保持精简。
  2. 添加 docker-compose.yml,包含应用及其依赖。
  3. 源代码用绑定挂载,数据用命名卷。
  4. 不要把密钥写进文件;使用 .env 文件并通过 ${VAR} 引用变量。
  5. 添加 .dockerignore,包含 node_modules、.git 和构建产物,以加快构建速度。
  6. 运行 docker compose up --build 并迭代。

如果你的服务产生的日志之后需要检查,请记住容器日志只是文本。解析服务器日志的工具,例如 nginx 日志分析器,与容器配合得很好,因为你可以将日志管道输出或复制出来,而无需在宿主机上安装任何东西。

常见陷阱

常见问题

镜像和容器有什么区别?

镜像是包含文件系统和元数据的不可变模板。容器是该镜像的一个运行实例,带有一层额外的可写层。你可以从一个镜像启动多个容器。

单个服务也需要 Docker Compose 吗?

不需要。单个容器用一条 docker run 命令就够了。当你有多个服务、共享网络或想在一个地方声明环境变量时,Compose 才有价值。

容器之间如何通信?

Compose 将所有服务放在一个共享网络中,每个服务都可以通过其服务名访问。用该名称作为主机名,例如 db:5432,而不是 localhost。

Docker 值得你投入一点前期时间。一旦理解了镜像、容器和 Compose,帮助队友上手或更换机器就不再是繁琐的安装仪式,而是一条命令的事。