Docker 生产级单机部署指南
适用场景:服务器资源有限、需要精细化控制每个容器启动参数、或希望深入理解 Docker 底层命令的生产环境。本指南完全基于
docker run和原生 Docker 命令构建。
1. 核心架构与设计理念
在不使用 Docker Compose 的情况下,我们需要手动管理容器的生命周期、网络互联和数据卷挂载。
1.1 架构拓扑
[客户端] --> [Nginx 容器 (Port 80/443)] --> [App 容器 (Port 8000)]
|
|
[Backend Network]
|
+-------------+-------------+
| | |
[Postgres] [Redis] [MinIO]
(Port 5432) (Port 6379) (Port 9000)
1.2 关键设计原则
- 网络隔离:创建独立的 Bridge 网络,后端服务不暴露端口到宿主机,仅通过容器名通信。
- 数据持久化:所有状态数据(数据库、日志、配置)必须挂载到宿主机目录。
- 资源限制:为每个容器设置 CPU 和内存上限,防止单个服务耗尽资源。
- 自动重启:配置
--restart unless-stopped确保服务异常退出后自动恢复。
2. 基础设施准备
2.1 系统优化脚本
在生产环境部署前,执行以下优化以提升稳定性:
#!/bin/bash
# system-prep.sh
1. 增加文件描述符限制
echo "* soft nofile 65536" >> /etc/security/limits.conf
echo "* hard nofile 65536" >> /etc/security/limits.conf
2. 内核参数调优
cat < /etc/sysctl.d/99-docker-prod.conf
net.ipv4.ip_forward = 1
net.core.somaxconn = 65535
vm.max_map_count = 262144
fs.file-max = 65536
EOF
sysctl -p /etc/sysctl.d/99-docker-prod.conf
3. 创建项目目录结构
mkdir -p /opt/myapp/{logs,config,data,backups,ssl}
mkdir -p /opt/myapp/logs/{app,nginx,postgres,redis}
mkdir -p /opt/myapp/data/{postgres,redis,minio}
2.2 Docker Daemon 配置
编辑 /etc/docker/daemon.json 以优化全局日志和存储:
{
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "5"
},
"live-restore": true
}
执行 systemctl restart docker 生效。
3. 网络与存储初始化
在启动任何容器之前,先创建基础架构资源。
3.1 创建专用网络
# 创建后端内部网络(外部无法直接访问)
docker network create --driver bridge backend-net
创建前端网络(可选,如果 Nginx 需要与其他外部服务通信)
docker network create --driver bridge frontend-net
3.2 准备数据卷目录
确保宿主机目录权限正确,避免容器内写入权限问题:
# 创建数据目录
mkdir -p /opt/myapp/data/postgres
mkdir -p /opt/myapp/data/redis
mkdir -p /opt/myapp/data/minio
设置权限(根据具体用户 ID 调整,通常 postgres 用户 ID 为 999)
chown -R 999:999 /opt/myapp/data/postgres
4. 服务部署详解
我们将按照依赖顺序启动服务:数据库/缓存 → 应用 → 反向代理。
4.1 部署 PostgreSQL 数据库
docker run -d \
--name myapp-postgres \
--network backend-net \
--restart unless-stopped \
-e POSTGRES_DB=myapp_db \
-e POSTGRES_USER=myapp_user \
-e POSTGRES_PASSWORD=SecurePassword123! \
-v /opt/myapp/data/postgres:/var/lib/postgresql/data \
-v /opt/myapp/logs/postgres:/var/log/postgresql \
-p 127.0.0.1:5432:5432 \
--memory=4g \
--cpus=2.0 \
postgres:15-alpine \
-c max_connections=200
注意:-p 127.0.0.1:5432:5432 仅允许宿主机本地访问,增强安全性。
4.2 部署 Redis 缓存
docker run -d \
--name myapp-redis \
--network backend-net \
--restart unless-stopped \
-v /opt/myapp/data/redis:/data \
-v /opt/myapp/logs/redis:/var/log/redis \
-p 127.0.0.1:6379:6379 \
--memory=1g \
--cpus=1.0 \
redis:7-alpine \
redis-server --requirepass SecureRedisPass! --maxmemory 512mb --appendonly yes
4.3 部署应用服务 (App)
假设你已经构建了镜像 myapp:latest。
docker run -d \
--name myapp-app \
--network backend-net \
--restart unless-stopped \
-e APP_ENV=production \
-e LOG_LEVEL=INFO \
-e DATABASE_URL=postgresql://myapp_user:SecurePassword123!@myapp-postgres:5432/myapp_db \
-e REDIS_URL=redis://:SecureRedisPass!@myapp-redis:6379/0 \
-v /opt/myapp/logs/app:/app/logs \
-v /opt/myapp/config:/app/config:ro \
--memory=2g \
--cpus=2.0 \
--health-cmd="curl -f http://localhost:8000/health || exit 1" \
--health-interval=30s \
--health-timeout=10s \
--health-retries=3 \
myapp:latest
关键点:使用 --network backend-net 后,应用可以通过容器名 myapp-postgres 和 myapp-redis 直接解析 IP。
4.4 部署 Nginx 反向代理
docker run -d \
--name myapp-nginx \
--network frontend-net \
--restart unless-stopped \
-p 80:80 \
-p 443:443 \
-v /opt/myapp/config/nginx.conf:/etc/nginx/nginx.conf:ro \
-v /opt/myapp/logs/nginx:/var/log/nginx \
-v /opt/myapp/ssl:/etc/nginx/ssl:ro \
--memory=512m \
--cpus=0.5 \
nginx:alpine
注意:Nginx 需要能访问 App 容器。如果 App 在 backend-net,Nginx 在 frontend-net,它们默认不通。
解决方案:将 Nginx 也加入 backend-net,或者创建一个共享网络。
# 将 Nginx 连接到后端网络,使其能解析 app 容器名
docker network connect backend-net myapp-nginx
5. 日志管理深度实践
在没有 Compose 的情况下,日志管理更依赖于宿主机文件和 Docker 驱动配置。
5.1 策略一:Docker JSON File 驱动(推荐)
这是最简单且性能较好的方式。我们在 daemon.json 中已经配置了全局限制,也可以针对特定容器覆盖:
# 示例:为高日志量应用单独配置
docker run -d \
--name myapp-app \
--log-driver json-file \
--log-opt max-size=200m \
--log-opt max-file=10 \
...其他参数...
查看日志:
# 实时查看
docker logs -f --tail 100 myapp-app
查看特定时间段
docker logs --since 2026-07-23T10:00:00 myapp-app
5.2 策略二:应用层文件日志 + 宿主机挂载
对于生产环境,建议应用在容器内将日志写入文件,并挂载到宿主机。这样即使容器崩溃,日志依然保留。
应用代码配置 (logger.py):
import logging
from logging.handlers import RotatingFileHandler
def setup_logger():
logger = logging.getLogger('app')
logger.setLevel(logging.INFO)
# 写入容器内的 /app/logs/app.log,该目录已挂载到宿主机
handler = RotatingFileHandler(
'/app/logs/app.log',
maxBytes=10010241024,
backupCount=10
)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
宿主机日志清理
由于是普通文件,可以使用 Linux 原生的 logrotate 进行管理。
创建 /etc/logrotate.d/myapp:
/opt/myapp/logs/app/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 0644 root root
postrotate
# 如果应用支持信号重载日志句柄,在此处发送信号
# docker kill -s HUP myapp-app
endscript
}
5.3 策略三:集中式日志收集 (Syslog/Fluentd)
如果有多台服务器,建议将 Docker 日志驱动改为 syslog 或 fluentd,发送到中央日志服务器。
docker run -d \
--name myapp-app \
--log-driver syslog \
--log-opt syslog-address=tcp://192.168.1.100:514 \
--log-opt tag="myapp-production" \
...
6. 运维与维护脚本
由于没有 docker-compose.yml,我们需要编写 Shell 脚本来管理集群。
6.1 启动脚本 (start.sh)
#!/bin/bash
set -e
echo "🚀 启动生产环境服务..."
确保网络存在
docker network inspect backend-net >/dev/null 2>&1 || docker network create backend-net
启动基础服务
echo "Starting Database..."
docker start myapp-postgres || docker run -d --name myapp-postgres --network backend-net ... (参考上文完整命令)
echo "Starting Redis..."
docker start myapp-redis || docker run -d --name myapp-redis --network backend-net ...
等待数据库就绪
echo "Waiting for DB..."
sleep 5
启动应用
echo "Starting App..."
docker start myapp-app || docker run -d --name myapp-app --network backend-net ...
启动 Nginx
echo "Starting Nginx..."
docker start myapp-nginx || docker run -d --name myapp-nginx --network frontend-net ...
docker network connect backend-net myapp-nginx
echo "✅ 所有服务已启动"
docker ps --filter "name=myapp"
6.2 停止与清理脚本 (stop.sh)
#!/bin/bash
echo "🛑 停止服务..."
docker stop myapp-nginx myapp-app myapp-redis myapp-postgres
echo "🧹 清理未使用的资源..."
docker system prune -f
6.3 备份脚本 (backup.sh)
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/opt/myapp/backups"
mkdir -p $BACKUP_DIR
echo "💾 备份 PostgreSQL..."
docker exec myapp-postgres pg_dump -U myapp_user myapp_db > $BACKUP_DIR/db_$DATE.sql
echo "💾 备份 Redis..."
docker exec myapp-redis redis-cli -a SecureRedisPass! SAVE
cp /opt/myapp/data/redis/dump.rdb $BACKUP_DIR/redis_$DATE.rdb
echo "✅ 备份完成: $BACKUP_DIR"
可选:删除 7 天前的备份
find $BACKUP_DIR -type f -mtime +7 -delete
7. 监控与健康检查
7.1 原生健康检查
在 docker run 中使用 --health-cmd 参数,Docker 引擎会自动监控容器状态。
docker inspect --format='{{.State.Health.Status}}' myapp-app
7.2 资源监控
使用 docker stats 实时监控:
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}" myapp-postgres myapp-app myapp-nginx
7.3 简单告警脚本
结合 Crontab 每 5 分钟检查一次:
#!/bin/bash
# check_health.sh
SERVICES=("myapp-postgres" "myapp-app" "myapp-nginx")
for svc in "${SERVICES[@]}"; do
if [ "$(docker inspect -f '{{.State.Running}}' $svc 2>/dev/null)" != "true" ]; then
echo "CRITICAL: Service $svc is down!" | mail -s "Alert: $svc Down" admin@example.com
# 尝试自动重启
docker start $svc
fi
done
8. 安全加固清单
- 最小权限原则:
- 不要以 root 运行应用容器(在 Dockerfile 中使用 USER appuser)。
- 挂载配置文件时使用
:ro(Read-Only)。
- 网络隔离:
- 数据库和 Redis 绝不绑定 0.0.0.0,仅绑定 127.0.0.1 或通过内部 Docker 网络通信。
- 秘密管理:
- 避免在 docker run 命令历史中留下密码。
- 建议使用 Docker Secrets 或环境变量文件 (
--env-file .env)。
- 镜像安全:
- 定期扫描镜像漏洞:
docker scan myapp:latest
- 定期扫描镜像漏洞:
更多推荐

所有评论(0)