[笔记] 多层 Nginx反向代理与Docker容器化前端应用部署 : 客户端 -> 本地 Nginx -> Docker 内的 Nginx -> 前端应用

21 阅读6分钟

部署前端项目客户端 -> 本地 Nginx -> Docker 内的 Nginx -> 前端应用

在现代 Web 开发中,高效的部署流程对于确保应用的稳定性和性能至关重要。本文将介绍一种多层架构的部署方案,通过客户端访问本地 Nginx,再由本地 Nginx 转发请求到 Docker 内的 Nginx,最终到达前端应用。这种架构不仅提高了系统的可扩展性,还增强了安全性。以下是具体的部署步骤和注意事项。

环境准备 :

Nginx:用于本地和 Docker 内部的反向代理 , 可参考 [笔记] CentOS7 + Nginx 环境下,安装使用 Let‘s Encrypt 免费 SSL 证书 (自动续签) - 掘金

Docker:用于容器化前端应用 , 可参考 [笔记] Centos7 安装 Docker 和 Docker Compose 及 Docker 命令大全Docker和 - 掘金

一. 项目结构

/home/luchangqiu/project
├── app1
│   ├── index.html
│   └── Dockerfile
├── app2
│   ├── index.html
│   └── Dockerfile
├── docker-compose.yml
├── generate_nginx_conf.py
├── nginx.conf.j2
└── nginx_apps.conf

二. 步骤详解

1. 创建前端项目的文件和 Dockerfile

  • 创建 app1 的静态 HTML 文件
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>App 1</title>
    </head>
    <body>
        <h1>Welcome to App 1</h1>
        <p>This is the first application.</p>
    </body>
    </html>
    
  • 创建 app1 的 Dockerfile
    # app1/Dockerfile
    
    # 使用官方的 Nginx 镜像作为基础镜像
    FROM nginx:alpine
    
    # 复制静态文件到 Nginx 的默认目录
    COPY ./ /usr/share/nginx/html/app1
    
  • 创建 app2 的静态 HTML 文件
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>App 2</title>
    </head>
    <body>
        <h1>Welcome to App 2</h1>
        <p>This is the second application.</p>
    </body>
    </html>
    
  • 创建 app2 的 Dockerfile
    # app2/Dockerfile
    
    # 使用官方的 Nginx 镜像作为基础镜像
    FROM nginx:alpine
    
    # 复制静态文件到 Nginx 的默认目录
    COPY ./ /usr/share/nginx/html/app2
    

2. 创建 Docker Compose 文件

在项目根目录下创建 docker-compose.yml 文件。

version: '3'  # Docker Compose 文件版本,版本 3 支持更多高级功能

services:  # 定义服务列表
  nginx:  # 定义 Nginx 服务
    image: nginx:alpine  # 使用官方的 Nginx 镜像,基于 Alpine Linux
    container_name: nginx_container  # 设置容器名称
    volumes:  # 挂载卷,将主机文件映射到容器内
      - ./nginx_apps.conf:/etc/nginx/conf.d/default.conf  # 将主机上的 `nginx_apps.conf` 映射到容器内的 `/etc/nginx/conf.d/default.conf`
    ports:  # 端口映射,将容器端口映射到主机端口
      - "8080:80"  # 将容器的 80 端口映射到主机的 8080 端口
      - "4443:443"  # 将容器的 443 端口映射到主机的 4443 端口
    depends_on:  # 定义依赖关系,确保在启动 Nginx 之前先启动 `app1` 和 `app2`
      - app1
      - app2
    networks:  # 定义网络配置
      - frontend_network  # 使用名为 `frontend_network` 的网络
    healthcheck:  # 健康检查配置
      test: [ "CMD-SHELL", "nc -zv app1_container 80 && nc -zv app2_container 80" ]  # 使用 `nc` 命令检查 `app1_container` 和 `app2_container` 是否在监听 80 端口
      interval: 10s  # 每 10 秒执行一次健康检查
      timeout: 5s  # 每次健康检查超时时间为 5 秒
      retries: 5  # 如果健康检查失败,最多重试 5 次

  app1:  # 定义 `app1` 服务
    build: ./app1  # 使用 `app1` 目录下的 `Dockerfile` 构建镜像
    container_name: app1_container  # 设置容器名称
    networks:  # 定义网络配置
      - frontend_network  # 使用名为 `frontend_network` 的网络
    healthcheck:  # 健康检查配置
      test: [ "CMD-SHELL", "nc -zv 0.0.0.0 80" ]  # 使用 `nc` 命令检查容器是否在监听 80 端口
      interval: 10s  # 每 10 秒执行一次健康检查
      timeout: 5s  # 每次健康检查超时时间为 5 秒
      retries: 5  # 如果健康检查失败,最多重试 5 次

  app2:  # 定义 `app2` 服务
    build: ./app2  # 使用 `app2` 目录下的 `Dockerfile` 构建镜像
    container_name: app2_container  # 设置容器名称
    networks:  # 定义网络配置
      - frontend_network  # 使用名为 `frontend_network` 的网络
    healthcheck:  # 健康检查配置
      test: [ "CMD-SHELL", "nc -zv 0.0.0.0 80" ]  # 使用 `nc` 命令检查容器是否在监听 80 端口
      interval: 10s  # 每 10 秒执行一次健康检查
      timeout: 5s  # 每次健康检查超时时间为 5 秒
      retries: 5  # 如果健康检查失败,最多重试 5 次

networks:  # 定义网络
  frontend_network:  # 定义名为 `frontend_network` 的网络
    driver: bridge  # 使用桥接网络驱动
  • version: '3':指定 Docker Compose 文件的版本。
  • services::定义一组服务。
  • app1:app2::分别定义两个服务的详细配置,包括构建目录、容器名称、端口映射和网络连接。
  • networks::定义一个名为 frontend_network 的桥接网络,供两个服务使用。

3. 创建 Nginx 配置文件模板

在项目根目录下创建 nginx.conf.j2 文件。

# nginx.conf.j2
server {
    listen 80;
    server_name {{ server_name }};

    {% for app in apps %}
    location /{{ app.name }} {
        proxy_pass http://{{ app.container }}:{{ app.port }};
        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;
    }
    {% endfor %}
}
  • server { ... }:定义一个服务器块。
  • listen 80;:监听 80 端口。
  • server_name your_domain_or_ip;:指定服务器的域名或 IP 地址。
  • {% for app in apps %} ... {% endfor %}:循环遍历应用列表。
  • location /{{ app.name }} { ... }:定义处理特定路径的请求。
  • proxy_pass http://127.0.0.1:{{ app.port }};:将请求转发到后端服务。
  • proxy_set_header:设置传递给后端服务的头部信息。

4. 创建生成 Nginx 配置文件的脚本

在项目根目录下创建 generate_nginx_conf.py 文件。

# -*- coding: utf-8 -*-
# 导入必要的模块
import jinja2
import argparse
import json

# 创建模板加载器,指定模板文件的搜索路径
# searchpath="./" 表示模板文件在当前目录下
template_loader = jinja2.FileSystemLoader(searchpath="./")

# 创建 Jinja2 环境,使用上面的模板加载器
# Jinja2 环境用于加载和渲染模板
template_env = jinja2.Environment(loader=template_loader)

# 解析命令行参数
def parse_arguments():
    parser = argparse.ArgumentParser(description="Generate Nginx configuration file.")
    parser.add_argument("--server-name", type=str, required=True, help="Server name (e.g., localhost)")
    parser.add_argument("--apps", type=str, required=True, help="JSON string of apps list")
    parser.add_argument("--output-file", type=str, required=True, help="Output file path")
    return parser.parse_args()

# 主函数
def main():
    # 解析命令行参数
    args = parse_arguments()

    # 解析 JSON 字符串为 Python 列表
    apps = json.loads(args.apps)

    # 加载模板文件 nginx.conf.j2
    template = template_env.get_template('nginx.conf.j2')

    # 渲染模板,将应用列表传递给模板
    rendered_config = template.render(server_name=args.server_name, apps=apps)

    # 将渲染后的配置写入输出文件
    with open(args.output_file, 'w') as f:
        f.write(rendered_config)

    print(f"Nginx 配置文件已生成:{args.output_file}")

if __name__ == "__main__":
    main()

5. 构建和运行 Docker 容器

  • 生成 Nginx 配置文件
    cd /home/luchangqiu/project
    
    # 运行脚本
    python generate_nginx_conf.py --server-name=localhost --apps='[{"name": "app1", "container": "app1_container", "port": 80}, {"name": "app2", "container": "app2_container", "port": 80}]' --output-file=nginx_apps.conf
    

在这里插入图片描述

  • server_name:服务器名称为 localhost

  • apps:一个包含两个应用信息的 JSON 字符串,每个应用包含名称、容器名称和端口。

  • output_file:生成的 Nginx 配置文件的路径为 nginx_apps.conf

    • 解决 ImportError: No module named jinja2

      jinja2 是一个 Python 模板引擎,需要先安装才能使用。您可以使用 pip 来安装它。

      在您的终端中运行以下命令来安装 jinja2

      pip install jinja2
      

      如果您使用的是 Python 3,并且 pip 命令指向 Python 2pip

      pip3 install jinja2
      

      验证安装

      python3 -c "import jinja2; print(jinja2.__version__)"
      

      在这里插入图片描述

      再去重新运行命令 python3 generate_nginx_conf.py

  • 构建和运行 Docker 容器
    docker-compose up -d
    
    • 解决 command not found
      #  安装 docker-compose
      pip3 install docker-compose
      # 再去重新运行
      docker-compose up -d
      

    在这里插入图片描述

6. 修改本地nginx默认配置文件

  • 添加 map 变量

    map 变量和 service 同级

    	# 定义 map 变量
        map $http_upgrade $connection_upgrade {
            default upgrade;
            '' close;
        }
    
  • 修改 监听 443 端口的 service , 添加 CORS 配置
     		# CORS 配置
            location / {
                add_header Access-Control-Allow-Origin *;
                add_header Access-Control-Allow-Methods *;
                add_header Access-Control-Allow-Headers *;
                if ($request_method = 'OPTIONS') {
                    return 204;
                }
                proxy_redirect off;
                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_pass http://127.0.0.1:8080/;
                client_max_body_size 20m;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
            }
    
  • 完整的 nginx.conf 的 http 配置

    在这里插入图片描述转存失败,建议直接上传图片文件

  • 测试 Nginx 配置文件语法正确性
    nginx -t
    

    在这里插入图片描述

  • 重新加载 nginx 配置文件
    nginx -s reload
    

7. 访问项目地址

访问 https://yourdomain.com/app1https://yourdomain.com/app2,确保它们能够正确显示。

image.png