后端工程Technical Deep Dive

Docker 基础与实战:从小白到生产

发布时间2026/03/29
分类后端工程
预计阅读10 分钟
作者吴长龙
*

Docker 基础与实战:从小白到生产

01.内容

# Docker 基础与实战:从小白到生产

Docker 是现代应用部署的事实标准。本文从基础概念到 Dockerfile 最佳实践,系统介绍 Docker 的核心知识和生产环境应用。

02.一、核心概念

1.1 什么是 Docker

bash snippetbash
# Docker vs 虚拟机
# 虚拟机:完整操作系统 + 虚拟硬件 → 体积大(GB级),启动慢(分钟级)
# Docker:共享宿主机内核 → 体积小(MB级),启动快(秒级)

# 核心概念
# 镜像(Image):只读模板,用于创建容器
# 容器(Container):镜像的运行实例
# 仓库(Repository):存储镜像的地方

1.2 基础命令

bash snippetbash
# 镜像操作
docker pull node:20-alpine                    # 拉取镜像
docker images                                  # 列出镜像
docker rmi node:20-alpine                      # 删除镜像
docker build -t my-app:1.0 .                   # 构建镜像
docker tag my-app:1.0 my-registry.com/my-app:1.0  # 打标签

# 容器操作
docker run -d -p 3000:3000 --name my-app my-app  # 运行容器
docker ps                                      # 列出运行中容器
docker ps -a                                   # 所有容器
docker logs -f my-app                          # 查看日志
docker exec -it my-app sh                      # 进入容器
docker stop my-app                             # 停止容器
docker rm my-app                               # 删除容器
docker rm -f my-app                            # 强制删除

# 进阶
docker inspect my-app                          # 查看容器详情
docker stats my-app                            # 资源使用
docker cp my-app:/app/logs ./logs              # 复制文件

03.二、Dockerfile 最佳实践

2.1 基础 Dockerfile

dockerfile snippetdockerfile
# ❌ 糟糕的 Dockerfile
FROM ubuntu:20.04
RUN apt-get update
RUN apt-get install -y nodejs npm
COPY . /app
RUN cd /app && npm install
EXPOSE 3000
CMD ["node", "/app/index.js"]
dockerfile snippetdockerfile
# ✅ 优化的 Dockerfile
# 1. 使用特定版本标签
FROM node:20-alpine

# 2. 设置工作目录
WORKDIR /app

# 3. 先复制依赖文件,利用缓存
COPY package*.json ./
RUN npm ci --only=production

# 4. 再复制源码
COPY . .

# 5. 非 root 用户运行(安全)
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001
USER nodejs

# 6. 暴露端口
EXPOSE 3000

# 7. 启动命令
CMD ["node", "index.js"]

2.2 多阶段构建

dockerfile snippetdockerfile
# 构建 Node.js 应用
# 阶段1:构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 阶段2:运行
FROM node:20-alpine AS production
WORKDIR /app

# 复制构建产物
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./

# 生产依赖
RUN npm ci --only=production

USER nodejs
EXPOSE 3000
CMD ["node", "dist/index.js"]

2.3 前端构建

dockerfile snippetdockerfile
# React/Vue 构建
FROM node:20-alpine AS builder
WORKDIR /app

# 安装依赖(利用缓存)
COPY package*.json ./
RUN npm ci

# 构建
COPY . .
ENV NODE_ENV=production
RUN npm run build

# 生产服务
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
nginx snippetnginx
# nginx.conf
server {
    listen 80;
    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    # 缓存静态资源
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

04.三、Docker 网络与存储

3.1 网络

bash snippetbash
# 创建网络
docker network create my-network

# 运行容器并加入网络
docker run -d --name api --network my-network my-api
docker run -d --name web --network my-web-network my-web

# 容器间通信(使用服务名)
# 在 api 容器中访问 web:
# curl http://web:8080
# 在 web 容器中访问 api:
# curl http://api:3000
yaml snippetyaml
# docker-compose.yml 中的网络
services:
  api:
    build: ./api
    networks:
      - backend
  web:
    build: ./web
    depends_on:
      - api
    networks:
      - frontend
      - backend

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

3.2 存储

bash snippetbash
# 匿名卷(容器删除后丢失)
docker run -v /app/data my-app

# 命名卷(持久化)
docker volume create my-data
docker run -v my-data:/app/data my-app

# 绑定挂载(开发用,修改即时生效)
docker run -v $(pwd):/app -w /app node:20 npm run dev
yaml snippetyaml
# docker-compose.yml 中的存储
services:
  db:
    image: postgres:15
    volumes:
      # 命名卷
      - postgres-data:/var/lib/postgresql/data
      # 绑定挂载(初始化脚本)
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql

volumes:
  postgres-data:

05.四、生产环境配置

4.1 Node.js 生产配置

dockerfile snippetdockerfile
# node:production.Dockerfile
FROM node:20-alpine

# 环境变量
ENV NODE_ENV=production \
    PORT=3000

WORKDIR /app

# 安全:非 root 用户
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

# 复制依赖
COPY package*.json ./
RUN npm ci --only=production && \
    npm cache clean --force

# 复制源码
COPY --chown=nodejs:nodejs . .

USER nodejs

EXPOSE $PORT

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node -e "require('http').get('http://localhost:$PORT/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"

CMD ["node", "server.js"]

4.2 环境变量管理

bash snippetbash
# .env 文件(不提交到 Git)
# .env
DB_HOST=localhost
DB_PORT=5432
API_KEY=your-secret-key

# docker-compose.yml
services:
  api:
    env_file:
      - .env
    environment:
      - NODE_ENV=production
      - LOG_LEVEL=info

4.3 日志管理

bash snippetbash
# 限制日志大小
docker run --log-opt max-size=10m --log-opt max-file=3 my-app

# 使用 docker-compose
services:
  api:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

06.五、私有镜像仓库

5.1 Docker Hub

bash snippetbash
# 登录
docker login -u username

# 推送
docker build -t username/my-app:1.0 .
docker push username/my-app:1.0

# 拉取
docker pull username/my-app:1.0

5.2 GitHub Container Registry

bash snippetbash
# 登录
echo $GITHUB_TOKEN | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin

# 推送
docker build -t ghcr.io/username/my-app:1.0 .
docker push ghcr.io/username/my-app:1.0

# 拉取
docker pull ghcr.io/username/my-app:1.0

07.六、最佳实践清单

  • [ ] 使用特定版本标签(不用 latest)
  • [ ] 利用层缓存优化构建顺序
  • [ ] 使用 .dockerignore 排除不必要的文件
  • [ ] 以非 root 用户运行容器
  • [ ] 添加健康检查
  • [ ] 合理设置资源限制(CPU、内存)
  • [ ] 使用多阶段构建减小镜像体积
  • [ ] 敏感信息使用环境变量或密钥管理

---

*下一篇文章将介绍 Docker Compose 多容器编排。*