點燈坊

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

使用 Docker 將 Express + Vue 打包成 Image

Sam Xiao's Avatar 2021-10-31

Vue CLI 提供 yarn build 將 HTML / CSS / JS 編譯到 dist 目錄下,我們可再利用 Node + Express 當 Web Server,自行擴充 NPM Script,最後只要下 yarn docker 就可一鍵建立 Docker Image。

Version

Express 4.17.1
Vue 2.6.10

Vue Project

express000

使用 Vue CLI 建立 Vue project,並自行在根目錄新增或修改以下檔案:

  • dockerfile
  • package-node.json
  • app.js
  • package.json
  • docker-compose.yml

dockerfile

FROM node:alpine
WORKDIR /usr/src/app
COPY package-node.json ./package.json
RUN yarn install
COPY app.js .
COPY dist ./dist
CMD [ "node", "app.js" ]

須先建立 dockerfile,才能產生 image。

第 1 行

FROM node:alpine

使用最新版 node:alpine 為基底建立 image。

建議使用 node:alpine 為 production image,size 會小很多,以 Node 12.7 為例,node:latest 為 907 MB,node:12-alpine 為 79.3 MB

第 2 行

WORKDIR /usr/src/app

設定 image 內的 /usr/src/app 為工作目錄。

第 3 行

COPY package-node.json ./package.json

package-node.json 複製進 image,且改名為 package.json

稍後會建立 package-node.json

第 4 行

RUN yarn install

根據 image 內的 package.json 執行 yarn install 安裝 Node 所需的 Express。

第 5 行

COPY app.js .

app.js 複製進 image。

app.js 為 Node 啟動 Express 所需檔案,非 Vue 部分

第 6 行

COPY dist ./dist

dist 目錄下所有內容複製到 image 內 dist

dist 為 Vue CLI yarn build 編譯後最後結果,稍後會建立

第 8 行

CMD [ "node", "app.js" ]

最後將使用 Node 執行 app.js 啟動 Express。

package-node.json

{
  "name": "vue",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "express": "^4.17.1"
  }
}

設定 Node 所需的 dependency 的 package.json,為了有別於 Vue 的 package.json,特別建立成 package-node.json,在 dockerfile 內的 COPY package-node.json ./package.json 才會改名為 pakcage.json

app.js

let express = require('express')
let path = require('path')

let app = express()

app.use(express.static(path.join(__dirname, 'dist')))
app.listen(80, () => console.log('app listening on port 80!'))

Node 的啟動檔,由此啟動 Express。

第 5 行

app.use(express.static(path.join(__dirname, 'dist')))

設定 dist 目錄為 Express 放置 HTML / CSS / JS 目錄。

package.json

{
  "name": "docker-vue",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "docker": "yarn build && docker build -t oomusou/vue-express:0.0.1 ."
  },
  "dependencies": {
    "core-js": "^2.6.5",
    "vue": "^2.6.10"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.9.0",
    "@vue/cli-plugin-eslint": "^3.9.0",
    "@vue/cli-service": "^3.9.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^5.16.0",
    "eslint-plugin-vue": "^5.0.0",
    "vue-template-compiler": "^2.6.10"
  }
}

Vue 的 package.json

第 9 行

"docker": "yarn build && docker build -t oomusou/vue-express:0.0.1 ."

新增 NPM script,只要執行 yarn docker,就會先執行 yarn build,然後執行 docker build 建立 Docker image。

$ docker build -t oomusou/vue-express:0.0.1 .

使用 docker build 建立 oomusou/vue-express:0.0.1 image。

  • -t:加上 tag,其中 : 左側為 image 名稱,右側為版號;/ 左側為 Docker ID,右側為 image 名稱

docker-compose.yml

version: "3"
services:
  express:
    image: oomusou/vue-express:0.0.1
    restart: always
    ports:
      - "80:80"

建立執行 container 的 docker-compose.yml

  • image:使用剛建立的 oomusou/vue-express:0.001
  • restart:當 container crash 時,會自動重啟
  • ports:container 內的 80 port,對應到外部的 80 port

Build Image

$ yarn docker

先執行 yarn build 建立 dist 目錄,再執行 docker build 產生 image。

express001

$ docker images

已經建立 oomusou/vue-express:0.0.1,只比 node:12-alpine 大一點點,因為多了 dist 與 Express。

express002

Start Container

$ docker-compose up -d

使用 docker-compose up 啟動 container。

express003

Chrome

express004

http://localhost:80 成功執行 Vue。

Stop Container

$ docker-compose down

使用 docker-compose down 結束 container。

express005

Appendix

Publish to Docker Hub

$ docker push oomusou/vue-express:0.0.1

使用 docker push 將 image 發布到 Docker Hub。

Image 須以 ID/image:tag 格式描述

express006

Pull from Docker Hub

$ docker pull oomusou/vue-express:0.0.1

發布到 Docker Hub 之後,其他人就可使用 docker pull 下載 image。

Image 須以 ID/image:tag 格式描述

express007

Save Image to Tarball

$ docker save oomusou/vue-express:0.0.1 | gzip > vue-express-0.0.1.tgz

使用 docker save 將 Docker image 匯出後壓成 tarball。

| 為 pipe,表示透過 gzip 壓縮,最後成為 vue-0.001.tgz

express008

Restore Image from Tarball

$ docker load < vue-express-0.0.1.tgz

使用 docker load 將 tarball 還原成 image。

express009

Conclusion

  • 傳統若想將 Vue 打包成 image,直覺會想到 Nginx,但其實 Node + Express 也可提供 HTTP service,如此前後端均以 JavaScript 技術完成
  • 若要將 Node 用在 production,記得使用 alpine 系列 image
  • 最後可將 image 發布到 Docker Hub 或存成 tarball,如此其他人就可直接使用 image 建立環境

Reference

Node, Dockerizing a Node.js web app
Shekhar Gulati, Dockerizing a Vue.js application