K8s nginx-ingress 如何設定二級目錄轉發遠端靜態伺服器基於Vue路由history模式打包的應用程式

2022-09-19 06:06:17

背景

首先這標題有點繞,我先解釋下:

  1. 首先我們有靜態伺服器,上面某個目錄有Vue路由history模式打包的應用程式(也就是build後的產物);
  2. 但是靜態伺服器一般不做對外域名用的,我們需要在k8s nginx-ingress上做下域名二級目錄代理,轉發到該靜態目錄;

這就是本文的背景,相信也是很多開發/運維同學的需求;

由上:

#我們靜態服務目錄是,/cso/
https://static.chinacloudapi.cn/cso/

#靜態服務下檔案的url是
https://static.chinacloudapi.cn/cso/static/js/manifest.967d8a794130980087be.js

然後:

#我們部署的域名是:
http://test.mysite.com/cso/

#同樣,對應以上靜態服務檔案的url是:
http://test.mysite.com/cso/static/js/manifest.967d8a794130980087be.js

好了需求清楚了, 我們轉發二級目錄就是 /cso/,我們一一來看怎麼實現。

先設定好Vue

1、在入口檔案index.html檔案中新增

<meta base="/xxx/">

2、設定Vue History的路由模式(我這裡還是vue2.x)

export default new Router({
  mode: 'history',
  base: '/cso/',
  routes: [  
  ...

3、在config/index.js檔案修改build屬性下面的assetsPublicPath: '/xxx/'(用Cli3搭建的專案,應該是在vue.config.js檔案修改publicPath: '/xxx/');

...
  build: {
    index: path.resolve(__dirname, '../dist/index.html'),
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',
    
    // assetsPublicPath: '/', //預設的
    assetsPublicPath: '/cso/', //改為
 ...

4、存取驗證:

成功;

原生Nginx轉發設定

部署Nginx本機

location ~* /cso {
    root  html/cso;
    index index.html index.htm;
    try_files $uri $uri/ /index.html;   
}

這就是大家比較熟悉的history模式必配的try_files,原理是:

  • 像html/js/css等靜態資源請求,能本地能找到物理檔案的,直接返回;
  • 存取vue裡面的路由時,沒有對應的物理問題的,請求轉回到index.html由vue處理渲染;

部署到遠端靜態服務或OSS

轉發靜態assets

location ~* /cso.*\.(gif|jpg|jpeg|png|bmp|swf|css|js|eot|svg|ttf|woff|woff2|properties|json)$ {
    proxy_http_version 1.1; 
    proxy_pass https://static.chinacloudapi.cn;  
}

轉發document

location ~* /cso {
    proxy_http_version 1.1;  
    add_header Cache-Control 'no-store, no-cache';
    rewrite ^ /cso/index.html break;
    proxy_pass https://static.chinacloudapi.cn;
}

這裡設定兩個功能location,其實是參考try_files的原理實現的;

同時,這種設定方式也適用於解決很多想把第三方程式的UI(Hangfire等)掛載到二級域名時,靜態檔案404的問題;

K8s nginx-ingrss轉發設定

說真的,用慣nginx原生設定後,在nginx-ingress稍微設定有一點點難度的規則我就想哭(主要確實不太熟);

configuration-snippet方式:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:  
  name: my-custom-snippet
  namespace: cso-site
  annotations:
      nginx.ingress.kubernetes.io/configuration-snippet: | 
        location ~* /cso.*\.(gif|jpg|jpeg|png|bmp|swf|css|js|eot|svg|ttf|woff|woff2|properties|json)$ {
            proxy_http_version 1.1; 
            proxy_pass https://static.chinacloudapi.cn;  
        }
        location ~* /cso {
            proxy_http_version 1.1;  
            add_header Cache-Control 'no-store, no-cache';
            rewrite ^ /cso/index.html break;
            proxy_pass https://static.chinacloudapi.cn;
        }        
spec:
  ingressClassName: nginx
  rules:
  - host: test.mysite.com
  defaultBackend:
    service:
      name: cso-site-svc
      port:
        number: 80
  tls:
  - hosts:
    - test.mysite.com
    secretName: tls-secret

這種方式就比較簡單了,就直接設定支援nginx原生語法,但它也是有限制的具體參考檔案;

原生ingress寫法

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/upstream-vhost: "static.chinacloudapi.cn"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
    nginx.ingress.kubernetes.io/rewrite-target: /cso/index.html  
  name: slz-cso-ui-doc
  namespace: cso-site
spec:
  rules:
  - host: test.mysite.com
    http:
      paths:
      - path: /cso/(.*)
        pathType: Prefix
        backend:
          service:
            name: cso-site
            port: 
              number: 443

  defaultBackend:
    service:
      name: cso-site
      port: 
        number: 80
  tls:
  - hosts:
    - test.mysite.com
    secretName: tls-secret
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx    
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"

    nginx.ingress.kubernetes.io/upstream-vhost: "static.chinacloudapi.cn"
    nginx.ingress.kubernetes.io/rewrite-target: /cso/$1$2$3  
    nginx.ingress.kubernetes.io/use-regex: "true"
  name: slz-cso-ui-static
  namespace: cso-site
spec:
  rules:
  - host: test.mysite.com
    http:
      paths:
      - path: /cso/(.*)(\.)(gif|jpg|jpeg|png|bmp|swf|css|js|eot|svg|ttf|woff|woff2|properties|json)$
        pathType: Prefix
        backend:
          service:
            name: cso-site
            port: 
              number: 443
  defaultBackend:
    service:
      name: cso-site
      port: 
        number: 80
  tls:
  - hosts:
    - test.mysite.com
    secretName: tls-secret

我們來看看生成的nginx規則:

這是我抽取核心部分的規則,可以看到翻譯成原生寫法是規則生成正確的;

總結

k8s nginx-ingress設定稍微複雜點的規則真的很痛苦;

設定ingress時在不是特別熟的情況下跟我一樣先寫原生nginx,再翻譯成ingress是個不錯的方法;

水完, 歡迎討論。