Docker容器化部署实战:从镜像构建到生产环境一键上线
一、为什么你的项目需要容器化
在开发过程中,你一定遇到过这些场景:
- 代码在本地跑得好好的,部署到测试环境就报错
- 新人入职配环境要花大半天,装完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
九、总结
容器化部署的核心价值可以归纳为三点:
- 环境一致性:开发、测试、生产环境完全一致,彻底消除"在我电脑上能跑"的问题。
- 快速交付:新服务部署从小时级缩短到分钟级,一条命令即可完成。
- 弹性伸缩:配合编排工具,可以根据负载动态扩缩容,应对流量高峰。
更多推荐

所有评论(0)