Docker 部署 vue 專案
1.寫在前面:
Docker 作為輕量級虛擬化技術,擁有持續整合、版本控制、可移植性、隔離性和安全性等優勢。本文使用Docker來部署一個vue的前端應用,並儘可能詳盡的介紹了實現思路和具體步驟,以方便有類似需要的同學參考。
Docker 是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,該容器包含了應用程式的程式碼、執行環境、依賴庫、配置檔案等必需的資源,通過容器就可以實現方便快速並且與平臺解耦的自動化部署方式,無論你部署時的環境如何,容器中的應用程式都會執行在同一種環境下。(更多詳情請移步docker官網檢視docker)
預設已經安裝了 docker,@vue/cli
相關版本:
-
Docker version 18.09.2, build 6247962
-
vue cli --version 3.3.0
-
macOS Mojave Verison 10.14.1
執行環境為macOS,如果與閱讀者作業系統之間存在差異,請自行調整
相關映象:
- nginx:latest
- node:latest
2.具體實現:
- 用 vue cli 建立一個vue專案,修改一下建立出來的專案,在頁面上寫一個前端介面請求,構建一版線上資源 ,基於nginx docker映象構建成一個前端工程映象,然後基於這個前端工程映象,啟動一個容器 vuenginxcontainer。
- 啟動一個基於 node 映象的容器 nodewebserver,提供後端介面。
- 修改 vuenginxcontainer 的 nginx 配置,使前端頁面的介面請求轉發到 nodewebserver 上。
- 稍作優化和改進。
3 建立 vue 應用
3.1 vue cli 建立一個vue專案
執行命令yarn serve / npm run serve
複製程式碼
訪問 http://localhost:8081
3.2 改寫
稍微改寫一下頁面,在App.vue中 傳入HelloWorld 元件中的 msg 改為Hello Docker ; created 生命週期中加入一個介面請求
import axios from 'axios';
……
axios.get('/api/json', {
params: {}
}).then(
res => {
console.log(res);
}
).catch(
error => {
console.log(error);
}
)
……
複製程式碼
這時候會在頁面控制檯看到一個報錯資訊:
/api/json 介面 404,當然此時這個介面還不存在,暫時寫到這裡,一會再調這個介面。3.3 構建vue專案
執行命令
yarn build / npm run build
複製程式碼
此時工程根目錄下多出一個dist
資料夾
如果將該dist目錄整個傳到伺服器上,部署成靜態資源站點就能直接訪問到該專案。
接下來就來構建一個這樣的靜態資源站點。
4 構建vue應用映象
nginx 是一個高效能的HTTP和反向代理伺服器,此處我們選用 nginx 映象作為基礎來構建我們的vue應用映象。
4.1 獲取 nginx 映象
docker pull nginx
複製程式碼
docker
映象(Image)一個特殊的檔案系統。Docker映象是一個特殊的檔案系統,除了提供容器執行時所需的程式、庫、資源、配置等檔案外,還包含了一些為執行時準備的一些配置引數(如匿名卷、環境變數、使用者等)。 映象不包含任何動態資料,其內容在構建之後也不會被改變。- docker 映象相關操作有: 搜尋映象
docker search [REPOSITORY[:TAG]]
、拉取映象docker pull [REPOSITORY[:TAG]]
、檢視映象列表docker image ls
、刪除映象:docker image rm [REPOSITORY[:TAG]] / docker rmi [REPOSITORY[:TAG]]
等等。- docker 映象名稱由REPOSITORY和TAG組成
[REPOSITORY[:TAG]]
,TAG預設為latest
4.2 建立 nginx config配置檔案
在專案根目錄下建立nginx
資料夾,該資料夾下新建檔案default.conf
server {
listen 80;
server_name localhost;
#charset koi8-r;
access_log /var/log/nginx/host.access.log main;
error_log /var/log/nginx/error.log error;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
複製程式碼
該配置檔案定義了首頁的指向為 /usr/share/nginx/html/index.html
, 所以我們可以一會把構建出來的index.html檔案和相關的靜態資源放到/usr/share/nginx/html
目錄下。
4.3 建立 Dockerfile 檔案
FROM nginx
COPY dist/ /usr/share/nginx/html/
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
複製程式碼
- 自定義構建映象的時候基於Dockerfile來構建。
FROM nginx
命令的意思該映象是基於 nginx:latest 映象而構建的。COPY dist/ /usr/share/nginx/html/
命令的意思是將專案根目錄下dist資料夾下的所有檔案複製到映象中 /usr/share/nginx/html/ 目錄下。COPY nginx/default.conf /etc/nginx/conf.d/default.conf
命令的意思是將nginx目錄下的default.conf 複製到 etc/nginx/conf.d/default.conf,用本地的 default.conf 配置來替換nginx映象裡的預設配置。
4.4 基於該Dockerfile構建vue應用映象
執行命令(注意不要少了最後的 “.” )
docker build -t vuenginxcontainer .
複製程式碼
檢視本地映象,執行命令
-t
是給映象命名.
是基於當前目錄的Dockerfile來構建映象
docker image ls | grep vuenginxcontainer
複製程式碼
到此時我們的 vue
應用映象 vuenginxcontainer 已經成功建立。接下來,我們基於該映象啟動一個docker
容器。
4.5 啟動 vue app 容器
Docker 容器Container: 映象執行時的實體。映象(Image)和容器(Container)的關係,就像是物件導向程式設計中的類和例項一樣,映象是靜態的定義,容器是映象執行時的實體。容器可以被建立、啟動、停止、刪除、暫停等 。
基於 vuenginxcontainer 映象啟動容器,執行命令:
docker run \
-p 3000:80 \
-d --name vueApp \
vuenginxcontainer
複製程式碼
docker run
基於映象啟動一個容器-p 3000:80
埠對映,將宿主的3000埠對映到容器的80埠-d
後臺方式執行--name
容器名 檢視 docker 程式
docker ps
複製程式碼
可以發現名為 vueApp的容器已經執行起來。此時訪問 http://localhost:3000 應該就能訪問到該vue應用:
目前為止,已經通過docker
容器部署了一個靜態資源服務,可以訪問到靜態資原始檔。還有 /api/json這個介面資料沒有,接下來我們來解決一下這個問題。
5 介面服務
再部署一個 node 的容器來提供介面服務
5.1 express 服務
用 node web 框架 express
來寫一個服務,註冊一個返回json資料格式的路由 server.js:
'use strict';
const express = require('express');
const PORT = 8080;
const HOST = '0.0.0.0';
const app = express();
app.get('/', (req, res) => {
res.send('Hello world\n');
});
app.get('/json', (req, res) => {
res.json({
code: 0,
data :'This is message from node container'
})
});
app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
複製程式碼
執行該 express
應用需要 node
環境,我們基於 node
映象來構建一個新映象
5.2 獲取 node
映象
docker pull node
複製程式碼
5.3 編寫 Dockerfile 將 express
應用 docker
化
FROM node
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 8080
CMD [ "npm", "start" ]
複製程式碼
構建映象的時候 node_modules 的依賴直接通過 RUN npm install
來安裝,專案中建立一個 .dockerignore
檔案來忽略一些直接跳過的檔案:
node_modules
npm-debug.log
複製程式碼
5.4 構建 nodewebserver 映象
執行構建命令:
docker build -t nodewebserver .
複製程式碼
5.5 啟動 nodeserver 容器
基於剛剛構建的 nodewebserver 映象 啟動一個名為 nodeserver 的容器來提供介面服務8080埠,並對映宿主的5000埠
docker run \
-p 5000:8080 \
-d --name nodeserver \
nodewebserver
複製程式碼
檢視當前docker程式
docker ps
複製程式碼
可以發現 nodeserver 的容器也正常的執行起來。訪問以下 http://localhost:5000/json 能訪問到前面寫的json資料
到目前為止,後端介面服務也正常啟動了。只需最後把頁面請求的介面轉發到後端介面服務就能調通介面。
6. 跨域轉發
想要將 vueApp 容器 上的請求轉發到 nodeserver 容器上。首先需要知道 nodeserver 容器的ip
地址和埠,目前已知 nodeserver 容器內部服務監聽在 8080 埠,還需要知道ip
即可。
6.1 檢視 nodeserver 容器的 ip 地址:
檢視容器內部 ip
有多種方式,這裡提供兩種:
- 進入容器內部檢視
docker exect -it 02277acc3efc bash
複製程式碼
cat /etc/hosts
複製程式碼
- docker inspect [ containerId ] 直接檢視容器資訊:
docker inspect 02277acc3efc
複製程式碼
在其中找到 Networks 相關配置資訊:
記錄下node服務容器對應的ip,一會兒配置nginx轉發的時候會用到。6.2 修改 nginx 配置
- Nginx 配置 location 指向 node 服務 default.conf (前端想要了解的Nginx,關於Nginx的配置已經 location 的具體寫法可以參考(一文弄懂Nginx的location匹配))
- 新增一條重寫規則,將 /api/{path} 轉到目標服務的 /{path} 介面上。 在前面的nginx/default.conf檔案中加入:
location /api/ {
rewrite /api/(.*) /$1 break;
proxy_pass http://172.17.0.2:8080;
}
複製程式碼
修改完了之後意識到一個問題:vueApp 容器是基於 vuenginxcontainer 這個映象執行的,而在一開始構建映象的時候是將 nginx配置 default.conf 直接構建進去了。因此如果需要修改 default.conf 還得再重新構建一個新的映象,再基於新映象來執行新的容器。
7. 改進
能不能每次修改配置檔案後直接重啟容器就能讓新配置生效,答案當然是有。
在構建映象的時候 不把 Nginx 配置複製到映象中,而是直接掛載到宿主機上,每次修改配置後,直接重啟容器即可。
7.1 修改 Dockerfile 檔案
把 vueclidemo 專案下的 Dockerfile 修改一下
FROM nginx
COPY dist/ /usr/share/nginx/html/
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
複製程式碼
將COPY nginx/default.conf /etc/nginx/conf.d/default.conf
命令刪除,nginx配置都通過掛載命令掛載在宿主機上。再看 COPY dist/ /usr/share/nginx/html/
命令,如果每次構建的專案dist/下的內容變動都需要重新走一遍構建新映象再啟動新容器的操作,因此這條命令也可以刪除,使用掛載的方式來啟動容器。
7.2 重新執行vue應用容器
直接基於nginx映象來啟動容器 vuenginxnew ,執行命令:
docker run \
-p 3000:80 \
-d --name vuenginxnew \
--mount type=bind,source=$HOME/SelfWork/docker/vueclidemo/nginx,target=/etc/nginx/conf.d \
--mount type=bind,source=$HOME/SelfWork/docker/vueclidemo/dist,target=/usr/share/nginx/html \
nginx
複製程式碼
--mount type=bind,source={sourceDir},target={targetDir}
將宿主機的sourceDir 掛載到容器的 targetDir 目錄上。- 此處執行的命令較長,如果每次重新輸入難免麻煩,我們可以將完整的命令儲存到一個
shell
檔案vueapp.sh
中,然後直接執行sh vueapp.sh
。
這樣就能每次修改了nginx配置或者 重新構建了vue應用的時候,只需重啟容器就能立馬生效。 此時我們再訪問 http://localhost:3000/api/json 能看到介面能正常返回,說明轉發生效了。
至此介面服務的轉發也調通了。7.3 配置負載均衡
後端服務一般都是雙機或者多機以確保服務的穩定性。我們可以再啟動一個後端服務容器,並修改
nginx
的配置 來優化資源利用率,最大化吞吐量,減少延遲,確保容錯配置。
基於前面 4.5 節的類似操作,新啟動一個容器,並基於 5.1 節類似的操作,檢視到 新容器的 IP (172.17.0.3)
修改一下 nginx/default.conf
(新增 upstream ,修改 location /api/ 中的 proxy_pass):
upstream backend {
server 172.17.0.2:8080;
server 172.17.0.3:8080;
}
……
location /api/ {
rewrite /api/(.*) /$1 break;
proxy_pass backend;
}
複製程式碼
8. 寫在後面
不習慣命令列的同學可以選用 Kitematic 來管理docker
容器的狀態、資料目錄和網路。所有對容量的操作都可以視覺化的操作,這裡就不做過多介紹了,有興趣的同學可以自行體驗下。
9 總結
docker提供了非常強大的自動化部署方式與靈活性,對多個應用程式之間做到了解耦,提供了開發上的敏捷性、可控性以及可移植性。本文以vue專案為例實現一個前後分離專案使用docker
部署的完整步驟,希望能給想要擁抱 docker 的同學帶來一點幫助。
10 Tail by hand
快狗叫車前端團隊專注前端技術分享,定期推送高質量文章,歡迎關注點贊。