從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(終篇)

本文由圖雀社區成員 mRc 寫做而成,歡迎加入圖雀社區,一塊兒創做精彩的免費技術教程,予力編程行業發展。html

若是您以爲咱們寫得還不錯,記得 點贊 + 關注 + 評論 三連,鼓勵咱們寫出更好的教程💪前端

本篇文章是系列文章終篇,咱們將實現應用的部署,這篇教程將首先 Docker 來容器化你的應用,接着教你配置 MongoDB 的身份驗證機制,給你的數據庫添加一份安全守護,最後咱們會帶你使用阿里的雲的容器鏡像服務將整個全棧應用部署到雲端,使得你互聯網上的用戶能夠訪問你的網站,但願這篇教程能解決長期困擾你的部署上雲的問題!vue

歡迎閱讀《從零到部署:用 Vue 和 Express 實現迷你全棧電商應用》系列:node

應用容器化和 Docker Compose 配置

首先,若是你是一路跟着前面七篇教程一路敲過來的,那麼將整個 Vue 前端項目放到新建立的 client 目錄中,把整個 Express 後端項目放到新建立的 server 目錄。若是你打算直接從這一篇開始學習部署,能夠經過直接下載咱們提供的代碼:nginx

git clone -b deploy-start https://github.com/tuture-dev/vue-online-shop-frontend.git
複製代碼

本文所涉及的源代碼都放在了 Github 上,若是您以爲咱們寫得還不錯,但願您能給❤️這篇文章點贊+Github倉庫加星❤️哦git

在正式開始整個全棧應用的容器化以前,讓咱們經過一張圖來梳理一下思路:github

能夠看到,咱們將使用三個容器:面試

  • nginx 容器包括了 Nginx 服務器(存放了 Vue 框架實現的前端靜態頁面)
  • api 容器則包括了咱們用 Express 框架實現的 API 服務器
  • db 容器則是 MongoDB 數據庫

咱們將整個應用經過 Nginx 實現反向代理。也就是說,用戶想要訪問咱們的應用,必須得先通過 Nginx。而且,全部獲取前端資源的請求(例如 HTML、CSS、JS 等靜態文件資源),Nginx 能夠直接返回;全部獲取 API 端點的請求(例如 /api/v1/products ),則將請求轉交給給 API 服務器,而後再將 API 服務器返回的 JSON 數據返回給用戶。mongodb

這種經典的架構有如下優點:docker

  • 經過 Nginx 能夠實現訪問控制,過濾掉不合法的請求
  • 解決了先後端跨域的問題,由於前端頁面和後端 API 都經過同一個端點訪問
  • 整個應用架構對用戶透明,能夠輕鬆進行配置擴容,而且 Nginx 搭配了負載均衡

前端應用的容器化

首先,讓咱們來容器化以前用 Vue 完成的前端應用。進入 client 目錄,而後把 Vue 前端項目構建成靜態頁面:

npm run build
# 或者 yarn build
複製代碼

而後添加 client/config/nginx.conf 配置文件,代碼以下:

server {
    listen 80;
    root /www;
    index index.html;
    sendfile on;
    sendfile_max_chunk 1M;
    tcp_nopush on;
    gzip_static on;

    location /api/v1 {
      proxy_pass http://api:3000;
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
}
複製代碼

其中須要關注的就是兩條 location 規則:

  • 若是訪問 /api/v1 ,那麼一概把請求傳遞給 api 容器
  • 若是訪問 / ,則直接返回前端靜態頁面(index.html)

而後在前端訪問後端的代碼中,咱們須要作一點改變。打開 client/src/store/actions.js 文件,修改 API_BASE 常量以下:

// ...
import { Message } from 'element-ui';

const API_BASE = '/api/v1';

export const productActions = {
  // ...
};

export const manufacturerActions = {
  // ...
}
複製代碼

這樣修改後,前端實際訪問的 API 就取決於當前該頁面的 URL,而不是硬編碼的 localhost:3000

在作好準備工做後,咱們就要正式開始容器化了。

提示

若是你對 Docker 的核心概念不太熟悉,推薦學習一波咱們圖雀社區的《一杯茶的時間,上手 Docker》,幫助你快速掌握鏡像容器這兩個重要概念,並手把手帶你容器化第一個應用。

client 目錄下建立 Dockerfile 文件,代碼以下:

FROM nginx:1.13

# 刪除 Nginx 的默認配置
RUN rm /etc/nginx/conf.d/default.conf 
# 添加自定義 Nginx 配置
COPY config/nginx.conf /etc/nginx/conf.d/ 
# 將前端靜態文件拷貝到容器的 /www 目錄下
COPY dist /www 複製代碼

建立 client/.dockerignore 文件,確保在構建鏡像時忽略掉 node_modules

node_modules
複製代碼

後端應用的容器化

容器化前端應用以後,接下來就開始準備後端應用的容器化。首先咱們要把硬編碼的 MongoDB 鏈接字符串改爲經過環境變量注入。修改 server/app.js 文件,代碼以下:

// ...
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

// Datbase connection here
mongoose.connect(process.env.MONGO_URI || `mongodb://localhost:27017/test`);

// ...
複製代碼

接着建立 server/Dockerfile ,代碼以下:

FROM node:10

# 指定工做目錄爲 /usr/src/app,接下來的命令所有在這個目錄下操做
WORKDIR /usr/src/app 
# 將 package.json 拷貝到工做目錄
COPY package.json . 
# 安裝 npm 依賴
RUN npm config set registry https://registry.npm.taobao.org && npm install 
# 拷貝源代碼
COPY . . 
# 設置環境變量
ENV NODE_ENV=production
ENV MONGO_URI=mongodb://db:27017/test
ENV HOST=0.0.0.0
ENV PORT=3000

# 開放 3000 端口
EXPOSE 3000

# 設置鏡像運行命令
CMD [ "node", "./bin/www" ] 複製代碼

和前端同樣,建立 server/.dockerignore 文件,確保 node_modules 不會被打包到鏡像中去:

node_modules
複製代碼

Docker Compose 配置

Docker Compose 是一個強大的多容器管理工具,經過一個 YAML 文件配置完成後,只須要一個命令就能夠啓動所有容器(服務)。在項目根目錄建立 docker-compose.yml ,代碼以下:

version: '3'

services: 
 db:
 image: mongo
 restart: always
 api:
 build: server
 restart: always
 nginx:
 build: client
 restart: always
 ports:
 - 8080:80
複製代碼

能夠看到,咱們建立了三個 service ,對應咱們的三個容器(dbapinginx ):

  • db 服務指定鏡像爲 mongo ,而後設置 restart: always ,確保因某種緣由中止後自動重啓
  • api 服務指定鏡像經過 server 目錄構建,端口映射規則爲 3000:3000
  • nginx 服務指定鏡像經過 client 目錄構建,端口映射規則爲 8080:80

注意

在指定每一個 service 時,若是使用 image 字段指定鏡像,那麼就會直接從鏡像倉庫拉取該鏡像,這裏咱們的 db 服務就是如此。若是使用 build 字段指定鏡像,則會根據指定的目錄下的 Dockerfile 文件構建鏡像,例如這裏咱們指定 serverclient 目錄分別構建 apinginx

提示

Docker Compose 默認爲全部服務建立了一個 Docker 網絡,使得容器之間能夠經過服務發現的機制進行相互通訊(而不是經過固定 IP),這也就是爲何在 Nginx 配置中能夠直接指定 http://api:3000 ,以及將 MongoDB 鏈接字符串設置爲 mongodb://db:27017/test 。若是想要透徹理解 Docker 網絡,可瀏覽以前發佈的《夢境亦相通:用 Network 實現容器互聯》

一切就緒,咱們在電商根目錄下經過一個命令實現整個應用的構建 + 運行:

docker-compose up --build
複製代碼

初次構建可能須要至關久的時間(拉取基礎鏡像),這時候不妨給本身點一杯咖啡☕️,靜靜等待結果的到來~ 若是你在控制檯看到了以下輸出,就表明鏡像已經構建完畢了:

接着每一個鏡像會輸出各自的日誌信息。咱們經過

docker ps
複製代碼

命令進一步確認三個容器的狀態:

OK,咱們能夠經過 localhost:8080 訪問咱們的站點了!

而且,咱們還經過內網(例如同一 WiFi 下的其餘設備)訪問咱們的站點,經過查詢本機的內網 IP(例如 192.168.1.1),而後在手機的瀏覽器裏面輸入這個 IP 地址,就能夠經過 192.168.1.1:8080 訪問。查詢本機內網 IP 能夠自行去搜索引擎找答案哦。

小結

在這一小節中,咱們學習了:

  • 經過 Nginx 容器提供前端靜態頁面,並將後端請求轉發給 API 容器
  • 容器化後端應用,創建與數據庫的鏈接
  • 經過 Docker Compose 一鍵構建和啓動應用

配置 MongoDB 的身份驗證

在以前的部署配置中,有一個重大的安全隱患:咱們的 MongoDB 數據庫沒有配置任何的身份驗證措施,這意味着全部可以訪問數據庫的請求均可以對數據庫做出任何修改!接下來,咱們就來搞定 MongoDB 的身份驗證,爲咱們的數據安全保駕護航。

修改 MongoDB 鏈接設置

首先,咱們修改 server/app.js 中的 MongoDB 鏈接設置,代碼以下:

// ...

// Datbase connection here
mongoose.connect(process.env.MONGO_URI || `mongodb://localhost:27017/test`, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  user: process.env.MONGO_USER,
  pass: process.env.MONGO_PASSWORD,
});

// ...
複製代碼

四個選項的含義分別以下:

  • useNewUrlParser :使用新的 MongoDB 驅動 URL 解析器
  • useUnifiedTopology :使用新的鏈接管理引擎,可以大大提升鏈接的穩定性,支持重連
  • user :鏈接用戶名,經過環境變量注入
  • pass :鏈接密碼,經過環境變量注入

Dockerfile 中注入環境變量

而後在 server/Dockerfile 中加入這些環境變量:

// ...

# 設置環境變量
ENV NODE_ENV=production
ENV MONGO_URI=mongodb://db:27017/admin
ENV MONGO_USER=mongoadmin
ENV MONGO_PASSWORD=secret
ENV HOST=0.0.0.0
ENV PORT=3000

// ...
複製代碼

注意到咱們調整了 MONGO_URI ,把數據庫從 test 設置爲默認生成的 admin ,這是爲了使用 admin 做爲鑑權數據庫(Authentication Database)。

Docker Compose 中配置初始密碼

最後在 docker-compose.yml 裏面爲 db 服務添加初始密碼環境變量:

// ...
 db:
 image: mongo
 restart: always
 environment:
 MONGO_INITDB_ROOT_USERNAME: mongoadmin
 MONGO_INITDB_ROOT_PASSWORD: secret
 api:
 build: server
 restart: always
  // ...
複製代碼

OK,一切就緒,首先把以前建立的容器羣完全刪除:

docker-compose down --volumes
複製代碼

down 命令的含義就和以前的 up 恰好相反,把 up 建立的全部鏡像、容器、網絡、數據卷所有刪除,而且咱們經過 --volumes 參數刪除 MongoDB 容器建立的數據卷。

注意

若是不把以前 MongoDB 容器的數據卷刪乾淨,接下來建立帶有身份驗證的 MongoDB 容器就會複用以前的數據卷,直接跳過初始化用戶的過程(筆者在這個地方踩了接近兩個小時的坑)。若是你擔憂數據卷還沒刪乾淨,能夠運行 docker volume prune

而後從新構建並開啓容器羣:

docker-compose up --build
複製代碼

這時候再檢查咱們的應用(訪問 localhost:8080 ),應該看到一切正常。不過一顆懸着的心終於放下了——此次咱們的數據庫再也不處於「裸奔」狀態了!

小結

這一節中,咱們完整地實踐了一波如何爲 MongoDB 容器配備身份驗證。不過平心而論,咱們採用的方法仍是至關原始的,把機密信息明文寫在代碼文件中。在大型的容器編排系統中(例如 Kubernetes 和 Docker Swarm),都集成了完善的、企業級的機密信息管理方案。因爲這一系列教程的入門性質,咱們就點到爲止啦。

此外,咱們也沒有講 MongoDB 數據庫備份和恢復的細節,若是想要了解和學習,能夠閱讀咱們以前的《拒絕刪庫跑路!上手 Docker 容器數據管理》

使用鏡像倉庫服務

到了這一步,實際上咱們已經能夠輕鬆地進行應用部署了。經過 SSH(或其餘方式)鏈接到遠程主機後,而後運行如下命令:

# 把倉庫抓下來
git clone https://github.com/tuture-dev/vue-online-shop-frontend.git
cd vue-online-shop-frontend

# 構建前端代碼
cd client && npm install && npm run build && cd ..

# 經過 Docker Compose 啓動全部容器,並進入守護態運行
docker-compose up -d --build
複製代碼

這個時候,經過遠程主機的 IP(或域名)加上端口號(這裏是 8080 ,固然你能夠在 docker-compose.yml 中自行修改 nginx 服務的端口配置)。例如咱們遠程主機的 IP 是 1.2.3.4 ,那麼就能夠經過 1.2.3.4:8080 訪問咱們的網站啦!

實際上,咱們還能夠經過一種更高效的方式進行鏡像的分發與部署——雲端的鏡像倉庫服務

Docker Hub 和鏡像命名規則

實際上,Docker 公司已經作了一個叫 Docker Hub 的鏡像倉庫,提供了豐富的官方維護鏡像,以及自定義鏡像的存儲和分發。咱們在平時用的鏡像(例如 mongonginxnode 等)都是 Docker Hub 上的官方鏡像(或者是其餘代理加速器)。

鏡像的命名規則以下:

<registry_name>/<username>/<image_name>
複製代碼

其中:

  • registry_name 表明鏡像倉庫的名稱,若是省略的話就是 Docker Hub
  • username 表明鏡像倉庫的用戶名,若是和 registry_name 一塊兒省略的話就是 Docker 官方鏡像
  • image_name 就是鏡像名稱

Docker Hub 雖然說是官方出品,但實際上存在如下問題:

  1. 免費用戶支持 1 個私有鏡像
  2. 上傳和拉取的速度在國內不穩定
  3. 沒有鏡像安全掃描功能

而咱們接下來要體驗的阿里的雲鏡像倉庫服務則能很好地解決以上問題。

體驗阿里的雲鏡像倉庫服務

由於某些緣由,掘全不容許出現某雲廠商的名稱,因此如下都用阿里的雲代稱。

首先讓咱們訪問鏡像倉庫服務的官方網站,而後在產品列表中找到「鏡像倉庫服務」,點擊開通。開通後進入控制檯,建立鏡像命名空間,以下圖所示:

名稱隨意填寫,這裏咱們填的是 vue-online-shop。建立後以下圖所示:

建立好命名空間後,就能夠爲咱們應用的每一個鏡像(除了 MongoDB 數據庫鏡像)建立相應的鏡像倉庫。點擊「建立鏡像倉庫」按鈕,以下圖所示:

第一步,填寫鏡像倉庫相關信息:

第二步,選擇代碼源,這裏咱們選擇「本地倉庫」:

建立好兩個鏡像倉庫(apinginx)後,能夠看到鏡像列表以下:

OK,而後點擊單個倉庫的「管理」按鈕,按照指示進行鏡像的上傳。在這裏咱們貼一下示例代碼(實際操做時按本身控制檯的指示說明爲準):

# 登陸阿里的雲鏡像倉庫,aliyunUser 改爲本身的帳戶名
docker login --username=aliyunUser registry.cn-shanghai.aliyuncs.com

# 構建和推送 api 鏡像
docker build -t registry.cn-shanghai.aliyuncs.com/vue-online-shop/api server
docker push registry.cn-shanghai.aliyuncs.com/vue-online-shop/api

# 構建和推送 nginx 鏡像
docker build -t registry.cn-shanghai.aliyuncs.com/vue-online-shop/nginx client
docker push registry.cn-shanghai.aliyuncs.com/vue-online-shop/nginx
複製代碼

提示

實際部署時,建議給每一個鏡像加上一個標籤,推薦是當前 Git 提交的 Hash,例如:

docker build -t registry.cn-shanghai.aliyuncs.com/vue-online-shop/api:9ca500a server
複製代碼

在鏡像推送完成後,咱們把 docker-compose.yml 中的 apinginx 服務改爲使用雲端鏡像(下面是個人鏡像倉庫地址,記得改爲你本身的喔):

// ...
 MONGO_INITDB_ROOT_USERNAME: mongoadmin
 MONGO_INITDB_ROOT_PASSWORD: secret
 api:
 image: registry.cn-shanghai.aliyuncs.com/vue-online-shop/api
 restart: always
 nginx:
 image: registry.cn-shanghai.aliyuncs.com/vue-online-shop/nginx
 restart: always
 ports:
 - 8080:80
複製代碼

搞定以後,咱們只需把這個 docker-compose.yml 文件放到遠程主機上,而後在所在的目錄開啓 Docker Compose 容器羣便可:

# 拉取全部鏡像的最新版本
docker-compose pull

# 啓動全部容器
docker-compose up -d
複製代碼

小結

在這一步中,咱們:

  • 首先了解了如何經過 Git 抓取代碼的方式在遠程主機上進行部署
  • 而後瞭解了 Docker Hub 及鏡像命名的規則,並分析了一波 Docker Hub 的缺陷
  • 接着一步步帶你體驗和使用阿里的雲鏡像倉庫服務,輕鬆實現鏡像的分發與部署

在整整八篇教程後,咱們迷你全棧電商系列也進入尾聲了。自 2019 年 12月 21日發佈以來,歷時 86天,期間廣受讀者喜好,也有人要求圖雀社區快點更,這樣能夠看完整個系列好去面試,還有的人直接加了圖雀社區的學習交流羣,在裏面討論技術... 不管怎樣,咱們衷心的但願你,學習這系列教程是輕鬆愉悅的,能收穫實際的知識。

終於,咱們的迷你全棧電商系列實戰就到此爲止啦🎉🎉,感謝一路看下來始終不離不棄、熱愛學習的你😘!讓咱們在接下來更精彩的文章中再見👋~

想要學習更多精彩的實戰技術教程?來圖雀社區逛逛吧。

本文所涉及的源代碼都放在了 Github 上,若是您以爲咱們寫得還不錯,但願您能給❤️這篇文章點贊+Github倉庫加星❤️哦

相關文章
相關標籤/搜索