點燈坊

失くすものさえない今が強くなるチャンスよ

使用 Docker 將 Nginx + Sub-Path + Vue 打包成 Image

Sam Xiao's Avatar 2021-10-31

若只將 Vue Deploy 到單一 Domain 則比較單純,但若是 Domain 的 Sub-Path,則 Vue CLI 與 Nginx 都需要加上其他設定。

Version

Nginx 1.18
Vue 2.6.11

Chrome

subpath000

假設我們想將 Vue deploy 到 domain 的 /admin,Vue CLI 與 Nginx 各該如何設定呢 ?

.env

BASE_URL=admin

在 project 根目錄新增 .env,設定 BASE_URLadmin

為什麼要叫 BASE_URL 呢 ? 稍後會看到 Vue 很多檔案已經使用 BASE_URL 這個 environment variable

subpath001

vue.config.js

module.exports = {
  publicPath:`/${process.env.BASE_URL}`
}

在 project 根目錄新增 vue.config.js,設定 publicPathBASE_URL environment variable。

至此 Webpack 在 build-time 都會加上 admin sub-path。

Vue CLI 4.x 要在 path 前加上 /

subpath002

index.html

subpath003

Vue 對於 public 目錄下的 index.htmlfavicon.ico 已經加上 BASE_URL 修正 href attribute。

router/index.js

subpath004

Vue router 也使用了 BASE_URL environment variable。

這也是為什麼我們在 .env 配合 Vue 繼續使用 BASE_URL 原因

dockerfile

FROM nginx:stable-alpine
WORKDIR /usr/share/nginx/html
COPY dist .
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
EXPOSE 8080

第 1 行

FROM nginx:stable-alpine

nginx:stable-alpine 為基底建立 Docker image。

第 2 行

WORKDIR /usr/share/nginx/html

設定 /usr/share/nginx/html 為預設目錄,如此當進入 container 內部時,預設就是此目錄方便 debug。

第 3 行

COPY dist .

yarn build 下所有檔案複製到 container 內部的 /usr/share/nginx/html 預設目錄內。

第 4 行

COPY nginx/default.conf /etc/nginx/conf.d/default.conf

將 Nginx 的 default.conf 取代 container 內的 default.conf

第 5 行

EXPOSE 8080

表示 container 將使用 8080 port。

subpath005

Nginx Configuration

server {
  listen       8080;
  server_name  localhost;

  location /admin {
    alias /usr/share/nginx/html;

    expires -1;
    add_header Pragma "no-cache";
    add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";

    try_files $uri $uri/ /index.html = 404;
  }

  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
    root   /usr/share/nginx/html;
  }
}

第 1 行

listen 8080;

設定 Nginx 使用 8080 port,與 dockerfileEXPOSE 8080 相對應。

8080 最關鍵的設定是在 Nginx,dockerfileEXPOSE 只是給人寫 docker-compose 時參考使用

第 5 行

location /admin {

設定 Nginx 使用 admin sub-path。

這是本文最關鍵設定,否則 Nginx 無法使用 admin sub-path

第 6 行

alias /usr/share/nginx/html;

admin sub-path 相當於 container 內部的 /usr/share/nginx/html

注意要使用 alias 而非 root

12 行

try_files $uri $uri/ /index.html = 404;

由於 Vue 為 SPA,當 URL 找不到時,Nginx 會自動導到 index.html,由 Vue router 決定。

subpath006

docker-compose.yml

version: "3"
services:
  nginx:
    image: vue-publicpath:0.1.0
    container_name: MyNginx
    restart: always
    ports:
      - 80:8080

第 4 行

image: vue-publicpath:0.1.0

使用 vue-publicpath:0.1.0 Docker image。

第 5 行

container_name: MyNginx

自行命名 container 名稱為 MyNginx

第 6 行

restart: always

當執行失敗時會自動重啟 container。

第 7 行

ports:
  - 80:8080

將 host 的 80 port 對應到 container 的 8080 port。

subpath007

NPM Script

package.json

{
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "docker:build": "yarn build && docker build -t $npm_package_name:$npm_package_version .",
    "docker:login": "docker exec -it MyNginx sh",
    "docker:log": "docker logs MyNginx",
    "docker:up": "docker-compose up -d",
    "docker:down": "docker-compose down",
    "docker:rmi": "docker rmi $npm_package_name:$npm_package_version"
  }
}

第 3 行

"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",

原本 Vue CLI 所內建的 NPM Script。

第 6 行

"docker:build": "yarn build && docker build -t $npm_package_name:$npm_package_version .",

執行 yarn build 使用 Webpack 打包到 dist 目錄下,並且使用 docker build 根據 dockerfile 建立 Docker image。

直接使用 $npm_package_name$npm_package_version 讀取 package.json 中的 nameversion,將來改版只要修改 version 即可

第 7 行

"docker:login": "docker exec -it MyNginx sh",

進入 container 內 debug 用。

第 9 行

"docker:up": "docker-compose up -d",

根據 docker-compose.yml 啟動 container。

第 10 行

"docker:down": "docker-compose down",

結束 container。

11 行

"docker:rmi": "docker rmi $npm_package_name:$npm_package_version"

刪除 Docker image。

subpath008

Conclusion

  • 要使 Vue 能在 domain 的 sub-path 使用有兩個關鍵:Vue CLI 要使用 BASE_URL environment variable 且在 vue.config.js 使用 publicPath property;Nginx Configuration 則要建立 sub-path rule 並使用 alias

Reference

Vue CLI, publicPath
Arne, Serving a vue-cli Production Build on a Sub-Path with Nginx