TL;DR: Docker 通过容器技术将应用及其依赖打包成标准化单元,解决"在我机器上能跑"的经典难题。本文覆盖核心概念、常用命令、Dockerfile 编写和 Docker Compose 编排,帮助你在 30 分钟内上手。
本文假设读者熟悉 Linux 基础命令和软件开发流程,无需容器经验。
为什么需要 Docker Link to heading
在没有容器之前,部署应用最头疼的问题是环境一致性——不同机器的操作系统、库版本、依赖配置差异,导致同一份代码行为不一致。虚拟机能解决这个问题,但每个 VM 需要独立的操作系统,启动慢、资源开销大。
Docker 的方案是容器:与宿主机共享内核,通过 namespace 和 cgroup 实现进程隔离和资源限制。结果是:
| 对比项 | 虚拟机 | Docker 容器 |
|---|---|---|
| 启动时间 | 分钟级 | 秒级 |
| 镜像大小 | GB 级 | MB 级 |
| 资源开销 | 完整 OS | 共享内核,几乎无额外开销 |
| 隔离级别 | 硬件级 | 进程级 |
核心概念 Link to heading
Docker 有四个必须理解的概念:
镜像(Image):只读模板,包含应用运行所需的一切——代码、运行时、库、环境变量。类似 OOP 中的"类"。
容器(Container):镜像的运行实例,可启动、停止、删除。类似"对象"。
Dockerfile:构建镜像的指令文件,每行指令生成一层只读层(layer),多层叠加形成最终镜像。
仓库(Registry):存储和分发镜像的服务,Docker Hub 是默认的公共仓库。
它们的关系:编写 Dockerfile → docker build 生成镜像 → docker run 启动容器 → 从仓库拉取或推送镜像。
安装与验证 Link to heading
以 Ubuntu 为例:
# 安装 Docker
curl -fsSL https://get.docker.com | sh
# 将当前用户加入 docker 组,避免每次 sudo
sudo usermod -aG docker $USER
验证安装:
docker --version
# Docker version 27.5.1, build 9f9e405
docker run hello-world
# Unable to find image 'hello-world:latest' locally
# latest: Pulling from library/hello-world
# Hello from Docker!
hello-world 的完整流程:本地查找镜像 → 不存在则从 Docker Hub 拉取 → 创建容器 → 执行入口命令 → 输出信息 → 容器退出。
常用命令速查 Link to heading
# 拉取镜像
docker pull nginx:1.27-alpine
# 运行容器(-d 后台运行,-p 端口映射,--name 命名)
docker run -d -p 8080:80 --name my-nginx nginx:1.27-alpine
# 查看运行中的容器
docker ps
# 查看所有容器(含已停止的)
docker ps -a
# 查看容器日志
docker logs my-nginx
# 进入容器 shell
docker exec -it my-nginx /bin/sh
# 停止/启动/删除容器
docker stop my-nginx
docker start my-nginx
docker rm my-nginx
# 查看本地镜像
docker images
# 删除镜像
docker rmi nginx:1.27-alpine
# 清理所有已停止的容器和悬空镜像
docker system prune
关键端口映射 -p 8080:80 的含义是:宿主机端口:容器端口。访问 localhost:8080 等价于访问容器内的 80 端口。
编写 Dockerfile Link to heading
以一个 Node.js 应用为例:
# Dockerfile
# 使用 Alpine 基础镜像,体积小(约 5MB)
FROM node:20-alpine
# 设置工作目录
WORKDIR /app
# 先复制依赖文件,利用 Docker 层缓存
# 只有 package.json 变动时才重新安装依赖
COPY package*.json ./
RUN npm ci --only=production
# 复制应用代码
COPY . .
# 暴露端口(仅文档作用,不实际映射)
EXPOSE 3000
# 启动命令
CMD ["node", "server.js"]
构建镜像:
docker build -t my-app:1.0 .
# 构建输出示例
# [+] Building 12.3s (10/10) FINISHED
# => [internal] load build definition from Dockerfile
# => [1/5] FROM docker.io/library/node:20-alpine
# => [2/5] WORKDIR /app
# => [3/5] COPY package*.json ./
# => [4/5] RUN npm ci --only=production
# => [5/5] COPY . .
运行:
docker run -d -p 3000:3000 --name my-app my-app:1.0
Dockerfile 最佳实践 Link to heading
# 1. 使用多阶段构建减小镜像体积
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 生产阶段只包含运行产物
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production
CMD ["node", "dist/server.js"]
# 2. 使用 .dockerignore 排除不必要的文件
# .dockerignore
node_modules
.git
.env
*.md
多阶段构建前镜像可能 1GB+,构建后通常可控制在 200MB 以内。
Docker Compose:多容器编排 Link to heading
实际项目很少只有一个容器。数据库、缓存、应用服务需要协同工作。Docker Compose 用 YAML 文件定义多容器应用。
# docker-compose.yml
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
- REDIS_URL=redis://cache:6379
depends_on:
- db
- cache
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=mydb
cache:
image: redis:7-alpine
volumes:
pgdata:
# 一键启动全部服务
docker compose up -d
# 查看服务状态
docker compose ps
# 查看日志
docker compose logs -f app
# 停止并清理
docker compose down
depends_on 控制启动顺序,volumes 实现数据持久化——容器删除后数据库文件不会丢失。
局限性与替代方案 Link to heading
Docker 不是万能方案,以下场景需要权衡:
- GUI 应用:容器主要面向后台服务,桌面应用容器化需要额外配置 X11 转发
- 强安全隔离:容器共享内核,逃逸漏洞理论上存在(虽然罕见)。高安全场景用 VM 或 gVisor/Firecracker
- macOS/Windows 性能:需要 Linux VM 层,磁盘 I/O 有 10-20% 损耗,可通过 VirtioFS 或 volume 挂载优化
- 复杂编排:Docker Compose 适合开发和简单部署,生产环境多容器调度推荐 Kubernetes
总结 Link to heading
Docker 的核心价值在于一次构建,随处运行——开发、测试、生产环境保持一致,消除环境差异带来的部署问题。
下一步可以深入的方向:
- Docker 网络模型(bridge、host、overlay)
- 容器健康检查和自动重启策略
- CI/CD 流水线中集成 Docker 构建
- 从 Docker Compose 迁移到 Kubernetes
进一步阅读 Link to heading
本文由 AI 生成