Docker 容器化部署实践:从开发环境到生产上线

前言

在大三这一年里,我参与了多个项目的开发与部署。从最初的"在我电脑上能跑啊"到后来的"一键部署",Docker 在其中扮演了关键角色。这篇文章记录我在实际项目中使用 Docker 进行容器化部署的经验和踩过的坑。

为什么需要 Docker

在团队协作中,环境不一致是最常见的问题之一。Node.js 版本不同、数据库版本差异、系统依赖缺失……这些问题在传统部署方式下层出不穷。

Docker 解决了这个痛点:

  • 环境一致性:开发、测试、生产环境完全一致
  • 快速部署:一条命令启动整个应用栈
  • 资源隔离:每个容器独立运行,互不干扰
  • 易于扩展:配合 Docker Compose 和 Kubernetes 实现弹性伸缩

Docker 核心概念速览

在动手之前,先理清三个核心概念:

概念 类比 说明
镜像(Image) 类(Class) 应用程序的只读模板
容器(Container) 实例(Instance) 镜像的运行实例
仓库(Registry) Maven/NPM 仓库 存储和分发镜像的地方

一句话概括:用 Dockerfile 构建镜像,用镜像创建容器,从 Registry 拉取或推送镜像。

实战:容器化一个 Node.js 应用

1. 编写 Dockerfile

以一个典型的 Express + MySQL 项目为例:

# 使用官方 Node.js 18 镜像作为基础
FROM node:18-alpine

# 设置工作目录
WORKDIR /app

# 复制 package.json 和 package-lock.json
COPY package*.json ./

# 安装依赖
RUN npm ci --production

# 复制源代码
COPY . .

# 暴露端口
EXPOSE 3000

# 启动应用
CMD ["node", "src/index.js"]

几个关键点

  • 使用 alpine 版本减小镜像体积(约 5 倍差距)
  • 先复制 package.jsonnpm install,利用 Docker 的层缓存机制
  • 生产环境使用 npm ci 而非 npm install,确保依赖版本严格一致

2. 构建镜像

docker build -t my-app:1.0.0 .

3. 运行容器

docker run -d \
  --name my-app \
  -p 3000:3000 \
  -e DB_HOST=mysql \
  -e DB_PASSWORD=secret123 \
  my-app:1.0.0

Docker Compose:编排多容器应用

现实中的项目很少只有一个服务。一个典型的 Web 应用至少包含:应用服务 + 数据库 + 缓存。Docker Compose 可以统一管理这些服务。

# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DB_HOST=mysql
      - DB_PASSWORD=${DB_PASSWORD}
      - REDIS_HOST=redis
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_DATABASE: myapp
    volumes:
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app
    restart: unless-stopped

volumes:
  mysql_data:
  redis_data:

关键设计

  • depends_on + healthcheck 确保数据库就绪后才启动应用
  • volumes 持久化数据,容器重启数据不丢失
  • restart: unless-stopped 实现自动重启
  • .env 文件管理敏感配置,不提交到 Git

Dockerfile 优化实践

多阶段构建(Multi-stage Build)

# 原始镜像:800MB+
# 优化后:120MB
# ====== 构建阶段 ======
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# ====== 运行阶段 ======
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --production && npm cache clean --force
EXPOSE 3000
CMD ["node", "dist/index.js"]

多阶段构建的好处:
- 构建依赖(如 TypeScript、Webpack)不进入最终镜像
- 最终镜像只包含运行时所需文件
- 镜像体积大幅减小,部署和拉取更快

.dockerignore

node_modules
npm-debug.log
.git
.env
.vscode
dist
coverage

善用 .dockerignore 可以避免将不必要的文件打入镜像,减小体积、加快构建。

实际项目中的踩坑记录

坑1:时区问题

容器默认是 UTC 时区,日志时间差了 8 小时:

# 解决方案
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

坑2:MySQL 连接拒绝

应用启动比 MySQL 快,导致连接失败。单纯 depends_on 不够,必须加 healthcheck

depends_on:
  mysql:
    condition: service_healthy  # 等 MySQL 真正可用

坑3:文件权限

在 Linux 容器中运行 Node.js 时,挂载的卷可能出现权限问题:

RUN chown -R node:node /app
USER node

坑4:环境变量注入

不要在 Dockerfile 中硬编码敏感信息:

# ❌ 错误
ENV DB_PASSWORD=123456

# ✅ 正确:运行时注入
# docker run -e DB_PASSWORD=xxx

部署流程最佳实践

我所在项目的最终部署流程:

本地开发 → Git Push → CI/CD 构建镜像 → 推送镜像仓库 → 服务器拉取部署

对应的 CI/CD 配置(GitLab CI 示例):

stages:
  - build
  - deploy

build:
  stage: build
  script:
    - docker build -t registry.example.com/my-app:$CI_COMMIT_SHA .
    - docker push registry.example.com/my-app:$CI_COMMIT_SHA
  only:
    - main

deploy:
  stage: deploy
  script:
    - docker pull registry.example.com/my-app:$CI_COMMIT_SHA
    - docker-compose down
    - docker-compose up -d
  only:
    - main

Docker 常用命令速查

命令 用途
docker ps 查看运行中的容器
docker images 查看本地镜像
docker logs -f 容器名 实时查看容器日志
docker exec -it 容器名 sh 进入容器 Shell
docker-compose up -d 后台启动所有服务
docker-compose logs -f 查看所有服务日志
docker system prune -a 清理未使用的镜像和容器
docker stats 查看容器资源使用情况

总结

Docker 不是一个"可选项",而是现代软件工程的"必修课"。从我个人的项目经验来看,引入 Docker 后:

  • 新成员入职从"配一天环境"变成"一条命令跑起来"
  • 部署从手动传文件变成自动化流水线
  • "我电脑上没问题"这句话从团队的字典里消失了

如果你还没有在项目中使用 Docker,强烈建议现在就动手试试。从写第一个 Dockerfile 开始,到用 Docker Compose 编排多服务,再到接入 CI/CD 流水线——每一步都能感受到开发体验的质变。


本文基于大三期间多个项目(ERP 系统、灵山胜境等)的实际部署经验总结。

posted @ 2026-07-01 16:56  C(5,3)  阅读(20)  评论(0)    收藏  举报