开发者 Docker 指南:镜像、容器与 Compose
你构建了一个在你机器上运行完美的 Web 应用,但当部署到服务器或与队友分享时,它却崩溃了。依赖缺失、版本冲突或环境变量错误。这就是经典的“在我机器上能跑”问题。Docker 通过将你的应用及其依赖打包成一个称为容器的标准化单元来解决这个问题。在本指南中,你将学习 Docker 的核心概念——镜像、容器和 Compose——以及如何使用它们来简化你的开发工作流。
什么是 Docker?
Docker 是一个用于在容器中构建、运行和交付应用的平台。容器是一个轻量级、独立、可执行的软件包,包含运行软件所需的一切:代码、运行时、系统工具、库和设置。容器将应用彼此隔离,并与底层基础设施隔离,确保跨环境的一致性。
镜像 vs. 容器
镜像是一个只读模板,包含创建容器的指令。它就像你的应用及其环境的快照。容器是镜像的可运行实例。你可以使用 Docker API 或 CLI 创建、启动、停止、移动或删除容器。
把镜像想象成类,容器想象成该类的对象(实例)。你可以从同一个镜像运行多个容器,每个容器彼此隔离。
使用 Dockerfile 构建镜像
Dockerfile 是一个包含构建镜像指令的文本文件。以下是一个 Node.js 应用的简单示例:
# Use an official Node.js runtime as a parent image
FROM node:18-alpine
# Set the working directory
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of the application code
COPY . .
# Expose the port the app runs on
EXPOSE 3000
# Define the command to run the app
CMD ["node", "server.js"]
要构建镜像,请运行:
docker build -t my-node-app .
-t 标志为镜像打上名称标签(my-node-app)。末尾的 . 指定构建上下文(当前目录)。
运行容器
有了镜像后,你可以运行一个容器:
docker run -p 3000:3000 -d my-node-app
这将主机的 3000 端口映射到容器的 3000 端口,并以分离模式(-d)运行。
常用命令:
docker ps– 列出正在运行的容器docker ps -a– 列出所有容器(包括已停止的)docker stop <container_id>– 停止一个容器docker rm <container_id>– 删除一个容器docker images– 列出镜像docker rmi <image_id>– 删除一个镜像
使用卷管理数据
容器是短暂的;当它们被删除时,数据会丢失。要持久化数据,请使用卷。卷是容器文件系统之外的目录,由 Docker 管理。
docker run -v my_volume:/app/data my-node-app
你也可以为开发挂载主机目录(绑定挂载):
docker run -v $(pwd):/app my-node-app
Docker Compose:多容器应用
现实世界的应用通常需要多个服务:Web 服务器、数据库、缓存等。Docker Compose 允许你使用 YAML 文件定义和运行多容器 Docker 应用。以下是一个带有 PostgreSQL 数据库的 Node.js 应用的 docker-compose.yml 示例:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:password@db:5432/mydb
depends_on:
- db
db:
image: postgres:15-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
使用以下命令运行整个栈:
docker compose up -d
这会构建 Web 服务、拉取 Postgres 镜像、创建网络并启动两个容器。depends_on 确保数据库在 Web 服务之前启动。
要停止并删除容器、网络和卷:
docker compose down -v
开发中 Docker 的最佳实践
- 使用 .dockerignore 将
node_modules和.git等文件排除在构建上下文之外。 - 利用多阶段构建 保持镜像小巧。例如,在一个阶段构建应用,仅将生产产物复制到更小的运行时镜像中。
- 固定版本 在 Dockerfile 中(例如,使用
node:18-alpine而不是node:latest)以确保可复现性。 - 使用卷持久化数据,并在开发期间使用绑定挂载实现代码实时重载。
- 保持容器无状态;将状态存储在卷或外部服务中。
对比:Docker vs. 虚拟机
| 方面 | Docker 容器 | 虚拟机 |
|---|---|---|
| 启动时间 | 秒级 | 分钟级 |
| 资源占用 | 轻量(共享 OS 内核) | 重量级(每个 VM 完整 OS) |
| 隔离性 | 进程级 | 硬件级 |
| 可移植性 | 高(可在任何运行 Docker 的地方运行) | 受限于 hypervisor 兼容性 |
常见问题
镜像和容器有什么区别?
镜像是定义应用及其环境的只读模板。容器是镜像的运行实例。你可以从同一个镜像创建多个容器。
什么时候应该使用 Docker Compose?
当你的应用由多个需要协同工作的服务组成时(例如,Web 服务器、数据库、缓存),使用 Docker Compose。它简化了开发和测试的编排。
如何在 Docker 中持久化数据?
使用卷将数据持久化到容器文件系统之外。卷由 Docker 管理,可以在容器之间共享。绑定挂载也可用于挂载主机目录。
准备好容器化你的下一个项目了吗?从为简单应用编写 Dockerfile 开始,然后逐步使用 Docker Compose 添加服务。如需更多开发者工具,请查看我们的 JSON Formatter 来验证和美化你的配置文件。