--- title: "【K8S Beginners 筆記】CH 4~6 PODs, YAML, ReplicaSet and Deployments" date: 2025-02-07 is_modified: true disqus: cynthiahackmd image: https://i.imgur.com/2F0W0aE.png categories: - "雲端網路 › 雲端運算" tags: - "Update" - "Udemy" - "Docker" - "K8S" - "Kubernetes Beginners" - "讀書筆記" --- {%hackmd @CynthiaChuang/Github-Page-Theme %} <br> Beginners 課程筆記第二彈!🎉 拖了有點久,課是上完了,但筆記還沒補完 Orz。這種感覺就像是考試已經結束,但還在瘋狂補交作業(淚)。不過,換個角度想,至少筆記還在持續更新,而不是直接擺爛(合理安慰自己 😌)。 話說,這篇寫完,我就完成一半了——至少從章節數來看是這樣 XDDD。想到這點,還是有點成就感的(雖然進度條還沒拉到 100%)。總之,趕快來補完這一篇吧!💪 <!--more--> <p class="illustration"> <img src="https://i.imgur.com/2F0W0aE.png" width="600px" alt="課程縮圖"> 課程縮圖(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> ## CH 4|Kubernetes Concepts 這章的標題雖然是 K8S 的概念,但實際上主要聚焦在 Pod 上。 對,沒錯,就是我在[上一篇](https://hackmd.io/@CynthiaChuang/Kubernetes-for-the-Absolute-Beginners-01#2-3|Kubernetes-Architecture)中自己去查定義的那個 Pod。這次,就來看看講師怎麼更有系統地解釋吧! <br class="big"> 開始前,先確認幾個先決條件: 1. 應用程式已經開發完成,並且打包成 Docker image。 2. 這個 Docker image 已經上傳至 registry,無論是公開的還是私人的,只要 K8S 拉得到就行。 3. K8S cluster 已經部署完成,並且正辛勤運作中!💪 ### 4-1|PODs #### Pod,K8S 的最小運作單位 對於 K8S 而言,終極目標是將應用程式以 container 形式部署到 work nodes 中。但如果細看,container 並==不會直接部署==到 node 上,而是先被封裝進 pod,最終會以 pod 為單位部署到 node 上,並在其中執行。 <p class="illustration"> <img src="https://imgur.com/SUtm72h.png" width="600px" alt="container 會被封裝進 pod,並以 pod 為單位部署到 node 上"> container 會被封裝進 pod,並以 pod 為單位部署到 node 上 </p> 本章的主角 **Pod**,它是 K8S 中==最小的運作單位==,可以將它想像成一個應用程式運行的 instance。注意,**是一個應用程式,而不是一個 container**!雖然在大多數情況下,pod 跟 container 是一對一的關係,但某些應用可能需要 helper containers 來執行輔助任務,例如作為服務的前端、資料庫等,這時會把它們放進同一個 pod 內一起啟動。 <br class="big"> 將同一應用的 containers 放在同一個 pod 除了方便外,更帶來幾個重要的好處: 1. 生命週期一致,K8S 只需針對一個 pod 就能同時關閉或啟動這些 containers。 2. 共享相同的 network namespace,所以 containers 之間可以直接用 `localhost` 溝通。 3. 共享存儲空間,讓不同 containers 可以存取相同的資料。 #### K8S 與 Pod 如何應對負載變動 了解了 pod 的角色後,接下來看看當 K8S 進行擴展時,pod 是如何變化的。 之前提過,當負載變動時,container 會進行垂直或水平擴展。或者更精確地說,K8S 會**透過新增 pod 來擴展**,而不是直接在原有的 pod 裡新增 container。此時,K8S 會在相同的 ==node 上新增一個相同的 pod==,這就是為什麼 pod 被稱為最小的運作單位。不過,如果當前 node 沒有足夠資源,K8S 會自動在 cluster 中的其他 node 上啟動新的 pod。 這其實蠻合理的,想像一下,如果同一個 pod 裡同時啟動兩個 web container,那 port 應該指派給誰? 縮小規模的方式也一樣,當需求下降時,K8S 會刪除 pod 來縮減服務,而不是直接刪除 container。 <p class="illustration"> <img src="https://i.imgur.com/S7HT0QO.png" alt="K8S 的擴展會在相同的 node 上新增一個相同的 pod;若資源不夠,會在其他 node 上啟動新的 pod" width="600px"> K8S 的擴展會在相同的 node 上新增一個相同的 pod;若資源不夠,會在其他 node 上啟動新的 pod(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> #### 使用 kubectl 操作 Pod 這一節我們會介紹一些常用的 kubectl 指令,主要用於部署與管理 Pod。由於這部分的內容與上一節有些重疊,因此我將它們合併在一起,並透過簡單的指令來示範如何快速啟動 Pod。 1. **kubectl run** ```bash= $ kubectl run {pod-name} --image {image-name} ``` 這是啟動 pod 用的指令,其中 `image` 預設會從 Docker Hub 下載所指定的映像檔。如果需要從私人倉庫下載,則需要額外配置 K8S,有需要的人[自行服用](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/),我們就先不深入討論。 請注意,這種方法啟動的 pod 和 container 通常是**一對一的關係**。 <p class="illustration"> <img src="https://i.imgur.com/LbPXTI9.png" alt="kubectl run"> kubectl run </p> 2. **kubectl get pods** ```bash= $ kubectl get pods ``` 此指令可以列出 cluster 中所有 pod 的基本資訊,像是 container 狀態、運行時間等。 <p class="illustration"> <img src="https://i.imgur.com/lwJehbW.png" alt="kubectl get pods"> kubectl get pods </p> <br class="big"> 如果需要查看更詳細的資訊,可以加上 `-o wide`,這樣會顯示更多資料,如 IP 和所屬 node 等。 ```bash= $ kubectl get pods -o wide ``` <p class="illustration"> <img src="https://i.imgur.com/vu82YB6.png" alt="kubectl get pods -o wide"> kubectl get pods -o wide </p> 3. **kubectl describe** ```bash= $ kubectl describe pod {pod-name} ``` 相較於 `get`,`describe` 提供更為詳盡的資訊,包括 label、IP、所屬 node、以及 pod 中所有 container 的資訊。如果 pod 裡有多個 container,這些資訊會一一列出。 我認為最有用的功能是,它會列出 pod 中發生的所有事件,這對於 debugging 非常有幫助! <p class="illustration"> <img src="https://i.imgur.com/Wh3NXLW.png" alt="kubectl describe"> kubectl describe </p> 對了,上面列出的 IP 只能在 node 內部存取。如果沒額外配置,外部是無法直接存取這些 container 的,這部分之後應該會講到 ### 4-2|練習與測驗 1. **The smallest unit you can create in Kubernetes object model is:** - [x] Pod 2. **A Pod can only have one container in it** - [x] False 3. **What is the right approach to scale an application** - [x] Deploy additional Pods ### 4-3|補充資料 - [Pod Overview](https://kubernetes.io/docs/concepts/workloads/pods/) ## CH 5|YAML Introduction 講師在兩個 Kubernetes Concepts 章節中間塞了一個 YAML 介紹章節,這安排讓人懷疑是不是標錯章節了?而且這章的投影片跟其他幾個章節的布景主題不太一致,看起來像是後來補充的,可能是有人反應不會寫 YAML吧? 不過話說回來,**YAML 確實在 K8S 的應用中扮演了重要角色**。可以把 YAML 想成 K8S 應用程式部署的**藍圖**或**說明書**。這些配置文件不只是描述應用的設定,還規範了如何運行、管理以及擴展應用。透過這些 YAML 文件,K8S 能夠理解並執行容器的部署、擴容、滾動更新等操作! <p class="illustration"> <img src="https://i.imgur.com/DSYkb8q.png" alt="YAML Logo" width="200px"> YAML Logo(圖片來源: <a href="https://zh.m.wikipedia.org/zh-hant/YAML">維基百科</a>) </p> ### 5-1|Introduction to YAML YAML(YAML Ain’t a Markup Language)是一種可讀性極高的資料格式,常用於表示資料結構與設定檔。在 K8s 中,YAML 是用來撰寫設定檔的主要格式。 #### 三大數據格式比較:XML、JSON、YAML 這節開始前,先快速比較一下三種常見的結構化數據格式:XML、JSON 與 YAML。 <p class="illustration"> <img src="https://i.imgur.com/7QNzYdg.png" alt="同資料以 XML、JSON 和 YAML 表示"> </p> * **XML**:==每個屬性都需要額外的標籤來包裹==,雖然不影響數據表示,但冗餘資訊是三者中最多的。 * **JSON**:相比於 XML,JSON 格式簡潔許多,因為它 ==改用 key-value pair(鍵值對)== 的方式來儲存資料,並透過 `:` 分隔鍵值、`,` 分隔每筆資料、`{}` 表示物件,`[]` 表示陣列,因此 JSON 在儲存大小與處理速度上,比 XML 更具優勢。 * **YAML**:與 JSON 類似,YAML 也是用 key-value pair 來表示資料,但進一步移除了所有括號與逗號,使結構更加精簡明瞭。至於具體的寫法,下一節再來細講。 #### YAML 基礎規則解析 <p class="illustration"> <img src="https://i.imgur.com/P0sD5ps.png" alt="YAML 撰寫規則"> YAML 撰寫規則(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> - **Key-Value Pair(鍵值對)** YAML 與 JSON 一樣使用 ==冒號(:)== 來分隔鍵值。記得**冒號後面要加空格**!(這樣可以避免 debug 時的崩潰 XDDD) - **Array/List(陣列)** 如果是陣列(Array),每個==元素前面要加上破折號(-)==。 - **Dictionary/Map(字典)** 如果要表示字典(Dictionary),對應的鍵值要**縮排對齊**,並且同一層級的項目要左對齊。YAML 對縮排的空格數量沒有硬性規定,但一定要==保持相同層級的縮排一致==。<br> 需要特別注意的是,YAML **不能讓同一個鍵(Key)同時對應數值和子屬性**,例如: ```yaml= cookie: Fat: 9.8 TransFats: 4.9 SaturatedFat: 0 ``` 應該改成 ```yaml= cookie: Fat: TransFats: 4.9 SaturatedFat: 0 ``` 或是 ```yaml= cookie: Fat: 9.8 TransFats: 4.9 SaturatedFat: 0 ``` - **List of Dictionary(字典陣列)** 其實沒這個專有名詞啦,它就是陣列但元素是字典而已。這類 YAML 結構很常見,重點是**每個字典元素前面都要加破折號(-)**,而內部的屬性則要對齊縮排。寫作時要小心別誤將它歸類錯誤。 ```yaml= - name: apple color: red - name: banana color: yellow ``` - **Comment(註釋)** YAML 的註解是以 `#` 開頭的。 #### Dictionary vs. Array:怎麼選? | 類型 | 適用情境 | 範例 | | -------- | -------- | -------- | | Dictionary(字典) | 當數據是無序集合時,或表示某個物件的屬性 | `car: { color: red, model: Tesla }` | | Array(陣列) | 當數據是有序集合時 | `fruits: [apple, banana, cherry]` | ### 5-2|練習與測驗 1. **Update the food.yml file to add a Vegetable - Carrot. Not sure how to solve this? Check the answer in the answer.yaml file.** ```yaml= Fruit: Apple Drink: Water Dessert: Cake ``` 答: ```yaml= Fruit: Apple Drink: Water Dessert: Cake Vegetable: Carrot ``` 2. **Update the food.yml file to add a list of Vegetables - Carrot, Tomato, Cucumber** ```yaml= Fruits: - Apple - Banana - Orange ``` 答: ```yaml= Fruits: - Apple - Banana - Orange Vegetables: - Carrot - Tomato - Cucumber ``` 3. **We have updated the food.yml file with nutrition information for Fruits. Similarly update the nutrition information for Vegetables. Use the below table for information** | Vegetables | Calories | Fat |Carbs | | -------- | -------- | -------- |-------- | | Carrot | 25 | 0.1 | 6 | | Tomato | 22 | 0.2 | 4.8 | | Cucumber | 8 | 0.1 | 1.9| ```yaml= Fruits: - Apple: Calories: 95 Fat: 0.3 Carbs: 25 - Banana: Calories: 105 Fat: 0.4 Carbs: 27 - Orange: Calories: 45 Fat: 0.1 Carbs: 11 ``` 答: ```yaml= Fruits: - Apple: Calories: 95 Fat: 0.3 Carbs: 25 - Banana: Calories: 105 Fat: 0.4 Carbs: 27 - Orange: Calories: 45 Fat: 0.1 Carbs: 11 Vegetables: - Carrot: Calories: 25 Fat: 0.1 Carbs: 6 - Tomato: Calories: 22 Fat: 0.2 Carbs: 4.8 - Cucumber: Calories: 8 Fat: 0.1 Carbs: 1.9 ``` 4. **Jacob is 30 year old Male working as a Systems Engineer at a firm. Represent Jacob's information (Name, Sex, Age, Title) in YAML format. Create a dictionary named Employee and define properties under it.** 答: ```yaml= Employee: Name: Jacob Sex: Male Age: 30 Title: Systems Engineer ``` 5. **Update the YAML file to represent the Projects assigned to Jacob. Remember Jacob works on Multiple projects - Automation and Support. So remember to use a list.** ```yaml= Employee: Name: Jacob Sex: Male Age: 30 Title: Systems Engineer ``` 答: ```yaml= Employee: Name: Jacob Sex: Male Age: 30 Title: Systems Engineer Projects: - Automation - Support ``` 6. **Update the YAML file to include Jacob's pay slips. Add a new property "Payslips" and create a list of pay slip details (Use list of dictionaries). Each payslip detail contains Month and Wage.** | Month | Wage | | ------ | ---- | | June | 4000 | | July | 4500 | | August | 4000 | ```yaml= Employee: Name: Jacob Sex: Male Age: 30 Title: Systems Engineer Projects: - Automation - Support ``` 答: ```yaml= Employee: Name: Jacob Sex: Male Age: 30 Title: Systems Engineer Projects: - Automation - Support Payslips: - Month: June Wage: 4000 - Month: July Wage: 4500 - Month: August Wage: 4000 ``` ## CH 6|Kubernetes Concepts - Pods, ReplicaSets, Deployments 基本上,YAML 並不難寫,但縮排、破折號、鍵值對格式等細節一定要特別注意,否則很容易踩進坑裡。接下來,我們將學習如何在 K8S 中使用 YAML 部署 Pod、Service、ReplicaSets 和 Deployment。 ### 6-1|PODs 這一節我把「PODs with YAML」和「Demo:PODs with YAML」兩個章節合併了。這樣讀起來更順,也能少點標題,避免像原課程那樣落落長 #### K8S 的 4 個關鍵屬性 在標準的 K8S 定義文件中,包含了 ==四個必須的根屬性:apiVersion、kind、metadata 和 spec==。 <p class="illustration"> <img src="https://i.imgur.com/vtkrTEP.png" alt="標準的 K8S 定義文件所包含的根屬性與對應的 API 版本"> 標準的 K8S 定義文件所包含的根屬性與對應的 API 版本(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> 1. **apiVersion** > Which version of the Kubernetes API you're using to create this object 簡單來說,就是我們所使用的 ==K8S API 版本==,最常用的版本大概是 `v1` 和 `apps/v1`,還有其他版本如:`apps/v1beta1`、`extensions/v1beta1` 等等。這些 API 版本的差異,應該會在後續課程中說明。如果講師沒講到...那就自己[啃文件](https://kubernetes.io/docs/concepts/overview/kubernetes-api/)吧。 不過說真的,感覺 Kubernetes 的版本更新有點隨性,不僅是名字,連功能都跟著版本一起跳來跳去。有些功能在上一版還是 beta,下一版就變成 stable 了... RC?你的 rc 版本去哪兒啦?🤔 2. **kind** > What kind of object you want to create 這裡是指定你要==建立的物件類型==。在這一節,我們要填的是 `Pod`,其他常見的選項還有 `ReplicaSet`、`Deployment` 或 `Service`。需要注意的是,不同物件類型會搭配不同的 `apiVersion`,所以使用時要留意這一點。 3. **metadata** > Data that helps uniquely identify the object, including a name string, UID, and optional namespace `metadata` 用來==放置描述性資料==,像是 `name`、`label` 和 `annotation` 等。`name` 是必填的,其他的 `label` 和 `annotation` 則用來幫助識別、分群、管理和部署物件。特別要注意的是,`metadata` 的子屬性必須符合 K8S 的規範,而在 `label` 和 `annotation` 下,你可以自由填寫任何需要記錄的 Key-Value Pair。 與前兩個根屬性不同的是,metadata 使用的是 **dictionary** 格式,所以在撰寫時,一定要特別注意空格和縮排,免得把層級搞錯,認錯父親,這可是會 Error 大爆發的 XDDD 4. **spec** > What state you desire for the object `spec` 是對==物件內容的具體定義==,最重要的 container 和其 image 就是在這裡配置的。舉例來說,前面提到過,一個 Pod 可以包含多個 container,這些 containers 會在 spec 這一段中列出來,並指定對應的 image。 對了,`spec` 是 dictionary 格式,但它的子屬性 `containers` 卻是 list 格式,可別搞混了!還有,記得 `containers` 是有「s」的!不然一不小心,錯誤會卡住超久 XDDD #### 使用 YAML 定義 Pod 之前我們已經用 `kubectl run` 指令建立過簡易的 Pod,但現在了解了 K8S YAML 文件的基本格式後,我們可以透過 YAML 來定義更完整的 Pod 配置。接下來,講師將帶我們實際操作,撰寫一份 YAML 文件來建立並啟動一個更複雜的 Pod。 <br class="big"> 首先,得建立一個名為 `pod.yml` 的文件,然後使用任意編輯器(比如記事本、vim 等)來撰寫它。根據上節介紹的四個根屬性,我們可以依序填寫內容,記得注意細節: - **apiVersion**:pod 搭配的版號是 `v1`。 - **kind**:pod 使用的物件類型是 `Pod`,因為 ==YAML 是大小寫敏感==的,所以**開頭的 P 一定要大寫**。 - **metadata**:使用 `name` 給定 pod 名稱,並使用標籤來幫助識別 pod。 - **spec**:這邊定義 container 及其配置,包含 name 和 image 等屬性。這裡的 name 和 image 乍看很像,但 name 只是 Pod 裡的 container 名稱,而 image 指的是 container 會用哪個 Docker 映像(通常是 Docker Hub 上的名稱)。 - **縮排對齊**:同一層級的縮排要對齊,還有記得加 `s` 的地方別忘了!另外,雖然 YAML 對縮排的空格數量沒有硬性規定,但建議使用兩個空格來縮排,且不要使用 Tab。 ```yaml= apiVersion: v1 kind: Pod metadata: name: cynthia-pod-2 labels: app : nginx tier: frontend spec: containers: - name: nginx image: nginx ``` #### 使用 kubectl 操作 Pod(Again!) 一旦 YAML 定義文件準備好,就可以用指令來建立 Pod 了: ```bash= $ kubectl create -f pod.yml or $ kubectl apply -f pod.yml ``` 這裡有兩個指令可以用:`create` 和 `apply`,講師可能覺得兩者沒差(It doesn’t matter),但我們這種好奇寶寶怎麼可能不去研究一下?🤢! <br class="big"> **`create` vs `apply`** 從資料上看來,`kubectl create` 屬於 ==命令式(Imperative)== 操作,意思是**你需要明確告訴電腦執行哪些步驟,才能完成想要的狀態**。在這裡,它會直接告訴 K8S API:「**我要新建這個資源!**」但如果這個資源已經存在,就會立刻報錯,像這樣: ```bash= $ kubectl get pods No resources found. $ kubectl create -f pod.yml pod/cynthia-pod-2 created $ kubectl create -f pod.yml Error from server (AlreadyExists): error when creating “pod.yml”: pods “cynthia-pod-2” already exists ``` <br> 另一方面,`kubectl apply` 採用的是 ==宣告式(Declarative)== 方法,意思是你告訴**電腦要完成什**麼,由電腦自動幫你完成想要的狀態。在這裡,就是定義「**應該長什麼樣**」,至於怎麼實現,交給 K8S 處理。它會根據 YAML 檔的內容來調整狀態: - **如果資源不存在** ➜ 直接建立 - **如果資源已經存在** ➜ 只調整有變更的部分 <br class="big"> 兩者間主要差異就是: - **apply** 可以透過修改 YAML 檔並重新執行來套用變更。 - **create** 則無法這樣做,如果要修改,只能刪除重建,或是用其他指令來更新。 <p class="illustration"> <img src="https://i.imgur.com/zBhPPFc.png" alt="Kubectl apply vs Kubectl create"> Kubectl apply vs Kubectl create(圖片來源: <a href="https://blog.csdn.net/textdemo123/article/details/104400985">大鹏blog|CSDN</a>) </p> <br> 這邊分享個有趣的例子,假設我現在人在**河樂廣場**,想搭計程車 🚖 前往**台南火車站**,命令式(Imperative)和宣告式(Declarative)的差異就像這樣: - **命令式(Imperative)** 我們需要逐步告訴司機該怎麼開,具體指示如下: 1. 往東走中正路朝康樂街前進 2. 在康樂街向右轉進入中正路 3. 沿中正路進入圓環 4. 從圓環的第四個出口中山路出去 5. 沿中山路向左轉進入北門路一段 6. 台南火車站在右邊 <p class="illustration"> <img src="https://i.imgur.com/TdjYDmr.png" width="500px"> </p> - **宣告式(Declarative)** 這種方式就簡單多了——我只需要告訴司機:「我要去台南火車站」,路線怎麼走就交給他決定。 <p class="illustration"> <img src="https://i.imgur.com/puSdwYi.png" width="500px"> </p> <br class="big"> 不過,不管是用 `create` 還是 `apply`,Pod 都能成功建立 🎉! 一旦 Pod 建立,接下來,就可以來看看它的狀態了: ```bash= $ kubectl get pods ``` <p class="illustration"> <img src="https://i.imgur.com/AUmNLdt.png" alt="檢查 pods 狀態"> 檢查 pods 狀態 </p> 如果想要看更詳細的資訊,例如事件記錄、錯誤訊息等,可以使用: ```bash= $ kubectl describe pod {name} ``` <p class="illustration"> <img src="https://i.imgur.com/1P7SjDt.png" alt="查看 pod 詳細狀況"> 查看 pod 詳細狀況 </p> ### 6-2|K8S YAML Validation in IDEs 這邊介紹一些可以支援 K8S YAML 的 IDE,雖然用 vim 跟筆記本也撰寫,但遇到大型 YAML,這會使難度直線上升啊,再加上大家都被 autocomplete 給慣壞了 🤣 XDDD 下面是兩款常見的 IDE 及其設定: 1. **JetBrains 系列** JetBrains 的 IDE(例如 IntelliJ IDEA、PyCharm 等)本身就支援 YAML 格式的編輯與檢查,能夠協助檢查 YAML 檔案的排版與結構錯誤。若要支持 K8S YAML,則需要安裝相應的 [plugin](https://plugins.jetbrains.com/plugin/9354-kubernetes-and-openshift-resource-support)。安裝插件後,JetBrains 會提供更多與 Kubernetes 相關的功能,如自動完成、語法檢查和格式化等。 2. **VS code** VS Code 是一款輕量級且高度擴充的編輯器,對 YAML 格式有良好的支援,但對 K8S YAML 的支援則需要安裝額外的插件。影片中介紹了使用 [RedHat 所發布的 YAML](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml),安裝後可以實現 YAML 格式的自動補全與語法檢查。若要進一步支援 Kubernetes 的 YAML 檔案,我們需要進行以下設定: `設定` → 選擇作用域 `User` / `Workspace` → `Yaml:Schems` → 在 `settings.json` 內編輯。在 `settings.json` 中,找到 `yaml.schemas` 並增加 `kubernetes`: ```json= "yaml.schemas": { "kubernetes": "*.yaml" } ``` <br class="big"> 雖然這些 IDE 能夠幫助我們檢查 YAML 的排版與結構,並提供語法提示,但它們並非萬能。像是實際配置錯誤或標籤等問題,只有在執行階段,經 Kubernetes 真正解析這些配置時,才能顯示出來。因此,在編輯過程中仍然需要小心核對配置是否符合預期,並進行實際的測試來確保其正確性。 ### 6-3|Replication Controllers and ReplicaSets 雖然跨了兩個章節,但終於結束了 Pod 的介紹與啟動!這一節,我們再次專注於 K8S 如何應對負載變動的問題。之前在 [4-1 章](#K8S-與-Pod-如何應對負載變動)中提過,當負載變動時,K8S 會根據需要增加或刪除 Pod,來保持系統的穩定性。 #### Replication Controllers and ReplicaSets 是什麼? 而監督 K8S 內部狀態變化並做出相應反應的,正是由 K8S 的控制中心 —— controllers 所負責。這些 controllers 就像是整個 orchestration 的大腦,根據不同的情境與需求,會觸發不同的行為和反應。 因此當負載出現變動時,controller 中的一個 **Replication Controller** 會開始動作。顧名思義,Replication Controller 的工作是控制副本。具體來說,它會根據用戶所設定的副本數量,==生成並維持對應的 Pod 數量==,確保系統在負載變動時仍能穩定運行,從而達到高可用性與可靠性。 <p class="illustration"> <img src="https://imgur.com/BdAZecy.png" width="600px" alt="Replication Controller 會生成並維持對應的 Pod 數量"> Replication Controller 會生成並維持對應的 Pod 數量 </p> 在新版的 K8S 中,**ReplicaSets** 被推薦用來取代 Replication Controller。雖然兩者在基本功能上相似,都是負責生成並維持 Pod 副本,但 ReplicaSets 增加了更強大的功能 —— ==支援集合式的 selector==。利用 selector,ReplicaSets 能夠篩選匹配 `label` 的 pod,這樣不僅能管理 replica sets 中已經宣告的 pod,還能夠擴展到所有符合條件的 pod,無論它們是已建立還是未來建立的。 <br class="big"> 基於 **備援性**、 **擴展性**、 **共享性**,Replication Controllers 和 ReplicaSets 擁有以下優勢: 1. **High Availability** 如前面所說,Replication Controller 可以**生成並維持**指定數量的 pod。換句話說,若有 pod 異常或故障,Replication Controller 會立即生成新的 pod 來替代它;如果多出來的 Pod 則會自動回收。 因此,當某個 Pod 失效時,系統會自動恢復,生成一個新的 Pod 填補空缺,確保應用服務不會中斷,從而提供高可用性。新生成的 Pod 會根據 Node 資源使用狀況,分配到原本的 Node 或是其他 Node 上。 <p class="illustration"> <img src="https://i.imgur.com/3q2qPC0.png" alt="當某個 Pod 失效時,系統生成一個新的 Pod 確保服務不中斷"> 當某個 Pod 失效時,系統生成一個新的 Pod 確保服務不中斷(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> 2. **Load Balancing & Scaling** 使用 Replication Controller 的另一大優勢是能夠建立多個 Pod 來共享負載。隨著使用者數量增加,系統可以自動部署更多的 Pod 來平衡各 Pod 之間的負載,實現應用的擴展;而當使用者減少時,Pod 的數量也會適當減少,避免資源浪費。 這樣的負載平衡機制確保了應用能夠在多變的流量條件下高效運行,提升了系統的彈性與擴展性。 <p class="illustration"> <img src="https://i.imgur.com/0kPbrvX.png" alt="隨著使用者數量增加,系統可以自動部署更多的 Pod 來平衡各 Pod 之間的負載"> 隨著使用者數量增加,系統可以自動部署更多的 Pod 來平衡各 Pod 之間的負載(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> #### 使用 YAML 定義 Replication Controllers 與 ReplicaSets 接下來,我們來看看如何使用 YAML 定義 Replication Controllers 和 ReplicaSets。跟所有 K8S 元件一樣,YAML 撰寫的起手勢都是四個必需的根屬性:apiVersion、kind、metadata 和 spec。 **Replication Controller** - **`apiVersion` 與 `kind`** 既然是寫 Replication Controller,所以 `kind` 當然設為 `ReplicationController` ,而與其對應的 `apiVersion` 則設為 `v1`。 - **`spec`** 這是與 Pod YAML 最大的差異。不同於 Pod 中直接定義 containers 和配置,Replication Controller 的 `spec` 部分包含了兩個關鍵屬性:`template` 與 `replicas` 。 - `template` 用來描述 Replication Controller 需要管理的 Pod。這裡的 Pod 配置和單獨撰寫 Pod YAML 一樣,不過在這裡不需要再次宣告 `kind` 和 `apiVersion`,因為它們已經被上層的 Replication Controller 定義。 - `replicas` 屬性則用來設定需要多少副本。需要注意的是,`template` 和 `replicas` 是 `spec` 的直接子屬性,所以它們的縮排必須一致。 <p class="illustration"> <img src="https://i.imgur.com/keUY2Dm.png" alt="RC with YAML" width="600px"> RC with YAML(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> **ReplicaSets** ReplicaSet 的 YAML 撰寫方式和 Replication Controller 類似,但有以下關鍵區別: - **`apiVersion` 與 `kind`** 在這裡,`kind` 需要設為 `ReplicaSet`,並且 `apiVersion` 必須設為 `apps/v1`。如果繼續使用舊版的 `v1`,將會遇到錯誤: <p class="illustration"> <img src="https://i.imgur.com/nzPQTWj.png" alt="使用錯誤 apiVersion 會出現錯誤訊息"> 使用錯誤 apiVersion 會出現錯誤訊息(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> - **selector** 前面提到,Replication Controller 和 ReplicaSets 之間最重要的區別在於 **集合式 selector** 的支援。因此,ReplicaSet 的 `spec` 中必須包含 `selector`,通常會搭配 `matchLabels` 使用來篩選 Pod。 <p class="illustration"> <img src="https://i.imgur.com/YPXY28P.png" alt="RS with YAML" width="600px"> RS with YAML(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> #### 使用 kubectl 操作 ReplicaSets 雖然前面介紹了 Replication Controllers 與 ReplicaSets 兩種元件的 YAML 寫法,但在實做上會建議採用較新的 ReplicaSets。基本步驟與重點在上一節已經介紹過,這邊就直接實作吧!🧑‍🏭 **建立 ReplicaSet 並啟動 Pod** 不過執行前,需要注意==selector 和 Pod label 的配對==。在 ReplicaSets 的 `spec` 中,有一個非常重要的屬性是 `selector` 。這個 `selector` 需要與 Pod 的 `label` 相互對應。具體來說,Pod 的 label 是放在 `spec` 下面的 `template` 中的 `metadata` 內。如果兩者不匹配,ReplicaSet 是管不到這些 Pod 的呀 XDDD :::info **小提醒**:ReplicaSet 本身的 label(位於 metadata 的 labels 中)則不需要與 selector 配對,它只是用來描述 ReplicaSet 的特性,並不是描述不是它的守備範圍。 ::: ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: name: cynthia-app-replicaset labels: app: cynthia-app spec: selector: matchLabels: tier: frontend replicas: 3 template: metadata: name: cynthia-pod-2 labels: app : nginx tier: frontend spec: containers: - name: nginx image: nginx ``` <br class="big"> 一旦準備好 YAML 定義後,就可以透過 `kubectl create` 或 `kubectl apply` 命令來建立 ReplicaSet,並且根據設定自動啟動 Pod: ```bash= $ kubectl create -f replicasets.yaml or $ kubectl apply -f replicasets.yaml pod/cynthia-pod-2 created ``` <br class="big"> 建立完成後,可以檢查 ReplicaSet 的狀態: ```bash= $ kubectl get replicaset ``` <p class="illustration"> <img src="https://i.imgur.com/PbTmFND.png" alt="檢查 replica set 狀態" width="600px"> 檢查 replica set 狀態 </p> <br class="big"> 查看 Pod 列表,會發現 ReplicaSet 會自動維持指定數量的 Pod: ```bash= $ kubectl get pods ``` <p class="illustration"> <img src="https://i.imgur.com/FNWW34X.png" alt="觀察 pod 數目" width="600px"> 觀察 pod 數目 </p> 即便刪除一個 Pod,ReplicaSet 也會立即啟動新的 Pod 來補上。我們可以再查詢 ReplicaSet 的事件記錄,會發現 replicas 仍然維持 3 個,並且 Event 記錄會顯示 4 次 create,因為我們手動刪除了一個 Pod: <p class="illustration"> <img src="https://i.imgur.com/QW9zTn0.png" alt="查看 replica set 詳細狀況" width="600px"> 查看 replica set 詳細狀況 </p> 如果手動新增 Pod,一旦超過指定數量,就會被自動中止: <p class="illustration"> <img src="https://i.imgur.com/LyCCOox.png" alt="新增超過指定數量的 pod 會被中止" width="600px"> 新增超過指定數量的 pod 會被中止 </p> <br class="big"> **如何調整 Replicas?** 先前提到使用 ReplicaSet 的好處就是便於縮放!如果需要調整 ReplicaSet 的 Pod 數量,可以使用以下幾種方式: 1. **`edit`** 這個方法會直接修改執行中的暫存檔,因此需要特別小心,可能會導致意料之外的副作用。 ```bash= $ kubectl edit replicaset cynthia-app-replicaset ``` <p class="illustration"> <img src="https://i.imgur.com/KTsNh3S.png" alt="暫存 YAML 檔" width="600px"> 暫存 YAML 檔 </p> 將 replicas 數量改為 4,ReplicaSet 會自動補足 Pod 數量: <p class="illustration"> <img src="https://i.imgur.com/IsX032m.png" alt="增加 replicas 會新增 Pod 以補足數量" width="600px"> 增加 replicas 會新增 Pod 以補足數量 </p> 2. **`scale`** 這也是一次性的修改,但不需要調整 YAML 檔,可以直接透過指令設定: ```bash= $ kubectl scale replicaset cynthia-app-replicaset --replicas=2 ``` <p class="illustration"> <img src="https://i.imgur.com/l9v7mcV.png" alt="scale 縮放 replica set" width="600px"> scale 縮放 replica set </p> 3. **`apply`** 最後一個方法,也是我最喜歡用的,就是直接修改 YAML 檔,然後透過 apply 調整資源配置: <p class="illustration"> <img src="https://i.imgur.com/KT5DA6F.png" alt="使用 apply 去調整資源" width="600px"> 使用 apply 去調整資源 </p> <br class="big"> **刪除物件** 回顧了下前面的筆記,發現我似乎沒有紀錄過如何刪除物件,這邊補一下筆記: ```bash= $ kubectl delete <資源名稱> <物件名稱> or $ kubectl delete -f <YAML 檔> ``` 所以,如果要刪除剛剛建立的 ReplicaSet,可以用下列兩個指令 ```bash= $ kubectl delete replicaset cynthia-app-replicasetor or $ kubectl delete -f <replicasets.yaml> ``` ### 6-4|Deployments 到目前為止,我們已經學會如何建立 Pod,也學會使用 ReplicaSet 來確保 Pod 數量的一致性。但[官方](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/#when-to-use-a-replicaset)並不建議直接使用 ReplicaSet,而是推薦改用 Deployment。 #### Deployments 是什麼? 在[官方文件](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/#when-to-use-a-replicaset)中,有這樣的描述: > a Deployment is a higher-level concept that manages ReplicaSets and provides declarative updates to Pods along with a lot of other useful features. 這段話說明了 Deployment 是一個比 ReplicaSet 更高級的概念,負責管理 ReplicaSet,並提供 ==聲明性更新(Declarative Updates)== 及其他許多強大的功能。 聲明性更新的好處是,K8S 會記錄所有**變更歷史**,讓我們可以**撤銷變更、暫停或恢復變更**,提供更靈活的**版本控制能力**,**並確保系統穩定性**。 <br class="big"> 而這樣的版本管理能力,正是 K8S 改善傳統部署方式的重要關鍵。在 K8S 出現之前,部署應用程式往往會遇到以下問題: 1. **手動管理多個 instance** 在生產環境中,服務可能需要多個 instance,但無論是為了調整負載還是增加高可用性,都必須手動逐一啟動,過程繁瑣且容易出錯,從而影響系統穩定性。 2. **升級麻煩** 當服務需要升級時,這些 instance 必須逐一升級,既耗時又費力還容易出錯。此外,為不影響使用者的使用,通常採用無停機服務逐步升級 instance,這需要額外的管理成本。這種更新策略稱為==滾動更新(RollingUpdate)==,後續章節會有更詳細的介紹。 3. **撤消更新困難** 如果某次升級發生錯誤,想要 Rollback 到先前版本,往往需要手動執行一系列反向操作,過程既繁瑣又容易出錯。 這些痛點正是 K8S,或者更精確地說,Deployment 所能解決的問題。因此 K8S 推薦使用者使用 Deployment 管理 ReplicaSet,再由 ReplicaSet 負責管理 Pod,這樣的架構讓我們能夠輕鬆應對 Update、Rollback 等操作。 <p class="illustration"> <img src="https://i.imgur.com/Dxw6RxA.png" width="350px" alt="Deployment 不直接管理 Pod,而是由 Deployment 管理 ReplicaSet,再由 ReplicaSet 負責管理 Pod。"> Deployment 不直接管理 Pod,而是由 Deployment 管理 ReplicaSet,再由 ReplicaSet 負責管理 Pod。(圖片來源: <a href="https://medium.com/aspnetrun/deploying-microservices-on-kubernetes-35296d369fdb">aspnetrun|Medium</a>) </p> #### 使用 YAML 定義 Deployments 我們前面已經有一個 ReplicaSet 的 YAML 檔,因此要過渡到 Deployment,步驟其實相當簡單。只需要將 `kind` 欄位中的 `ReplicaSet` 改為 `Deployment`,就可以輕鬆轉換過來。 <p class="illustration"> <img src="https://i.imgur.com/cb5hlUD.png" width="350px" alt="Deployments with YAML"> Deployments with YAML(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> #### 使用 kubectl 操作 Deployment 這次我們照慣例來寫一個 Deployment 的 YAML 檔。不過難度不大,只需要將之前的 ReplicaSet YAML 檔複製過來,然後把 `kind` 欄位從 `ReplicaSet` 改成 `Deployment`,就可以輕鬆完成轉換了。 ```yaml= apiVersion: apps/v1 kind: Deployment metadata: name: cynthia-app-deployment labels: app: cynthia-app spec: selector: matchLabels: tier: frontend replicas: 3 template: metadata: name: cynthia-pod-2 labels: app : nginx tier: frontend spec: containers: - name: nginx image: nginx ``` <br class="big"> YAML 定義後,就可以透過 `kubectl create` 或 `kubectl apply` 來建立 Deployment: <p class="illustration"> <img src="https://i.imgur.com/aDOIFDx.png" width="600px" alt="create deployment"> </p> <br class="big"> Deployment 建立後,查看其狀態的指令如下: <p class="illustration"> <img src="https://i.imgur.com/Ml5PHiy.png" width="600px" alt="get deployment"> </p> <p class="illustration"> <img src="https://i.imgur.com/0sevHud.png" width="600px" alt="get replicaset"> </p> <p class="illustration"> <img src="https://i.imgur.com/oELfxkA.png" width="600px" alt="get pods"> </p> <br class="big"> 如果覺得逐個查看有點麻煩,也可以直接使用以下指令查看所有資源: ```bash= $ kubectl get all ``` <p class="illustration"> <img src="https://i.imgur.com/Gz6yrVo.png" width="600px" alt="get all"> </p> ### 6-5|Update and Rollback 剛剛我們透過 Deployment 讓應用程式的部署變得更簡單,但還有兩個問題尚未解決——**如何順利升級?如果升級出問題,又該如何恢復?** 這一章的重點,就是解決這兩個關鍵挑戰:**升級(Update)** 與 **回滾(Rollback)**。Kubernetes 提供了內建機制,讓我們能夠平滑地升級服務,並且保留所有變更歷史,確保在需要時可以迅速恢復到穩定版本。 <p class="illustration"> <img src="https://i.imgur.com/pPyRp2C.png" width="500px" alt=" Rolling and Rollback Deployments work"> Rolling and Rollback Deployments work(圖片來源: <a href="https://yankeexe.medium.com/how-rolling-and-rollback-deployments-work-in-kubernetes-8db4c4dce599">Yankee Maharjan|Medium</a>) </p> #### Deployment Update Strategy 前面我們有提到滾動更新(RollingUpdate),這次終於要來補完這個話題了! 在 K8S 中,Deployment Controller 提供了兩種更新策略: 1. **重建更新 (Recreate)** 這是一種最簡單直接的更新方式。當新版本部署時,系統會同時停止所有舊版 instance,然後一次性啟動所有新版 instance。 **優點** - 簡單直覺:不需要考慮新舊版本之間的兼容性問題,管理上較為直接。 - 節省資源:不會同時運行多個版本,因此不會額外佔用系統資源。 **缺點** - 會有停機時間:新舊版本交替期間,使用者無法存取服務,對高可用性應用影響較大。 - 風險較高:如果新版本有問題,會導致整個服務中斷,因此需要準備快速 Rollback。 <p class="illustration"> <img src="https://i.imgur.com/5UEsNkY.png" width="600px" alt="Recreate 部署時會終止所有正在運行的 instance,然後用較新的版本來取代"> Recreate 部署時會終止所有正在運行的 instance,然後用較新的版本來取代(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> 2. **滾動更新(RollingUpdate)** 滾動更新是一種**逐步替換**舊版 instance 的策略,而不是一次性關閉所有舊版 instance。它會分批啟動新版本的 instance,同時逐步關閉舊版本,直到所有 instance 都成功更新。 與重建式更新相比,滾動式更新的最大優勢是==可實現無停機更新==,這對高可用性應用尤為重要。此外,由於更新是分批進行的,因此可以在每個批次更新後進行檢查,萬一出現問題,也能及時停止或 Rollback,降低風險。 **優點** - 無停機更新:舊版本和新版本會並存一段時間,確保服務不中斷。 - 風險可控:可逐步監測更新狀態,有問題時能及時 Rollback,減少影響範圍。 **缺點** - 可能有兼容性問題:在更新過程中,新舊版 instance 會共存,可能會引發兼容性問題。 - 部署時間較長:相較於一次性更新,滾動更新需要更多時間來完成整個版本切換。 在 K8S 中,Deployment ==預設採用 RollingUpdate 策略==。在 RollingUpdate 基礎上,還衍生出多種變體,例如 Ramped Slow Rollout 和 Best-effort Controlled Rollout,主要是透過 **maxSurge** 和 **maxUnavailable** 兩個參數來調整更新過程的穩定性,這部分我們後續會再介紹。 <p class="illustration"> <img src="https://i.imgur.com/au8q8dY.png" width="600px" alt="RollingUpdate 是一種逐步替換舊版 instance 的策略"> RollingUpdate 是一種逐步替換舊版 instance 的策略(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> <br class="big"> 說到部署策略,除了我們剛介紹的 Recreate 和 RollingUpdate,DevOps 圈內還有其他常見策略,例如 Blue-Green Deployment、Canary Deployment、Shadow Deployment 和 A/B Testing。 不過,Kubernetes 內建的 **Deployment Controller 只支援 Recreate 和 RollingUpdate**,如果要在 K8S 叢集中實現其他策略,通常需要搭配額外的工具或客製化設定,例如 Argo Rollouts、Flagger 或 Istio。 如果你對這些部署策略有興趣,可以參考這兩篇文章: - [〈5 Kubernetes Deployment Strategies: Roll Out Like the Pros|pot〉](https://spot.io/resources/kubernetes-autoscaling/5-kubernetes-deployment-strategies-roll-out-like-the-pros/) - [〈Top 6 Kubernetes Deployment Strategies and How to Choose|codefresh〉](https://codefresh.io/learn/software-deployment/top-6-kubernetes-deployment-strategies-and-how-to-choose/) #### Revision and Rollback 儘管不同的部署策略會影響更新方式,但不管是哪種策略,都需要一個強力的救援機制——**版本管理(Revision)**。這讓我們在更新出問題時,能夠迅速回到穩定版本。 在 K8S 裡,這個版本管控機制不是用 commit,而是 **revision**。每次建立新的 Deployment 或升級現有 Deployment,K8S 都會自動建立一個 revision,當然,也可以手動記錄。正因為有這些歷史紀錄,才能讓我們輕鬆 Rollback。 不過,Rollback 的方式會取決於你的部署策略——是 Recreate 還是 RollingUpdate。 <p class="illustration"> <img src="https://i.imgur.com/iR9zje7.png" width="600px" alt="每次建立新的 Deployment 或升級現有 Deployment 都會自動建立一個 revision"> 每次建立新的 Deployment 或升級現有 Deployment 都會自動建立一個 revision(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> <br class="big"> <br class="big"> 來看個範例,讓 Update 和 Rollback 的變化過程更清楚。 假設目前有一個 Deployment,它會建立一個 ReplicaSet,並創建滿足需求的 pod。當我們升級應用時,K8S 會建立新的 ReplicaSet,並根據 RollingUpdate 的策略逐步取代舊的 pod。 <p class="illustration"> <img src="https://i.imgur.com/8aneM3g.png" width="600px" alt="升級時,K8S 會建立新的 ReplicaSet,並逐步取代舊的"> 升級時,K8S 會建立新的 ReplicaSet,並逐步取代舊的(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> 用 `kubectl get rs` 來看下變化,可以看到舊的 ReplicaSet 變成 0 個 pod,而新的 ReplicaSet 則擁有 5 個 pod。 <p class="illustration"> <img src="https://i.imgur.com/bjr7kKV.png" width="600px" alt="升級時,K8S 會建立新的 ReplicaSet,並逐步取代舊的"> 升級時,K8S 會建立新的 ReplicaSet,並逐步取代舊的(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> 如果要 Rollback,K8S 會刪除新的 ReplicaSet,並重新啟動舊的 ReplicaSet。 <p class="illustration"> <img src="https://i.imgur.com/pkn29pd.png" width="600px" alt="Rollback,K8S 會刪除新的 ReplicaSet,並重新啟動舊的"> Rollback,K8S 會刪除新的 ReplicaSet,並重新啟動舊的(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> 再用 `kubectl get rs` 比較 Rollback 前後的 ReplicaSet 變化: <p class="illustration"> <img src="https://i.imgur.com/DNB8mVJ.png" alt="比較 Rollback 前後的 ReplicaSet 變化"> 比較 Rollback 前後的 ReplicaSet 變化(圖片來源: <a href="https://www.udemy.com/course/learn-kubernetes/">課程截圖</a>) </p> #### 使用 YAML 定義 Update Strategy 這一節我們來看看如何在 Deployment 的 YAML 檔案中設定更新策略,確保應用程式能夠按照預期方式進行升級。 在前面,我們提到從 ReplicaSet 轉換為 Deployment 只需要將 `kind` 欄位從 `ReplicaSet` 改成 `Deployment`。但如果想要控制升級方式,就需要在 `spec` 裡新增 `strategy` 欄位。 在 `strategy` 內,最關鍵的是 **`type`** 屬性,它決定了要使用:`Recreate` 或是 `RollingUpdate`(預設)。 <p class="illustration"> <img src="https://i.imgur.com/aXZBq67.png" width="300px" alt="用 YAML 設定 Update Strategy"> 用 YAML 設定 Update Strategy </p> <br class="big"> 如果最終決定採用 `RollingUpdate` 策略,在 `strategy` 下就會多一個 `rollingUpdate` 子屬性。在這個子屬性中,需要設定前面提過的 `maxSurge` 和 `maxUnavailable`: - **`maxSurge`** 預設值為 **25%**。這個參數表示==允許超過設定 replicas 數量的最大比例==,可以是整數(例如 5),或是所需 pod 總數的百分比(例如 10%,但會四捨五入向上取整)。 舉例來說,若 maxSurge 為 2,表示最多會先建立 2 個新 pod,再刪除舊 pod。假設 replicas 設為 3,那麼升級過程中最多會有 `3 + 2 = 5` 個 pod 同時運行。 - **`maxUnavailable`** 預設值為 **25%**。這個參數定義了==升級過程中最多有多少個 pod 無法提供服務==,同樣可以是整數或百分比。 需要特別注意的是,根據 [K8S 官方文件](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#max-unavailable),==maxSurge 和 maxUnavailable 不能同時為 0==,否則 Deployment 根本沒辦法進行更新。 我在查資料時,發現有些文章提到「當 maxSurge 不為 0 的時候,maxUnavailable 也不能為 0」,這讓我有點困惑,畢竟如果這兩個值都設為 0,不是完全無法更新嗎?後來翻了[K8S 官方文件](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#max-surge)才確定,正確的說法應該是==maxSurge 為 0,則 maxUnavailable 不能為 0==。這點在設定時要特別留意,以免影響部署流程。 #### 使用 kubectl 操作 Update 與 Rollback 首先透過 `kubectl create` 或 `kubectl apply` 來建立 Deployment: <p class="illustration"> <img src="https://i.imgur.com/W3XKxzH.png" width="600px" alt="create deployment"> </p> 在部署的過程中,除了可以使用 `kubectl get all` 來查看目前的資源狀態,還可以用 `kubectl rollout` 來檢查部署或升級的狀況。不過現在資訊可能還不多,但等等進行 Update 和 Rollback 就會有感了~ ```bash= $ kubectl rollout status deployment <deployment> ``` <p class="illustration"> <img src="https://i.imgur.com/MoN9e0F.png" width="600px" alt="kubectl rollout status"> </p> 當 Deployment 第一次建立時,K8S 會自動觸發 rollout,並產生第一個修訂版(Revision 1)。如果想查看歷史記錄,可以使用: ```bash= $ kubectl rollout history ``` <p class="illustration"> <img src="https://i.imgur.com/bnRWOwX.png" width="600px" alt="kubectl rollout history"> </p> 但這樣的紀錄就像沒寫 commit message 的 commit,回想變更內容時,簡直是在考驗記憶力!所以,通常會在執行指令時加上 `--record` ,這樣 K8S 就會自動在歷史紀錄中留下變更的具體指令,未來回顧時會更加方便。: <p class="illustration"> <img src="https://i.imgur.com/nd8YqSq.png" width="600px" alt="--record"> </p> <br class="big"> 部署完成後,就可以嘗試升級。升級方式主要有兩種: 1. **使用指令直接更新狀態** 如果只需要修改部分設定,例如變更副本數或更新映像版本,可以使用: - `kubectl scale` 來調整 replicas 數量 - `kubectl set image` 來更新容器映像版本 2. **修改 YAML 定義文件並套用** 如果變更較大,建議直接修改 YAML 檔: - `kubectl edit deployment <deployment>` 直接修改執行中的 YAML 暫存檔 - `kubectl apply`/`kubectl replace` 重新應用變更後的 YAML 檔案 <br class="big"> 為方便起見,這邊使用 `kubectl set image` 來更新 images,並使用 `kubectl rollout status` 來觀察資源配置的狀況,你可以看到 Pod 服務的變動狀態: <p class="illustration"> <img src="https://i.imgur.com/Asbwykl.png" width="600px" alt="set image"> </p> 如果出現任何問題想暫停更新可以使用 `kubectl rollout pause`: ```bash= # 暫停滾動升級  $ kubectl rollout pause deployment <deployment> # 繼續滾動升級  $ kubectl rollout resume deployment <deployment> ``` 每次更新時都會自動產生新修訂版,因此經過幾次升級後,可以看到歷史紀錄中多了幾筆 revision,若想倒回去的話,則是使用: ```bash= $ kubectl rollout undo deployment <deployment> ``` <p class="illustration"> <img src="https://i.imgur.com/twhRHB9.png" width="600px" alt="rollout undo"> </p> 但它的 undo 跟 git 不太一樣,並不會移除最後的 commit,而是把原本的 revision 2 改成 revision 4,然後移到最上層: <p class="illustration"> <img src="https://i.imgur.com/eGdxnha.png" width="600px" alt="`Recreate` 或是 `RollingUpdate` 的差別"> </p> <br class="big"> 關於 `Recreate` 或是 `RollingUpdate` 兩者的差異可以從 describe 的事件中清楚看到。在使用 `Recreate` 策略時,舊的 ReplicaSet 首先會縮減到 0,然後新的 ReplicaSet 才會擴展到設定的數量;而在 `RollingUpdate` 策略下,舊的 ReplicaSet 和新的 ReplicaSet 會逐漸相互替換。 <p class="illustration"> <img src="https://i.imgur.com/XlYjbIQ.png" alt="`Recreate` 或是 `RollingUpdate` 的差別"> </p> <br class="big"> 最後來張指令表給大家參考! <p class="illustration"> <img src="https://i.imgur.com/5gw4SCD.png" width="600px" alt="指令表"> </p> ### 6-6|練習與測驗 第六章的練習與測驗非常多,依照上一版的經驗,如果每一個測驗都單獨拉成一個章節,會讓整個排版看起來超冗長的,所以我把它們全集結在一起,放在第六章的末尾,方便大家跳過XDDD #### 編碼練習:PODs 1. **Let us start simple! Given a pod-definition.yml file. We are only getting started with it. I have added two root level properties - apiVersion and kind.** Add the missing two properties - metadata and spec ```yaml= apiVersion: kind: ``` 答: ```yaml= apiVersion: kind: metadata: spec: ``` 2. **Let us now populate values for each property. Start with apiVersion.** Update value of apiVersion to v1. Remember to add a space between colon (:) and the value (v1) ```yaml= apiVersion: kind: metadata: spec: ``` 答: ```yaml= apiVersion: v1 kind: metadata: spec: ``` 3. **Update value of kind to Pod.** ```yaml= apiVersion: v1 kind: metadata: spec: ``` 答: ```yaml= apiVersion: v1 kind: Pod metadata: spec: ``` 4. **Let us now get to the metadata section.** Add a property "name" under metadata with value "myapp-pod". Remember to add a space before 'name' to make it a child of metadata ```yaml= apiVersion: v1 kind: Pod metadata: spec: ``` 答: ```yaml= apiVersion: v1 kind: Pod metadata: name: myapp-pod spec: ``` 5. **Let us add some labels to our Pod** Add a property "labels" under metadata with a child property "app" with a value "myapp". Remember to have equal number of spaces before "name" and "labels" so they are siblings ```yaml= apiVersion: v1 kind: Pod metadata: name: myapp-pod spec: ``` 答: ```yaml= apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: myapp spec: ``` 6. **We now need to provide information regarding the docker image we plan to use.** Add a property containers under spec section. Do not add anything under it yet. ```yaml= apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: myapp spec: ``` 答: ```yaml= apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: myapp spec: containers: ``` 7. **Containers is an array/list. So create the first element/item in the array/list and add the following properties to it: name - nginx and image - nginx** ```yaml= apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: myapp spec: containers: ``` 答: ```yaml= apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: myapp spec: containers: - name: nginx image: nginx ``` 8. **Perfect! You have successfully created a Kubernetes-Definition file. Let us try it one more time, this time all on your own!** Create a Kubernetes Pod definition file using values below: - Name: postgres - Labels: tier => db-tier - Container name: postgres - Image: postgres 答: ```yaml= apiVersion: v1 kind: Pod metadata: name: postgres labels: tier: db-tier spec: containers: - name: postgres image: postgres ``` 9. **Postgres Docker image requires an environment variable to be set for password.** Set an environment variable for the docker container. POSTGRES_PASSWORD with a value mysecretpassword. I know we haven't discussed this in the lecture, but it is easy. To pass in an environment variable add a new property 'env' to the container object. It is a sibling of image and name. env is an array/list. So add a new item under it. The item will have properties name and value. name should be the name of the environment variable - POSTGRES_PASSWORD. And value should be the password - mysecretpassword ```yaml= apiVersion: v1 kind: Pod metadata: name: postgres labels: tier: db-tier spec: containers: - name: postgres image: postgres ``` 答: ```yaml= apiVersion: v1 kind: Pod metadata: name: postgres labels: tier: db-tier spec: containers: - name: postgres image: postgres env: - name: POSTGRES_PASSWORD value: mysecretpassword ``` #### Hands-On Labs:PODs 因為這是第一次使用 Lab 所以在操作 Pod 前,先熟悉下環境。 1. **How many nodes are part of the cluster?** - [x] 1 <p class="illustration"> <img src="https://imgur.com/GbXESmt.png" width="600px" alt="How many nodes are part of the cluster?"> </p> 2. **What is the version of Kubernetes running on the nodes ?** - [x] v1.24.1+k3s1 <p class="illustration"> <img src="https://i.imgur.com/k6ggkyu.png" width="600px" alt="What is the version of Kubernetes running on the nodes ?"> </p> 3. **What is the flavor and version of Operating System on which the Kubernetes nodes are running?** - [x] Alpine Linux <p class="illustration"> <img src="https://i.imgur.com/48bh9RF.png" width="600px" alt="What is the flavor and version of Operating System on which the Kubernetes nodes are running?"> </p> <br class="big"> 好了,開始本章重點吧 ~! 1. **How many pods exist on the system? In the current(default) namespace.** - [x] 0 <p class="illustration"> <img src="https://i.imgur.com/Hi71hKq.png" width="600px" alt="How many pods exist on the system? In the current(default) namespace."> </p> 2. **Create a new pod with the nginx image.** <p class="illustration"> <img src="https://i.imgur.com/sBEngaD.png" width="600px" alt="Create a new pod with the nginx image."> </p> 3. **How many pods are created now?** We have created a few more pods. So please check again. - [x] 4 <p class="illustration"> <img src="https://i.imgur.com/iJhiBTB.png" width="600px" alt="How many pods are created now?"> </p> 4. **What is the image used to create the new pods?** You must look at one of the new pods in detail to figure this out. - [x] busybox。喔對,別選自己建的那個,那個 image 絕對是 nginx 呀 XDDD <p class="illustration"> <img src="https://i.imgur.com/MNaQWsS.png" width="600px" alt="What is the image used to create the new pods?"> </p> 5. **Which nodes are these pods placed on?** You must look at all the pods in detail to figure this out. - [x] controlplane <p class="illustration"> <img src="https://i.imgur.com/3NkzFZp.png" width="600px" alt="Which nodes are these pods placed on?"> </p> 6. **How many containers are part of the pod webapp?** We just created a new POD. Ignore the state of the POD for now - [x] 2 <p class="illustration"> <img src="https://i.imgur.com/vmZd2hv.png" width="600px" alt="How many containers are part of the pod webapp?"> </p> 7. **What images are used in the new webapp pod?** You must look at all the pods in detail to figure this out. - [x] agentx & nginx <p class="illustration"> <img src="https://i.imgur.com/Oxzt7Ux.png" width="600px" alt="What images are used in the new webapp pod?"> </p> 8. **What is the state of the container agentx in the pod webapp?** Wait for it to finish the ContainerCreating state - [x] error & waiting <p class="illustration"> <img src="https://i.imgur.com/c087JPu.png" width="600px" alt="What is the state of the container agentx in the pod webapp?"> </p> 9. **Why do you think the container agentx in pod webapp is in error?** Try to figure it out from the events section of the pod. - [x] A docker image with this name doesn't exist on Docker Hub <p class="illustration"> <img src="https://i.imgur.com/0puQHFA.png" width="600px" alt="Why do you think the container agentx in pod webapp is in error?"> </p> 10. **What does the READY column in the output of the kubectl get pods command indicate?** - [x] Rununing / Totoal contaier on pod。回顧看來,在 webapp pod 中要求了 2 個 container,其中一個順利建立,但另一個因為 ErrImagePull 而啟動失敗。 <p class="illustration"> <img src="https://i.imgur.com/jpbWKVE.png" width="600px" alt="What does the READY column in the output of the kubectl get pods command indicate?"> </p> 11. **Delete the webapp Pod.** Once deleted, wait for the pod to fully terminate. <p class="illustration"> <img src="https://i.imgur.com/8w7WwEd.png" width="600px" alt="Delete the webapp Pod."> </p> 12. **Create a new pod with the name redis and with the image redis123.** Use a pod-definition YAML file. And yes the image name is wrong! ```yaml= apiVersion: v1 kind: Pod metadata: name: redis spec: containers: - name: redis image: redis123 ``` <p class="illustration"> <img src="https://i.imgur.com/wVsZ5jQ.png" width="600px" alt="Create a new pod with the name redis and with the image redis123.-1"> </p> <br> 或者不用準備 YAML 檔,直接建立物件。 <p class="illustration"> <img src="https://i.imgur.com/kNR7u1F.png" width="600px" alt="Create a new pod with the name redis and with the image redis123.-2"> </p> 在執行時,可添加 `-o yaml` 將物件輸出成 YAML 檔 submit 出去。此時通常會搭配 `--dry-run=client` 使用,在 submit 時會先拿掉一些 default 參數。恩...如果沒加輸出的 YAML 檔會這麼長: <p class="illustration"> <img src="https://i.imgur.com/O7cuqwG.png" width="600px" alt="Create a new pod with the name redis and with the image redis123.-3"> </p> 13. **Now change the image on this pod to redis.** Once done, the pod should be in a running state. 直接修改 YAML 檔,再使用 apply 套用修改。 ```yaml= apiVersion: v1 kind: Pod metadata: name: redis spec: containers: - name: redis image: redis ``` <p class="illustration"> <img src="https://i.imgur.com/8dksFo8.png" width="600px" alt="Now change the image on this pod to redis.-1"> </p> <br> 或者利用 `kubectl edit` 來修改 pod: <p class="illustration"> <img src="https://i.imgur.com/e37ElOd.png" width="600px" alt="Now change the image on this pod to redis.-2"> </p> #### 編碼練習:ReplicaSet 1. **Let us start with ReplicaSets! Given a blank replicaset-definition.yml file. We are only getting started with it, so let's get it populated. Add all the root level properties to it.** Only add the properties, not any values yet. **答:** ```yaml= apiVersion: kind: metadata: spec: ``` 2. **Let us now add values for ReplicaSet. ReplicaSet is under apiVersion - apps/v1. Update values for apiVersion and kind**  ```yaml= apiVersion: kind: metadata: spec: ``` **答:** ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: spec: ``` 3. **Let us now add values for metadata. Name the ReplicaSet - frontend. And add labels app=>mywebsite and tier=> frontend.** ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: spec: ``` **答:** ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: name: frontend labels: app: mywebsite tier: frontend spec: ``` 4. **Let us now get to the specification. The spec section for ReplicaSet has 3 fields: replicas, template and selector. Simply add these properties. Do not add any values yet.** ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: name: frontend labels: app: mywebsite tier: frontend spec: ``` **答:** ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: name: frontend labels: app: mywebsite tier: frontend spec: replicas: selector: template: ``` 5. **Let us update the number of replicas to 4.** ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: name: frontend labels: app: mywebsite tier: frontend spec: replicas: selector: template: ``` **答:** ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: name: frontend labels: app: mywebsite tier: frontend spec: replicas: 4 selector: template: ``` 6. **The template section expects a Pod definition. Luckily, we have the one we created in the previous set of exercises. Next to the replicaset-definition.yml you will now find the same pod-definition.yml file that you created before.  Let us now copy the contents of the pod-definition.yml file, except for the apiVersion and kind and place it under the template section. Take extra care on moving the contents to the right so it falls under template.** pod-definition.yml: ```yaml= apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: myapp spec: containers: - name: nginx image: nginx ``` ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: name: frontend labels: app: mywebsite tier: frontend spec: replicas: 4 selector: template: ``` **答:** ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: name: frontend labels: app: mywebsite tier: frontend spec: replicas: 4 selector: template: metadata: name: myapp-pod labels: app: myapp spec: containers: - name: nginx image: nginx ``` 7. **Let us now link the pods to the ReplicaSet by updating selectors.  Add a property "matchLabels" under selector and copy the labels defined in the pod-definition under it.** **Note**: This may not work in play-with-k8s as it runs on 1.8 version of kubernetes. ReplicaSets moved to apps/v1 in 1.9 version of Kubernetes. ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: name: frontend labels: app: mywebsite tier: frontend spec: replicas: 4 selector: template: metadata: name: myapp-pod labels: app: myapp spec: containers: - name: nginx image: nginx ``` **答:** ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: name: frontend labels: app: mywebsite tier: frontend spec: replicas: 4 selector: matchLabels: app: myapp template: metadata: name: myapp-pod labels: app: myapp spec: containers: - name: nginx image: nginx ``` #### Hands-On Labs:ReplicaSet 1. **How many PODs exist on the system?** - [x] 0 <p class="illustration"> <img src="https://i.imgur.com/ZqIs642.png" alt="How many PODs exist on the system?" width="600px"> </p> 2. **How many ReplicaSets exist on the system?** - [x] 0 <p class="illustration"> <img src="https://i.imgur.com/bCfZomQ.png" alt="How many ReplicaSets exist on the system?" width="600px"> </p> 3. **How about now? How many ReplicaSets do you see?** - [x] 1 <p class="illustration"> <img src="https://i.imgur.com/lP4bbHw.png" alt="How many ReplicaSets do you see?" width="600px"> </p> 4. **How many PODs are DESIRED in the new-replica-set?** - [x] 4 <p class="illustration"> <img src="https://i.imgur.com/RGEcEPR.png" width="600px" alt="How many PODs are DESIRED in the new-replica-set?"> </p> 5. **What is the image used to create the pods in the new-replica-set?** - [x] busybox777 <p class="illustration"> <img src="https://i.imgur.com/UWBbpbw.png" width="600px" alt="What is the image used to create the pods in the new-replica-set?"> </p> 6. **How many PODs are READY in the new-replica-set?** - [x] 0 <p class="illustration"> <img src="https://i.imgur.com/c7f9MUp.png" width="600px" alt="How many PODs are READY in the new-replica-set?"> </p> 7. **Why do you think the PODs are not ready?** - [x] The image busybox777 doesn't exist <p class="illustration"> <img src="https://i.imgur.com/BkrIZwk.png" width="600px" alt="Why do you think the PODs are not ready?"> </p> 8. **Delete any one of the 4 PODs.** <p class="illustration"> <img src="https://i.imgur.com/FnYwkEt.png" width="600px" alt="Delete any one of the 4 PODs."> </p> 9. **How many PODs exist now?** - [x] 4 <p class="illustration"> <img src="https://i.imgur.com/FnYwkEt.png" width="600px" alt="How many PODs exist now?"> </p> 10. **Why are there still 4 PODs, even after you deleted one?** - [x] replica set ensures that desired number of pods always run 11. **Create a ReplicaSet using the replicaset-definition-1.yaml file located at /root/. There is an issue with the file, so try to fix it.** ```yaml= apiVersion: v1 kind: ReplicaSet metadata: name: replicaset-1 spec: replicas: 2 selector: matchLabels: tier: frontend template: metadata: labels: tier: frontend spec: containers: - name: nginx image: nginx ``` - [x] 可以立刻發現是 `apiVersion` 填錯了,把它由 `v1` 改成 `apps/v1` 即可。 ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: name: replicaset-1 spec: replicas: 2 selector: matchLabels: tier: frontend template: metadata: labels: tier: frontend spec: containers: - name: nginx image: nginx ``` <p class="illustration"> <img src="https://i.imgur.com/ra9vNby.png" width="600px" alt="Create a ReplicaSet using the replicaset-definition-1.yaml file located at /root/."> </p> 是說如果沒有注意到的話,直接 `create` 也會跑錯誤訊息給你: <p class="illustration"> <img src="https://i.imgur.com/toR1JHT.png" width="600px" alt="Create replicaset-definition-1.yaml error msg"> </p> 如果有注意到,但不確定應該換成哪個版號,則可以透過 `explain` 來取得相關資訊: ```bash= $ kubectl explain replicaset ``` <p class="illustration"> <img src="https://i.imgur.com/8eTSMya.png" width="600px" alt="kubectl explain"> </p> 12. **Fix the issue in the replicaset-definition-2.yaml file and create a ReplicaSet using it. This file is located at /root/.** ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: name: replicaset-2 spec: replicas: 2 selector: matchLabels: tier: frontend template: metadata: labels: tier: nginx spec: containers: - name: nginx image: nginx ``` - [x] selector 的 label 跟 pod 的 label 無法相匹配。有兩個改法,一是改 selector 的 label,讓它與 pod 的 label 一致;另一種改法就是反過來了。 ```yaml= apiVersion: apps/v1 kind: ReplicaSet metadata: name: replicaset-2 spec: replicas: 2 selector: matchLabels: tier: nginx template: metadata: labels: tier: nginx spec: containers: - name: nginx image: nginx ``` <p class="illustration"> <img src="https://i.imgur.com/opF7X9q.png" width="600px" alt="Fix the issue in the replicaset-definition-2.yaml file and create a ReplicaSet using it."> </p> 一樣直接跑 `create` 也會丟錯誤訊息給你: <p class="illustration"> <img src="https://i.imgur.com/MIRyoA1.png" width="600px" alt="Create replicaset-definition-2.yaml error msg"> </p> 13. **Delete the two newly created ReplicaSets - replicaset-1 and replicaset-2** <p class="illustration"> <img src="https://i.imgur.com/5uPpcwX.png" width="600px" alt="Delete the two newly created ReplicaSets - replicaset-1 and replicaset-2"> </p> 對了,如果嫌 replicaSet 太長,可以直接使用縮寫 rs ```yaml= $ kubectl get rs ``` 14. **Fix the original replica set new-replica-set to use the correct busybox image.** Either delete and recreate the ReplicaSet or Update the existing ReplicaSet and then delete all PODs, so new ones with the correct image will be created. **答:** ```yaml= $ kubectl edit replicaset new-replica-set ``` 將 container 的 image 修改由 busybox777 成 busybox <p class="illustration"> <img src="https://i.imgur.com/07HAS2p.png" width="600px" alt="kubectl edit replicaset new-replica-set"> </p> 然後刪掉就有的 pod,讓 replica set 重起 pod 以套用新的修改。 <p class="illustration"> <img src="https://i.imgur.com/OmCCfHH.png" width="600px" alt="kubectl edit replicaset new-replica-set"> </p> 15. **Scale the ReplicaSet to 5 PODs.** Use kubectl scale command or edit the replicaset using kubectl edit replicaset. <p class="illustration"> <img src="https://i.imgur.com/MrxGRZl.png" width="600px" alt="Scale the ReplicaSet to 5 PODs."> </p> 16. **Now scale the ReplicaSet down to 2 PODs.** Use the kubectl scale command or edit the replicaset using kubectl edit replicaset. <p class="illustration"> <img src="https://i.imgur.com/3NjCUdF.png" width="600px" alt="Now scale the ReplicaSet down to 2 PODs."> </p> #### 編碼練習:Deployments 1. **Let us start with Deployments! Given a deployment-definition.yml file. We are only getting started with it, so let's get it populated. Add all the root level properties to it. Note: Only add the properties, not any values yet.** **答:** ```yaml= apiVersion: kind: metadata: spec: ``` 2. **Let us now add values for Deployment. Deployment is under apiVersion apps/v1. Update values for apiVersion and kind.** ```yaml= apiVersion: kind: metadata: spec: ``` **答:** ```yaml= apiVersion: apps/v1 kind: Deployment metadata: spec: ``` 3. **Let us now add values for metadata. Name the Deployment frontend. And add labels app=>mywebsite and tier=> frontend.** ```yaml= apiVersion: apps/v1 kind: Deployment metadata: spec: ``` **答:** ```yaml= apiVersion: apps/v1 kind: Deployment metadata: name: frontend labels: app: mywebsite tier: frontend spec: ``` 4. **Let us now get to the specification. The spec section for Deployment has 3 fields: replicas, template and selector. Simply add these properties. Do not add any values.** ```yaml= apiVersion: apps/v1 kind: Deployment metadata: name: frontend labels: app: mywebsite tier: frontend spec: ``` **答:** ```yaml= apiVersion: apps/v1 kind: Deployment metadata: name: frontend labels: app: mywebsite tier: frontend spec: replicas: selector: template: ``` 5. **Let us update the number of replicas to 4.** ```yaml= apiVersion: apps/v1 kind: Deployment metadata: name: frontend labels: app: mywebsite tier: frontend spec: replicas: selector: template: ``` **答:** ```yaml= apiVersion: apps/v1 kind: Deployment metadata: name: frontend labels: app: mywebsite tier: frontend spec: replicas: 4 selector: template: ``` 6. **The template section expects a Pod definition. Luckily, we have the one we created in the previous set of exercises. Next to the deployment-definition.yml you will now find the same pod-definition.yml file that you created before. Let us now copy the contents of the pod-definition.yml file, except for the apiVersion and kind and place it under the template section. Take extra care on moving the contents to the right so it falls under template** pod-definition.yml: ```yaml= apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: myapp spec: containers: - name: nginx image: nginx ``` ```yaml= apiVersion: apps/v1 kind: Deployment metadata: name: frontend labels: app: mywebsite tier: frontend spec: replicas: 4 selector: template: ``` **答:** ```yaml= apiVersion: apps/v1 kind: Deployment metadata: name: frontend labels: app: mywebsite tier: frontend spec: replicas: 4 selector: template: metadata: name: myapp-pod labels: app: myapp spec: containers: - name: nginx image: nginx ``` 7. **Let us now link the pods to the Deployment by updating selectors. Add a property "matchLabels" under selector and copy the labels defined in the pod-definition under it.** ```yaml= apiVersion: apps/v1 kind: Deployment metadata: name: frontend labels: app: mywebsite tier: frontend spec: replicas: 4 selector: template: metadata: name: myapp-pod labels: app: myapp spec: containers: - name: nginx image: nginx ``` **答:** ```yaml= apiVersion: apps/v1 kind: Deployment metadata: name: frontend labels: app: mywebsite tier: frontend spec: replicas: 4 selector: matchLabels: app: myapp template: metadata: name: myapp-pod labels: app: myapp spec: containers: - name: nginx image: nginx ``` #### Hands-On Labs:Deployments 1. **How many PODs exist on the system?** - [x] 0 <p class="illustration"> <img src="https://i.imgur.com/zYtD0X8.png" width="600px" alt="How many PODs exist on the system?"> </p> 2. **How many ReplicaSets exist on the system?** - [x] 0 <p class="illustration"> <img src="https://i.imgur.com/X52bNJt.png" width="600px" alt="How many ReplicaSets exist on the system?"> </p> 3. **How many Deployments exist on the system?** - [x] 0 <p class="illustration"> <img src="https://i.imgur.com/5S4UbXN.png" width="600px" alt="How many Deployments exist on the system?"> </p> 4. **How many Deployments exist on the system now?** - [x] 1 <p class="illustration"> <img src="https://i.imgur.com/azDWvvM.png" width="600px" alt="How many Deployments exist on the system now?"> </p> 5. **How many ReplicaSets exist on the system now?** - [x] 1 <p class="illustration"> <img src="https://i.imgur.com/ArkNJn6.png" width="600px" alt="How many ReplicaSets exist on the system now?"> </p> 6. **How many PODs exist on the system now?** - [x] 4 <p class="illustration"> <img src="https://i.imgur.com/YJ7W6Sq.png" width="600px" alt="How many PODs exist on the system now?"> </p> 7. **Out of all the existing PODs, how many are ready?** - [x] 0 <p class="illustration"> <img src="https://i.imgur.com/D0Pxcaf.png" width="600px" alt="Out of all the existing PODs, how many are ready?"> </p> 8. **What is the image used to create the pods in the new deployment?** - [x] busybox888 <p class="illustration"> <img src="https://i.imgur.com/Ik4w03z.png" width="600px" alt="What is the image used to create the pods in the new deployment?"> </p> 9. **Why do you think the deployment is not ready?** - [x] the image busybox888 doesn't exist <p class="illustration"> <img src="https://i.imgur.com/r1DQHxm.png" width="600px" alt="Why do you think the deployment is not ready?"> </p> 10. **Create a new Deployment using the deployment-definition-1.yaml file located at /root/. There is an issue with the file, so try to fix it.** ```yaml= apiVersion: apps/v1 kind: deployment metadata: name: deployment-1 spec: replicas: 2 selector: matchLabels: name: busybox-pod template: metadata: labels: name: busybox-pod spec: containers: - name: busybox-container image: busybox888 command: - sh - "-c" - echo Hello Kubernetes! && sleep 3600 ``` **答:** 直接跑 create 可以看到錯誤訊息,看起來如果不是 `apiVersion` 有誤,就是 `kind` 出差錯了。 <p class="illustration"> <img src="https://i.imgur.com/dvkwjmd.png" width="600px" alt="Create a new Deployment using the deployment-definition-1.yaml"> </p> 打開 YAML 可以看到是 `kind` 寫錯了,把它改成 `deployment` ```yaml= apiVersion: apps/v1 kind: Deployment metadata: name: deployment-1 spec: replicas: 2 selector: matchLabels: name: busybox-pod template: metadata: labels: name: busybox-pod spec: containers: - name: busybox-container image: busybox888 command: - sh - "-c" - echo Hello Kubernetes! && sleep 3600 ``` 再重新執行一次 `create` ,就能看到 deplyment 被執行了: <p class="illustration"> <img src="https://i.imgur.com/lo2KudY.png" width="600px" alt="create"> </p> 11. **Create a new Deployment with the below attributes using your own deployment definition file.** - Name: httpd-frontend; - Replicas: 3; - Image: httpd:2.4-alpine **答**: 按照上面的條件寫一份 YAML,因為沒給 label,所以我直接把 name 當 label 用了 XDDD ```yaml= apiVersion: apps/v1 kind: Deployment metadata: name: httpd-frontend spec: replicas: 3 selector: matchLabels: name: httpd-frontend template: metadata: labels: name: httpd-frontend spec: containers: - name: httpd-frontend image: httpd:2.4-alpine ``` 然後 `create` 就行了,你要 `apply` 也行: <p class="illustration"> <img src="https://i.imgur.com/2Fil98M.png" width="600px" alt="Create a new Deployment with the below attributes using your own deployment definition file."> </p> 解答則是沒寫 YAML 全用指令代入: <p class="illustration"> <img src="https://i.imgur.com/XvyWTMM.png" width="600px" alt="Create a new Deployment with the below attributes using your own deployment definition file."> </p> #### Hands-On Labs:Update and Rollback 1. **We have deployed a simple web application. Inspect the PODs and the Services. Wait for the application to fully deploy and view the application using the link called Webapp Portal above your terminal.** 它只要我們檢查,也沒要我們做啥,所以我就用了 `get all`: <p class="illustration"> <img src="https://i.imgur.com/g1bTOC8.png" width="600px" alt="We have deployed a simple web application."> </p> 順便依照要求看看 Portal 長怎樣: <p class="illustration"> <img src="https://i.imgur.com/rFpoqll.png" width="600px" alt="Webapp Portal"> </p> 2. **What is the current color of the web application? Access the Webapp Portal.** - [x] blue 3. **Run the script named curl-test.sh to send multiple requests to test the web application. Take a note of the output. Execute the script at /root/curl-test.sh.** 就照要求跑個 script,看看結果: <p class="illustration"> <img src="https://i.imgur.com/LEuHNX1.png" width="400px" alt="Run the script named curl-test.sh"> </p> 4. **Inspect the deployment and identify the number of PODs deployed by it.** - [x] 4 <p class="illustration"> <img src="https://i.imgur.com/mHW3cni.png" width="600px" alt="Inspect the deployment and identify the number of PODs deployed by it"> </p> 5. **What container image is used to deploy the applications?** - [x] kodekloud/webapp-color:v1 <p class="illustration"> <img src="https://i.imgur.com/gmBXiMg.png" width="600px" alt="What container image is used to deploy the applications"> </p> 6. **Inspect the deployment and identify the current strategy** - [x] RollingUpdate <p class="illustration"> <img src="https://i.imgur.com/04z3cwf.png" width="600px" alt="Inspect the deployment and identify the current strategy"> </p> 7. **If you were to upgrade the application now what would happen?** - [x] Pods are upgraded few at a time 8. **Let us try that. Upgrade the application by setting the image on the deployment to kodekloud/webapp-color:v2. Do not delete and re-create the deployment. Only set the new image name for the existing deployment.** 透過 `edit` 直接更改 image: <p class="illustration"> <img src="https://i.imgur.com/uKkjYdG.png" width="600px" alt="Upgrade the application by setting the image"> </p> 改完後可以觀察下升級結果,不過我指令下晚了已經更新完了 XDDD <p class="illustration"> <img src="https://i.imgur.com/jV02cgR.png" width="600px" alt="Upgrade the application by setting the image"> </p> 不過可以直接從 Webapp Portal 觀看更新結果,他的底色已經從藍色變成綠色了: <p class="illustration"> <img src="https://i.imgur.com/yweYnrN.png" width="600px" alt="Webapp Portal"> </p> 9. **Run the script curl-test.sh again. Notice the requests now hit both the old and newer versions. However none of them fail. Execute the script at /root/curl-test.sh.** <p class="illustration"> <img src="https://i.imgur.com/LV1u61D.png" width="450px" alt="Run the script curl-test.sh again"> </p> 10. **Up to how many PODs can be down for upgrade at a time. Consider the current strategy settings and number of PODs - 4.** - [x] 1,這邊 max unavailable 為 25%,$4*25\%=1$ <p class="illustration"> <img src="https://i.imgur.com/604lbAO.png" width="600px" alt="Run the script curl-test.sh again"> </p> 11. **Change the deployment strategy to Recreate. Delete and re-create the deployment if necessary. Only update the strategy type for the existing deployment.** 因為我沒找到現成的 YAML 檔,所以先用 get 匯出 YAML 檔,再刪除 deployment 後重建。 <p class="illustration"> <img src="https://i.imgur.com/vmb9HRJ.png" width="600px" alt="Run the script curl-test.sh again"> </p> 在改 YAML 檔時,除了把 type 改成 Recreate 外,別忘了把 rollingUpdate 給移除: <p class="illustration"> <img src="https://i.imgur.com/4nRGXVK.png" width="600px" alt="Run the script curl-test.sh again"> </p> 12. **Upgrade the application by setting the image on the deployment to kodekloud/webapp-color:v3. Do not delete and re-create the deployment. Only set the new image name for the existing deployment.** 這邊其實跟前面的操作相仿: <p class="illustration"> <img src="https://i.imgur.com/tn7bMcE.png" width="600px" alt="Upgrade the application"> </p> 13. **Run the script curl-test.sh again. Notice the failures. Wait for the new application to be ready. Notice that the requests now do not hit both the versions. Execute the script at /root/curl-test.sh.** 截錯圖了 XDD 不過我有記得截圖 Portal,script 表示他是紅色的,但我怎麼看都是橘色的? <p class="illustration"> <img src="https://i.imgur.com/JrIGWk5.png" width="600px" alt="Run the script curl-test.sh again."> </p> ## 其他連結 1. 課程內容:[Kubernetes for the Absolute Beginners - Hands-on](https://www.udemy.com/course/learn-kubernetes/) 2. 目錄: [【K8S Beginners 筆記】目錄](https://hackmd.io/@CynthiaChuang/Kubernetes-for-the-Absolute-Beginners-Contents) ## 參考資料 1. CharyGao (2021-07-30)。[XML 與 JSON 優劣對比,TOML、CSON、YAML](https://www.twblogs.net/a/610398bf9eb5e3210c708c18)。檢自 台部落 (2022-11-11)。 2. 天府云创 (2021-04-13)。[Kubernetes之YAML语法](https://blog.csdn.net/enweitech/article/details/115674083)。檢自 天府云创的博客|CSDN (2022-11-11)。 3. FoxuTech (2022-04-13)。[Kubectl apply vs Kubectl create.](https://foxutech.medium.com/kubectl-apply-vs-kubectl-create-8d946ed603a1) 。檢自 Medium (2022-11-16)。 4. Will 保哥 (2022-10-24)。[Kubernetes 101:釐清 kubectl create 與 kubectl apply 的差異](https://blog.miniasp.com/post/2022/10/24/Kubernetes-101-diff-between-kubectl-create-and-kubectl-apply) 。檢自 The Will Will Web (2022-11-17)。 5. Bob Reselman (2021-07-27)。[Kubectl apply vs. create: What's the difference?](https://www.theserverside.com/answer/Kubectl-apply-vs-create-Whats-the-difference)。檢自 TheServerSide (2022-11-16)。 6. 大鹏blog (2020-02-19)。[kubernetes: kubectl create与kubectl apply的区别](https://blog.csdn.net/textdemo123/article/details/104400985)。檢自 大鹏blog的博客|CSDN (2022-11-16)。 7. (2022-11-21)。[ReplicationController 和 ReplicaSet](https://doc.cncf.vip/kubernetes-handbook/gai-nian-yu-yuan-li/controllers/replicaset)。檢自 kubernetes中文手册 (2022-11-21)。 8. godleon (2022-08-31)。[[Kubernetes] Deployment Overview](https://godleon.github.io/blog/Kubernetes/k8s-Deployment-Overview/)。檢自 小信豬的原始部落 (2022-11-23)。 9. Andy Chen (2020-02-12)。[Kubernetes 那些事 — Deployment 與 ReplicaSet(一)](https://medium.com/andy-blog/kubernetes-那些事-deployment-與-replicaset-一-406234a63d43) 。檢自 Andy的技術分享blog|Medium (2022-11-23)。 10. Akhil Chawla (2021-09-02)。[Deployment Strategies In Kubernetes](https://auth0.com/blog/deployment-strategies-in-kubernetes/)。檢自 auth0 (2022-11-24)。 11. [Top 6 Kubernetes Deployment Strategies and How to Choose](https://codefresh.io/learn/software-deployment/top-6-kubernetes-deployment-strategies-and-how-to-choose/)。檢自 codefresh (2022-11-24)。 12. [5 Kubernetes Deployment Strategies: Roll Out Like the Pros](https://spot.io/resources/kubernetes-autoscaling/5-kubernetes-deployment-strategies-roll-out-like-the-pros/)。檢自 Spot by NetApp (2022-11-24)。 13. Kevin Yang (2021-07-16)。[[K8s] 開始學習 Kubernetes - Deployment Strategies](https://blog.kevinyang.net/2021/07/16/k8s-note-002/)。檢自 CK's Notepad (2022-11-24)。 14. sixinshuier (2020-08-26)。[文kubectl create / replace 与kubectl apply 的区别章文稱](https://www.cnblogs.com/shix0909/p/13566148.html)。檢自 sixinshuier|博客园 (2022-11-25)。 ## 更新紀錄 :::spoiler 最後更新日期:2025-02-07 - 2025-02-07 更新:重構筆記內文、並重新製圖 - 2022-12-30 發布 - 2022-11-25 完稿 - 2022-11-04 起稿 ::: {%hackmd @CynthiaChuang/Github-Page-Footer %}