藉由實際的案例來學習如何將服務部署到GKE上
RD1
此教學主要是希望藉由一個小專案部署到GKE上的過程,讓大家學習如何將服務部署到GKE上。操作這個Lab之後應該會對k8s的這些元件更加熟悉:
另外,由於是將服務部署到GKE上,所以也會學習到一些GCP提供的功能:
甚至是一些GCP工具的操作…
這次要部署的專案可以使用git指令下載回來
或是直接到github頁面下載:
https://github.com/TerryHuangchungyo/gallery
由於該專案的說明與啟動方式在專案的README文件已經有說明了,這邊就不再贅述了;不妨使用docker-compose本地啟動該專案並操作看看,並閱讀README文件,可以幫助了解該專案的架構。
這個章節我們先會操作如何將專案部署到GKE上,首先會先用docker在本地將映象檔打包好,上傳到GCP的Container Registry上,之後利用這些打包好的映象檔部署服務。如果想知道如何使用GCP Cloud build服務觸發打包部署的話,建議可以直接跳到後面Cloud build的章節。
首先我們必須在GCP上新增一個專案,可以使用google的免費方案,google會給你的帳號90天300美元的額度。
首先進到gcp平台頁面,然後點選建立專案:
輸入專案名稱後按建立:
建立好之後,可以選擇專案,並看到專案資訊頁。
這邊需要看到每個專案都會有一個專案ID,專案建立之後專案ID是不能修改的,許多操作都是會用這個專案ID而不是使用專案名稱,可以把專案ID當作是這個專案的身分證號。
Container Registry是GCP提供的映象檔版本管理服務,是本章節會用到的GCP服務。
要到Container Registry的頁面,點選導覽選單 > Container Registry。
Google有提供如何將映象檔推上Container Register的教學以及配置。
Container Registry - Pushing and pulling images
Container Registry - Authentication methods
將這次要部署的專案使用git指令下載回來。
切換到專案資料夾底下,並觀察專案目錄。
這次將打包的會是gallery-backend與gallery-frontend服務的映象檔,可以觀察一下兩個服務的Dockerfile,應該會發現沒有差別,主要是複製到/go/src資料夾底下的程式碼不同而已。
現在,我們切換到gallery-frontend的資料夾底下進行打包並推到GCR(Container Registry)上。
PROJECT_ID就是前面講的專案ID,可以到GCP的專案頁進行查看
gallery-backend的打包方式也相同
如果上傳成功的話Container Registry上就會有兩個映像檔
接下來準備要開始部署專案了,可以考慮使用GCP提供的Cloud Shell進行操作,裡面已經將kubectl工具安裝好了。
首先我們先看一下專案原本在本地的架構,gallery-frontend跟gallery-backend的映象檔我們已經上傳到GCR了,而mysql與paint-image的儲存空間我們必須用到PVC來取得儲存空間。
對PV、PVC或GCE Persistent disk不熟的話可以參考一些教學:
要創建叢集我們必須選擇要在哪一個專案底下創建,這時我們選擇剛剛創建好的專案。
接下來到導覽列的Kubernetes Engine的叢集選項來使用UI介面創建叢集。
按下建立
這邊會詢問你要使用哪一種叢集設定,這邊我們選擇標準,然後按下設定。
接下來設定叢集基本設定,這邊我們設定名稱為
"gallery"與叢集所在地區"asia-east1-a。
叢集的種類說明可以參考這篇文章
Google Cloud - Types of clusters
創建之後我們可以在叢集頁面看到創建好的叢集,叢集預設會產生的node數是3個,這個可以在創建時或創建後修改,這邊我使用預設數量就好。
創建好之後記得用gcloud指令取得cluster的授權,這樣才有辦法使用kubectl指令對該cluster操作。
如果有正確授權的話,使用以下命令應該可以看到kubectl的context被設定為該叢集
首先我們要先創建一個StorageClass元件,之後創建PVC時就可以依此StorageClass去要一塊PV。這邊要注意的是StorageClass的reclaimPolicy設置成Retain代表說如果Pod被刪除了,綁定在Pod上PV裡的資料會被保存下來,畢竟應該沒有人希望mysql服務重啟後裡面的資料不見吧?:worried:
這邊我們打開文字編輯器將上面的yaml檔複製貼上
然後使用kubectl用yaml檔創建物件
創建好後可以到Kubernetes Engine > 儲存空間 查看剛剛創建好的StorageClass。
接下來我們要來創建一個PVC來跟StorageClass要一塊儲存空間了,只要有設定 StorageClass ,K8S就會依照StorageClass的Spec幫我們創建一個PV。
這邊可以看到storageClassName設置的就是我們剛剛創建的StorageClass;然後一樣使用kubectl命令從檔案創建物件。
創建好後可以到Kubernetes Engine > 儲存空間 > 永久磁碟區要求標籤 查看 查看剛剛創建好的PVC。
紅色框框標起來寫PV的部分就是由StorageClass創建出來的PV,點進去並觀察他的yaml檔,你會發現有一些有的沒的設定其實是GKE幫我們自動設定的。
如果想知道儲存空間是從哪裡產生出來的,可以到導覽標籤 > Compute Engine > 磁碟 查看,有時候想對gce Persistent Disk做管理也是會到這個頁面管理。
好了之後我們要來部署mysql伺服器了,首先我們需要創建一個secret物件來保存mysql root帳號的密碼。
上面看到root-password有一串奇怪的代碼"cm9vdA==",其實這個是由base64編碼後的內容,原本的內容應該是"root"。這是secret物件設定上的一些規定,可以到k8s官方文件看更詳細的說明:
一樣也是複製到檔案後,用k8s指令創建。
這邊我們會用StatefulSets進行mysql伺服器服務的部署。通常Deployment用在無狀態服務的部署,而StatefulSets則是用在有狀態服務的部署。
為甚麼這邊會使用StatefulSets而不是使用Deployment,主要原因是因為gcePersistentDisk 只能同時被一個node進行讀寫,如果用Deployment進行部署的話有可能會發生死結的狀況;更詳細的原因這邊就不詳細說明了,可以參考以下文件說明:
GKE - Persistent volumes and dynamic provisioning
以下是這次部署使用到的yaml檔:
可以看到MySQL儲存的volume是用我們剛剛創建的PVC物件掛上去的,而root的密碼則是剛剛我們創建secret物件以環境變數的方式帶入,這次部署replicas設為1,代表我們只維持一個mysql伺服器的pod運行就好。
而service的種類使用的是無頭服務(headless service),就是沒有Cluster IP的Service,原因是StatefulSets的一些規定,這邊可以去查看官方的說明。
一樣複製到文件後,使用kubectl指令部署
好了之後查看工作負載與Service頁面應該能看到以下畫面。
工作負載
有時候會看到一些warning訊息,像是pending之類的,代表pod在部署中等待一下重整應該就好了。
Service的確沒有ClusterIP
不過點進去看時還是能看到他有綁到mysql pod服務的端點。
想確認mysql的服務是否有部署成功,除了看GCP UI的介面以外,也可以下以下命令看Pod的狀態
然後進到Pod裡面執行mysql看看
由於使用指令去管理mysql不是很方便,可以部署phpmyadmin,這樣就可以用GUI對mysql操作。
可以看到這邊phpmyadmin部署時,環境參數PMA_HOST是帶gallery-db,就是我們剛剛創建資料庫service的名字。可以用service就能獲取服務的IP這個要歸功於kube-dns,有興趣的話可以搜尋看看kube-dns的功能,這邊就不過多說明了。
一樣下kubectl創建物件。
可以使用kubectl port-forward獲取這個工具服務。
如果是在本地端port-forward的話,打開瀏覽器輸入localhost:8080應該就能看到phpmyadmin的登入頁面了。
如果是使用gcloud shell的話,可以使用以下方法。
這邊我們需要登入mysql資料庫做一些資料表的初始化。(密碼是"root")
進入後點選SQL標籤。
複製以下SQL語法
貼到框框內,然後按執行。
完成後應該可以看到資料庫與資料表被建好了。
部署MySQL到這邊就算完成了,接下來我們要來部署圖片上傳儲存的空間。
接下來我們要來部署圖片的上傳空間,不過有一個議題還是要去克服,就是gcePersistentDisk只能有兩種存取模式ReadWriteOnce與ReadOnlyMany。
Persistent Volume的存取模式有三種:
不同種類或廠商提供的儲存方案對於存取模式的支援也會有所不同,這邊可以參考以下文件來了解。
Persistent Volumes - Access Mode
如果使用ReadWriteOnce模式來當圖片儲存空間的話,未來gallery-frontend跟gallery-backend在node之間的調度會很麻煩,對於自動擴展也是一大問題。而使用ReadOnlyMany模式的話,就沒有辦法實現上傳圖片的功能了。
這邊可以架設一個nfs伺服器,nfs對於三種模式都有支援。GCP本身有提供一個叫FileStore的NFS服務,不過FileStore對於小型應用似乎不是那麼划算,所以可以架設一個自己的NFS伺服器。
首先必須從gcePersistent Disk要一塊空間
好了之後應該就能在 Compute Engine > 磁碟 看到請求到的硬碟。
創建好之後,我們要來部署NFS伺服器。
NFS伺服器的部署一樣是用StatefulSets來部署(也是因為ReadWriteOnce的關係),用的volumes就是我們剛剛創建的gcePersistentDisk,而Service也是剛剛所說的headless service。
一樣用kubectl指令部署
接下來就可以創建圖片儲存空間的PV與PVC了。
這邊我們是預先創建一個PV,而不是像之前一樣使用StorageClass動態配置的方式。可以發現到accessModes的設定已經是ReadWriteMany了,這樣就可以由多個node進行讀跟寫了。
指令
之後部署的gallery-frontend跟gallery-backend就可以以創建好的PV掛載volume了。
還記得一開始我們有在本地端打包映象檔並推到GCR上面嗎? 現在我們要來利用剛剛推送的映象檔進行部署了。
可以看到這次部署是containers的image就是剛剛我們推送上去的映象檔(<PROJECT_ID> 要填您的專案ID),並且volumeMounts掛載的就是我們創建圖片儲存空間的PVC。
指令
這邊我們希望從Cluster外也能拿到服務,所以必須創建一個種類是Loadbalancer的Service。
對Loadbalancer不熟悉的話可以參考k8s官方文件。
k8s - Loadbalancer
指令
部署好了到 Kubernetes Engine > Service 頁面查看service創建狀況,應該能看到service已經有對外端點了,透過此對外端點我們就能從Cluster外獲取服務了。
這邊我們要來部署gallery-frontend了,流程與剛剛部署gallery-backend時類似。
指令
指令
部署好了之後一樣到 Kubernetes Engine > Service 頁面查看service創建狀況。
恭喜! 這個章節已經完成了~
之前的章節,我們已經成功部署專案到GKE上了。而gallery-frontend與gallery-backend的部署則是由我們從本地端將映象檔打包後推上GCP後,使用kubectl進行服務的部署與更新。
除了在本地端打包映象檔之外,是否有其他的方法能夠將打包更新部署這個動作自動化呢?這個時候可能就可以使用GCP提供的Cloud Build工具來達到自動化部署,更進階一點甚至能建構一個CI/CD Pipeline。
現代DevOps實踐涉及軟體應用程式在整個開發生命周期內的持續開發、持續測試、持續集成、持續部署和持續監控。CI/CD實踐或CI/CD管道(CI/CD pipeline)構成了現代DevOps業務的主幹。
以上截取自維基百科 - CI/CD
關於DevOps與CI/CD的介紹可以參考以下文章:
此次教學會參考此篇google cloud build的教學,為gallery-frontend與gallery-backend建構一個GitOps-style的CI/CD pipeline。