一、为什么你的项目需要容器化

在开发过程中,你一定遇到过这些场景:

  • 代码在本地跑得好好的,部署到测试环境就报错
  • 新人入职配环境要花大半天,装完Python装Node,装完数据库装Redis,版本还经常对不上
  • 项目从测试环境迁移到生产环境,又要重新配一遍依赖和运行环境
  • 多个项目依赖同一个服务的不同版本,互相冲突

这些问题的根源都是一样的:运行环境不一致。

Docker通过容器化技术,把应用程序及其所有依赖打包成一个标准化的镜像,在任何安装了Docker的机器上都能以完全一致的方式运行。一句话概括就是:“Build once, run anywhere.”


二、核心概念快速梳理

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

  • 镜像(Image):一个只读的模板,包含了运行应用所需的全部文件、依赖和配置。可以理解为"应用的安装包"。
  • 容器(Container):镜像的运行实例,彼此之间相互隔离。可以理解为"正在运行的应用"。
  • Dockerfile:定义镜像构建过程的文本文件,描述了从基础镜像到最终镜像的每一步操作。

它们之间的关系是:Dockerfile → 构建 → 镜像 → 运行 → 容器。


三、环境准备

本文以一台CentOS 7/Ubuntu 20.04+ 的Linux服务器为例,演示完整的容器化部署流程。

安装Docker Engine:

# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y docker.io
sudo systemctl enable docker
sudo systemctl start docker

# CentOS/RHEL
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io
sudo systemctl enable docker
sudo systemctl start docker

验证安装:

docker --version
docker run hello-world

看到 Hello from Docker! 的输出即表示安装成功。


四、编写Dockerfile:以Node.js项目为例

假设我们有一个基于Express框架的Node.js项目,项目结构如下:

my-app/
├── src/
│   └── index.js
├── package.json
├── package-lock.json
└── Dockerfile

src/index.js 的内容:

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({ message: 'Hello from Docker!', timestamp: new Date().toISOString() });
});

app.get('/health', (req, res) => {
  res.status(200).json({ status: 'ok' });
});

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

对应的 Dockerfile:

# 第一阶段:构建阶段
FROM node:18-alpine AS builder

WORKDIR /app

# 先复制依赖描述文件,利用Docker缓存层
COPY package.json package-lock.json ./
RUN npm ci --only=production

# 再复制源代码
COPY src/ ./src/

# 第二阶段:运行阶段(多阶段构建,减小最终镜像体积)
FROM node:18-alpine

WORKDIR /app

# 创建非root用户,提升安全性
RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup

# 从构建阶段复制node_modules和源码
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/src ./src
COPY --from=builder /app/package.json ./package.json

USER appuser

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

CMD ["node", "src/index.js"]

Dockerfile关键要点解析

多阶段构建:将构建阶段和运行阶段分离,最终镜像中不包含构建工具和中间产物,镜像体积可以从800MB+压缩到150MB左右。

层缓存优化:先复制 package.json 再复制源码,这样只要依赖没变,npm ci 这一步就会命中缓存,不用每次重新安装依赖,构建速度大幅提升。

非root用户运行:容器内默认以root身份运行,一旦被攻破,攻击者将获得容器内的root权限。创建专用用户运行应用是基本的安全实践。

HEALTHCHECK指令:让Docker定期检测容器内应用的健康状态,配合编排工具可以实现自动重启异常容器。


五、构建与运行镜像

# 构建镜像
docker build -t my-app:1.0.0 .

# 查看镜像
docker images | grep my-app

# 运行容器
docker run -d \
  --name my-app \
  -p 8080:3000 \
  --restart unless-stopped \
  my-app:1.0.0

# 验证运行状态
curl http://localhost:8080/
# 输出: {"message":"Hello from Docker!","timestamp":"2026-08-17T03:30:00.000Z"}

参数说明:

  • -d:后台运行
  • --name:指定容器名称
  • -p 8080:3000:将宿主机的8080端口映射到容器的3000端口
  • --restart unless-stopped:容器异常退出时自动重启,手动停止的除外

六、使用Docker Compose编排多服务应用

实际项目中通常不止一个服务,一个典型的Web应用至少包含:应用服务、数据库、缓存。手动用 docker run 逐个启动和管理这些容器非常繁琐,Docker Compose就是为解决这个问题而生的。

编写 docker-compose.yml:

version: "3.8"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: my-app
    ports:
      - "8080:3000"
    environment:
      - NODE_ENV=production
      - DB_HOST=redis
      - DB_PORT=6379
      - PORT=3000
    depends_on:
      redis:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - app-network

  redis:
    image: redis:7-alpine
    container_name: my-redis
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3
    restart: unless-stopped
    networks:
      - app-network

  nginx:
    image: nginx:alpine
    container_name: my-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - app
    restart: unless-stopped
    networks:
      - app-network

volumes:
  redis-data:

networks:
  app-network:
    driver: bridge

对应的 nginx.conf 反向代理配置:

events {
    worker_connections 1024;
}

http {
    upstream app_backend {
        server app:3000;
    }

    server {
        listen 80;
        server_name example.com;

        # HTTP强制跳转HTTPS
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl;
        server_name example.com;

        ssl_certificate     /etc/nginx/ssl/cert.pem;
        ssl_certificate_key /etc/nginx/ssl/key.pem;
        ssl_protocols       TLSv1.2 TLSv1.3;

        location / {
            proxy_pass http://app_backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

一键启动所有服务:

# 启动(后台运行)
docker compose up -d

# 查看运行状态
docker compose ps

# 查看日志
docker compose logs -f app

# 停止所有服务
docker compose down

# 停止并清除数据卷(慎用,会删除持久化数据)
docker compose down -v

七、生产环境部署清单

将容器化应用部署到生产环境之前,需要逐项检查以下内容:

镜像安全

  • 使用官方基础镜像的alpine变体,减小攻击面
  • 定期扫描镜像漏洞:docker scout cves my-app:1.0.0
  • 不要在镜像中硬编码密钥、密码等敏感信息,使用环境变量或密钥管理服务注入

资源限制

# 在docker-compose.yml中为每个服务添加资源限制
services:
  app:
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
        reservations:
          cpus: "0.5"
          memory: 256M

日志管理

services:
  app:
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

避免容器日志无限增长占满磁盘空间。

数据持久化

数据库、上传文件等需要持久化的数据,必须挂载到宿主机目录或使用Docker Volume,否则容器重建后数据会丢失。

自动重启策略

生产环境务必设置 restart: unless-stopped,确保服务器重启或容器异常退出后能自动恢复。


八、常用运维命令速查

# 进入运行中的容器
docker exec -it my-app /bin/sh

# 查看容器资源占用
docker stats

# 查看容器日志(最近100行,持续跟踪)
docker logs -f --tail 100 my-app

# 清理无用镜像、容器和网络
docker system prune -f

# 导出/导入镜像(离线部署场景)
docker save my-app:1.0.0 -o my-app.tar
docker load -i my-app.tar

# 批量更新镜像并重启
docker compose pull && docker compose up -d

九、总结

容器化部署的核心价值可以归纳为三点:

  1. 环境一致性:开发、测试、生产环境完全一致,彻底消除"在我电脑上能跑"的问题。
  2. 快速交付:新服务部署从小时级缩短到分钟级,一条命令即可完成。
  3. 弹性伸缩:配合编排工具,可以根据负载动态扩缩容,应对流量高峰。

更多推荐