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

不加 .dockerignoreCOPY . . 会把 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-api

5. 环境变量

// 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-api

6. 镜像优化

6.1 用 Alpine 基础镜像

# ❌ 大镜像(~1GB)
FROM node:22
 
# ✅ 小镜像(~200MB)
FROM node:22-alpine

Alpine 是一个轻量级 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-lockfile

Docker 会缓存每一层。如果 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 --prod

7. 推送到 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:latest

8. 常见平台部署

Docker 镜像可以部署到:

平台说明
云服务器ECS、EC2、VPS,直接 docker run
AWS ECS容器编排服务
Google Cloud RunServerless 容器平台
Fly.io全球边缘容器平台
Railway简单的容器部署平台
Kubernetes容器编排系统(大规模)

9. 安全最佳实践

  1. 不用 root 用户运行
FROM node:22-alpine
RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser
USER appuser
  1. 固定基础镜像版本
# ❌ 不固定版本,可能在不同时间构建出不同的镜像
FROM node:latest
 
# ✅ 固定版本
FROM node:22.3.0-alpine
  1. 扫描漏洞
// terminal
# 用 docker scout 扫描
docker scout cves my-api:latest

10. 小结

Docker 容器化的核心:

  1. Dockerfile 定义构建步骤
  2. 多阶段构建 减小镜像体积
  3. .dockerignore 排除不需要的文件
  4. Alpine 基础镜像 减小体积
  5. 利用 Docker 缓存 加速构建

Docker 是云原生的基础,学会 Docker 是学习 K8s、CI/CD 和现代部署的前提。