社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  docker

前端开发必会的Nginx知识及结合Docker的项目部署实战

奇舞精选 • 6 月前 • 116 次点击  

起因

截屏2025-03-14 10.00.12

事情的起因来自一封邮件,年前上线了一个项目官网,在过年期间,突然收到了一封来自安全部门邮件,说上线的官网存在以上安全风险,让尽快处理。现在看来解决这个问题很容易,只需要在Nginx上的server 块中配置 server_name为备案域名,再设置一个默认的server模块,匹配所有未明确指定 server_name 的请求并返回403就OK,但是当时对Nginx并不熟悉,导致走了些弯路。所以这篇文章主要是前端在项目部署中,针对常见的Nginx功能的介绍。基于目前前端项目部署方式大多采用Docker的CI/CD工作流来进行,所以也会涉及到Docker以及常见项目部署配置的相关知识。

Nginx介绍

Nginx是由伊戈尔·赛索耶夫在2002年创建的,最初是为了解决C10K问题,也就是同时处理上万个并发连接的问题。Apache服务器当时在处理大量并发时效率不高,所以Nginx采用了事件驱动的异步架构,性能更好,资源消耗更低。后来在2004年公开发布,凭借其轻量级、事件驱动、异步非阻塞的架构,迅速成为高性能 Web 服务器和反向代理的热门选择。

前端常用的Nginx功能

我们将从以下方面介绍前端开发与项目部署中常见的Nginx相关功能,主要涉及:基于Docker的Nginx安装、配置文件、静态资源托管、反向代理、负载均衡、HTTPS 配置(SSL 证书)等

基于Docker的Nginx安装

基于Mac电脑,假设你已经安装好看Docker,通过下面的一行命令就可以安装基于Docker容器的Nginx

docker run -d -p 80:80 --name my-nginx nginx

这段命令用于运行一个 Nginx Docker 容器,并将其端口映射到主机的端口。

  • docker run:这是 Docker 命令,用于创建并运行一个新的容器。

  • -d:这是一个选项,表示以分离模式(detached mode)运行容器。容器将在后台运行,而不会占用当前终端。

  • -p 80:80:这是一个端口映射选项,格式为 主机端口:容器端口, 80:80 表示将主机的端口 80 映射到容器的端口 80。这样,访问主机的端口 80 时,实际上是在访问容器的端口 80。

  • --name my-nginx:这是一个选项,用于为容器指定一个名称。在这里,容器的名称被指定为 my-nginx。 这样你可以通过这个名称来管理和引用这个容器,而不需要使用容器的 ID。

  • nginx:这是要运行的镜像名称。在这里,使用的是官方的 Nginx 镜像。 Docker 会从 Docker Hub 拉取这个镜像(如果本地没有的话),并基于这个镜像创建并运行一个新的容器。

当我们在浏览器访问: http://localhost,若看到欢迎页即安装成功。

截屏2025-03-12 19.46.42
配置文件

当我们在主机上运行my-nginx容器后,可以通过命令:

docker exec -it my-nginx /bin/sh 

来进入到容器中,查看和修改nginx的相关配置,nginx的主配置文件为: /etc/nginx/nginx.conf,执行 cat nginx.conf查看文件为:

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


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"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;
    # 引入站点配置
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

这是 Nginx 的核心配置文件,定义了全局配置(如 Worker 进程数、日志格式、事件模型等)。通常不直接在这里配置具体的站点,而是通过 include 指令引入其他配置文件。我们一般不会修改这里的主配置文件,而是通过下面的/etc/nginx/conf.d/ 或 /etc/nginx/sites-available/来进行站点配置文件的配置。

站点配置目录: /etc/nginx/conf.d/ 或 /etc/nginx/sites-available/为什么有两个目录?

  • /etc/nginx/conf.d/:这是一个简单的配置目录,通常用于存放单个站点的配置文件。每个文件通常对应一个站点或服务,文件名以 .conf 结尾。适合小型项目或简单场景。也是我们前端开发最常用的目录。

  • /etc/nginx/sites-available/ 和 /etc/nginx/sites-enabled/:这是一个更灵活的配置管理方式,适合复杂的多站点场景。

    /sites-available/: 存放所有站点的配置文件(相当于“配置仓库”), /sites-enabled/: 存放当前启用的站点配置,这种方式可以方便地启用或禁用站点,而无需删除配置文件。

执行cat /etc/nginx/conf.d/default.conf 查看默认的配置文件

server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    root   /usr/share/nginx/html;
    index  index.html index.htm;
    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        try_files $uri $uri/ /index.html;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

我们部署前端项目,通过Nginx实现的相关功能,都是在这个文件中进行相关字段的配置而实现的。

  • server : 用于定义一个虚拟主机(Virtual Host),它决定了 Nginx 如何处理来自不同域名或 IP 的请求。

  • listen 80:监听 80 端口(HTTP 默认端口)。

  • server_name localhost:指定匹配的域名,用于虚拟主机识别,当请求的 Host 头匹配 localhost 时,该服务器块会处理请求。

  • root /usr/share/nginx/html;:指定服务器的根目录,用于查找静态文件。

  • index index.html:设置默认的首页文件: 用于指定默认的索引文件,当请求的路径是目录时,Nginx 会尝试返回 index.html 或 index.htm

  • location 块: 根据请求的 URL 路径定义不同的处理规则。

  • error_page:定义错误页面的处理规则。当服务器返回 500、502、503 或 504 错误时,返回 /50x.html

  • location = /50x.html : 精确匹配 /50x.html 路径,并指定其根目录, 也就是/50x.html 文件会从 /usr/share/nginx/html 目录中查找

根据这些字段的配置就可以解决上面邮件中提到的问题了。只需要在Nginx上的server 块中配置 server_name为备案域名,再设置一个默认的server快,匹配所有未明确指定 server_name 的请求并返回403就可以了。



    
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/dist;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}

# 默认 server 块,拒绝非法请求
server {
    listen 80 default_server;
    server_name _;
    return 403;
}

上面我们定义了两个server块,只有当来自host为localhost的请求才会匹配第一个server块,第二个是默认的server,匹配任意的host,我们直接返回403状态码。

注意:server_name 是必须的吗?不是必须的,但强烈建议配置。如果 server_name 未指定,Nginx 可能会匹配默认服务器,导致:潜在的安全问题:恶意用户可以通过 IP 直接访问 你的 Nginx 服务器,可能会暴露本应受限的服务。错误的路由: 多个站点可能会被错误地解析到默认的 server 块,导致访问到错误的网站或泄露敏感信息。

静态资源托管

Nginx 采用事件驱动架构,能够高效处理大量并发请求,适合托管静态资源(如 HTML、CSS、JavaScript、图片等),最长见的方式是托管一个单页应用(SPA)。我们将前端打包好的dist文件直接拷贝到Nginx的静态资源目录,并进行Nginx的相关配置就可以实现SPA应用的部署。

  • 在启动的my-nginx docker容器中执行



    
docker cp ./Desktop/dist my-nginx:/usr/share/nginx/

将本地的dist文件拷贝到容器的/usr/share/nginx/目录下,你可以通过docker  exec -it my-nginx /bin/bash 进入容器,查看是否拷贝成功。

  • 在本地修改default.conf 配置文件,然后将其拷贝到Nginx主配置文件下进行覆盖

    server {
        listen       80;
        listen  [::]:80;
        server_name  localhost;

        #access_log  /var/log/nginx/host.access.log  main;
        # 这个静态资源目录进行了修改
         root   /usr/share/nginx/dist;
         index  index.html index.htm;
        location / {
            try_files $uri $uri/ /index.html;
        }
        #error_page  404              /404.html;
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }

         # 设置缓存时间 
        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
            expires 1y;
            add_header Cache-Control "public";
        }
    }

    # 默认 server 块,拒绝非法请求
    server {
        listen 80 default_server;
        server_name _;
        return 403;
    }

    注意将原来的静态资源目录修改为 root /usr/share/nginx/dist;  这个是刚才我们拷贝的打包资源的目录。

  • 执行覆盖命令

    docker cp ./Desktop/default.conf my-nginx:/etc/nginx/conf.d/default.conf

    执行完成后,重新启动 Docker 容器:docker restart my-nginx, 浏览器访问http://localhost/就可以看到项目部署成功了。

  • try_files 指令的设置为:**try_files $uri $uri/ /index.html;** 当访问指定路径时,$uri:尝试查找请求的文件, $uri/:尝试查找请求的目录,/index.html:如果未找到文件或目录,则返回index.html。SPA 只有一个 index.html,前端路由由 JavaScript 处理,所有它最终都返回 index.html,由前端来解析路径。这适用于 Vue Router、React Router、Angular Router 的 history 模式,避免 404 的问题。

反向代理与负载均衡

为什么前端要了解Nginx的反向代理?

  • 解决跨域问题:对于前后端分离的项目比如:如前端部署在 http://frontend.com,后端在 http://api.example.com),通过 Nginx 将前后端请求统一到同一域名下(如前端 http://example.com,API http://example.com/api),浏览器认为这是同源请求,避免跨域限制。
  • 隐藏后端服务:另一方面可以避免直接暴露后端服务的 IP 或端口存在安全风险,通过反向代理方案,后端服务仅对 Nginx 可见,外部请求通过 Nginx 转发,隐藏真实后端地址。
  • 高并发场景下的负载均衡:高并发场景下,单台后端服务器可能无法处理所有请求。通过反向代理,Nginx 将流量分发到多个后端实例,提升系统吞吐量和容错能力。

如何实现反向代理:

还是基于上面的例子,假如我们有个后端的服务地址为:http://example.com/api, 而前端是本地的localhost,可以通过如下配置实现反向代理:

server {
    listen 80;
    server_name localhost;

    # 前端静态资源
    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html;  # 支持 SPA 路由
    }

    # 反向代理到后端 API
    location /api/ {
        proxy_pass http://example.com;  # 后端服务地址
        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:将匹配的请求转发到后端服务。这里需要注意location后面的路径与proxy_pass 的匹配规则,当前的设置会匹配以 /api/ 开头的请求(例如 /api/user/api/data),** proxy_pass**末尾不带/,则会将客户端请求的 URI 完整追加到目标地址后,

    实际的代理路径为:http://example.com/api/user 与http://example.com/api/data, 若 proxy_pass 包含路径且以 / 结尾,Nginx 会替换 location 匹配部分,实际路径为:http://example.com/user,http://example.com/data

  • proxy_set_header:传递客户端真实 IP 和域名信息到服务端。方便后端记录和分析。后端可以通过 X-Real-IP 读取用户 IP,用于日志记录、IP 限制、用户追踪等操作。

负载均衡

要配置 Nginx 实现负载均衡,你需要在 Nginx 配置文件中定义一个 upstream 块,并在服务器块中使用 proxy_pass 指令将请求转发到定义的上游服务器组。以下是一个示例配置,展示了如何配置 Nginx 实现负载均衡:

upstream backend {
    server backend1.example.com weight=3;
    server backend2.example.com;
    server backend3.example.com;
}

server {
    listen       80;
    server_name  example.com;

    # 反向代理到上游服务器组
    location / {
        proxy_pass http://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;
    }
}
  • 上游服务器组:使用 upstream 指令定义一个上游服务器组,包含多个后端服务器。
  • 负载均衡策略:默认情况下,Nginx 使用轮询(round-robin)策略进行负载均衡。你可以通过设置权重(weight)来调整服务器的负载分配,权重用于控制请求的分配比例:
    • 权重越高:分配到的请求越多。
    • 默认权重:如果未指定权重,默认为 1。
  • 反向代理:在服务器块中使用 proxy_pass 指令将请求转发到上游服务器组。
HTTPS 配置(SSL 证书)

通过 Nginx 配置网站的 HTTPS 协议访问是一个常见且重要的任务,首先我们准备好SSL/TLS 证书,一般是从权威证书颁发机构(CA)来获取,证书通常包括以下两个文件:

  • 证书文件(如 example.com.crt )
  • 私钥文件(如  example.com.key )

将证书文件拷贝到Nginx服务的目录下一般放在:/etc/nginx/ssl/目录下

进行Nginx的配置:

server {
    listen 443 ssl;
    server_name example.com; 

    # SSL 证书和私钥路径 
    ssl_certificate /etc/nginx/ssl/example.com.crt; 
    ssl_certificate_key /etc/nginx/ssl/example.com.key; 

    # SSL 配置 
    ssl_protocols TLSv1.2 TLSv1.3;  # 推荐使用 TLS 1.2 和 1.3 
    ssl_ciphers HIGH:!aNULL:!MD5;    # 推荐使用安全的加密套件 
    ssl_prefer_server_ciphers on;

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

    # 处理请求 
    location / {
        try_files $uri $uri/ =404;
    }
}

监听443端口,并且通过certificatessl_certificate_key设置对应的证书与key的路径

为了确保所有访问都通过 HTTPS,可以配置 HTTP 请求自动重定向到 HTTPS:

server {
    listen 80;
    server_name example.com; 

    # 重定向所有 HTTP 请求到 HTTPS 
    return 301 https://$host$request_uri;
}

以上就是前端常见的Nginx 的配置方式,下面我们结合docker来实际部署我们项目。

部署SPA项目

假设你已经打包好了你的项目,并且生成了dist文件,下面是文件目录结构:

my-nginx-spa/
├─ dist/
│  ├─ index.html 
│  └─ styles.css 
├─ default.conf
└─ Dockerfile

default.conf

server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/dist;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

# 默认 server 块,拒绝非法请求
server {
    listen 80 default_server;
    server_name _;
    return 403;
}

Dockerfile

# 使用官方 Nginx 镜像作为基础镜像
FROM nginx:1.25-alpine 

# 将本地的 default.conf 复制到容器中
COPY default.conf /etc/nginx/conf.d/

#将本地的 dist 目录复制到容器中的 Nginx 默认静态文件目录
COPY dist /usr/share/nginx/dist

# 暴露 80 端口
EXPOSE 80

# 启动 Nginx
CMD ["nginx""-g""daemon off;"]

执行docker构建命令打包镜像

docker build -t nginx-spa-image .

基于镜像运行docker容器

docker run --name nginx-spa-container -d -p 80:80 nginx-spa-image

运行后浏览器访问: http://localhost 就可以看到我们部署的项目了。

部署nuxtjs的SSR项目

相比SPA项目,SSR项目的部署要麻烦一点,因为不仅需要一个Nginx服务来托管我们的静态资源,还需要一个Node服务来进行服务端的渲染逻辑。

这里介绍两种部署方案:由于需要两个容器来分别进行node服务与Nginx服务的启动,我们需要使用docker-compose 来进行容器的管理与编排,这就是方案1的思路,当然也可以不使用docker-compose,而是将node与Nginx服务放在一个容器中来简化部署,这就是方案2的部署思路。

方案1:
my-nginx-ssr/
├─ .output/
│  ├─ public/ 
│  └─ server/ 
├─ node/
│  └─ Dockerfile
├─ nginx/
│  └─ nginx.conf 
├─ docker-compose.yml
└─ package.json

nginx.conf

server {
    listen 80;
    server_name localhost;

    # 静态资源处理(由 Nginx 直接提供)
    location /_nuxt/ {
        alias /usr/share/nginx/html/_nuxt/; # 静态资源目录(需与 Docker 挂载路径一致)
        expires 1d;
        add_header Cache-Control "public";
        try_files $uri $uri/ =404;
    }

    # 反向代理到 Node 服务(处理 SSR 请求)
    location / {
        proxy_pass http://node-service:3000; # 使用 Docker 服务名通信
        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;
    }
}

# 默认 server 块,拒绝非法请求
server {
    listen 80 default_server;
    server_name _;
    return 403;
}

Dockerfile

FROM node:18-alpine

# 设置工作目录
WORKDIR /app

# 1. 复制依赖清单文件(利用 Docker 层缓存加速构建)
COPY package*.json ./

# 2. 安装生产依赖
RUN npm install 

# 3. 复制构建产物(.output 目录)
COPY .output/ ./.output/

# 4. 暴露服务端口(与 Nuxt 配置的端口一致,默认 3000)
EXPOSE3000

# 5. 启动命令(直接运行 Node 服务)
CMD ["node"".output/server/index.mjs"]

docker-compose.yml

version: '3.8'

services:
# Node.js 服务
node-service:
    build:
      context:.          # 上下文路径为项目根目录
      dockerfile:node/Dockerfile# 指定 Dockerfile 路径
    container_name:nuxt-node
    environment:
      -NODE_ENV=production
    networks:
      -nuxt-network

# Nginx 服务
nginx:
    image:nginx:alpine   # 使用官方 Nginx Alpine 镜像
    container_name:nuxt-nginx
    ports:
      -"80:80"           # 映射宿主机 80 端口到容器 80 端口
    volumes:
      # 挂载 Nginx 配置文件(覆盖默认配置)
      -./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      # 挂载静态资源目录(Nginx 直接提供 _nuxt 内容)
      -./.output/public/_nuxt:/usr/share/nginx/html/_nuxt
    depends_on:
      -node-service      # 确保 Node 服务先启动
    networks:
      -nuxt-network

# 定义共享网络(容器间通过服务名通信)
networks:
nuxt-network:
    driver:bridge

为了减轻node服务的压力,实现静态资源的高效访问,这里利用了Nginx的静态资源托管的能力,将Nuxt的静态资源通过docker-compose的volumes来挂载到Nginx对应的目录上,静态资源处理由 Nginx 直接提供。

需要运行docker-compose命令来进行镜像的打包与容器的启动

#构建镜像并启动容器(后台运行)
docker-compose up -d --build
方案2:
my-nginx-node-ssr/
├─ .output/
│  ├─ public/ 
│  └─ server/ 
├─ node/
│  └─ Dockerfile
├─ nginx/
│  └─ nginx.conf 
└─ package.json

不使用docker-compose,而是将node与Nginx服务放在一个容器中来简化部署的方案:

nginx.conf

server {
    listen 80;
    server_name localhost;

    location /_nuxt/ {
        alias /usr/share/nginx/html/_nuxt/;
        expires 1d;
        add_header Cache-Control "public";
        try_files $uri $uri/ =404;
    }

    location / {
        proxy_pass http://localhost:3000;
        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;
    }
}

server {
    listen 80 default_server;
    server_name _;
    return 403;
}

Dockerfile

# 使用 Node.js 官方镜像(Alpine 版本体积更小)
FROM node:18-alpine

# 设置工作目录
WORKDIR /app

# 1. 复制依赖清单文件(利用 Docker 层缓存加速构建)
COPY package*.json ./

# 2. 安装生产依赖
RUN npm install

# 3. 复制构建产物(.output 目录)
COPY .output/ ./.output/

# 4. 安装 Nginx
RUN apk add --no-cache nginx

# 5. 复制 Nginx 配置文件
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf

# 6. 确保 Nginx 主配置文件包含必要的基本结构
RUN echo "user  nginx; \
worker_processes  auto; \
error_log  /var/log/nginx/error.log warn; \
pid        /var/run/nginx.pid; \
events { \
    worker_connections  1024; \
} \
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\"'; \
    access_log  /var/log/nginx/access.log  main; \
    sendfile        on; \
    keepalive_timeout  65; \
    include /etc/nginx/conf.d/*.conf; \
}"
 > /etc/nginx/nginx.conf


# 7. 检查 Nginx 配置语法
RUN nginx -t

# 8. 复制静态资源到 Nginx 目录
COPY .output/public/_nuxt /usr/share/nginx/html/_nuxt

# 9. 暴露端口
EXPOSE80

# 10. 启动命令
CMD sh -c "npm run start & nginx -g 'daemon off;'"

同样的先运行docker镜像构建命令然后运行docker命令

# 构建镜像
docker build -t nginx-node-image -f node/Dockerfile .

# 基于镜像启动容器
docker run -d -p 80:80 --name nginx-node-container nginx-node-image

总结

通过掌握上述Nginx核心功能及Docker化部署技巧,前端开发者可独立完成从本地开发到生产上线的全链路工作,构建高效、安全、易维护的Web应用。



-END -

如果您关注前端+AI 相关领域可以扫码进群交流



添加小编微信进群😊


关于奇舞团

奇舞团是 360 集团最大的大前端团队,非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。



Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/181337