使用Docker的Nginx反向代理到Vue Vite和后端容器中出现了意外结果。

9
如下所示,我已经使用docker容器设置了环境。这样做的原因是因为我只有一个VPS可以在上面运行前端和后端。
我想解决问题的方式是将Nginx放入docker容器中,并使用certbot进行验证(已经可以工作),然后根据用户请求的位置反向代理到前端或后端。 enter image description here 如图所示,我不确定Nginx应该如何与前端或后端通信。
当我尝试执行时,出现了问题。
upstream docker-frontend {
    server frontend:8081;
}

upstream docker-backend {
    server backend:8080;
}

我收到了一个"502 bad gateway"错误。在Stackoverflow上,我找到了一个解决方法,它建议不要纯粹使用8080->8080/tcp或8081->8081/tcp来运行后端或前端,而是将两者都运行在80/tcp上。我还确保将所有容器放在同一个网络中,这似乎有所帮助。
接下来是Nginx配置的编写方式:
upstream docker-frontend {
    server frontend:80;
}

upstream docker-backend {
    server backend:80;
}

然而,这样做后,我现在有一个完全空白的页面,没有任何内容显示。我可以向您保证,在前端页面(使用Vue 3.0 vite构建)中没有空白页。

nginx.conf

user nginx;
worker_processes 1;

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;

    upstream docker-frontend {
        server frontend:80;
    }

    upstream docker-backend {
        server backend:80;
    }
 
    server {
        listen 8081;
 
        location / {
            proxy_pass         http://docker-frontend/;
            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_set_header   X-Forwarded-Host $server_name;
        }
    }
 
    server {
        listen 8080;
 
        location / {
            proxy_pass         http://docker-backend/example/;
            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_set_header   X-Forwarded-Host $server_name;
        }
    }

    server {
        listen 80;
        listen [::]:80;
        server_name www.example.com;

        include letsencrypt-acme-challenge.conf;

        return 301 https://www.example.com;
    }

    server {
        listen 443 ssl;
        listen [::]:443 ssl;
        server_name example.com;

        ssl_certificate fullchain.pem;
        ssl_certificate_key privkey.pem;
        ssl_trusted_certificate chain.pem;
        return 301 https://www.example.com;
    }

    server {
        listen 443 ssl default_server;
        listen [::]:443 ssl default_server;
        server_name www.example.com;
        ssl_certificate fullchain.pem;
        ssl_certificate_key privkey.pem;
        ssl_trusted_certificate chain.pem;
        root /usr/share/nginx/html/;

        location / {
            gzip off;
            root /usr/share/nginx/html/;
            index index.html;
            try_files $uri $uri/ /index.html;

            add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
            add_header X-Frame-Options DENY;
            add_header X-Content-Type-Options nosniff;
            add_header X-XSS-Protection "1; mode=block";
            add_header Referrer-Policy "origin";

            proxy_pass http://docker-frontend/;
            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_set_header X-Forwarded-Host $server_name;
        }

        location /example/ {
            add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
            add_header X-Frame-Options DENY;
            add_header X-Content-Type-Options nosniff;
            add_header X-XSS-Protection "1; mode=block";
            add_header Referrer-Policy "origin";

            proxy_pass http://docker-backend/example/;
            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_set_header X-Forwarded-Host $server_name;
        }
    }
}

在尝试了解反向代理工作过程中创建的nginx.conf文件,但似乎无法弄清楚为什么仍然显示空白页面。希望能得到任何帮助。

编辑:看起来确实显示index.html,但我没有在页面中看到任何CSS或JavaScript。目前Vue应用程序的Dockerfile配置如下:

Dockerfile

FROM node:lts-alpine as build-stage
RUN mkdir -p /app
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:stable-alpine as production-stage
COPY ./nginx.conf /etc/nginx/nginx.conf
RUN rm -rf /usr/share/nginx/html/*
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

然而,我仍然不确定为什么它不能加载javascript或css。是否有一种特殊的设置Vue反向代理的方式?还是我的nginx.conf没有正确设置?
编辑:即使添加了热重载。
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

它仍然没有显示任何内容,只有一个空白页面。
当我使用 docker exec -it example /bin/sh 检查 Vue 应用程序的 Docker 容器时,在 /html 文件夹中,它显示了 favicon、assets 和 index.html,但是没有 css 或 js 文件夹?但在 assets 文件夹中,有一个奇怪的 index.a6f56555.jsindex.1212255f.css。是否还有其他人遇到过这种情况,还是只有我遇到了?
编辑:我发现 Vue 没有正确地将自己构建到 Dockerfile 中的 /html 文件夹中,出于某种原因,它只将 index.html 放在那里,而不是 javascript 或 css?

vite.config.js

import { fileURLToPath, URL } from 'url'
import vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'
import svgLoader from 'vite-svg-loader'

export default defineConfig({
  server: {
    port: 8081
  },
  plugins: [vue(), svgLoader()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
    },
  },
  publicPath: process.env.NODE_ENV === 'production'
    ? ''
    : '/',
  css: {
    loaderOptions: {
      sass: {
        prependData: `@import "@/styles/_variables.scss";`
      },
    },
    preprocessorOptions: {
      scss: {
        implementation: require('sass'),
        additionalData: `
            @import "./src/assets/scss/main.scss";
        `
      }
    }
  },
  base: './'
})

即使按多个来源推荐的设置Vite配置文件以及包含以下内容的package.json文件:

npm run build & & vite preview --port 8081 --host

命令,它仍然显示为空白页,有人可以告诉我我做错了什么吗? 因为我现在一点头绪都没有...
2个回答

9

我成功解决了问题。我花了很多时间才找到答案,但是偶然间我找到了这个页面:https://dev.to/programmingdecoded/docker-configuration-with-nginx-routing-for-vue-and-laravel-49e9

前端文件夹/文件结构

/node_modules 
/src
nginx.conf
Dockerfile
vite.config.js

新的nginx.conf

upstream docker-frontend {
    server example-frontend:8081;
}

upstream docker-backend {
    server example-backend:8080;
}

server {
    listen 80;
    listen [::]:80;
    server_name www.example.com;

    include letsencrypt-acme-challenge.conf;

    return 301 https://$host;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com;

    ssl_certificate fullchain.pem;
    ssl_certificate_key privkey.pem;
    ssl_trusted_certificate chain.pem;
    return 301 https://$host;
}

server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    server_name www.example.com;
    ssl_certificate fullchain.pem;
    ssl_certificate_key privkey.pem;
    ssl_trusted_certificate chain.pem;


    location / {
        gzip off;
        index index.html;
        root /usr/share/nginx/html/;
        error_log  /var/log/nginx/error.log;
        access_log /var/log/nginx/access.log main;

        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
        add_header Referrer-Policy "origin";

        proxy_pass http://docker-frontend;
        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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-Host $server_name;
        proxy_cache_bypass $http_upgrade;
    }

    location /example/ {
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
        add_header Referrer-Policy "origin";

        proxy_pass http://docker-backend/example;
        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_set_header X-Forwarded-Host $server_name;
    }
}

如上所示,您可以看到我还将上游(upstream)设置为8081和8080端口。您还可以看到我删除了http标记、workers标记等。这是因为我替换的不是/etc/nginx中的nginx.conf文件,而是用我自定义的nginx.conf文件替换了/etc/nginx/conf.d/default.conf文件。
接下来,我按照以下步骤配置了Dockerfile
FROM node:lts-alpine as build-stage
RUN mkdir -p /app
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx:stable-alpine as production-stage
EXPOSE 8081
COPY nginx.conf /etc/nginx/conf.d/default.conf
RUN rm -rf /usr/share/nginx/html/*
COPY --from=build-stage /app/dist /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]

这里我确保暴露了8081端口而不是80端口,并且我还确保在这里不应该替换/etc/nginx中的nginx.conf,而是用以下nginx.conf替换/etc/nginx/conf.d/default.conf

然后Vue应用程序还需要自己的nginx.conf。 Vue nginx.conf

server {
    listen 8081;
    root /usr/share/nginx/html;
    include /etc/nginx/mime.types;

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

然后运行以下命令启动它:

docker run -it -d -p 8081:8081 --network=example --name example-frontend example

以上命令运行前端或后端。

docker run -it --rm --name nginx -v ${PWD}/example/nginx.conf:/etc/nginx/conf.d/default.conf -v ${PWD}/example/letsencrypt-acme-challenge.conf:/etc/nginx/snippets/letsencrypt-acme-challenge.conf -v ${PWD}/example:/letsencrypt -v ${PWD}/example/letsencrypt/certs:/etc/letsencrypt --network=example -p 80:80 -p 443:443 nginx

此命令运行nginx并确保将Ubuntu服务器中的外部文件存储在docker容器内。同样,确保将其配置为 /etc/nginx/conf.d/default.conf,而不是替换实际的 /etc/nginx 中的nginx.conf 。

对我来说,这是一个非常奇怪的解决方案,但也很有道理。


0

由于您没有多个BE或FE容器,我认为您不需要upstream。(请参见此处解释的负载平衡用例here

您想要的是nginx有一个服务器,在端口80(通常)上侦听,并将调用重定向到不同的位置。类似这样的东西

server {
        listen 80;
        server_name  your.url.com;

        location /api {
            proxy_pass         http://backend_container_name:8080;
            ...
        }
 
        location / {
            proxy_pass         http://frontend_container_name:8081;
            ...
        }
    }

我们需要 服务器名称 来知道基本的 URL。当请求到达 some.url.com/ 或者 some.url.com/a-page 时,它将被重定向到前端容器;当您的代码向 some.url.com/api/some_endpoint 发出请求时,它将被重定向到后端容器。
这些请求必须从浏览器发出到相同的 URL:端口,否则会出现跨域问题,这就是为什么我们要使用反向代理的原因。
从一个容器到另一个容器,只要它们满足以下条件之一,连接就很容易建立:
  1. 在同一个 Docker 网络中。或者
  2. 有一些发现服务 URL(例如使用 AWS ECS 或类似服务时)
要使容器在同一个网络中,您只需要创建一个网络并将网络链接到容器,或者将它们放在同一个 docker-compose.yaml 中并使用 docker-compose up 即可。

将来它需要支持多个BE和FE容器,因此我需要考虑上游构建它,我也不使用docker-compose.yaml。 - King Reload
当前设置已经正确地执行了反向代理,但是Vue Vite正在做一些奇怪的事情,导致它显示一个空白页面,它已经正确地指向了FE容器。 - King Reload
啊,好的,抱歉我一开始误解了问题。但是现在看来这个问题更多地与使用Docker构建vite应用程序有关,而不是反向代理,对吗?我对vite并不熟悉,但总体上看起来没问题。你在浏览器控制台中看到任何错误吗?所有的ja和css请求都被发出了吗? - Chai
我看到它加载了index.html,但实际上没有显示任何.js或.css文件。当我进入包含FE应用程序的Docker容器时,它也显示在dist文件夹中,其中它被构建为仅包含index.html。然而,当我在本地桌面上执行相同的操作时,它构建了相同的内容并且正确显示,这真的让我感到困惑,不知道为什么它不起作用。 - King Reload

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接