若是您以爲咱們寫得還不錯,記得 點贊 + 關注 + 評論 三連,鼓勵咱們寫出更好的教程💪前端
本篇文章是系列文章終篇,咱們將實現應用的部署,這篇教程將首先 Docker 來容器化你的應用,接着教你配置 MongoDB 的身份驗證機制,給你的數據庫添加一份安全守護,最後咱們會帶你使用阿里的雲的容器鏡像服務將整個全棧應用部署到雲端,使得你互聯網上的用戶能夠訪問你的網站,但願這篇教程能解決長期困擾你的部署上雲的問題!vue
歡迎閱讀《從零到部署:用 Vue 和 Express 實現迷你全棧電商應用》系列:node
首先,若是你是一路跟着前面七篇教程一路敲過來的,那麼將整個 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
首先,讓咱們來容器化以前用 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 是一個強大的多容器管理工具,經過一個 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
,對應咱們的三個容器(db
,api
和 nginx
):
db
服務指定鏡像爲 mongo
,而後設置 restart: always
,確保因某種緣由中止後自動重啓api
服務指定鏡像經過 server
目錄構建,端口映射規則爲 3000:3000
nginx
服務指定鏡像經過 client
目錄構建,端口映射規則爲 8080:80
注意
在指定每一個 service 時,若是使用
image
字段指定鏡像,那麼就會直接從鏡像倉庫拉取該鏡像,這裏咱們的db
服務就是如此。若是使用build
字段指定鏡像,則會根據指定的目錄下的Dockerfile
文件構建鏡像,例如這裏咱們指定server
和client
目錄分別構建api
和nginx
。
提示
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 能夠自行去搜索引擎找答案哦。
在這一小節中,咱們學習了:
在以前的部署配置中,有一個重大的安全隱患:咱們的 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
:鏈接密碼,經過環境變量注入而後在 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.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 公司已經作了一個叫 Docker Hub 的鏡像倉庫,提供了豐富的官方維護鏡像,以及自定義鏡像的存儲和分發。咱們在平時用的鏡像(例如 mongo
、nginx
、node
等)都是 Docker Hub 上的官方鏡像(或者是其餘代理加速器)。
鏡像的命名規則以下:
<registry_name>/<username>/<image_name>
複製代碼
其中:
registry_name
表明鏡像倉庫的名稱,若是省略的話就是 Docker Hubusername
表明鏡像倉庫的用戶名,若是和 registry_name
一塊兒省略的話就是 Docker 官方鏡像image_name
就是鏡像名稱Docker Hub 雖然說是官方出品,但實際上存在如下問題:
而咱們接下來要體驗的阿里的雲鏡像倉庫服務則能很好地解決以上問題。
由於某些緣由,掘全不容許出現某雲廠商的名稱,因此如下都用阿里的雲代稱。
首先讓咱們訪問鏡像倉庫服務的官方網站,而後在產品列表中找到「鏡像倉庫服務」,點擊開通。開通後進入控制檯,建立鏡像命名空間,以下圖所示:
名稱隨意填寫,這裏咱們填的是 vue-online-shop
。建立後以下圖所示:
建立好命名空間後,就能夠爲咱們應用的每一個鏡像(除了 MongoDB 數據庫鏡像)建立相應的鏡像倉庫。點擊「建立鏡像倉庫」按鈕,以下圖所示:
第一步,填寫鏡像倉庫相關信息:
第二步,選擇代碼源,這裏咱們選擇「本地倉庫」:
建立好兩個鏡像倉庫(api
和 nginx
)後,能夠看到鏡像列表以下:
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
中的 api
和 nginx
服務改爲使用雲端鏡像(下面是個人鏡像倉庫地址,記得改爲你本身的喔):
// ...
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
複製代碼
在這一步中,咱們:
在整整八篇教程後,咱們迷你全棧電商系列也進入尾聲了。自 2019 年 12月 21日發佈以來,歷時 86天,期間廣受讀者喜好,也有人要求圖雀社區快點更,這樣能夠看完整個系列好去面試,還有的人直接加了圖雀社區的學習交流羣,在裏面討論技術... 不管怎樣,咱們衷心的但願你,學習這系列教程是輕鬆愉悅的,能收穫實際的知識。
終於,咱們的迷你全棧電商系列實戰就到此爲止啦🎉🎉,感謝一路看下來始終不離不棄、熱愛學習的你😘!讓咱們在接下來更精彩的文章中再見👋~
想要學習更多精彩的實戰技術教程?來圖雀社區逛逛吧。
本文所涉及的源代碼都放在了 Github 上,若是您以爲咱們寫得還不錯,但願您能給❤️這篇文章點贊+Github倉庫加星❤️哦