Physical Address

304 North Cardinal St.
Dorchester Center, MA 02124

GCP 筆記: Migrating a Monolithic Website to Microservices on Google Kubernetes Engine

為什麼需要把完整的網站拆成若干微服務 (Microservices)?

  • 微服務可以個別部署、測試。當部署的單元越小,部署越容易。
  • 每個微服務可以使用獨立的程式語言,因此可以針對個別語言的特性做最佳的規劃。
  • 可以由不同的團隊進行管理,可以清楚區隔職務界線。
  • 減少不同小組間的依存性。每個小組可以更著重在相依的 API,而不需要知道 API 背後服務的細部機制。
  • 可以更容易設計失敗的情況。由於服務間有明確的界線,因此可以更容易判斷服務當機時應該做什麼樣的處置。

分割成微服務的缺點:

  • 以為服務為基礎的應用程式,是由多個服務所組成的網路。彼此間的互動方式有時並不明顯,整體複雜度也會提高。
  • 微服務間透過網路進行溝通,而這種方式在某些情境下可能會被視為安全性疑慮。Google 透過 Istio 自動加密微服務間的連線。
  • 有時會因為服務間的延遲,在效能上可能不如單一程式。
  • 系統行為並非由單一服務造成,因此會較難理解在正式環境中的系統會如何行動 (可檢視性較低),Google 也是透過 Istio 服務處理這個問題。

關於微服務的架構,可以參考這篇文件

這個 Lab 的目標是將程式拆成前端、產品後端與訂單後端三個服務。

從儲存庫複製原始碼

# 設定預設的區域
gcloud config set compute/zone us-central1-f

cd ~
git clone https://github.com/googlecodelabs/monolith-to-microservices.git
cd ~/monolith-to-microservices
./setup.sh

建立 GKE 叢集

gcloud services enable container.googleapis.com
gcloud container clusters create fancy-cluster --num-nodes 3
gcloud compute instances list

將既有專案以單一應用程式的方式部署

cd ~/monolith-to-microservices
./deploy-monolith.sh

# 取得單一服務 IP
kubectl get service monolith

分割訂單微服務 (後端)

Cloud Build 可以同時建立 Docker 容器,並將映像檔推送至 Container Registry。

cd ~/monolith-to-microservices/microservices/src/orders
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/orders:1.0.0 .

部署微服務到 GKE

利用 kubectl create deployment 部署 orders 服務。

kubectl create deployment orders \
	--image=gcr.io/${GOOGLE_CLOUD_PROJECT}/orders:1.0.0

接著可以用 kubectl get all 驗證部署情況。

公開微服務

預設 GKE 上的服務是無法透過外部網路存取,因此需要透過 kubectl expose deployment 的方式,公開存取權限。其中 --target-port 是在 GKE 內部溝通的連接埠,而 --port 則是指將外部的 HTTP 流量透過負載平衡器,路由傳送 (route) 到內部溝通的 target-port 中。

kubectl expose deployment orders --type=LoadBalancer --port 80 --target-port 8081

# 取得微服務的 IP
kubectl get service orders

重新調校單一程式

由於個別服務是在不同的伺服器上進行運作,因此必須透過路由傳送的方式,將原本的參照改到微服務的伺服器上。

在轉換的過程中,會有停機的時間,在規劃時須一併考慮進去。

首先,先用 kubectl get service orders 取得微服務的外部 IP (34.136.227.165)。接著開啟單一程式的環境設定檔 .env

cd ~/monolith-to-microservices/react-app
nano .env.monolith
# 將 REACT_APP_ORDERS_URL 改為帶有外部 IP 的連結
REACT_APP_ORDERS_URL=/service/orders
REACT_APP_PRODUCTS_URL=/service/products

# 修改後
REACT_APP_ORDERS_URL=http://xx.xxx.xxx.xxx/api/orders
REACT_APP_PRODUCTS_URL=/service/products

儲存後重新建立程式,並將新版本重新推送到 Cloud Build 上。

npm run build:monolith
cd ~/monolith-to-microservices/monolith
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:2.0.0 .

透過新的映像檔,重新部署 monolith 服務。

kubectl set image deployment/monolith \
	monolith=gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:2.0.0

分割商品微服務 (後端)

# 建立容器映像檔
cd ~/monolith-to-microservices/microservices/src/products
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/products:1.0.0 .

# 部署容器
kubectl create deployment products --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/products:1.0.0

# 公開服務
kubectl expose deployment products --type=LoadBalancer --port 80 --target-port 8082

# 取得微服務的 IP
kubectl get service products

重新調校單一程式

cd ~/monolith-to-microservices/react-app
nano .env.monolith

REACT_APP_PRODUCTS_URL 改為外部 IP。

# 將 REACT_APP_PRODUCTS_URL 改為帶有外部 IP 的連結
REACT_APP_ORDERS_URL=http://xx.xxx.xxx.xxx/api/orders
REACT_APP_PRODUCTS_URL=/service/products

# 修改後
REACT_APP_ORDERS_URL=http://xx.xxx.xxx.xxx/api/orders
REACT_APP_PRODUCTS_URL=http://xx.xxx.xxx.xxx/api/products
npm run build:monolith

cd ~/monolith-to-microservices/monolith
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:3.0.0 .

kubectl set image deployment/monolith \
	monolith=gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:3.0.0

分割前端

cd ~/monolith-to-microservices/react-app
cp .env.monolith .env
npm run build

cd ~/monolith-to-microservices/microservices/src/frontend
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/frontend:1.0.0 .

# 部署容器
kubectl create deployment frontend --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/frontend:1.0.0

# 公開服務
kubectl expose deployment frontend --type=LoadBalancer --port 80 --target-port 8080

清理舊服務

kubectl delete deployment monolith
kubectl delete service monolith

# 驗證結果
kubectl get services

## 結果
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)        AGE
frontend     LoadBalancer   10.39.246.135   35.227.21.154    80:32663/TCP   12m
kubernetes   ClusterIP      10.39.240.1     <none>           443/TCP        18d
orders       LoadBalancer   10.39.243.42    35.243.173.255   80:32714/TCP   31m
products     LoadBalancer   10.39.250.16    35.243.180.23    80:32335/TCP   21m

此時便可以透過 frontend 的外部 IP 驗證是否有正確部署。

Eric Chuang
Eric Chuang

正職是廣告行銷人員,因為 Google Tag Manager 的關係開始踏入網站製作的領域,進一步把 WordPress 當成 PHP + HTML + CSS + JavaScript 的學習教材。此外,因為工作的關係,曾經用 Automattic 的 Underscores (_s) 替客戶與公司官網進行全客製化佈景主題開發。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料