Skip to content

Instantly share code, notes, and snippets.

@yoyoys
Last active August 18, 2023 12:37
Show Gist options
  • Save yoyoys/dbaa1ba6985cf540df24887d3d7f2983 to your computer and use it in GitHub Desktop.
Save yoyoys/dbaa1ba6985cf540df24887d3d7f2983 to your computer and use it in GitHub Desktop.
Dockerize vue resources
theme background class highlighter lineNumbers drawings transition title
seriph
./timelab-yx20mpDyr2I-unsplash.jpg
text-center
shiki
false
persist
slide-left
Vite 前端容器化的心得分享

Vite 前端容器化的心得分享

@yoyoys


為什麼選擇容器化

  • 版本控管簡單
  • 受到環境影響很小
  • 測試相當方便,特別是要置換資料庫的測試

容器化和其他部署方式的差異

  • S3 或是類似的 CDN
    • 部署很簡單,但不太好確定線上的版本
  • Apache 或 Nginx
    • 初次佈版較為困難
    • 快取問題通常伺服器管理員沒辦法準確處理
    • 有特定副檔名不想揭露(如 map)或是需要特定的 MIME-Type (如 mjs),需要給參考設定
  • 丟一包給後端放在程式內
    • 快取問題通常不會被處理
    • 有特定副檔名不想揭露或是需要特定的 MIME-Type ,通常無法處理

layout: cover

容器版本規劃


何時建立 image

  • 一般在進入 dev 分枝建立 image 已經足夠
  • PR 也可以建立 image, 但是取用上較不方便
  • 進入 prod 分支則可以看政策決定

不同環境要不要共用 image ?

其實兩種方法都有人在做,我偏好共用

  • 不共用 image
    • 編譯特定 image 給特定環境。
    • 可以在 dev 環境加入更多元素,在 prod 環境做更深度的調整。
    • 可以將參數寫在 image 內。
    • 實際部署時要重新編譯,或是要提前編譯好。
  • 共用 image
    • 每一個 commit 都只會對應到一個 image
    • debug 簡單多了,可以很容易的置換變數
    • 從 dev 部署到生產環境是一瞬間的事情

要用什麼 image

  • 前端
    • node 搭配 http-server
    • node 搭配 nuxt
    • nginx
  • 後端
    • 有開發框架的後端
      • 通常都會有官方建議的容器化設定
      • 也有一些三方的如 Laradock,但我不太了解 php 不好意思...
    • 基於作業系統的 Image,如 alpine, ubuntu
      • 很有裝機體驗的部署方式
      • 推薦給還沒開始容器化想嘗試看看的單位..
  • 資料庫
    • 資料庫在開發環境用 docker 好處多多,推薦直接使用官方提供的 image 即可

layout: cover

Vite 容器化


Vue 容器化時我會考慮什麼

  • 套件的整理,有沒有需要讓套件外部化?
  • 頁面要不要做非同步載入?
  • 快取 Header 處理
    • 我習慣用 nginx 來處理
    • 原則是 index.html 不做快取
  • 參數怎麼給
    • 不同環境有不同 image 的話,可以考慮透過 .env 檔案於編譯時置入
    • Nuxt 的話基本上從 ENV 給就可以了
    • 共用 image
      • 參數要將它外部化以便後續維護
      • image 要能夠接收 ENV 來確保可以在啟動時修改參數為你需要的樣子

參數外部化的方式

  • 這邊有做 monorepo ,由於篇幅的關係會另外分享一個 repo 參考,以下範例使用 vite 4.4.7
  • 使用參數的方法
// api-endpoints.ts
export MY_API_PATH: string = "http://my-api.com";
  • 匯入參數的方式
import { MY_API_PATH } from '@myorg/api-endpoints'; // got types!
...
  • 修改 vite.config.ts
  build: {
		...
    rollupOptions: {
      external: ['@myorg/api-endpoints'],
      output: {
        paths: {
          '@myorg/api-endpoints': '../lib/api-endpoints.mjs',
        },
      },
    },
  },

參數外部化的方式

  • 編譯後的目錄架構大約會像是這樣,這樣就完成參數的外部化了
                                       
dist
├── assets
│   ├── ...
├── favicon.ico
├── index.html
└── lib
    └── api-endpoints.mjs

Container 執行期間的參數置換

  • 測試用的指令 docker run -e MY_API_PATH="http://myapi-from-env.com" -p 80:80 my-website
  • 可以透過是判斷 env 變數是否存在,存在的話就替換為輸入的數值。
  • 這邊提供一個 sed 替換參數的範例,也可以用自己喜歡的工具。
CONFIG_PATH="/usr/share/nginx/html/lib/api-endpoints.mjs"

echo "setting config..."
if [ ! -z $MY_API_PATH ]; then
  sed -i "/const MY_API_PATH /c\const MY_API_PATH = '$MY_API_PATH';" $CONFIG_PATH; 
fi;

echo "config file:"
cat $CONFIG_PATH
nginx -g 'daemon off;'

單機模擬上線

  • docker compose
    • 相容性好很多的方案,測試簡單
    • compose 檔案本身不算很好維護
  • Minikube
    • Mount Volume 在不同作業系統會有不同的狀況。
    • ingress-dns 很好用,但我只有在 mac 上成功。

Sample code Gist


layout: cover

END :)

FROM nginx
COPY ./dist /usr/share/nginx/html
COPY ./build/config/nginx-default.conf /etc/nginx/conf.d/default.conf
COPY ./build/config/start.sh /
RUN ls -la /usr/share/nginx/html
RUN chmod 755 /start.sh
EXPOSE 80
CMD /start.sh
docker run -e MY_API_PATH="http://myapi-from-env.com" -p 80:80 my-website
server {
listen 80;
server_name localhost;
index index.html index.html;
gzip_static on;
location / {
root /usr/share/nginx/html;
index index.html;
}
location =/index.html {
root /usr/share/nginx/html;
add_header "Cache-Control" "max-age=0,no-cache,no-store,must-revalidate" ;
etag on;
}
location =/lib/api-endpoints.mjs {
root /usr/share/nginx/html;
add_header "Cache-Control" "max-age=0,no-cache,no-store,must-revalidate";
add_header "Content-Type" "application/x-javascript";
etag on;
gzip_static off;
}
location ~ \.map$ {
return 404;
}
}
CONFIG_PATH="/usr/share/nginx/html/lib/api-endpoints.mjs"
echo "setting config..."
if [ ! -z $MY_API_PATH ]; then
sed -i "/const MY_API_PATH /c\const MY_API_PATH = '$MY_API_PATH';" $CONFIG_PATH;
fi;
echo "config file:"
cat $CONFIG_PATH
nginx -g 'daemon off;'
"compilerOptions": {
...
"paths": {
"@myorg/*": [
"packages/@myorg/*/src"
],
}
}
export default defineConfig({
base: './',
resolve: {
alias: {
'@': path.resolve(__dirname, '/src'),
'@myorg/api-endpoints': path.resolve(__dirname, './packages/@myorg/api-endpoints/src'),
},
},
// ...
build: {
sourcemap: true,
rollupOptions: {
external: ['@myorg/api-endpoints'],
output: {
paths: {
'@myorg/api-endpoints': '../lib/api-endpoints.mjs',
},
},
},
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment