[手把手系列之]Docker 部署 vue 項目

Docker 部署 vue 項目

1.寫在前面:

Docker 做爲輕量級虛擬化技術,擁有持續集成、版本控制、可移植性、隔離性和安全性等優點。本文使用Docker來部署一個vue的前端應用,並儘量詳盡的介紹了實現思路和具體步驟,以方便有相似須要的同窗參考。javascript

Docker 是一個開源的應用容器引擎,讓開發者能夠打包他們的應用以及依賴包到一個可移植的容器中,該容器包含了應用程序的代碼、運行環境、依賴庫、配置文件等必需的資源,經過容器就能夠實現方便快速而且與平臺解耦的自動化部署方式,不管你部署時的環境如何,容器中的應用程序都會運行在同一種環境下。(更多詳情請移步docker官網查看dockerhtml

默認已經安裝了 docker,@vue/cli前端

相關版本:vue

  • Docker version 18.09.2, build 6247962java

  • vue cli --version 3.3.0node

  • macOS Mojave Verison 10.14.1ios

運行環境爲macOS,若是與閱讀者操做系統之間存在差別,請自行調整nginx

相關鏡像:git

  • nginx:latest
  • node:latest

2.具體實現:

  1. 用 vue cli 建立一個vue項目,修改一下建立出來的項目,在頁面上寫一個前端接口請求,構建一版線上資源 ,基於nginx docker鏡像構建成一個前端工程鏡像,而後基於這個前端工程鏡像,啓動一個容器 vuenginxcontainer。
  2. 啓動一個基於 node 鏡像的容器 nodewebserver,提供後端接口。
  3. 修改 vuenginxcontainer 的 nginx 配置,使前端頁面的接口請求轉發到 nodewebserver 上。
  4. 稍做優化和改進。

3 建立 vue 應用

3.1 vue cli 建立一個vue項目

vue create vueclidemo
運行命令

yarn serve / npm run serve
複製代碼

yarn serve
訪問 http://localhost:8081

vue app main page

3.2 改寫

稍微改寫一下頁面,在App.vue中 傳入HelloWorld 組件中的 msg 改成Hello Docker ; created 生命週期中加入一個接口請求web

import axios from 'axios';

……

axios.get('/api/json', {
  params: {}
}).then(
  res => {
    console.log(res);
  }
).catch(
  error => {
      console.log(error);
  }
)

……

複製代碼

這時候會在頁面控制檯看到一個報錯信息:

console 404 error
/api/json 接口 404,固然此時這個接口還不存在,暫時寫到這裏,一會再調這個接口。

3.3 構建vue項目

運行命令

yarn build / npm run build
複製代碼

vue build output
此時工程根目錄下多出一個 dist文件夾

out put dist dir and files

若是將該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 build vuenginxcontainer image
查看本地鏡像,運行命令

docker image ls | grep vuenginxcontainer
複製代碼

docker grep image 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 container
能夠發現名爲 vueApp的容器已經運行起來。此時訪問 http://localhost:3000 應該就能訪問到該vue應用:
vueApp index
目前爲止,已經經過 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 .
複製代碼

docker build 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數據
api json
到目前爲止,後端接口服務也正常啓動了。只需最後把頁面請求的接口轉發到後端接口服務就能調通接口。

6. 跨域轉發

想要將 vueApp 容器 上的請求轉發到 nodeserver 容器上。首先須要知道 nodeserver 容器的ip地址和端口,目前已知 nodeserver 容器內部服務監聽在 8080 端口,還須要知道ip便可。

6.1 查看 nodeserver 容器的 ip 地址:

查看容器內部 ip 有多種方式,這裏提供兩種:

  • 進入容器內部查看
docker exect -it 02277acc3efc bash
複製代碼
cat /etc/hosts
複製代碼

nodeserver ip

  • docker inspect [ containerId ] 直接查看容器信息:
docker inspect 02277acc3efc
複製代碼

在其中找到 Networks 相關配置信息:

nodeserver 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容器的狀態、數據目錄和網絡。全部對容量的操做均可以可視化的操做,這裏就不作過多介紹了,有興趣的同窗能夠自行體驗下。

docker kitematic

9 總結

docker提供了很是強大的自動化部署方式與靈活性,對多個應用程序之間作到了解耦,提供了開發上的敏捷性、可控性以及可移植性。本文以vue項目爲例實現一個先後分離項目使用docker部署的完整步驟,但願能給想要擁抱 docker 的同窗帶來一點幫助。

10 Tail by hand

快狗打車前端團隊專一前端技術分享,按期推送高質量文章,歡迎關注點贊。

參考資源

docker 官網

nginx 官網

docker 從入門到實踐

Kitematic user guide

前端想要了解的Nginx

一文弄懂Nginx的location匹配

相關文章
相關標籤/搜索