---
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 %}