實務上會遇到有些 API 並沒有開放 CORS,或希望所有 API 統一由 Web Server 出去,此時可利用 Nginx 提供 Reverse Proxy,並自行擴充 NPM Script,最後只要下 yarn docker
就可一鍵建立 Docker Image。
Version
Nginx 1.17.2
Vue 2.6.10
Vue Project
使用 Vue CLI 建立 Vue project,並自行在根目錄新增或修改以下檔案:
- dockerfile
- default.conf
- package.json
- docker-compose.yml
dockerfile
FROM nginx:alpine
COPY dist /usr/share/nginx/html
COPY default.conf /etc/nginx/conf.d/
須先建立 dockerfile
,才能產生 image。
第 1 行
FROM nginx:alpine
使用最新版 nginx:alpine
為基底建立 image。
nginx:apline
為最新版為 Docker 最佳化的 image,size 較小
第 2 行
COPY dist /usr/share/nginx/html
將 dist
目錄下所有檔案複製到 image 內的 /usr/share/nginx/html
目錄下,此為 Nginx 放 HTML 之處。
dist
為 Vue CLIyarn build
編譯後最後結果,稍後會建立
第 3 行
COPY default.conf /etc/nginx/conf.d/
將 Nginx 設定檔 default.conf
複製到 image 內的 /etc/nginx/conf.d/
目錄下。
default.conf
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location /api {
proxy_pass http://express:3000;
}
}
第 2 行
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
為 Nginx 預設的設定,就不加以修改。
15 行
location /api {
proxy_pass http://express:3000;
}
所有打到 http://localhost/api
的 request,都會導到 http://express:3000/api
因為 Nginx 與 API server 都在 Docker 內,因此
proxy_pass
的 host name 為 container 的 service name
package.json
{
"name": "vue-nginx-proxy",
"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-nginx:$npm_package_version ."
},
"dependencies": {
"core-js": "^2.6.5",
"vue": "^2.6.10"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.10.0",
"@vue/cli-plugin-eslint": "^3.10.0",
"@vue/cli-service": "^3.10.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-nginx:$npm_package_version ."
新增 NPM script,只要執行 yarn docker
,就會先執行 yarn build
,然後執行 docker build
建立 Docker image。
$ docker build -t oomusou/vue-nginx:$npm_package_version .
使用 docker build
建立 oomusou/vue-nginx:0.1.0
image。
-t
:加上 tag,其中:
左側為 image 名稱,右側為版號;/
左側為 Docker ID,右側為 image 名稱
$npm_package_version
為直接讀取 package.json
的 version
property。
若要讀取
package.json
的 property,以$npm_package_
為 prefix 即可讀到
docker-compose.yml
version: "3"
services:
nginx:
image: oomusou/vue-nginx:0.0.1
restart: always
ports:
- "80:80"
networks:
- node-dev
express:
image: oomusou/node-express:0.0.1
restart: always
networks:
- node-dev
networks:
node-dev:
第 1 行
version: "3"
services:
nginx:
...
express:
...
docker-compose.yml
一共啟動兩個 service:
nginx
:Nginx 提供 HTTP 與 reverse-proxy 服務express
:Node 提供 API 服務
第 3 行
nginx:
image: oomusou/vue-nginx:0.0.1
restart: always
ports:
- "80:80"
networks:
- node-dev
設定 nginx
service:
image
:使用剛建立的oomusou/vue-nginx:0.001
imagerestart
:當 container crash 時,會自動重啟ports
:container 內的80
port,對應到外部的80
portnetworks
:與express
service 共用node-dev
network
11 行
express:
image: oomusou/node-express:0.0.1
restart: always
networks:
- node-dev
設定 express
service:
image
:使用剛建立的oomusou/node-express:0.001
imagerestart
:當 container crash 時,會自動重啟networks
:與nginx
service 共用node-dev
network
express
service 並沒有對 container 外部開放 port,因此外部無法使用express
提供的 API 服務
Build Image
$ yarn docker
先執行 yarn build
建立 dist
目錄,再執行 docker build
產生 image。
Start Container
$ docker-compose up -d
使用 docker-compose up
啟動 container。
同時啟動了 nginx
與 express
兩個 service。
Chrome
在 http://localhost:80
成功執行 Vue。
使用 Postman 針對 http://localhost/api/hello-world
GET 測試,可順利透過 Nginx 的 reverse proxy 打到 express
service。
Stop Container
$ docker-compose down
使用 docker-compose down
結束 container。
Apendix
可使用任何後端技術建立 API,在此以 Node + Express 為例建立 node-express
image。
Node Project
使用 express-get
目錄,並自行在根目錄新增以下檔案:
dockerfile
app.js
package.json
dockerfile
FROM node:alpine
WORKDIR /usr/src/app
COPY package.json ./package.json
RUN yarn install
COPY app.js .
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:alpine
為 79.3 MB
第 2 行
WORKDIR /usr/src/app
設定 image 內的 /usr/src/app
為工作目錄。
第 3 行
COPY package.json ./package.json
將 package.json
複製進 image。
第 4 行
RUN yarn install
根據 image 內的 package.json
執行 yarn install
安裝 Node 所需的 Express。
第 5 行
COPY app.js .
將 app.js
複製進 image。
第 6 行
CMD [ "node", "app.js" ]
最後將使用 Node 執行 app.js
啟動 Express。
app.js
let express = require('express')
let app = express()
app.get('/api/hello-world', (req, res) => res.send('Hello World'))
app.listen(3000, () => console.log('app listening on port 3000!'))
Node 的啟動檔,由此啟動 Express。
第 1 行
let express = require('express')
Import express
module。
第 3 行
let app = express()
建立 app
Express instance。
第 5 行
app.get('/api/hello-world', (req, res) => res.send('Hello World'))
建立 /api/hello-world
GET,回傳 Hello World
。
第 6 行
app.listen(3000, () => console.log('app listening on port 3000!'))
啟動 Express 在 3000
port。
package.json
{
"name": "express-get",
"version": "1.0.0",
"private": true,
"dependencies": {
"express": "^4.17.1"
},
"scripts": {
"docker": "docker build -t oomusou/node-express:0.0.1 ."
}
}
設定 Node 所需的 dependency 的 package.json
。
第 5 行
"dependencies": {
"express": "^4.17.1"
},
安裝 express
package。
第 9 行
"docker": "docker build -t oomusou/node-express:0.0.1 ."
新增 Yarn script,只要執行 yarn docker
,就會執行 docker build
建立 Docker image。
-t
:加上 tag,其中:
左側為 image 名稱,右側為版號;/
左側為 Docker ID,右側為 image 名稱
Conclusion
- 與 Vue + Nginx 包成 Docker image 類似,唯必須將 Nginx 設定檔
default.conf
也包進 image,因為 reverse proxy 要在此設定 - 使用 Node + Express 建立 API server 時,建議以
/api
開頭,如此可方便 Nginx 的 reverse proxy 設定