1. 簡介
為什麼要從單體式應用程式遷移至微服務架構?將應用程式拆解成微服務具有下列優點:其中大多數的概念源自於微服務是鬆耦合的。
- 微服務可以單獨測試及部署。部署單位越小,部署也越容易。
- 微服務可以用不同的語言及架構實作。您可以自由選擇最適合每種微服務的技術。
- 微服務可以由不同的團隊管理。微服務之間的界線可讓不同團隊輕鬆分配給一或多個微服務。
- 遷移至微服務可以降低團隊之間的依賴性。每個團隊只需關注其依賴的微服務 API。團隊不需要考慮微服務的實作方式和發布週期等等。
- 失敗時更容易解決設計問題。只要區分服務之間的界線,就能更輕鬆地在服務故障時決定處理方式。
與單體相比,以下列舉幾個缺點:
- 以微服務為基礎的應用程式是由不同服務組成的網路,互動時通常不易察覺,因此系統的整體複雜性往往會持續增加。
- 與單體的內部架構不同,微服務會透過網路進行通訊。在某些情況下,這可能會存在安全疑慮。Istio 會自動加密微服務之間的流量,藉此解決這個問題。
- 由於服務之間存在延遲,因此很難達到與單體式方法相同的效能。
- 系統行為不是單一服務造成的,而是由許多服務和它們之間的互動造成。因此,要瞭解系統在正式環境中的表現 (即系統可觀測性) 會比較困難。Istio 也是此問題的解決方案。
在這個研究室中,我們會透過 Google Kubernetes Engine (GKE) 執行微服務。Kubernetes 是可用於管理、託管、部署和部署容器的平台。容器是一種封裝和執行程式碼的可攜式方法。為微服務模式,這些模式非常適合在專屬的容器中執行。
在本研究室中,我們會部署現有的單體式應用程式至 Google Kubernetes Engine 叢集,然後拆分為多項微服務!
我們的微服務架構圖
我們會先將單體拆解成三項微服務,一次一個。微服務包括訂單、產品和前端。我們使用 Cloud Build 為每個微服務建構 Docker 映像檔,這個映像檔會從 Cloud Shell 中觸發。接著,我們會透過 Kubernetes 服務類型的 LoadBalancer,在 Google Kubernetes Engine (GKE) 上部署及公開微服務。我們會分別針對每項服務進行這項作業,同時以單體進行重構。在整個過程中,我們會同時執行單體和微服務,直到可以刪除單體。
課程內容
- 如何拆解單體到微服務
- 如何建立 Google Kubernetes Engine 叢集
- 如何建立 Docker 映像檔
- 如何將 Docker 映像檔部署至 Kubernetes
必要條件
- 具備管理員權限的 Google Cloud Platform 帳戶,可透過「專案擁有者」角色建立專案或專案
- 對 Docker 和 Kubernetes 有基本瞭解
2. 環境設定
自修環境設定
如果您還沒有 Google 帳戶 (Gmail 或 Google Apps),請先建立帳戶。登入 Google Cloud Platform 控制台 ( console.cloud.google.com),並建立新專案:
提醒您,專案 ID 是所有 Google Cloud 專案的專屬名稱 (已經有人使用上述名稱,很抱歉對您不符!)。稍後在本程式碼研究室中會稱為 PROJECT_ID
。
接下來,您必須前往 Developers Console 啟用計費功能,才能使用 Google Cloud 資源並啟用 Container Engine API。
執行本程式碼研究室所需的費用不應超過數美元,但如果您決定使用更多資源,或讓這些資源繼續運作,費用會增加 (請參閱本文件結尾的「清理」一節)。Google Kubernetes Engine 定價請參閱這裡。
新使用者符合 $300 美元免費試用資格的 Google Cloud Platform。
Google Cloud Shell
雖然 Google Cloud 和 Kubernetes 可以從筆記型電腦遠端操作,但在本程式碼研究室中,我們會使用 Google Cloud Shell,這是一種在 Cloud 中執行的指令列環境。
這種以 Debian 為基礎的虛擬機器,搭載各種您需要的開發工具。提供永久的 5 GB 主目錄,而且在 Google Cloud 中運作,大幅提高網路效能和驗證能力。換言之,本程式碼研究室只需要在 Chromebook 上運作即可。
- 如要透過 Cloud 控制台啟用 Cloud Shell,只要點選「啟用 Cloud Shell」 圖示 即可 (整個佈建作業只需幾分鐘的時間,操作完畢即可)。
連線至 Cloud Shell 後,您應會發現自己通過驗證,且專案已設為 PROJECT_ID
。
gcloud auth list
指令輸出
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
指令輸出
[core] project = <PROJECT_ID>
如因故未設定專案,請直接發出以下指令:
gcloud config set project <PROJECT_ID>
正在尋找 PROJECT_ID
嗎?查看您在設定步驟中使用的 ID,或在 Cloud 控制台資訊主頁查詢:
根據預設,Cloud Shell 也會設定一些環境變數,方便您之後執行指令。
echo $GOOGLE_CLOUD_PROJECT
指令輸出
<PROJECT_ID>
- 最後,進行預設可用區和專案設定。
gcloud config set compute/zone us-central1-f
您可以選擇各種不同的可用區。詳情請參閱「區域與可用區。
3. 複製原始碼存放區
我們使用虛構的電子商務網站現有的單體式應用程式,搭配簡單的歡迎頁面、產品頁面和訂單記錄頁面。只需要從 Git 存放區複製原始碼,就能專注於將原始碼拆成微服務,並部署至 Google Kubernetes Engine (GKE)。
執行下列指令,將 Git 存放區複製到 Cloud Shell 執行個體,並變更為適當的目錄。我們也會安裝 NodeJS 依附元件,以便在部署前測試單體。執行這個指令碼可能需要幾分鐘的時間。
cd ~ git clone https://github.com/googlecodelabs/monolith-to-microservices.git cd ~/monolith-to-microservices ./setup.sh
這會複製 GitHub 存放區、變更至目錄,並安裝在本機執行應用程式所需的依附元件。執行這個指令碼可能需要幾分鐘的時間。
4. 建立 GKE 叢集
您有了運作中的開發人員環境,接下來就需要 Kubernetes 叢集來部署單體,最終我們將微服務部署至其中!建立叢集之前,我們必須確認已啟用適當的 API。執行下列指令,啟用 Container API,以便使用 Google Kubernetes Engine:
gcloud services enable container.googleapis.com
現在可以開始建立叢集了!執行下列指令,建立具有 3 個節點並命名為 fancy-cluster 的 GKE 叢集。
gcloud container clusters create fancy-cluster --num-nodes 3
可能需要幾分鐘時間才能建立叢集。指令執行完畢後,請執行下列指令,查看叢集的三個工作站 VM 執行個體:
gcloud compute instances list
輸出:
NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS gke-fancy-cluster-default-pool-ad92506d-1ng3 us-east4-a n1-standard-1 10.150.0.7 XX.XX.XX.XX RUNNING gke-fancy-cluster-default-pool-ad92506d-4fvq us-east4-a n1-standard-1 10.150.0.5 XX.XX.XX.XX RUNNING gke-fancy-cluster-default-pool-ad92506d-4zs3 us-east4-a n1-standard-1 10.150.0.6 XX.XX.XX.XX RUNNING
您也可以在 Google Cloud 控制台中查看 Kubernetes 叢集和相關資訊。按一下左上方的選單按鈕,向下捲動至 Kubernetes Engine,然後按一下「叢集」。您應該會看到名為 fancy-cluster 的叢集。
恭喜!您已建立第一個 Kubernetes 叢集!
5. 部署現有單體
由於本研究室的重點是逐步將單體拆分為多項微服務,因此必須啟動並執行單體應用程式。執行下列指令碼,將單體式應用程式部署至 GKE 叢集,以用於本研究室:
cd ~/monolith-to-microservices ./deploy-monolith.sh
存取單體
如要找出單體應用程式的外部 IP 位址,請執行下列指令。
kubectl get service monolith
畫面會顯示類似下列輸出內容:
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE monolith 10.3.251.122 203.0.113.0 80:30877/TCP 3d
注意:您必須為此佈建外部負載平衡器和 IP,這項作業需要一些時間才能完成。如果您的輸出內容列出了
<pending>
請幾分鐘後再試。
確認單體的外部 IP 位址後,請複製該 IP 位址。將瀏覽器指向這個網址 (例如 http://203.0.113.0),檢查單體是否可以存取。
您應該會看到單體式網站的歡迎頁面,如上圖所示。歡迎頁面是靜態頁面,前端微服務將於稍後提供。您現在已在 Kubernetes 中完整執行單體!
6. 將訂單遷移至微服務
現在我們已建立在 GKE 上執行的單體網站,可以開始將每項服務拆解為微服務。一般而言,規劃工作應針對哪些服務進行細分,通常是針對應用程式的特定部分,例如業務網域。為方便示範,我們以簡單的範例分別列出各項服務,並將服務網域、訂單、產品和前端分門別類。程式碼已遷移完畢,我們會專注於在 Google Kubernetes Engine (GKE) 建構及部署服務。
建立新訂單微服務
我們第一個細分的服務是訂單服務我們會使用所提供的獨立程式碼集,並為這項服務建立獨立的 Docker 容器。
使用 Google Cloud Build 建立 Docker 容器
由於我們已為您遷移程式碼集,因此第一步是使用 Google Cloud Build 建立訂單服務的 Docker 容器。
通常,您必須採取兩個步驟,也就是建構 Docker 容器並推送至登錄檔,儲存映像檔供 GKE 提取。但我們可以簡化作業,使用 Google Cloud Build 建構 Docker 容器,並透過單一指令將映像檔放入 Google Cloud Container Registry 中!如此一來,我們就能發出一個指令來建構映像檔,並將映像檔移至 Container Registry。如要查看建立 Docker 檔案並推送檔案的手動程序,請前往這裡。
Google Cloud Build 會壓縮目錄中的檔案,並將檔案移至 Google Cloud Storage 值區。接著,建構程序會擷取值區中的所有檔案,並使用 Dockerfile 執行 Docker 建構程序。由於我們已為 Docker 映像檔指定 --tag
旗標,並將主機設為 gcr.io,因此系統會將產生的 Docker 映像檔推送至 Google Cloud Container Registry。
執行下列指令,建構 Docker 容器並推送至 Google Container Registry:
cd ~/monolith-to-microservices/microservices/src/orders gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/orders:1.0.0 .
這項程序需要幾分鐘才能完成,但作業完成後,終端機中會顯示類似下方的輸出內容:
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ID CREATE_TIME DURATION SOURCE IMAGES STATUS 1ae295d9-63cb-482c-959b-bc52e9644d53 2019-08-29T01:56:35+00:00 33S gs://<PROJECT_ID>_cloudbuild/source/1567043793.94-abfd382011724422bf49af1558b894aa.tgz gcr.io/<PROJECT_ID>/orders:1.0.0 SUCCESS
如要查看建構作業記錄或即時觀看程序,請前往 Google Cloud 控制台。按一下左上方的選單按鈕,然後向下捲動至「Tools」→「Cloud Build」,然後按一下「History」。這裡會列出您先前的所有建構作業,但應該只會有 1 個您剛建立的建構作業。
如果按一下版本 ID,即可查看該版本的所有詳細資料,包括記錄輸出。
在建構詳細資料頁面中,按一下建構資訊區段中的映像檔名稱,即可查看建立的容器映像檔。
將容器部署至 GKE
我們現在已將容器容器化,並推送至 Google Container Registry,接著是時候部署至 Kubernetes 了!
Kubernetes 會以 Pod 的形式呈現應用程式,這是容器 (或緊密結合的容器群組) 的單位。Pod 是 Kubernetes 中最小的可部署單位。在這個教學課程中,每個 Pod 僅包含您的微服務容器。
如要在 GKE 叢集上部署及管理應用程式,您必須與 Kubernetes 叢集管理系統通訊。一般來說,您可以使用 Cloud Shell 中的 kubectl 指令列工具來完成此操作。
首先,請建立「Deployment」資源。Deployment 會管理應用程式的多個副本 (稱為備用資源),並安排這些副本在叢集中的個別節點上執行。在這個情況下,Deployment 只會執行應用程式的一個 Pod。Deployment 會透過建立 ReplicaSet 來確保這一點。ReplicaSet 會確保指定的備用資源數量一律在運作中。
下列 kubectl create deployment
指令會讓 Kubernetes 在具有 1 備用資源的叢集上建立名為 orders 的 Deployment。
執行下列指令來部署應用程式:
kubectl create deployment orders --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/orders:1.0.0
驗證部署作業
如要確認 Deployment 是否已成功建立,請執行下列指令,Pod 狀態可能需要一點時間才會啟動:
kubectl get all
輸出:
NAME READY STATUS RESTARTS AGE pod/monolith-779c8d95f5-dxnzl 1/1 Running 0 15h pod/orders-5bc6969d76-kdxkk 1/1 Running 0 21s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.39.240.1 <none> 443/TCP 19d service/monolith LoadBalancer 10.39.241.130 34.74.209.57 80:30412/TCP 15h NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/monolith 1/1 1 1 15h deployment.apps/orders 1/1 1 1 21s NAME DESIRED CURRENT READY AGE replicaset.apps/monolith-779c8d95f5 1 1 1 15h replicaset.apps/orders-5bc6969d76 1 1 1 21s
這個輸出內容顯示了一些資訊。您可以看到目前的 Deployment、ReplicaSet 具備所需 Pod 數量為 1 的 ReplicaSet,以及正在執行的 Pod。系統似乎已成功建立所有內容!
公開 GKE 容器
我們已在 GKE 上部署應用程式,但無法在叢集外存取應用程式。根據預設,您在 GKE 上執行的容器無法透過網際網路存取,因為這些容器沒有外部 IP 位址。您必須透過服務資源,將應用程式明確公開到來自網際網路的流量。Service 可為應用程式的 Pod 提供網路和 IP 支援。GKE 會為應用程式建立外部 IP 和負載平衡器 ( 取決於計費方式)。
在部署訂單服務之後,我們透過 Kubernetes 部署在內部於通訊埠 8081 上公開這項服務。為了對外公開這項服務,我們必須建立 LoadBalancer 類型的 Kubernetes 服務,以便將訂單服務從外部通訊埠 80 的流量轉送至內部通訊埠 8081。執行下列指令,將網站公開到網際網路:
kubectl expose deployment orders --type=LoadBalancer --port 80 --target-port 8081
存取服務
GKE 會將外部 IP 位址指派給服務資源,而非 Deployment。如要瞭解 GKE 為應用程式佈建的外部 IP,則可使用 kubectl get service 指令檢查 Service:
kubectl get service orders
輸出:
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE orders 10.3.251.122 203.0.113.0 80:30877/TCP 3d
決定應用程式的外部 IP 位址後,請複製這個 IP 位址。當我們變更單體,以指向新的訂單服務時,請儲存起來以便進行下一步!
重新設定單體
由於我們從單體中移除「訂單」服務,因此必須修改單體,指向新的外部「訂單」微服務。
拆解單體時,我們會將程式碼片段從單一程式碼集中移除,並分別部署至多個程式碼。由於微服務是在不同的伺服器上執行,我們無法再將服務網址參照為絕對路徑,因此需要將訂單轉送至訂單微服務的新伺服器位址。請注意,單體 Service 需要經過一段停機時間,才能為受影響的每項服務更新網址。在微服務遷移期間,規劃將微服務和單體遷移至實際工作環境時,應考量這點。
我們需要更新單體中的設定檔,指向新的訂單微服務 IP 位址。使用 nano 編輯器,將本機網址替換成新 Orders 微服務的 IP 位址。執行下列指令,
cd ~/monolith-to-microservices/react-app nano .env.monolith
編輯器開啟後,檔案應如下所示:
REACT_APP_ORDERS_URL=/service/orders REACT_APP_PRODUCTS_URL=/service/products
請將 REACT_APP_ORDERS_URL
替換為新格式,同時替換為您的訂單微服務 IP 位址,使其符合以下情況:
REACT_APP_ORDERS_URL=http://<ORDERS_IP_ADDRESS>/api/orders REACT_APP_PRODUCTS_URL=/service/products
依序按下 CTRL+O
、ENTER
和 CTRL+X
,即可將檔案儲存在 nano 編輯器。
只要前往您剛在此檔案中設定的網址,即可查看新的微服務。網頁應會傳回訂單微服務的 JSON 回應。
接下來,我們必須重新建構單體前端,並重複建構程序來為單體建構容器,並重新部署至 GKE 叢集。執行下列指令,完成這些步驟:
重建單體設定檔
npm run build:monolith
使用 Google Cloud Build 建立 Docker 容器
cd ~/monolith-to-microservices/monolith gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:2.0.0 .
將容器部署至 GKE
kubectl set image deployment/monolith monolith=gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:2.0.0
透過瀏覽器中的單體應用程式,然後前往「訂單」頁面,確認應用程式現在會進入新的「訂單」微服務。所有訂單 ID 的結尾都必須為 -MICROSERVICE 結尾,如下所示:
7. 將產品遷移至微服務
建立新產品微服務
我們可以繼續遷移「產品」服務,繼續分割服務。我們將按照上一個步驟的流程操作。執行下列指令來建立 Docker 容器、部署容器,並透過 Kubernetes 服務公開。
使用 Google Cloud Build 建立 Docker 容器
cd ~/monolith-to-microservices/microservices/src/products gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/products:1.0.0 .
將容器部署至 GKE
kubectl create deployment products --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/products:1.0.0
公開 GKE 容器
kubectl expose deployment products --type=LoadBalancer --port 80 --target-port 8082
請使用下列指令,找出 Google 產品服務的公開 IP (和處理訂單服務的方式相同):
kubectl get service products
輸出:
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE products 10.3.251.122 203.0.113.0 80:30877/TCP 3d
當我們重新設定單體,以指向新的「產品」微服務時,請儲存 IP 位址,以便在下一個步驟中使用。
重新設定單體
使用 nano 編輯器,將本機網址改為新產品微服務的 IP 位址:
cd ~/monolith-to-microservices/react-app nano .env.monolith
編輯器開啟後,檔案應如下所示:
REACT_APP_ORDERS_URL=http://<ORDERS_IP_ADDRESS>/api/orders REACT_APP_PRODUCTS_URL=/service/products
請將 REACT_APP_PRODUCTS_URL
換成新格式,同時替換為產品微服務 IP 位址,讓格式如下所示:
REACT_APP_ORDERS_URL=http://<ORDERS_IP_ADDRESS>/api/orders REACT_APP_PRODUCTS_URL=http://<PRODUCTS_IP_ADDRESS>/api/products
依序按下 CTRL+O
、ENTER
和 CTRL+X
,即可將檔案儲存在 nano 編輯器。
只要前往您剛在此檔案中設定的網址,即可查看新的微服務。網頁應會傳回產品微服務的 JSON 回應。
接下來,我們必須重新建構單體前端,並重複建構程序來為單體建構容器,並重新部署至 GKE 叢集。執行下列指令,完成這些步驟:
重建單體設定檔
npm run build:monolith
使用 Google Cloud Build 建立 Docker 容器
cd ~/monolith-to-microservices/monolith gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:3.0.0 .
將容器部署至 GKE
kubectl set image deployment/monolith monolith=gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:3.0.0
您可以在瀏覽器中開啟單體應用程式,然後前往「產品」頁面,確認應用程式現已達到新的產品微服務。所有產品名稱的開頭都必須是 MS,如下所示:
8. 將前端遷移至微服務
遷移程序的最後一個步驟是將前端程式碼移至微服務,並關閉單體!完成這個步驟後,我們就會成功將單體遷移至微服務架構!
建立新的前端微服務
讓我們按照最後兩個步驟的相同程序,建立新的前端微服務。
我們先前在重建單體時,將設定更新為指向單體,但現在必須使用相同的 Frontend 微服務設定。執行下列指令,將微服務網址設定檔複製到前端微服務程式碼集:
cd ~/monolith-to-microservices/react-app cp .env.monolith .env npm run build
完成後,我們將按照先前的步驟進行。執行下列指令來建立 Docker 容器、部署容器,並透過 Kubernetes 服務公開。
使用 Google Cloud Build 建立 Docker 容器
cd ~/monolith-to-microservices/microservices/src/frontend gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/frontend:1.0.0 .
將容器部署至 GKE
kubectl create deployment frontend --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/frontend:1.0.0
公開 GKE 容器
kubectl expose deployment frontend --type=LoadBalancer --port 80 --target-port 8080
刪除單體
現在所有服務均以微服務的形式執行,我們可以刪除單體應用程式!請注意,在實際遷移作業中,這也必須包括 DNS 變更等,以便將現有網域名稱指向應用程式的新前端微服務。執行下列指令,刪除單體:
kubectl delete deployment monolith kubectl delete service monolith
測試您的作品
為了確認一切運作正常,單體 Service 中的舊 IP 位址應該無法運作,且前端服務的新 IP 位址應用於託管新的應用程式。如要查看所有服務和 IP 位址的清單,請使用下列指令:
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
決定前端微服務的外部 IP 位址後,請複製這個 IP 位址。將瀏覽器指向這個網址 (例如 http://203.0.113.0),檢查前端是否可供存取。您的網站應該與我們將單體式拆解成微服務前的結果一樣!
9. 清除
準備就緒後,如要清除已執行的所有活動,最簡單的方法就是刪除專案。刪除專案會刪除在此程式碼研究室中建立的所有資源,以免產生非預期的週期性費用。在 Cloud Shell 中執行下列指令,其中 PROJECT_ID 是完整的專案 ID,而不只是專案名稱。
gcloud projects delete [PROJECT_ID]
如果確定要刪除,請輸入「Y」系統顯示提示時
10. 恭喜!
您已成功將單體式應用程式拆解為微服務,並部署至 Google Kubernetes Engine!
後續步驟
請查看下列程式碼研究室,進一步瞭解 Kubernetes:
- 在 Google Kubernetes Engine 上部署、擴充及更新您的網站
- 在 Kubernetes 中使用 Node.js 建構 Slack 機器人
- 使用 Spinnaker 持續推送軟體更新至 Kubernetes
- 將 Java 應用程式部署至 Google Kubernetes Engine 中的 Kubernetes
其他資源
- Docker:https://docs.docker.com/
- Kubernetes:https://kubernetes.io/docs/home/
- Google Kubernetes Engine (GKE):https://cloud.google.com/kubernetes-engine/docs/
- Google Cloud Build:https://cloud.google.com/cloud-build/docs/
- Google Container Registry:https://cloud.google.com/container-registry/docs/
- 將單體遷移至微服務 - https://cloud.google.com/solutions/migrating-a-monolithic-app-to-microservices-gke