Docker 搭建 Nginx 服务

一、基础单节点Nginx服务

1.1 基础快速部署

  1. 最简单启动的 Nginx 容器

    bash 复制代码
    # 拉取 Nginx 镜像
    docker pull nginx:alpine
    
    # 直接运行Nginx容器
    docker run -d --name my-nginx -p 80:80 nginx:alpine
    
    # 测试访问
    curl http://localhost
  2. 常用参数启动的 Nginx 容器

    bash 复制代码
    # 完整命令示例
    docker run -d \
      --name my-nginx \
      --restart unless-stopped \
      -p 80:80 \
      -p 443:443 \
      -v /my/custom/nginx.conf:/etc/nginx/nginx.conf:ro \
      -v /my/html:/usr/share/nginx/html:ro \
      -v /my/logs:/var/log/nginx \
      -v /my/certs:/etc/nginx/certs:ro \
      -e TZ=Asia/Shanghai \
      nginx:alpine

1.2 完整生产环境配置

1.2.1 目录结构准备

bash 复制代码
# 创建目录结构
mkdir -p /usr/local/src/nginx/nginx-docker/{conf,html,logs,certs,conf.d}

# 切换到工作目录
cd /usr/local/src/nginx/nginx-docker

# 目录结构
/usr/local/src/nginx/nginx-docker/
├── docker-compose.yml
├── conf/
│   ├── nginx.conf          # 主配置文件
│   └── mime.types          # MIME类型配置
├── conf.d/
│   ├── default.conf        # 默认主机配置
├── html/
│   ├── index.html
│   └── error/
│       ├── 50x.html
│       └── 404.html
├── logs/                  # 日志目录(自动挂载)
└── certs/                 # SSL证书目录

1.2.2 Docker Compose 配置

yaml 复制代码
# /usr/local/src/nginx/nginx-docker/docker-compose.yml
version: '3.8'

services:
  nginx:
    image: nginx:1.24-alpine  # 指定稳定版本
    container_name: nginx-prod
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      # 主配置文件
      - ./conf/nginx.conf:/etc/nginx/nginx.conf:ro
      # MIME类型配置
      - ./conf/mime.types:/etc/nginx/mime.types:ro
      # 虚拟主机配置
      - ./conf.d:/etc/nginx/conf.d
      # 网站文件
      - ./html:/usr/share/nginx/html:ro
      # 日志目录
      - ./logs:/var/log/nginx
      # SSL证书
      - ./certs:/etc/nginx/certs:ro
    environment:
      - TZ=Asia/Shanghai
      - NGINX_ENV=production
    networks:
      - nginx-network
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost/health || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nginx.rule=Host(`localhost`)"
      - "traefik.http.services.nginx.loadbalancer.server.port=80"

volumes:
  nginx-certs:
    external: false

networks:
  nginx-network:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16

1.2.3 Nginx 主配置文件

bash 复制代码
# /usr/local/src/nginx/nginx-docker/conf/nginx.conf

# 运行用户
user nginx;

# 工作进程数,通常设置为CPU核心数
worker_processes auto;

# 错误日志路径和级别
error_log /var/log/nginx/error.log warn;

# PID文件路径
pid /var/run/nginx.pid;

# 事件模块配置
events {
    # 每个工作进程的最大连接数
    worker_connections 10240;
    
    # 使用epoll事件模型(Linux)
    use epoll;
    
    # 尽可能接受更多的连接
    multi_accept on;
}

# HTTP模块配置
http {
    # 包含MIME类型定义
    include /etc/nginx/mime.types;
    
    # 默认MIME类型
    default_type application/octet-stream;
    
    # 日志格式定义
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    
    log_format json escape=json '{'
        '"time_local":"$time_local",'
        '"remote_addr":"$remote_addr",'
        '"remote_user":"$remote_user",'
        '"request":"$request",'
        '"status": "$status",'
        '"body_bytes_sent":"$body_bytes_sent",'
        '"request_time":"$request_time",'
        '"http_referrer":"$http_referer",'
        '"http_user_agent":"$http_user_agent",'
        '"http_x_forwarded_for":"$http_x_forwarded_for"'
    '}';
    
    # 访问日志
    access_log /var/log/nginx/access.log main buffer=32k flush=5s;
    
    # 性能优化
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    
    # 连接超时时间
    keepalive_timeout 65;
    keepalive_requests 1000;
    
    # 客户端相关
    client_max_body_size 100m;
    client_body_buffer_size 128k;
    client_header_buffer_size 1k;
    large_client_header_buffers 4 4k;
    client_body_timeout 12;
    client_header_timeout 12;
    
    # 代理相关
    proxy_connect_timeout 75s;
    proxy_send_timeout 600s;
    proxy_read_timeout 600s;
    proxy_buffer_size 4k;
    proxy_buffers 8 32k;
    proxy_busy_buffers_size 64k;
    proxy_temp_file_write_size 64k;
    
    # 响应超时
    send_timeout 60;
    
    # 隐藏Nginx版本号
    server_tokens off;
    
    # 开启文件缓存
    open_file_cache max=100000 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;
    
    # Gzip压缩配置(可以在单独的gzip.conf中配置)
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_min_length 1024;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml;
    
    # 包含虚拟主机配置
    include /etc/nginx/conf.d/*.conf;
    
    # 限制请求速率(防攻击)
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m;
    
    # 限制连接数
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    
    # 响应头安全配置
    map $sent_http_content_type $x_content_type_options {
        ~*text/html "nosniff";
        default "";
    }
}

1.2.4 MIME 类型配置(使用 Nginx 默认)

bash 复制代码
# 复制 Nginx 默认的 mime.types 文件
docker run --rm nginx:alpine cat /etc/nginx/mime.types > /usr/local/src/nginx/nginx-docker/conf/mime.types

1.2.5 默认主机配置

bash 复制代码
# Nginx 默认服务器配置
server {
    listen 80;
    listen [::]:80;
    server_name _;

    root /usr/share/nginx/html;
    index index.html index.htm;

    # 安全头部
    add_header X-Frame-Options SAMEORIGIN always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-XSS-Protection "1; mode=block" always;

    # 主路由
    location / {
        try_files $uri $uri/ /index.html;
    }

    # 静态文件缓存
    location ~* \.(jpg|jpeg|png|gif|ico|webp|svg)$ {
        expires 365d;
        add_header Cache-Control "public, immutable";
        access_log off;
        log_not_found off;
    }

    location ~* \.(css|js)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
        access_log off;
        log_not_found off;
    }

    location ~* \.(woff|woff2|ttf|eot|otf)$ {
        expires 365d;
        add_header Cache-Control "public, immutable";
        access_log off;
        log_not_found off;
    }

    # 健康检查
    location /health {
        access_log off;
        return 200 "healthy\n";
        add_header Content-Type text/plain;
    }

    # 禁止访问隐藏文件
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }

    # 错误页面
    error_page 404 /error/404.html;
    error_page 500 502 503 504 /error/50x.html;

    location /error/ {
        internal;
        alias /usr/share/nginx/html/error/;
    }
}

1.2.6 HTML 文件

bash 复制代码
<!-- /usr/local/src/nginx/nginx-docker/html/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Docker Nginx 服务</title>
    <meta name="description" content="运行在Docker容器中的Nginx Web服务器">
    <meta name="keywords" content="Docker, Nginx, Web服务器, 容器">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
            line-height: 1.6;
            color: #333;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        
        header {
            text-align: center;
            padding: 4rem 0;
            color: white;
        }
        
        header h1 {
            font-size: 3rem;
            margin-bottom: 1rem;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }
        
        header p {
            font-size: 1.2rem;
            opacity: 0.9;
        }
        
        .status-badge {
            display: inline-block;
            background: #4CAF50;
            color: white;
            padding: 5px 15px;
            border-radius: 20px;
            font-size: 0.9rem;
            margin-top: 10px;
        }
        
        .main-content {
            background: white;
            border-radius: 10px;
            padding: 30px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.1);
            margin-bottom: 30px;
        }
        
        .info-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 20px;
            margin: 30px 0;
        }
        
        .info-card {
            background: #f8f9fa;
            border-radius: 8px;
            padding: 20px;
            border-left: 4px solid #667eea;
        }
        
        .info-card h3 {
            color: #667eea;
            margin-bottom: 10px;
        }
        
        .code-block {
            background: #282c34;
            color: #abb2bf;
            padding: 15px;
            border-radius: 5px;
            font-family: 'Courier New', monospace;
            overflow-x: auto;
            margin: 15px 0;
        }
        
        .button-group {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            margin: 20px 0;
        }
        
        .btn {
            display: inline-block;
            padding: 10px 20px;
            background: #667eea;
            color: white;
            text-decoration: none;
            border-radius: 5px;
            transition: background 0.3s;
        }
        
        .btn:hover {
            background: #5a67d8;
        }
        
        .btn-secondary {
            background: #718096;
        }
        
        .btn-secondary:hover {
            background: #4a5568;
        }
        
        footer {
            text-align: center;
            padding: 20px;
            color: white;
            opacity: 0.8;
        }
        
        .server-info {
            background: #2d3748;
            color: #e2e8f0;
            padding: 15px;
            border-radius: 5px;
            margin: 20px 0;
        }
        
        @media (max-width: 768px) {
            header h1 {
                font-size: 2rem;
            }
            
            .container {
                padding: 10px;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>🚀 Docker Nginx 服务</h1>
            <p>基于 Docker Compose 的现代化 Nginx 部署方案</p>
            <div class="status-badge" id="status">运行中</div>
        </header>
        
        <div class="main-content">
            <h2>📋 服务概览</h2>
            <p>这是一个运行在 Docker 容器中的 Nginx Web 服务器,配置了生产级的安全和性能优化。</p>
            
            <div class="info-grid">
                <div class="info-card">
                    <h3>📁 目录结构</h3>
                    <div class="code-block">
nginx-docker/
├── docker-compose.yml
├── conf/
│   ├── nginx.conf
│   └── mime.types
├── conf.d/
│   ├── default.conf
│   ├── ssl.conf
│   └── gzip.conf
├── html/
│   ├── index.html
│   └── error/
├── logs/
└── certs/
                    </div>
                </div>
                
                <div class="info-card">
                    <h3>⚙️ 配置特性</h3>
                    <ul>
                        <li>性能优化(Gzip、缓存)</li>
                        <li>安全头部(CSP、HSTS)</li>
                        <li>SSL/TLS 支持</li>
                        <li>HTTP/2 支持</li>
                        <li>健康检查</li>
                        <li>访问控制</li>
                    </ul>
                </div>
                
                <div class="info-card">
                    <h3>🔧 管理命令</h3>
                    <div class="code-block">
# 启动服务
docker-compose up -d

# 查看日志
docker-compose logs -f

# 重启服务
docker-compose restart

# 测试配置
docker exec nginx-prod nginx -t
                    </div>
                </div>
            </div>
            
            <div class="server-info">
                <h3>📊 服务器信息</h3>
                <p><strong>服务器地址:</strong> <span id="server-addr">获取中...</span></p>
                <p><strong>当前协议:</strong> <span id="protocol">获取中...</span></p>
                <p><strong>环境:</strong> <span id="environment">production</span></p>
                <p><strong>启动时间:</strong> <span id="uptime">获取中...</span></p>
            </div>
            
            <h3>🔗 快速链接</h3>
            <div class="button-group">
                <a href="/health" class="btn">健康检查</a>
                <a href="/error/404.html" class="btn">404 页面</a>
                <a href="/error/50x.html" class="btn">50x 页面</a>
                <a href="/nginx_status" class="btn">Nginx 状态</a>
                <a href="https://github.com/nginx/nginx" class="btn" target="_blank">Nginx 文档</a>
                <a href="https://docs.docker.com" class="btn" target="_blank">Docker 文档</a>
            </div>
            
            <h3>📝 部署说明</h3>
            <div class="code-block">
# 1. 克隆或创建项目目录
mkdir -p /usr/local/src/nginx/nginx-docker

# 2. 复制配置文件到对应目录

# 3. 启动服务
cd /usr/local/src/nginx/nginx-docker
docker-compose up -d

# 4. 验证部署
curl http://localhost
            </div>
        </div>
        
        <footer>
            <p>© 2024 Docker Nginx 服务 | 基于 Nginx {{NGINX_VERSION}} 和 Docker</p>
            <p>最后更新: <span id="current-time">加载中...</span></p>
        </footer>
    </div>
    
    <script>
        // 更新当前时间
        function updateTime() {
            const now = new Date();
            document.getElementById('current-time').textContent = 
                now.toLocaleString('zh-CN', {
                    year: 'numeric',
                    month: '2-digit',
                    day: '2-digit',
                    hour: '2-digit',
                    minute: '2-digit',
                    second: '2-digit'
                });
        }
        
        // 获取服务器信息
        function updateServerInfo() {
            // 服务器地址
            document.getElementById('server-addr').textContent = window.location.hostname;
            
            // 协议
            document.getElementById('protocol').textContent = window.location.protocol;
            
            // 更新时间
            updateTime();
            
            // 定期更新时间
            setInterval(updateTime, 1000);
            
            // 检查健康状态
            fetch('/health')
                .then(response => {
                    const statusBadge = document.getElementById('status');
                    if (response.ok) {
                        statusBadge.style.background = '#4CAF50';
                        statusBadge.textContent = '运行正常';
                    } else {
                        statusBadge.style.background = '#F44336';
                        statusBadge.textContent = '服务异常';
                    }
                })
                .catch(error => {
                    console.error('健康检查失败:', error);
                });
        }
        
        // 页面加载完成后执行
        document.addEventListener('DOMContentLoaded', updateServerInfo);
    </script>
</body>
</html>
}

# HTTP重定向到HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name your-domain.com;
    return 301 https://$server_name$request_uri;
}

1.2.7 404 错误页面

bash 复制代码
<!-- /usr/local/src/nginx/nginx-docker/html/error/404.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>404 - 页面未找到</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            text-align: center;
            padding: 20px;
        }
        
        .error-container {
            max-width: 600px;
            background: rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(10px);
            padding: 40px;
            border-radius: 20px;
            box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2);
        }
        
        .error-code {
            font-size: 8rem;
            font-weight: bold;
            line-height: 1;
            margin: 0;
            text-shadow: 5px 5px 0 rgba(0, 0, 0, 0.2);
        }
        
        .error-message {
            font-size: 1.8rem;
            margin: 20px 0;
        }
        
        .error-description {
            font-size: 1.1rem;
            opacity: 0.9;
            margin-bottom: 30px;
            line-height: 1.6;
        }
        
        .home-link {
            display: inline-block;
            padding: 12px 30px;
            background: white;
            color: #667eea;
            text-decoration: none;
            border-radius: 50px;
            font-weight: bold;
            transition: all 0.3s ease;
        }
        
        .home-link:hover {
            background: #f8f9fa;
            transform: translateY(-2px);
            box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
        }
        
        .error-details {
            margin-top: 30px;
            font-size: 0.9rem;
            opacity: 0.7;
            border-top: 1px solid rgba(255, 255, 255, 0.2);
            padding-top: 20px;
        }
        
        @media (max-width: 768px) {
            .error-code {
                font-size: 5rem;
            }
            
            .error-message {
                font-size: 1.4rem;
            }
        }
    </style>
</head>
<body>
    <div class="error-container">
        <h1 class="error-code">404</h1>
        <h2 class="error-message">页面未找到</h2>
        <div class="error-description">
            抱歉,您请求的页面不存在或已被移动。<br>
            请检查URL是否正确,或返回首页。
        </div>
        <a href="/" class="home-link">返回首页</a>
        <div class="error-details">
            请求的URL: <span id="requested-url">未知</span><br>
            时间: <span id="error-time">加载中...</span>
        </div>
    </div>
    
    <script>
        // 显示请求的URL
        document.getElementById('requested-url').textContent = 
            window.location.pathname + window.location.search;
        
        // 更新时间
        function updateTime() {
            const now = new Date();
            document.getElementById('error-time').textContent = 
                now.toLocaleString('zh-CN');
        }
        
        updateTime();
        setInterval(updateTime, 1000);
    </script>
</body>
</html>

1.2.8 50x 错误页面

bash 复制代码
<!-- /usr/local/src/nginx/nginx-docker/html/error/50x.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>服务器错误</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            text-align: center;
            padding: 20px;
        }
        
        .error-container {
            max-width: 600px;
            background: rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(10px);
            padding: 40px;
            border-radius: 20px;
            box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2);
        }
        
        .error-icon {
            font-size: 5rem;
            margin-bottom: 20px;
        }
        
        .error-title {
            font-size: 2.5rem;
            margin: 0 0 20px 0;
        }
        
        .error-description {
            font-size: 1.1rem;
            opacity: 0.9;
            margin-bottom: 30px;
            line-height: 1.6;
        }
        
        .action-buttons {
            display: flex;
            gap: 15px;
            justify-content: center;
            flex-wrap: wrap;
        }
        
        .btn {
            padding: 12px 25px;
            border-radius: 50px;
            text-decoration: none;
            font-weight: bold;
            transition: all 0.3s ease;
        }
        
        .btn-primary {
            background: white;
            color: #f5576c;
        }
        
        .btn-secondary {
            background: rgba(255, 255, 255, 0.2);
            color: white;
            border: 2px solid rgba(255, 255, 255, 0.3);
        }
        
        .btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
        }
        
        .error-details {
            margin-top: 30px;
            font-size: 0.9rem;
            opacity: 0.7;
            border-top: 1px solid rgba(255, 255, 255, 0.2);
            padding-top: 20px;
        }
        
        .status-info {
            display: flex;
            justify-content: space-around;
            margin-top: 20px;
            font-size: 0.9rem;
        }
        
        .status-item {
            padding: 10px;
        }
        
        @media (max-width: 768px) {
            .action-buttons {
                flex-direction: column;
                align-items: center;
            }
            
            .btn {
                width: 100%;
                max-width: 250px;
            }
        }
    </style>
</head>
<body>
    <div class="error-container">
        <div class="error-icon">⚠️</div>
        <h1 class="error-title">服务器遇到问题</h1>
        <div class="error-description">
            抱歉,服务器在处理您的请求时遇到了问题。<br>
            我们的技术团队已经收到通知,正在努力修复。
        </div>
        
        <div class="action-buttons">
            <a href="/" class="btn btn-primary">返回首页</a>
            <a href="javascript:location.reload()" class="btn btn-secondary">刷新页面</a>
            <a href="javascript:history.back()" class="btn btn-secondary">返回上一页</a>
        </div>
        
        <div class="error-details">
            <div class="status-info">
                <div class="status-item">
                    <strong>错误类型:</strong> 50x 服务器错误
                </div>
                <div class="status-item">
                    <strong>时间:</strong> <span id="error-time">加载中...</span>
                </div>
                <div class="status-item">
                    <strong>状态:</strong> <span id="server-status">检查中...</span>
                </div>
            </div>
            <p style="margin-top: 20px; font-size: 0.8rem;">
                如果问题持续存在,请联系系统管理员。
            </p>
        </div>
    </div>
    
    <script>
        // 更新时间
        function updateTime() {
            const now = new Date();
            document.getElementById('error-time').textContent = 
                now.toLocaleString('zh-CN');
        }
        
        // 检查服务器状态
        function checkServerStatus() {
            fetch('/health')
                .then(response => {
                    if (response.ok) {
                        document.getElementById('server-status').textContent = '服务器运行中';
                        document.getElementById('server-status').style.color = '#4CAF50';
                    } else {
                        document.getElementById('server-status').textContent = '服务器异常';
                        document.getElementById('server-status').style.color = '#F44336';
                    }
                })
                .catch(() => {
                    document.getElementById('server-status').textContent = '无法连接服务器';
                    document.getElementById('server-status').style.color = '#FF9800';
                });
        }
        
        // 初始化
        updateTime();
        setInterval(updateTime, 1000);
        checkServerStatus();
        
        // 30秒后重新检查状态
        setInterval(checkServerStatus, 30000);
    </script>
</body>
</html>

1.2.9 一键启动脚本

bash 复制代码
#!/bin/bash
# /usr/local/src/nginx/nginx-docker/deploy.sh
set -e

echo "🚀 开始部署 Nginx Docker 服务..."
cd /usr/local/src/nginx/nginx-docker

# 1. 检查 Docker 和 Docker Compose
if ! command -v docker &> /dev/null; then
    echo "❌ Docker 未安装"
    exit 1
fi

if ! command -v docker-compose &> /dev/null; then
    echo "❌ Docker Compose 未安装"
    exit 1
fi

# 2. 生成自签名证书(如果不存在)
if [ ! -f certs/fullchain.pem ] || [ ! -f certs/privkey.pem ]; then
    echo "🔐 生成自签名SSL证书..."
    mkdir -p certs
    
    # 生成私钥
    openssl genrsa -out certs/privkey.pem 2048
    
    # 生成证书签名请求
    openssl req -new -key certs/privkey.pem -out certs/cert.csr \
        -subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/OU=IT/CN=localhost"
    
    # 生成自签名证书
    openssl x509 -req -days 365 -in certs/cert.csr \
        -signkey certs/privkey.pem -out certs/fullchain.pem
    
    # 生成Diffie-Hellman参数(可选)
    openssl dhparam -out certs/dhparam.pem 2048
    
    # 创建默认证书(防止IP直接访问SSL时的错误)
    cp certs/fullchain.pem certs/default.crt
    cp certs/privkey.pem certs/default.key
    
    rm certs/cert.csr
    echo "✅ SSL证书生成完成"
fi

# 3. 创建日志目录
mkdir -p logs

# 4. 构建并启动服务
echo "📦 启动 Docker 服务..."
docker-compose up -d --build

# 5. 等待服务启动
echo "⏳ 等待服务启动..."
sleep 5

# 6. 验证部署
echo "🔍 验证部署..."
if docker-compose ps | grep -q "Up"; then
    echo "✅ 服务启动成功"
    
    # 测试HTTP访问
    HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost/health)
    if [ "$HTTP_STATUS" = "200" ]; then
        echo "✅ HTTP 服务正常 (状态码: $HTTP_STATUS)"
    else
        echo "⚠️  HTTP 服务异常 (状态码: $HTTP_STATUS)"
    fi
    
    # 获取服务器IP
    SERVER_IP=$(hostname -I | awk '{print $1}')
    echo ""
    echo "🌐 服务信息:"
    echo "  HTTP 地址: http://$SERVER_IP"
    echo "  HTTP 地址: http://localhost"
    echo "  HTTPS 地址: https://localhost (使用自签名证书)"
    echo ""
    echo "📊 管理命令:"
    echo "  查看日志: docker-compose logs -f"
    echo "  停止服务: docker-compose down"
    echo "  重启服务: docker-compose restart"
    echo "  进入容器: docker-compose exec nginx sh"
    echo ""
    echo "📁 配置文件位置: /usr/local/src/nginx/nginx-docker/"
else
    echo "❌ 服务启动失败"
    echo "查看日志: docker-compose logs"
    exit 1
fi

二、使用Docker Compose搭建Nginx集群

2.1 单机多 Nginx 容器集群(基础版)

2.1.1 架构图

2.1.2 创建目录

bash 复制代码
# 目录结构
/usr/local/src/nginx/nginx-cluster/
├── docker-compose.yml          # 主配置文件
├── ssl/
│   ├── cert.pem          # SSL证书
│   └── key.pem           # SSL私钥
├── nginx/
│   ├── loadbalancer/          # 负载均衡器配置
│   │   ├── nginx.conf
│   │   └── conf.d/
│   │       └── upstream.conf
│   ├── server1/               # 服务器1配置
│   │   ├── nginx.conf
│   │   └── html/
│   │       └── index.html
│   ├── server2/               # 服务器2配置
│   │   ├── nginx.conf
│   │   └── html/
│   │       └── index.html
│   └── server3/               # 服务器3配置
│       ├── nginx.conf
│       └── html/
│           └── index.html
├── logs/
└── ...其他目录

# 创建必要的目录
mkdir -p nginx/{loadbalancer/conf.d,server1/html,server2/html,server3/html}
mkdir -p logs/{loadbalancer,server1,server2,server3}
mkdir -p ssl

2.1.3 docker-compose.yml 配置

bash 复制代码
version: '3.8'

services:
  # 负载均衡器
  loadbalancer:
    image: nginx:alpine
    container_name: nginx-lb
    hostname: nginx-loadbalancer
    ports:
      - "80:80"
      - "443:443" # 不需要https可以注释掉
    volumes:
      # 负载均衡器配置
      - ./nginx/loadbalancer/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/loadbalancer/conf.d/upstream.conf:/etc/nginx/conf.d/upstream.conf:ro
      # SSL证书目录(可选,不需要https可以注释掉)
      - ./ssl:/etc/nginx/ssl:ro
      
      # 日志目录
      - ./logs/loadbalancer:/var/log/nginx
    networks:
      nginx-cluster:
        ipv4_address: 172.20.0.10
    depends_on:
      - server1
      - server2
      - server3
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 20s

  # Nginx服务器1
  server1:
    image: nginx:alpine
    container_name: nginx-server-1
    hostname: nginx-01
    volumes:
      # 服务器1配置
      - ./nginx/server1/nginx.conf:/etc/nginx/nginx.conf:ro
      
      # 网站文件
      - ./nginx/server1/html:/usr/share/nginx/html:ro
      
      # 日志
      - ./logs/server1:/var/log/nginx
    networks:
      nginx-cluster:
        ipv4_address: 172.20.0.11
    expose:
      - "80"
    environment:
      - SERVER_ID=1
      - SERVER_NAME=nginx-01
      - TZ=Asia/Shanghai
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 10s
      retries: 3

  # Nginx服务器2
  server2:
    image: nginx:alpine
    container_name: nginx-server-2
    hostname: nginx-02
    volumes:
      - ./nginx/server2/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/server2/html:/usr/share/nginx/html:ro
      - ./logs/server2:/var/log/nginx
    networks:
      nginx-cluster:
        ipv4_address: 172.20.0.12
    expose:
      - "80"
    environment:
      - SERVER_ID=2
      - SERVER_NAME=nginx-02
      - TZ=Asia/Shanghai
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 10s
      retries: 3

  # Nginx服务器3
  server3:
    image: nginx:alpine
    container_name: nginx-server-3
    hostname: nginx-03
    volumes:
      - ./nginx/server3/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/server3/html:/usr/share/nginx/html:ro
      - ./logs/server3:/var/log/nginx
    networks:
      nginx-cluster:
        ipv4_address: 172.20.0.13
    expose:
      - "80"
    environment:
      - SERVER_ID=3
      - SERVER_NAME=nginx-03
      - TZ=Asia/Shanghai
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 10s
      retries: 3

  # 监控服务(可选)
  monitor:
    image: dockersamples/visualizer:latest
    container_name: nginx-cluster-monitor
    ports:
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - nginx-cluster
    restart: unless-stopped
    deploy:
      placement:
        constraints:
          - node.role==manager

networks:
  nginx-cluster:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.20.0.0/24
          gateway: 172.20.0.1

2.1.4 Nginx配置文件

  1. 负载均衡器配置
    • nginx/loadbalancer/nginx.conf

      bash 复制代码
      user nginx;
      worker_processes auto;
      error_log /var/log/nginx/error.log warn;
      pid /var/run/nginx.pid;
      
      events {
          worker_connections 1024;
          use epoll;
          multi_accept on;
      }
      
      http {
          include /etc/nginx/mime.types;
          default_type application/octet-stream;
      
          log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for" '
                          'upstream: $upstream_addr response_time: $upstream_response_time';
      
          access_log /var/log/nginx/access.log main;
      
          # 性能优化
          sendfile on;
          tcp_nopush on;
          tcp_nodelay on;
          keepalive_timeout 65;
          keepalive_requests 1000;
          client_max_body_size 10M;
      
          # Gzip压缩
          gzip on;
          gzip_vary on;
          gzip_min_length 1024;
          gzip_comp_level 6;
          gzip_types text/plain text/css text/xml text/javascript 
                     application/json application/javascript 
                     application/xml+rss application/x-javascript;
      
          # 包含上游服务器配置
          include /etc/nginx/conf.d/*.conf;
      }
    • nginx/loadbalancer/conf.d/upstream.conf

      bash 复制代码
      # 定义上游服务器组
      upstream nginx_backend {
          # 负载均衡算法(可选):
          # 默认轮询,其他选项:
          # least_conn;   # 最少连接数
          # ip_hash;      # IP哈希(会话保持)
          # hash $request_uri consistent;  # URL哈希
          
          # 服务器配置
          server server1:80 weight=3 max_fails=3 fail_timeout=30s;
          server server2:80 weight=2 max_fails=3 fail_timeout=30s;
          server server3:80 weight=1 max_fails=3 fail_timeout=30s;
          
          # 健康检查(需要Nginx Plus)
          # health_check interval=5s fails=3 passes=2;
          
          # 连接池
          keepalive 32;
      }
      
      # 负载均衡器虚拟主机
      server {
          listen 80;
          server_name localhost;
          
          # 安全头部
          add_header X-Frame-Options "SAMEORIGIN" always;
          add_header X-Content-Type-Options "nosniff" always;
          add_header X-XSS-Protection "1; mode=block" always;
          
          # 负载均衡状态页面
          location /nginx_status {
              stub_status on;
              access_log off;
              allow 172.20.0.0/24;  # 只允许集群内访问
              deny all;
          }
          
          # 健康检查端点
          location /health {
              access_log off;
              return 200 "loadbalancer healthy\n";
              add_header Content-Type text/plain;
          }
          
          # 集群状态页面
          location /cluster_status {
              proxy_pass http://nginx_backend;
              proxy_set_header Host $host;
              
              # 自定义响应头,显示负载均衡信息
              add_header X-Upstream-Addr $upstream_addr always;
              add_header X-Upstream-Response-Time $upstream_response_time always;
          }
          
          # 默认代理到后端服务器
          location / {
              proxy_pass http://nginx_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;
              proxy_set_header X-Forwarded-Host $host;
              proxy_set_header X-Forwarded-Port $server_port;
              
              # 超时设置
              proxy_connect_timeout 5s;
              proxy_send_timeout 10s;
              proxy_read_timeout 10s;
              
              # 连接池
              proxy_http_version 1.1;
              proxy_set_header Connection "";
              
              # 缓冲设置
              proxy_buffering on;
              proxy_buffer_size 4k;
              proxy_buffers 8 4k;
              proxy_busy_buffers_size 8k;
              
              # 错误处理
              proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
              proxy_next_upstream_timeout 0;
              proxy_next_upstream_tries 3;
              
              # 添加响应头,显示后端服务器
              add_header X-Backend-Server $upstream_addr;
          }
      }
      
      # HTTPS配置(可选,不需要https可以注释掉)
      server {
          listen 443 ssl;
          http2 on;
          server_name localhost;
          
          ssl_certificate /etc/nginx/ssl/cert.pem;
          ssl_certificate_key /etc/nginx/ssl/key.pem;
          
          ssl_protocols TLSv1.2 TLSv1.3;
          ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
          ssl_prefer_server_ciphers off;
          
          # SSL会话缓存
          ssl_session_cache shared:SSL:10m;
          ssl_session_timeout 10m;
          
          # 其他配置与HTTP相同
          location / {
              proxy_pass http://nginx_backend;
              proxy_set_header Host $host;
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header X-Forwarded-Proto https;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          }
      }
    • nginx/server2/nginx.conf 和 nginx/server3/nginx.conf 类似,只需修改:

      • X-Server-ID 和 X-Server-Name 头
      • /api/server-info 返回的JSON内容
      • 如果需要,可以有不同的root路径或配置

2.1.5 生成证书(可选,不需要https可以不用)

bash 复制代码
# 创建ssl目录
mkdir -p ssl

# 生成证书(使用-subj避免交互式提问)
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
  -keyout ssl/key.pem \
  -out ssl/cert.pem \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=LocalDev/CN=localhost"

2.1.6 HTML文件示例

  • nginx/server1/html/index.html

    bash 复制代码
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Nginx Server 1 - Docker集群示例</title>
        <style>
            * {
                margin: 0;
                padding: 0;
                box-sizing: border-box;
            }
            
            body {
                font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                line-height: 1.6;
                background: linear-gradient(135deg, #4a90e2 0%, #7b4397 100%);
                min-height: 100vh;
                color: #333;
            }
            
            .container {
                max-width: 1200px;
                margin: 0 auto;
                padding: 20px;
            }
            
            header {
                text-align: center;
                color: white;
                margin-bottom: 40px;
                padding: 40px;
                background: rgba(255, 255, 255, 0.1);
                border-radius: 20px;
                backdrop-filter: blur(10px);
            }
            
            .server-card {
                background: white;
                border-radius: 15px;
                padding: 30px;
                margin-bottom: 30px;
                box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
                border-left: 5px solid #4a90e2;
            }
            
            .server-id {
                display: inline-block;
                background: #4a90e2;
                color: white;
                padding: 5px 15px;
                border-radius: 20px;
                font-weight: bold;
                margin-bottom: 15px;
            }
            
            .info-grid {
                display: grid;
                grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
                gap: 20px;
                margin-top: 20px;
            }
            
            .info-item {
                background: #f8f9fa;
                padding: 15px;
                border-radius: 10px;
                border-left: 4px solid #7b4397;
            }
            
            .links {
                display: flex;
                gap: 15px;
                margin-top: 20px;
                flex-wrap: wrap;
            }
            
            .link-btn {
                display: inline-block;
                padding: 10px 20px;
                background: #4a90e2;
                color: white;
                text-decoration: none;
                border-radius: 8px;
                transition: all 0.3s ease;
            }
            
            .link-btn:hover {
                background: #7b4397;
                transform: translateY(-2px);
            }
            
            footer {
                text-align: center;
                color: white;
                margin-top: 40px;
                padding: 20px;
                font-size: 0.9rem;
                opacity: 0.8;
            }
        </style>
    </head>
    <body>
        <div class="container">
            <header>
                <h1>🚀 Nginx Server 1</h1>
                <p>Docker容器集群 - 节点1</p>
            </header>
            
            <div class="server-card">
                <div class="server-id">Server ID: 1</div>
                <h2>服务器信息</h2>
                <p>这是Nginx集群中的第一个服务器节点。</p>
                
                <div class="info-grid">
                    <div class="info-item">
                        <strong>容器名称:</strong> nginx-server-1
                    </div>
                    <div class="info-item">
                        <strong>主机名:</strong> nginx-01
                    </div>
                    <div class="info-item">
                        <strong>IP地址:</strong> 172.20.0.11
                    </div>
                    <div class="info-item">
                        <strong>端口:</strong> 80
                    </div>
                </div>
                
                <div class="links">
                    <a href="/health" class="link-btn">健康检查</a>
                    <a href="/api/server-info" class="link-btn">服务器信息API</a>
                    <a href="/status" class="link-btn">Nginx状态</a>
                </div>
            </div>
            
            <div class="server-card">
                <h2>集群信息</h2>
                <p>当前服务器属于一个三节点Nginx集群,通过负载均衡器提供服务。</p>
                
                <div class="info-grid">
                    <div class="info-item">
                        <strong>负载均衡器:</strong> nginx-lb:80
                    </div>
                    <div class="info-item">
                        <strong>集群网络:</strong> 172.20.0.0/24
                    </div>
                    <div class="info-item">
                        <strong>节点数量:</strong> 3
                    </div>
                    <div class="info-item">
                        <strong>部署方式:</strong> Docker Compose
                    </div>
                </div>
            </div>
            
            <footer>
                <p>© 2024 Docker Nginx Cluster Example | 
                    <a href="http://localhost:8080" target="_blank" style="color:white;">监控面板</a> |
                    <a href="http://localhost/nginx_status" target="_blank" style="color:white;">负载均衡器状态</a>
                </p>
            </footer>
        </div>
    </body>
    </html>
  • Server2和Server3的HTML文件只需修改:

    • 标题和服务器ID
    • 服务器信息内容
    • 颜色方案(可选,用于区分不同服务器)
    bash 复制代码
    <!-- 修改标题和ID -->
    <h1>🚀 Nginx Server 2</h1>
    <div class="server-id">Server ID: 2</div>
    
    <!-- 修改颜色 -->
    <style>
        .server-card {
            border-left-color: #e24a4a;  /* 不同颜色 */
        }
        .link-btn {
            background: #e24a4a;
        }
    </style>

2.1.7 部署脚本 deploy-cluster.sh

bash 复制代码
#!/bin/bash

set -e

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

echo -e "${BLUE}================================${NC}"
echo -e "${GREEN}  Nginx Docker集群部署脚本${NC}"
echo -e "${BLUE}================================${NC}"

# 检查依赖
check_dependencies() {
    echo -e "\n${YELLOW}[1/3] 检查依赖...${NC}"
    
    if ! command -v docker &> /dev/null; then
        echo -e "${RED}错误: 未安装Docker${NC}"
        exit 1
    fi
    
    if ! command -v docker-compose &> /dev/null; then
        echo "使用docker compose替代docker-compose"
        COMPOSE_CMD="docker compose"
    else
        COMPOSE_CMD="docker-compose"
    fi
    
    echo -e "${GREEN}✓ Docker和Docker Compose已安装${NC}"
}

# 启动集群
start_cluster() {
    echo -e "\n${YELLOW}[2/3] 启动Docker集群...${NC}"
    
    # 停止并清理旧容器
    echo "清理旧容器..."
    $COMPOSE_CMD down 2>/dev/null || true
    
    # 启动新集群
    echo "启动集群..."
    $COMPOSE_CMD up -d --build
    
    echo -e "${GREEN}✓ 集群启动完成${NC}"
}

# 显示部署信息
show_info() {
    echo -e "\n${YELLOW}[3/3] 部署完成!${NC}"
    
    echo -e "\n${GREEN}========== 集群信息 ==========${NC}"
    echo "🌐 负载均衡器: http://localhost"
    echo "📊 监控面板:    http://localhost:8080"
    echo "🔧 管理命令:    ./manage-cluster.sh"
    echo -e "${GREEN}==============================${NC}"
    
    echo -e "\n${BLUE}容器状态:${NC}"
    $COMPOSE_CMD ps
}

# 主流程
main() {
    check_dependencies
    start_cluster
    show_info
}

main
相关推荐
中杯可乐多加冰1 小时前
openEuler软件生态体验:快速部署Nginx Web服务器
服务器·前端·nginx
邪恶喵喵1 小时前
nginx反向代理和负载均衡
运维·nginx·负载均衡
退役小学生呀1 小时前
二十六、K8s集群备份恢复
linux·云原生·容器·kubernetes·k8s
聚梦小课堂2 小时前
【Docker实战】n8n容器如何访问宿主机ComfyUI?详解 host.docker.internal 与网络配置
docker·网络配置·comfyui·n8n·自动化工作流
肥仔哥哥19302 小时前
Jenkins+Docker+Harbor全链路CI/CD重温笔记
ci/cd·docker·jenkins
呆子罗2 小时前
[解决方案]企业级ASP.NET CORE项目部署方案 IIS NGINX Win/Linux
linux·nginx·asp.net
A-刘晨阳2 小时前
【云原生】Kubernetes 指定节点部署 Pod
运维·云原生·容器·kubernetes·云计算
AI云原生2 小时前
《开箱即用的高性能:openEuler 默认配置下的 Web 服务性能评测》
运维·前端·docker·云原生·开源·开源软件·开源协议
汪碧康2 小时前
【k8s-1.34.2安装部署】一.系统初始化及k8s集群规划
云原生·容器·kubernetes