22.08-Docker容器化
要点
- Docker 是容器化的标准工具,把应用和依赖打包成可移植的镜像
- Hono 项目可以用 Docker 部署到任何支持 Docker 的平台
- Dockerfile 的多阶段构建可以大幅减小镜像体积
- Docker 是学习 K8s、CI/CD 和云原生技术的基础
内容
1. Docker 是什么
Docker 是一个容器化平台,把应用和它的所有依赖(运行时、库、配置文件)打包成一个镜像(image)。这个镜像可以在任何安装了 Docker 的机器上运行,保证「在我机器上能跑」的问题不再出现。
核心概念:
| 概念 | 说明 |
|---|---|
| 镜像(Image) | 应用的只读模板,包含代码和依赖 |
| 容器(Container) | 镜像的运行实例 |
| Dockerfile | 构建镜像的脚本 |
| Registry | 存储和分发镜像的服务(Docker Hub) |
2. Hono 项目的 Dockerfile
一个典型的 Hono 项目 Dockerfile:
# 阶段 1: 构建
FROM node:22-alpine AS builder
WORKDIR /app
# 先复制依赖文件(利用 Docker 缓存)
COPY package.json pnpm-lock.yaml ./
# 安装 pnpm 和依赖
RUN npm install -g pnpm
RUN pnpm install --frozen-lockfile
# 复制源代码
COPY . .
# 构建
RUN pnpm build
# 阶段 2: 运行
FROM node:22-alpine AS runner
WORKDIR /app
# 从构建阶段复制产物
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
EXPOSE 3000
CMD ["node", "dist/index.js"]2.1 多阶段构建的意义
多阶段构建(multi-stage build)的核心目的:减小最终镜像的体积。
- 构建阶段需要 TypeScript、esbuild 等工具
- 运行阶段只需要编译后的 JS 和 node_modules
把两个阶段分开,最终镜像不包含构建工具,体积会小很多。
3. .dockerignore
和 .gitignore 类似,.dockerignore 告诉 Docker 哪些文件不需要复制到镜像里:
# .dockerignore
node_modules
dist
.git
.env
.env.*
!.env.example
*.md
.vscode
coverage
不加 .dockerignore,COPY . . 会把 node_modules、.git 等全部复制进去,既浪费时间又增大镜像。
4. 构建和运行
// terminal
# 构建镜像
docker build -t my-api .
# 查看镜像
docker images
# 运行容器
docker run -p 3000:3000 --name my-api my-api
# 后台运行
docker run -d -p 3000:3000 --name my-api my-api
# 查看运行中的容器
docker ps
# 查看日志
docker logs my-api
docker logs -f my-api # 实时跟踪
# 停止容器
docker stop my-api
# 删除容器
docker rm my-api
# 删除镜像
docker rmi my-api5. 环境变量
// terminal
# 通过 -e 传递环境变量
docker run -p 3000:3000 \
-e NODE_ENV=production \
-e DATABASE_URL=postgresql://... \
--name my-api \
my-api
# 或者用 .env 文件
docker run -p 3000:3000 --env-file .env --name my-api my-api6. 镜像优化
6.1 用 Alpine 基础镜像
# ❌ 大镜像(~1GB)
FROM node:22
# ✅ 小镜像(~200MB)
FROM node:22-alpineAlpine 是一个轻量级 Linux 发行版,基于 musl libc 而不是 glibc。大部分 Node.js 应用都可以在 Alpine 上跑。
6.2 利用 Docker 缓存
# ✅ 好:先复制依赖文件,再复制源码
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
# ❌ 坏:每次改代码都要重新安装依赖
COPY . .
RUN pnpm install --frozen-lockfileDocker 会缓存每一层。如果 package.json 没变,pnpm install 这一步会用缓存,不需要重新安装。
6.3 不安装 devDependencies
# 构建阶段安装所有依赖
RUN pnpm install --frozen-lockfile
# 运行阶段只安装生产依赖
FROM node:22-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package.json ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile --prod7. 推送到 Registry
// terminal
# 登录 Docker Hub
docker login
# 给镜像打 tag
docker tag my-api username/my-api:latest
# 推送
docker push username/my-api:latest推送到 Registry 后,可以在任何机器上拉取:
// terminal
docker pull username/my-api:latest
docker run -p 3000:3000 username/my-api:latest8. 常见平台部署
Docker 镜像可以部署到:
| 平台 | 说明 |
|---|---|
| 云服务器 | ECS、EC2、VPS,直接 docker run |
| AWS ECS | 容器编排服务 |
| Google Cloud Run | Serverless 容器平台 |
| Fly.io | 全球边缘容器平台 |
| Railway | 简单的容器部署平台 |
| Kubernetes | 容器编排系统(大规模) |
9. 安全最佳实践
- 不用 root 用户运行
FROM node:22-alpine
RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser
USER appuser- 固定基础镜像版本
# ❌ 不固定版本,可能在不同时间构建出不同的镜像
FROM node:latest
# ✅ 固定版本
FROM node:22.3.0-alpine- 扫描漏洞
// terminal
# 用 docker scout 扫描
docker scout cves my-api:latest10. 小结
Docker 容器化的核心:
- Dockerfile 定义构建步骤
- 多阶段构建 减小镜像体积
.dockerignore排除不需要的文件- Alpine 基础镜像 减小体积
- 利用 Docker 缓存 加速构建
Docker 是云原生的基础,学会 Docker 是学习 K8s、CI/CD 和现代部署的前提。