前言
Docker 实操:从 0 到 1 构建生产级容器化应用
第一阶段:重新理解 Docker 的本质
- Docker 不是虚拟机,它是“进程隔离 + 文件系统快照”
基于cgroups(资源限制)、namespaces(隔离)、UnionFS(分层镜像) - 镜像(Image)vs 容器(Container):只读层 vs 可写层
docker run= 启动一个可写层挂载在镜像之上 - Registry 机制:
docker pull从哪里下载?manifest如何支持多架构?index.docker.io/library/node:18-alpine - TypeScript/Nest/Bun 项目为何必须容器化?
环境一致性、依赖隔离、部署标准化 - DevOps 流水线基石:Docker 是 CI/CD 的“交付单元”
第二阶段:深入 Dockerfile 构建优化
FROM 指令
- 选择基础镜像:
node:18-alpinevsnode:18-slimvsdistroless
Alpine 最小(~5MB),但 musl libc 兼容性问题;distroless无 shell,最安全 - 多阶段构建(Multi-stage Build):
--from=builder如何分离构建与运行环境?
示例:前端npm run build输出静态文件,最终镜像只包含nginx和dist/ AS命名阶段:FROM node:18 AS builder,便于引用
COPY vs ADD
COPY . /app:仅复制文件,推荐用于源码
更透明、更安全ADD特殊行为:自动解压 tar/gz,支持 URL 下载
风险:URL 变更导致构建失败,隐藏逻辑- 最佳实践:优先用
COPY,除非需要ADD的特殊功能
.dockerignore
- 为什么比
.gitignore更重要?
防止node_modules、.env、logs/被复制进构建上下文 - 常见忽略项:
node_modules,.git,.env.local,npm-debug.log
分层缓存优化
- Docker 缓存机制:每层指令结果缓存,仅当上层变更时失效
COPY package*.json ./→RUN npm ci→COPY . .的顺序为何关键? npm civsnpm install:为何 CI 环境必须用ci?ci删除node_modules重装,避免缓存污染--cache-from:CI 中如何复用远程缓存?
WORKDIR 与 USER
WORKDIR /app:设置工作目录,避免路径混乱- 最小权限原则:
USER node运行非 root 用户
安全加固,防止容器逃逸
第三阶段:掌握容器运行与调试
docker run 核心参数
-d:后台运行,-it:交互式终端docker run -it ubuntu:22.04 /bin/bash-p 3000:3000:端口映射,主机 3000 → 容器 3000-v /host/path:/container/path:卷挂载,开发环境热更新docker run -v $(pwd):/app node:18 npm run dev-e NODE_ENV=production:环境变量注入--network:自定义网络,服务间通信
调试技巧
docker logs <container>:查看输出日志docker exec -it <container> sh:进入运行中容器
Alpine 用sh,Debian 用bashdocker inspect <container>:查看详细配置(IP、挂载点、环境变量)docker top <container>:查看容器内运行的进程- 高级调试:
strace -p <pid>抓系统调用,tcpdump抓包
第四阶段:生产级 docker-compose 编排
docker-compose.yml 结构
yaml
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:关键特性
depends_on:启动顺序控制,但不等待服务就绪
需配合健康检查healthcheck:如何判断服务是否 ready?test: ["CMD-SHELL", "pg_isready -U postgres"]networks:自定义网络,服务间通过服务名通信web→http://db:5432secrets/configs:安全注入密码与配置文件
开发 vs 生产配置
docker-compose.dev.yml:挂载源码、开启调试docker-compose.prod.yml:使用预构建镜像、关闭 shell 访问docker-compose -f docker-compose.yml -f docker-compose.prod.yml up
第五阶段:CI/CD 与自动化部署
GitHub Actions 实战
yaml
name: Deploy
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker Image
run: |
docker build -t myapp:${{ github.sha }} .
- name: Push to Registry
run: |
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
docker push myapp:${{ github.sha }}
- name: Deploy to Server
run: |
ssh user@server "docker pull myapp:${{ github.sha }} && docker restart myapp"镜像标签策略
latest危险:永远不要在生产用latest
不可追溯,版本混乱- 推荐:
git sha(v1.2.3-${sha})或语义化版本 digest:sha256:abc...,最精确的镜像标识
第六阶段:安全加固与性能优化
安全实践
- 最小化镜像:移除
curl、vim等非必要工具 - 扫描漏洞:
docker scan myapp:latest或trivy USER node:禁止 root 运行- 只读文件系统:
--read-only+tmpfs distroless/scratch:极致精简,无 shell 入侵风险
性能优化
- 多阶段构建:最终镜像仅包含运行时依赖
.dockerignore:减少构建上下文传输时间- 并行构建:
DOCKER_BUILDKIT=1启用 BuildKit - 缓存共享:
--cache-to type=registry推送缓存到远程
第七阶段:与 K8s 和边缘部署衔接
kubernetes 部署
Deployment:管理 Pod 副本,声明式更新Service:暴露服务,ClusterIP/NodePort/LoadBalancerIngress:7 层路由,基于域名和路径转发ConfigMap/Secret:外部化配置与密钥
边缘部署(Edge)
bun+Docker:极致轻量镜像,冷启动 < 100msCloudflare Workers/Vercel:Serverless 替代方案Wasm+Docker:运行 WebAssembly 模块