Skip to main content

Docker 容器化技术

对于全栈开发者而言,掌握 Docker 是从「本地开发」走向「生产部署」的必经之路。Docker 解决了环境不一致的问题,使应用的打包、分发和运行变得异常简单。

1. 核心概念

  • 镜像 (Image):一个只读的模板,包含了运行应用所需的代码、运行环境(如 Node.js)、系统库等。可以类比为面向对象编程中的「类」。
  • 容器 (Container):镜像的运行实例。可以被启动、停止、删除。容器之间相互隔离。可以类比为「对象」。
  • 仓库 (Repository):集中存放镜像的地方(如 Docker Hub、阿里云镜像仓库等),类似于 GitHub。
  • Dockerfile:一个文本文件,包含了一系列指令,用于自动化构建 Docker 镜像。

2. 为什么全栈开发需要 Docker?

  1. 环境一致性:“在我的电脑上运行正常”——Docker 通过打包操作系统级依赖,保证了开发、测试、生产环境的完全一致。
  2. 隔离性:一台服务器上可以运行多个不同版本的 Node.js/数据库实例,互不干扰。
  3. 快速部署与扩缩容:启动容器只需毫秒级到秒级,配合 CI/CD 极大地提升了交付效率。
  4. 易于集成基础设施:配合 docker-compose 可以一键启动项目所需的所有依赖(Redis、MySQL、MQ 等)。

3. 常用基础命令

镜像操作

docker pull node:20-alpine       # 拉取镜像
docker images # 查看本地镜像
docker rmi <image_id> # 删除镜像
docker build -t my-app:1.0 . # 根据当前目录下的 Dockerfile 构建镜像

容器操作

docker ps                        # 查看正在运行的容器 (加 -a 查看所有)
docker run -d -p 3000:3000 my-app# 启动容器 (-d 后台运行,-p 端口映射)
docker stop <container_id> # 停止容器
docker rm <container_id> # 删除容器
docker logs -f <container_id> # 持续查看容器日志
docker exec -it <container_id> sh # 进入容器内部终端

4. 编写高质量的 Dockerfile (Node.js 最佳实践)

对于 Node.js / 前端全栈项目,编写 Dockerfile 时最关键的是减小镜像体积利用缓存加速构建

标准多阶段构建方案

# ============================
# 阶段 1:构建阶段 (Builder)
# ============================
FROM node:20-alpine AS builder

# 设置工作目录
WORKDIR /app

# 优先拷贝 package.json 和 lock 文件
# 目的:只要依赖没变,就可以利用 Docker 层缓存,跳过耗时的 npm install
COPY package.json package-lock.json ./

# 安装所有依赖(包含 devDependencies)
RUN npm ci

# 拷贝源代码并进行构建 (如 TypeScript 编译, Webpack 打包等)
COPY . .
RUN npm run build

# ============================
# 阶段 2:生产运行阶段 (Runner)
# ============================
# 使用极简的 alpine 基础镜像
FROM node:20-alpine AS runner

# 设置环境变量为生产环境
ENV NODE_ENV=production
WORKDIR /app

# 仅拷贝 package.json 和 lock 文件
COPY package.json package-lock.json ./

# 只安装生产环境依赖,减小体积
RUN npm ci --omit=dev

# 从构建阶段拷贝编译后的产物
COPY --from=builder /app/dist ./dist

# 暴露服务端口
EXPOSE 3000

# 启动命令
CMD ["node", "dist/main.js"]

.dockerignore 文件的必要性

必须在项目根目录创建 .dockerignore,防止无用或敏感文件进入构建上下文:

node_modules
npm-debug.log
dist
.git
.env

5. Docker Compose 多容器编排

在全栈开发中,一个应用往往需要数据库(MySQL / PostgreSQL)和缓存(Redis)。使用 docker-compose 可以通过一个 YAML 文件统一定义和运行多容器应用。

创建 docker-compose.yml

version: '3.8'

services:
# Node.js 后端服务
api:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DB_HOST=db
- REDIS_HOST=redis
depends_on:
- db
- redis
networks:
- app-network

# 数据库服务
db:
image: mysql:8.0
restart: always
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: my_database
volumes:
- db-data:/var/lib/mysql
networks:
- app-network

# Redis 服务
redis:
image: redis:7-alpine
restart: always
networks:
- app-network

# 定义命名卷,实现数据持久化
volumes:
db-data:

# 定义自定义网络,使容器可通过服务名(如 db, redis)互相通信
networks:
app-network:

常用指令

  • docker-compose up -d:后台启动所有服务
  • docker-compose down:停止并移除所有容器及网络
  • docker-compose logs -f api:查看 API 服务的日志

6. 面试高频问题

  1. Docker 和虚拟机的区别是什么?
    • 虚拟机(VM)包含完整的操作系统,启动慢、占用资源多。
    • Docker 容器共享宿主机的操作系统内核,没有 Guest OS,启动极快(秒级),极其轻量。
  2. 如何减小 Docker 镜像的体积?
    • 使用轻量级基础镜像(如 alpine)。
    • 使用多阶段构建(Multi-stage builds),将构建环境与运行环境分离,只保留最终产物。
    • 合并 RUN 指令,清理缓存文件。
  3. Docker 容器的数据是如何持久化的?
    • 使用数据卷(Volumes):由 Docker 引擎管理,性能好,推荐使用。
    • 挂载主机目录(Bind mounts):直接映射宿主机的特定路径到容器内,适合开发环境。