---
# System prepended metadata

title: Docker & Kubernetes（K8s）
tags: [技術筆記]

---

# Docker & Kubernetes（K8s）

## Docker簡介
![](https：//hackmd.io/_uploads/rkG87W1Jp.png)
Docker是一種容器的實現，是一種容器化的解決方案與平台，用於建構、發佈和執行應用程式和服務。
它使用容器技術，允許您將應用程式及其相關依賴項打包成一個獨立且可攜帶的容器。
這個容器包括應用程式的程式碼、執行時環境、系統工具和函式庫，確保應用程式在不同的環境中能夠一致運行，
不需要另外安裝作業系統，建立容器所需要的硬碟容量可以大幅降低，且啟動速度可以更快。

Docker是一個開發(developing)、傳遞(shipping)及執行(running)應用程式的**開放平台**，可以讓你將應用程式及資訊基礎設施分離，所以你可以快速的部署及執行你所開發的軟體。應用Dockers你可以如同管理你的應用程式來管理你的資訊基礎設施，藉由Docker提供的各種方法，你可以快速地進行應用程式的傳遞、測試及部署，並顯著地降低程式開發與營運環境執行的時間遞延。

Docker是一個Client-Server架構的應用程式，在一個Docker執行環境中，包括了Docker用戶端程式、和在背景執行（Daemon）的Docker伺服器（也稱為Docker Engine），另外還有將Container封裝後的Docker映象檔，用來儲存映象檔的Registry服務。官方提供的映象檔Registry服務就稱為Docker Hub，這是類似Github程式碼Repository儲存服務的映象檔Repository儲存服務。

**Docker建議一個Container只放一個程式，再疊起來提供一個完整的服務。**

#### Docker主要分為三大重點：

1. Image 映像檔
**Docker映像檔(Image)是唯讀(read-only)的一個模板**
是一個輕量級、獨立且可攜帶的軟體封裝，其中包含了一個應用程式及其相關依賴項的所有資源，
包括程式碼、執行環境、系統工具、函式庫和設定。
Image可以包含一個完整的MySql、Python或Ubuntu 作業系統等...，所以Image可以包含很多應用程式及資源。
用物件導向的方式思考就是Image是一個類別，而容器是根據Image所做的實體。

2. Container 容器
容器是用映像檔建立出來的執行實例，它可以被啟動、開始、停止、刪除，每個容器都是相互隔離、保證安全的平台，所以一定要先有Image映像檔才有Container容器存在的意義
Container容器在啟動的時候會建立一層可以被修改的可寫層作為最上層
3. Repository 倉庫
儲存映像檔(Image)的一個場所，就有點像GitHub，可以把自己設計的映像檔(Image)Push上倉庫(Repository)，也可以Pull下映像檔(Image)。
最大的公開倉庫註冊伺服器是**Docker Hub**，上面有許多人設計好的映像檔(Image)可提供做使用

把Image映像檔想成安裝軟體
把Container容器想成一台電腦
把Repository倉庫想成一個安裝軟體平台(類似Steam)
情境舉例：從**Steam**(Repository)下載**遊戲**(Image)，並且把**遊戲**(Image)安裝到**電腦**(Container)
### Docker解決了什麼問題?
1. 簡化部署流程
2. 共用部署包
3. 跨平台部署

### 相關指令
Docker 指令的寫法，大致上可歸納成：
`docker 對象 動作 各種參數`
### Image
```
docker pull "image-name" => 從 Docker Hub 下載一個 Image
docker push "image-name" => 將一個 Image 推送到 Docker Hub
docker build -t "image-name：tag path-to-dockerfile" => 建立一個自己設計的Image，並且自己tag(標籤)
docker image ls => 指令僅會列出已經被標記為使用中的鏡像
docker image ls -a => 這個指令會列出所有本地的鏡像，包括被標記為未使用的和中間層鏡像
docker rm "image-name or image-id" => 刪除一個 Image
docker image inspect "image-name or image-id" => 顯示 Image 的詳細資訊
docker search "image-name" => 搜索 Docker Hub 上的 Image
```
### Container容器相關指令
```
docker container run "image-name" => 建立和啟動新的容器實例(通常用在第一次啟動)
docker container start MyDockerTutorial => 啟動容器
docker container stop "container-name or container-id" => 停止容器
docker container restart "container-name or container-id" => 重新啟動容器
docker container ps => 列出正在執行的容器
docker container ps -a => 列出所有容器包括已停止的
docker container inspect "container-name or container-id" => 顯示容器的詳細資訊
docker container exec -it "container-name or container-id" bash => 進入容器的 Shell
docker container rm "container-name or container-id" => 刪除容器
docker container rm "container-name or container-id" -f => 強制刪除容器
docker container exec "container-name or container-id command" => 在容器內執行命令
docker container commit {容器 id | 容器名稱} {映像檔名稱} => 以容器建立映像檔
```
### Volume 掛載(永久資料保存)
掛載能夠將主機的檔案空間，直通容器之內，方便我們管理當中的資料。例如：存放log，避免容器在關閉後log一起被刪除。
在建立容器的指令中，可添加掛載相關的參數，指令寫法為：
```
docker container run -v {主機絕對路徑}：{容器路徑} {其他參數}
docker container run -d -v "C：\host-test-folder"："/container-test-folder" --name MyDockerTutorial -p 80：80 docker/getting-started：latest
```
此處會將主機的 C：\host-test-folder 資料夾，掛載到容器的 container-test-folder 資料夾。
若雙方資料夾不存在，則 Docker 將自動建立。要注意的是，若容器在該路徑下，已存在相同名稱的資料夾，則會以主機資料夾覆蓋。

### 如何建立IMAGE?
撰寫Docker File後Build成Image後，再容器實例化
### Dockerfile
映像檔是按照層次結構來建構，每一層都是基於上一層。
所以要指定一個基礎映像檔，然後在這個基礎上增加應用程序。
例如：從作業系統Linux映像檔開始

以我的size Project:GoodBye Letter為例
```dockerfile=
# 使用官方的 OpenJDK 11 作為基礎映像
FROM openjdk:11-jdk-slim

# 設定應用程式的工作目錄
WORKDIR /GoodByeLetter-app

# 將 Maven/Gradle 打包的 Jar 文件複製到容器中
COPY target/GoodByeLetter-1.0.0.jar /GoodByeLetter-app/GoodByeLetter-1.0.0.jar

# 暴露應用程式的埠號（假設應用程式運行在 8080 埠）
EXPOSE 8080

# 執行應用程式
CMD ["java", "-jar", "/GoodByeLetter-app/GoodByeLetter-1.0.0.jar"]

```
相對更完整的範例
`FROM`FROM：用來指定當前 Docker 映像所基於的基礎映像。(通常是應用程式的執行環境，例如:Node)

`ADD` 複製檔案至映像檔內，與COPY指令不同處有二個，一個是來源可以是URL（但不建議），第二個是若來源是TAR檔會自動解壓縮。

`RUN`用來執行你要在映像檔內的任何指令。

`ENV`設定環境變數，這個值也可於docker run時將參數值帶入，進行替換。

`EXPOSE`設定要開放的PORT，但要docker run時也要下expose參數，例如 EXPOSE 8080。

`ENTRYPOINT` 啟動Container時要執行的指令，例如`ENTRYPOINT ["docker-entrypoint.sh"]`，此指令一定會被執行。

```dockerfile=
FROM python:3.6.8-alpine3.7                  # 基底映像檔是 python3.6.8 
MAINTAINER Jennifer <cutejaneii@hotmail.com> # 維護者資訊
COPY . /app                                  # 複製目前資料夾的檔案 至 映像檔內的/app資料夾
WORKDIR /app                                 # 切換工作目錄至 /app
RUN pip3 install -r requirements.txt         # pip3 install requirements.txt裡的套件
CMD ["python3", "main.py"]                   # 執行指令: python3 main.py
```
### 如何將自己寫好的程式產出，打包成IMAGE呢?
Dockerfile是一個文本文件，用來告訴DOCKER如何建構IMAGE映像檔
1. 撰寫 Dockerfile 製作IMAGE映像檔
2. 使用 Dockerfile 建立IMAGE映像檔
3. 使用IMAGE映像檔來建立容器
### Docker Compose 完成多容器部署(**模板化的容器部署**)
Docker Compose是Docker官方開源的的項目，用來定義跟運行多個Docker容器應用程序的工具
例如：
1. 前端
2. 後端
3. 資料庫
4. Redis
5. 負載平衡Nginx
上面幾個項目組成了一個完整應用程式的運行，也各別是一個容器化的應用程式，就可以透過Docker Compose組合在一起。
要使用yaml語法，docker-Compose.yaml
### Docker Swarm
Docker開發出的類似K8s工具，但業界使用率太低，所以學K8s就好。

## Kubernetes（K8s）簡介
Kubernetes在希臘文中原本的意思為「掌舵者」，K8s負責管理容器的部署與資源的分配管理。
那麼Docker跟Kubernetes差異是什麼?
Docker是**容器引擎**，負責容器的創建與部署。
Kubernetes是**容器管理平台**，透過撰寫YMAL檔案，可以**模板化的容器部署**，且有**自動化資源部署**與**多台主機部署**的功能。
### K8s可分成三大階段
#### 第一階段：Docker/Podman部署包建立
在K8s的世界中，所有要部署的東西都是一個「容器化後的應用程式」，也就是Image，有了Image就等於有了容器化過後的應用程式，也就是一個部署包，
這個部署包將成為K8s部署計畫中的一個重要來源。

#### 第二階段：K8s部署計畫撰寫
規劃計算資源(**computing**)，對應到K8s中最常見的叫做「**Deployment**」的部署模板。
而在computing中，有個K8s最強大的功能「**Scaling**」，對應到K8s中最常見的叫做「**HPA(Horizontal Path Autoscaling)**」的部署模板。
它可以針對流量的變化對運算資源進行動態增減。
而Scaling是K8s強項之一，Docker/Podman是無法針對動態的流量進行動態增減的。
並且K8s在Scaling上，有許多不同模式，提供我們去做動態的運算資源調整及設定。
在運算資源之外，我們也會利用K8s去掌管**Network**，也就是網路部分的控制。對應到K8s中是一個叫做「**Service**」的部署模板。
儲存資源Storage的部分，則對應到PVC(Persistent Volume Claim)的部署模板。

#### 第三階段：Local V.S. AWS/GCP/Azure資源部署
當主要部署計畫都寫好後，就要進行「實際的資源部署(actual deploy & actual resources)。
例如啟動虛擬機運算資源、設定網路、管理進出權限及建立儲存資源、永久化保存資料...等等。
在各種雲端商中，**GCP有GKE(Google Kubernetes Engine**)、
**AWS有EKS(Elastic Kubernetes Service)**，或**AWS自產的ECS(Elastic Container Service)**、
**Azure則是AKS(Azure Kubernetes Service)**
### K8s解決了什麼問題?
1. 動態的資源增減
K8s可以根據當前流量，動態增加或減少運算資源。
2. Self-Healing自我治療
K8s會自動幫我們把壞掉的應用程式重新啟動(Rebuilding)。
3. Zero Downtime Rollimg Update
沒有任何關機時間的動態版本更新。
例如同時有三個V1版本app在線上提供支援，可以根據我們的設定，陸續將V1 app升級轉換到V2。
4. Zero Down time Rollback
沒有中斷時間的版本復原。
### K8s基底結構：Master Node & Worker Node
#### Master Node
K8s之中第一層是一個叫做Cluster的東西，這是一個概念，它將包含所有這個K8s運作，會運用到的所有資源。
Master Node是對整個Cluster進行中央調控的地方，其中最重要的元件是API Server。
API Server是Master Node對外界開放的進入點，無論用cmd line or YAML去呼叫，都是透過API Server進行溝通，Work Node也是。

而API Server在收到相關請求後，會把該請求與Master Node中的ClusterStateStore這個狀態管理元件互動，並且把這所有的請求留下來。
或是API Server會把請求送到Cluster Controller Manager，這是一個Controller，會依據不同請求給予相對應的Controller進行調控。
Cluster Controller Manager也會跟Cluster State Store溝通，把需要的狀態保留下來。

Master Node中還有一個叫Scheduler的東西，目的是根據想要達到的目標狀態，去決定要把當下哪一個Pod，部署到哪一個Worker Noder中(K8s中最小的單位)。

通常在一個K8s的Cluster中，不會只有一個Master Node，因為當這台Node壞掉或消失，所有狀態都會不見，為了預防此意外發生，
還有另一個叫Master Replicate Node的東西，簡單來說就是Master Node的備份，用來當Master Node壞掉後的補救。
#### Worker Node
當Master Node收到相關請求後，其中一些動作就要跟Worker Node交流。
Worker Node中第一個重要的要件是**Kubelet Service**，它會處理所有來自Master Node的相關請求，來對當下的Worker Node進行內部設定。
**Kube Proxy Servicew**負責掌管哪些網路請求可以進/出當下的Worker Node。
另外還有個重要的環境元件**Container Runtime**。由於在K8s中，所有的部署專案都必須被容器化，變成一個Container Image，
為了要達成這個目的，就要有一個Container Runtime。市面上最常見的有Docker Runtime、Podman Runtime。

透過以上三大要件，Worker Node就可以把Master Node的請求進行完整的處理，並且將程式部署到自身上面。
而當Worker Node完成後，也會進行一個回報，告訴Master Node可以把那邊的狀態更新了。
與Master Node同理，Worker Node不會只有一台，而是有多台的。
這也是使用K8s非常好的功用，在Scaling的部分彈性變得很大，可以在不需要的時候快速縮減運算資源。
### 十大核心模板
1. 運算模板 Pod
2. 運算模板 ReplicatSet
3. 運算模板 Deployment
4. 網路模板 Service
5. 監控模板 Probe
6. 運算模板 Rolling Update
7. 儲存模板 PV，PVC，StorageClass
8. 資源模板  Namespace
9. 進階網路模板 Ingress
10. 進階運算模板 HPA
### 什麼是K8s Pod?
在一個K8s的Cluster中，當談及運算部署時，它的最小基本單位就是一個Pod。
一個Pod之中涵蓋Container的概念，而每個Containe裡面則會連結一個Image，此Image將依據專案進行相關的應用程式部署。
### K8s與Worker Node的關係
關於K8s的觀念中，有一個非常大的方向：「要不斷去分辨哪些資源屬於K8s，哪些屬於資源的提供者(Resource Provider)。」
### K8s運算架構：Deployments - ReplicaSet - Pods -
#### K8s Pod 使用時機
假設一個Cluster部署Pod都是用手動部署，部署三個Pod，每個Pod中的Container的Image版本都是V1。
當部署完成後，這個Cluster中的狀態就是有三個Pod正在運行，如果一切正常就會一直運行下去。
但假設出了一個意外，某一個Pod壞了，沒有其他機制修復，Cluster中的Pod數量就會從三個變成兩個，所以有K8s ReplicaSet。
### K8s ReplicaSet 使用時機
在一個ReplicaSet的資源中，包含對Pod部署的所有設定，其中最重要的就是使用的Image。
例如說這邊一樣使用V1這個版本的Image，在知道要部署的Image為何後，ReplicaSet一個最重要的設定，就是可以設定要維持幾個Pod持續運行。
設定完成部署ReplicaSet，經過一段時間後，它就會自動地幫你生出三個擁有V1 Image的Pod出來。

且ReplicaSet的好處是什麼? 當某個Pod壞掉被關閉消失時，ReplicaSet會馬上偵查到並自動啟用全新的Pod，讓Cluster永遠擁有三個Pod保持運行。
這就是ReplicaSet的主要功用。
#### K8s ReplicaSet 使用限制
ReplicaSet也有個問題，例如說當Image版本更新了變成V2。
當ReplicaSet也改成V2並部署後，原本在運行的Pod並不會刪掉，
如果沒有任何狀態改變，他們所使用的Image還會是前一個版本也就是V1。
如果想解決此問題，在目前架構中唯一能做的，就是手動把Pod刪掉，過一段時間後，ReplicaSet會偵測到，並重新啟起三個Pod，這一次它就會把最新Image V2的版本放上去，給剛產生出來的Pod使用。
### K8s Deployment 使用時機
由於上述過程需要手動介入，在實務狀況，我們需要的是一個自動化版本更新，
因此K8s提供第三個好用的資源就是Deployment。

Deployment同時包含ReplicaSet以及Pod的所有部署設定資訊，
因此首先要定義的是欲部署的Image，例如說要部署是V1版本的Image。
Deployment會涵蓋ReplicaSet的所有東西，其中要定義的是想要維持的Pod數量，當設定好後並將Deployment部署到Cluster中，過一段時間就會有三個Pod被啟動且使用的V1版本的Image。
這樣部署運作一段時間後，假設Image有更新，我們將會把Deployment上的部署模板，從V1換成V2，並且再次部署。

這樣部署後，Deployment會把「當前這個ReplicaSet"部分"的Pod刪掉」，
接著Deployment會建立一個新的ReplicaSet，並且這個新的ReplicaSet帶有V2版本的Image，帶有新版本的ReplicaSet就會創造一個新的Pod，且使用的Image就是最新的V2版本。

透過這樣的方式，Deployment會一個個取代掉原本舊的ReplicaSet，並換成新的，也就實現了Kubenetes Zero Downtime Rolling Updates的功用，
一步步將Pod更新成最新的版本，使用者不會體驗到任何關機或服務中止的狀況。

透過這個方式，Image版本更新就可以透過Deployment完成全部的自動化部署。
因此在實務中，並不會直接撰寫Pod或ReplicaSet的模板，而是直接撰寫Deployment層級的模板，來對K8s進行運算部署的模板計畫撰寫。

### K8s部署模板撰寫
#### pod模板(注意!實務不會直接撰寫pod!!)
kind是最重要的部分，定義本次模板主要是創造哪個種類，本次示範的是Pod。
metadata用來定義相關的輔助資訊，實際要做真正的改變時，定義在spec這個大類中。
首先，pod底下會有一個"containers："，可以給予這個container一個名稱，在此定義為app-container，而這個container的Image也可以定義。
目前使用教材中老師提供的image，由於此Image使用80 port，所以加入"ports："。
```yaml=
apiVersion： v1
kind： Pod
metadata：
  name： app-pod
spec：
  containers：
    - name： app-container
      image： uopsdod/K8s-hostname-amd64-beta：v1
      ports：
        - containerPort： 80
```
#### ReplicaSets模板(注意!實務不會直接撰寫ReplicaSets!!)
此模板最重要的部分為spec內容。
replicas：表示Pod要維持在多少個數量的狀態。

selector：表示設定那些資源是要套用到哪些Pod上，此範例定義想要去match對應到的labels，所以使用"matchLabels："，根據下方做的所有key-valye配對，key是"app"，value為"app-pod"。

template：跟上個pod模板非常類似，只不過透過這個Template語法，把Pod所做的東西都涵蓋在裡面。所以可以說ReplicaSet這個模板，是一個涵蓋Pod模板的一個更上層的好用模板。

首先metadata部分定義相關輔助資源，這邊的輔助資源就非常重要了，
Pod template的定義要先把它的Labels給放上去，而這邊的"labels："要跟前面spec中"app： app-pod"全度一起對到。
換句話說，因為這邊pod有著相同label，它就會對應到ReplicaSet的上面想要套用上面規則的這個label，以後的狀態它都會盡它所能把它維持成三個這邊定義的Pod。


```yaml=
apiVersion： apps/v1
kind： ReplicaSet
metedata：
  name： app-rs
spec：
  replicas： 3
  selector：
  matchLabels：
    app： app-pod
  template：
    metadata：
      label：
        app： app-pod
    spec： 
      containers：
        - name： app-container
          image： uopsdod/K8s-hostname-amd64-beta：v1
          ports：
            - containerPort： 80
```
#### Deployment模板 (實務上會撰寫此版本)
Deployment這個種類同時涵蓋ReplicaSet與Pod兩個模板

```yaml=
apiVersion： apps/v1
kind： Deployment
metadeta：
  name： app-deployment
spec：
  replicas： 3
  selector：
    matchLabels：
      app： app-pod
  template：
    metadata：
      labels：
        app： app-pod
    spec：
      containers：
      - name： app-container
        image： uopsdod/K8s-hostname-amd64-beta：v1
        ports：
        - containerPort： 80
```
如果使用語法刪除pod或ReplicaSet，都會重新被建立起來，除非直接刪除deployment。
### K8s網路架構： Service(L4) [三大種類]
Service中分為三個種類：
1. Cluster IP
只允許Cluster內部所送出的請求去進入到Service裡面的Pod，適合內部相關資源處理。
缺點：外部使用者無法發送請求進來。
2. Node Port
透過Worker Node上面特定的Port當作仲介者，連結到內部的Service以及裡面的Port。
缺點：需要同時開放多個Worker Node出來才可以連結到，有設定上的維護麻煩。
此外，Worker Node上面所使用的Port可選擇的範圍，只能在30000-32768 Port，無法設定成一個好看的Port如8080作為請求時所使用的Port。
3. Load Balancer
此種方式可以透過Load Balancer 例如80port的方式，統一進行請求的分送，並且不需要去開放Worker Node上面任何Port，可以乾乾淨淨的啟用一台Load Balancer運算資源，接受所有請求後，再轉送到相對應的Service之中進行最終的請求處理。
缺點：書上沒寫，但我猜就是要錢，呵呵。

### 1. K8s Service： Cluster IP
首先要介紹Cluster IP。
假設一個Cluster中部署了兩個Pod，並想要這兩個Pod做負載平衡處理，讓來自外面的請求可以被分散到不同的兩個Pod做處理，因應此場景K8s就提供了Service這個資源。

對於每個Service，它都會有一個Cluster IP的Address，這個Cluster IP Address是一「只允許在Cluster內部才能連結到的一個Address」。
例如說：如果請求是來自Cluster內部，就可以透過Cluster IP的方式去連結到Service，而這個Service會分佈此請求給Pod1 或 Pod2 去進行處理。

然而，若是使用者在外部，將無法透過Cluster IP去連結到相對應的Service，也就無法連結到裡面的Pod，請求就會失敗。

因此在Service Cluster IP這個部署設定，只允許Cluster內部溝通。
### 2. K8s Service： Node Port
假設一個Cluster中部署了兩個Pod，這次創建一個Service並將它設定成Node Port種類來將兩個Pod包起來。
Node Port Service這個方式，就是為了讓在Cluster外的人可以發請求進來。
這個架構是怎麼運行的?會有關於Pod最後會部署到的Work Node這個資源。
如果所使用的Service是Node Port這個種類，每個Worker Node上面會被開啟一個特定的Port，有了這些Port後，當請求進來時，將會透過這個Worker Node上面的Port連結進來，最終透過這個Worker Node去連結到內部Service，再由Service決定交由哪個Pod處理。

特別注意! Worker Node是需要一個實際的運算資源才可以啟動起來的。
因此我們必須注意到這些Worker Node，是由哪個資源提供者所提供。
例如說，我們在本地local自行建立的主機，或是運用不同雲端服務商所提供的計算資源(例如AWS的EC2)，都需要注意到這些Port是否可以連結成功，取決於主機上面的配置；
更準確地說，取決於主機上的網路配置，是否允許外面的使用者去連結到這個Port。
這也是為什麼K8s難度較高，除了得了解Cluster內部資源如何分佈，還要了解資源提供者相關的運算、網路設定。
### 3. K8s Service： Load Balancer
假設一個Cluster中部署了兩個Pod，並且用Service A並將它設定成Load Balancer種類，將兩個Pod包起來。
在Cluster外部，必須有一個實際存在的 Load Balancer支援這個模式。
透過此Load Balancer，外部使用者所發出的請求才可以連結到內部的Service A。

而這個Load Balancer的部署有個特別的地方，每一個Service配屬到一個自己所屬的Load Balancer。
例如：Service B也是設定成Load Balancer種類，那它也會需要另一台實際存在運行著的Load Balancer作為仲介者。
這樣請求才可以透過第二台Load Balancer送到Service B，並交由裡面的Pod進行請求處理。

特別注意，Load Balancer是一個實際要運行的運算支援，提供Load Balancer的來源有多種選擇。
可以自己建，或是運用雲端服務供應商的支援。例如AWS的NLB(Network Load Balancer)、GCP的External LB(External Load Balancer)、Azure上的Load Balancer...等。

完成設定後，就可以順利透過這個方式，將外界請求透過Load Balancer進入到所對應的Service，最後交由裡面的Pod完成請求處理。

### K8s Probe監控架構[三大機制]
在Pod啟動後，要如何去觀察它是否健全的運作，K8s提供了幾種監控的功能。
#### 1. K8s Startup Probe監控機制
啟動Pod時，第一個被啟動監控。
它會根據你所設定的一個API路徑，去打出API請求，如果可以收到200回應就表示Pod正常運作。
#### 2. K8s Liveness Probe監控機制
如果Startup Probe通過，它將會進行下一個Probe為Liveness Probe。
與前者不同的地方是Startup Probe為一次性的監控，在Pod啟動後Startup Probe就不會再次被執行。
而Liveness Probe將會跟著Pod終生進行持續監控。
一樣在Liveness Probe這邊，也要去設定它所發出的請求要到哪一個API的路線。
#### 3. K8s Readiness Probe監控機制
如果Startup Probe通過後，除了Liveness Probe還有另一個Readiness Probe。
Readiness Probe跟Liveness Probe一樣，都是跟著Pod終生監控的。

那差異就是，Startup Probe與Liveness Probe，都是對Pod本身進行監控。
而Readiness Probe則是對下游系統，也就是自身Pod以外的相關服務系統進行監控。
例如：DB連線、其他Service的API、遠端快取Redis...等等
因為如果周邊服務壞掉了，那麼也不應該開放這個Pod給外界使用。
### 三大Probe情境模擬與對應方式
三個Probe之中如果有壞掉的情況，各自會做什麼相對應的處理?
1. Startup Probe：在它進行監控的過程中，當它送出請求到Pod時，持續了好幾次沒收到200的成功回應，它會做的對應動作是「重新啟動Pod」，這是它遇到失敗情況會做的處理。

2. Liveness Probe：如果Startup Probe順利通過並延續到Liveness Probe，但是在某個時間點偵測到好幾次都沒有收到200的成功回應，那麼當Liveness Probe判定這個Pod是失敗時，它會做的動作跟Startup Probe一樣「重新啟動Pod」。

3. Readiness Probe：如果Startup Probe順利通過到Readiness Probe，在某個時間點中Readiness Probe偵測到好幾次沒有收到200成功回應，則表示某個使用的下游系統已經無法成功為Pod服務。
Readiness Probe它會做的對應動作是「將Pod標為Unready」。
注意此時Pod不會重新啟動，只是狀態變成Unready，表示Pod持續運行，但**K8s service不會把網路請求分配到這台Pod**上面，就這樣持續等待到下游系統修好或正常，它發出監控的API持續幾次都收到成功回應後，它就會把這台Pod的狀態調整為Ready。
### K8s Probe監控資源部署
參數用意:
httpGet:
path: /healthcheck 該API的路徑
port: 80
initialDelaySeconds:什麼時候要啟動這個監控，0秒表示不用等直接啟動。
timeoutSeconds:每一個Health check的請求出去後要等多久，1表示若等1秒以上就timeout失敗。
periodSeconds:定義每一次監測要隔多久，10表示第一個進出的Probe請求會是在Pod啟動後的10秒。
successThreshold:定義要有幾次成功才算成功，特別注意在startup probe中只能設1(如前面介紹，startup probe只跑啟動時這一次，但似乎livenessProbe也只能設1。)
failureThreshold:失敗幾次算是失敗，30表示30次。
```yaml=
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: app-pod
  template:
    metadata:
      labels:
        app: app-pod
    spec:
      containers:
      - name: app-container
        image: uopsdod/k8s-hostname-amd64-beta:v1
        ports:
        - containerPort: 80
        startupProbe:
          httpGet:
            path: /healthcheck
            port: 80
          initialDelaySeconds: 0
          timeoutSeconds: 1
          periodSeconds: 10
          successThreshold: 1
          failureThreshold: 30
        livenessProbe:
          httpGet:
            path: /healthcheck
            port: 80
          initialDelaySeconds: 0
          timeoutSeconds: 1
          periodSeconds: 1
          successThreshold: 1
          failureThreshold: 5
        readinessProbe:
          httpGet:
            path: /healthCheck_dependency
            port: 80
          initialDelaySeconds: 0
          timeoutSeconds: 1
          periodSeconds: 1
          successThreshold: 5
          failureThreshold: 7
```
### K8s Rolling Updates無中斷進版
spec中加入strategy，type=RollingUpdate
注意即便沒有手動設置strategy，預設就是RollingUpdate。

maxSurge:定義每次新版本部署時，最多允許有幾個「新Pod」正在啟動中。
1表示只允許一次來一個。

MaxUnavailable:當設置為0，表示告訴Deployment，如果原本的目標是維持3個Pod，那在整個部署過程中都必須維持3個Pod，不論這三個Pod各自的新、舊，在任何時間點都必須維持三個是Ready狀態，不允許有任何Pod狀態不為Ready。
```yaml=
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-deployment
spec:
  replicas: 3
  strategy: 
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      MaxUnavailable: 0
  selector:
    matchLabels:
      app: app-pod
  template:
    metadata:
      labels:
        app: app-pod
    spec:
      containers:
      - name: app-container
        image: uopsdod/k8s-hostname-amd64-beta:v2
        ports:
        - containerPort: 80
        startupProbe:
          httpGet:
            path: /healthcheck
            port: 80
          initialDelaySeconds: 0
          timeoutSeconds: 1
          periodSeconds: 10
          successThreshold: 1
          failureThreshold: 30
        livenessProbe:
          httpGet:
            path: /healthcheck
            port: 80
          initialDelaySeconds: 0
          timeoutSeconds: 1
          periodSeconds: 1
          successThreshold: 1
          failureThreshold: 5
        readinessProbe:
          httpGet:
            path: /healthCheck_dependency
            port: 80
          initialDelaySeconds: 0
          timeoutSeconds: 1
          periodSeconds: 1
          successThreshold: 5
          failureThreshold: 7
```
從練習中可觀察到，當部署了第二個版本的Deployment，
會產生第二個Replica Set去逐漸建立第二個版本的Pod，
且過程中會保持永遠三個Pod，當第一個新版本的Pod準備好，才會把舊版本的其中一個Pod刪除，如此的重複，直到最終三個新版本的Pod都被建立好，與舊版本的Pod都被刪除。

特別要注意的是，整個機制跟Readiness Probe息息相關，Readiness Probe設定的越長，Rolling Update每個Pod要變Ready的狀態所需判定時間就會越久。
### K8s Roll back無中斷退版
根據練習中可觀察到，即便部署了V2版本，舊的V1版本的Replicaset還是存在沒被刪掉，這是K8s的設計，為了方便回到上一個穩定版本，所以保留舊的Replicaset方便，直接Rollback退板使用。

```
指令:
kubectl get deployments 看到之前所部署的deployment資源
kubectl rollout history deployment/"deployment名稱" 查看部署歷史資訊
會看到有Revision為版本
kubectl rollout undo deployment/"deployment名稱" Rollback至前一版本
```
#### 業界實務經驗分享：
雖然透過`kubectl rollout undo`這種方式可以回到上一個版本，但這會導致實際部署版本跟在yaml模板上所寫的版本號不一致。
所以在實際部署時，還是建議去更改所部署的yaml檔案並重新部署。
例如說想要回到上一版本，就把image改回V1，或是要前進一個版本，就是改成V3。透過模板的更新，所有更動都可以記在版本控制中，讓yaml檔案永遠與實際部署的狀況一致，這樣較好維護。
### K8s 儲存架構：Persistent Volume
Persistent Volume允許將資料的儲存生命週期與Pod分隔開。
回憶之前所學的內容，假設一個Cluster中有一個Pod，Pod裡面可以有多個Container，每個Container中會有指定的Image，也就是專案要部署的東西。

這邊新的觀念就是，Container底下還有另一個概念：「Volume」。
也就是指定要哪個外界的儲存空間，去讓當下特定的Container使用。
這邊Container中的Volume，會與Cluster外界的資源相互連結。

在Pod之中Container的Volume，它會與Cluster之中一個叫PVC(Persistent Volume Claim)的資源相互連結。
一個PVC主要的目的是幫Pod去宣告，並拿到實際的儲存資源。
那麼這個儲存資源怎麼來的? PVC會根據它所指派的Storage Class，去找到相對應的PV(Persistent Volume)，並且會有靜態與動態兩種方式。
### K8s 靜態部署：Persistent Volume(PV) & Claim PVC
如果所使用的方式是一個靜態配置，也就是「手動創造儲存資源」，那麼就會連結到相同Storage Class的PV。
手動創造PV資源，並把它分配到某個特定的Storage Class。
在另一側Pod這邊，Volume將會透過一個PVC的方式去宣告，在一個特定的Storage Class之中，去尋找有沒有已存在且對應得到的PV資源，透過這個方式就可以讓Pod得到一個實際的儲存資源。
### K8s 動態部署：Storage Class(SC)
假設PVC是想透過動態的方式，對應到特定的Storage Class，而拿到真正的PV資源的話，在Storage Class要多一個東西，叫做「Provisioner」。
一個Provisioner會根據對應的雲端商，來做不同的配置。

它將會動態的去創建一個PV資源，無論透過靜態手動建立PV的方式，或動態透過Provisioner創建PV的方式，都可以在另一側的Pod中，透過PVC的方式去找到特定的Storage Class所對應到的PV資源。
#### 儲存資源的實際來源
實際的儲存資源由誰提供? 在K8s的PV設計之中，它與真正的儲存空間所間接是一個透過Plugin插件的方式進行。
在K8s的規格中，有許多Plugin外接的模式，例如：
HostPath：讓主機底下某個目錄作為真正儲存的空間
CSI(Container Storage Interface)：讓許多雲端商可以透過這個特定的規格，來實作其擁有的外接硬碟。
NFS(Network File System)：較常見，Network File System的相關儲存資源。
ISCSI：連結此規格的外接硬碟資源。

根據在PV範本中所配置的Plugin類別，就會對應到不同實際創建出儲存資源的地方。
例如說：
在本地自己維護的一個NAS資料儲存主機；雲端商如AWS的EFS(Elastic File System)、GCP的FileStore、Azure的Files...等網路儲存服務。
不論挑的是哪一種資源提供者的方式，都能實際創建出儲存資源，並搭配PV上面所定義的Plugin模式，來完成PV的創建與連結。

至於這個動態創建要創建出什麼、規格怎麼寫，將會根據不同的雲端商規格來實作。
### Persistent Volume(PV)模板撰寫
此為靜態的PV部署，storageClassName可以隨意寫自己想要的值，只要在後續創建的PVC中，可以與這個值對應起來即可。
這邊有個有趣的問題：實際上有沒有這個storageClassN的資源呢?
答案是沒有，但在靜態的部署中，並不影響實際部署結果，只要PV與PVC資源兩個名稱相互對應即可。

storage: 2Gi表示取得2GB的儲存空間。

accessModes: 在K8s的規範中有許多模式可選擇，ReadWriteMany將允許儲存空間可以讓多個在不同Node之中的Pod，同時進行資料讀取與寫入。

在PV架構中，實際的儲存空間都是使用一個plugin外接的方式給接進來的。
而K8s提供很多種外接的方式，這邊的範例是hostPath的plugin外接方式。
它會去跟當下主機(Host)要到一個主機之中的硬碟目錄，來當作儲存資源的來原位置。
接著指定要用主機中哪一個path也就是目錄，這邊想使用的是根目錄下的data這個目錄，因此打上path: /data，並且要使用其中一個叫DirectoryOrCreate的種類，如果這個目錄不存在的話，它就會自動建立一個。
```yaml=
apiVersion: v1
kind: PersistentVolume
metadata:
  name: app-pv
spec:
  storageClassName: sc-001
  volumeMode: Filesystem
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteMany
  hostPath:
    path: /data
    type: DirectoryOrCreate
```
### Persistent Volume Claim(PVC)模板撰寫
```yaml=
apiVersion: v1
kind: PersistentVolumeClaim
metadeta:
  name: app-pvc
spec:
  storageClassName: sc-001
  accessModes:
    - ReadWriteMany
  resource:
    requests:
      storage: 2Gi
```

### Persistent Volume(PV)動態部署模板撰寫：Storage Class
這邊看起來跟上面的PVC一樣，具體差異在於storageClassName，我們使用的是minikube中已經有預設叫做standard的storageClass。
其中它所指定的Provisioner是`k8s.io/minikube-hostpath`。
不同的Provision可以創建不同的硬碟資源。
在這個案例中這個minikube-hostpath，顧名思義可以去創造出「需要hostpath這種硬體資源的plugin」所需要的PV資源。
```yaml=
apiVersion: v1
kind: PersistentVolumeClaim
metadeta:
  name: app-pvc
spec:
  storageClassName: standard
  accessModes:
    - ReadWriteMany
  resource:
    requests:
      storage: 2Gi
```

### K8s 資源架構：Namespace
Namespace將允許Cluster之中的部署資源，去進行分群的管理。
#### K8s資源分類的概念
在前面介紹到的K8s資源中，不是所有資源都可以透過Namespace綁在一起。
所以要先來歸類，那些可以，哪些不行。
可以：
1. Pod
2. 將Pod包住所進行網路控制的Service
3. ReplicaSet
4. Deployment
5. PVC
6. Ingress(流量分流)
7. HPA(自動化運算部署資源)
不行：
1. StorageClass
2. PV
3. Worker node
以上三者皆屬於Cluster資源
#### K8s Namespace實際運用：資源分類與清理
首先在創建Cluster時，就會有一個預設的Namespace名為"kube-system"，
在kube-system中，將會包含要讓Cluster正常運作需要的所有資源。
除此之外，還會有一個"default Namespace"，如果對Namespace沒有任何概念，就直接進行部署的話，所有的資源就都會被放到這個default Namespace底下。

其中一個方便的地方在於，若是要將特定Namespace中所有的資源刪除，就直接將某個特定的Namespace刪掉，就可以一次清理這個Namespace中所有資源。
#### K8s Namespace模板撰寫
`---`代表在同一份yaml檔中要進行多個資原種類部署
`namespace: app-ns`代表未來創建這個資源時，會將它放到"app-ns"這個namespace底下。
```yaml=
apiVersion: v1
kind: Namespace
metadata:
  name: app-ns
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: beta-app-deployment
  namespace: app-ns
spec:
  replicas: 3
  selector:
    matchLabels:
      app: beta-app-pod
  template:
    metadata:
      labels:
        app: beta-app-pod
    spec:
      containers:
      - name: beta-app-container
        image: uopsdod/k8s-hostname-amd64-beta:v1
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: beta-app-service-clusterip
  namespace: app-ns
spec:
  type: ClusterIP
  selector:
    app: beta-app-pod
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 80
```
### K8s進階網路架構：Ingress(L7)
K8s Ingress是L7網路部署資源的概念介紹。
之前介紹過Service Load Balancer這個部署種類，在Service Load Balancer這種部署結構中有一個問題：「每個Service都需要創建一個對應的Load Balancer。」
且Service處於一個L4的網路配置，因此只能進行Ip和Port的設定進行更高級別L7的「請求分流」或「SSL」的相關設置。

為了解決高成本與無法使用L7網路配置等問題，K8s提供了一個更高級的資源，也就是「Ingress」。

部署K8s Ingress時，有個非常重要的先決條件：必須先擁有一個名為「Ingress Controller」的資源。
這個Ingress Controller必須先存在，Cluster才能接收並完成Ingress的部署請求。
每個Ingress Controller都有一個非常重要的責任，那就是創建相應的Load Balancer。

那麼，Ingress Controller是如何產生的呢?有很多方式可以選擇。
例如，可以使用開源的Nginx Ingress Controller進行部署，它會在本地啟動一個相應的Load Balancer資源。
或可以使用雲端商提供自訂的Ingress Controller，例如AWS的ALB(Application Load Balancer)、GCP的Load Balancer、Azure的Application Gataway，它們會根據不同雲端商的設置來創建相應的Load Balancer資源，這些決策由Ingress Controller自行決定要實際創建的那種Load Balancer資源。
### K8s Ingress部署模式
在Ingress有三種類型的網路請求設置。
#### 1. Default Backend
如果來自外部的請求沒有進行特定設置，預設都會進入一個叫做Default Backend的地方，Default Backend會將所有請求都轉發到一個特定的Service，比方說部署了一個Service A，其中包含兩個Pod，準備好來處理通過Default Backend發送過來的請求。

最常見的情況是，如果請求不知道從哪裡來的，Pod會回傳一個客製化的404頁面，告訴請求方不知道要去哪裡，請使用另一個URL送過來。
##### Default Backend的Ingress部署示範：模板撰寫
要使用Ingress之前，跟在Service使用Load Balancer Type的概念非常像，在本地必須有一個Ingress Controller運行著，才能同時的部署完成。
在Minikube中，有一個非常方便的功能：
`minikube addons enable ingress`這會啟動一個ingress-nginx-controller的相關部署資源
`kubectl get all -n ingress-nginx`可以看到相關部署資源

`ingressClassName: nginx`這邊指定的是剛剛透過Minikube所部署上的Nginx ingress controller。
如果將相同的模板放到雲端商，例如說AWS、GCP、Azure上，ingressClassName就必須跟著做相對應的設置。
相對於本地只是模擬Load Balancer行為，在雲端商上面，實際上都會啟動一個真正的Load Balancer進行流量管制。而Ingress所處在的網路階層是L7，可以做更多更細緻的網路請求導向管理。
```yaml=
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-defaultbackend
  namespace: app-ns
spec:
  ingressClassName: nginx
  defaultBackend:
    service:
      name: beta-app-service-clusterip
      port:
        number: 8080
```
#### 2. Hostname
Ingress是一個L7的網路設置，因此可以透過它來分流給不同的Hostname。
例如，在Cluster中，部署兩個Service，Service A和Service B各自處理不同請求。假設這邊來了一個請求，它的Hostname是`beta.demo.com`，如果這個請求是來自這個Hostname的話，可以設置Ingress把它送到Service A處理。

那麼如果送出第二個請求，這個請求來自`prod.demo.com`這個Hostname，就可以去設置Ingress把這個Hostname導向到Service B處理。

透過這種方式，Ingress就達到了根據不同的Hostname送到不同Service進行請求的分流處理。
##### Hostname的Ingress部署示範：模板撰寫
```yaml=
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-hostname
  namespace: app-ns
spec:
  ingressClassName: nginx
  rules:
  - host: beta.demo.com
    http:
      paths:
        - pathType: Prefix
          path: "/"
          backend:
            service:
              name: beta-app-service-clusterip
              port:
                number: 8080
  - host: prod.demo.com
    http:
      paths:
        - pathType: Prefix
          path: "/"
          backend:
            service:
              name: prod-app-service-clusterip
              port:
                number: 8080
```
##### 3. Path
在Ingress中就算Hostname是同一個，還可以根據Path的方式來對請求進行更客製化的分流處理。
例如說在這個Cluster中，一樣有Service A跟Service B，如果現在來了第一個請求來自`all.demo.com`，但是它的Path後面加上了/beta，如果有Path的地方，我們可以設定Ingress去把它送到Service A。

如果來了第二個請求，一樣是來自`all.demo.com`，但是它的Path加上的是/prod，那我們可以設定Ingress去把請求送到Service B處理。

透過這種方式，就可以達到即便Hostname是同一個，也可以根據它下一層的Path來進行客製化分流。
##### Path的Ingress部署示範：模板撰寫
```yaml=
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-path
  namespace: app-ns
spec:
  ingressClassName: nginx
  rules:
  - host: all.demo.com
    http:
      paths:
      - pathType: Prefix
        path: /beta
        backend:
          service:
            name: beta-app-service-clusterip
            port:
              number: 8080
      - pathType: Prefix
        path: /prod
        backend:
          service:
            name: prod-app-service-clusterip
            port:
              number: 8080
```
### K8s進階運算架構：Horizontal Pod AutoScaling(HPA)
HPA允許對於一個動態的請求流量進行運算資源部署的增、減。
建造HPA之前，有一個先決條件：必須要有一個「Metrics Server」。
它的目的是蒐集Pod所有相關的資源資訊，例如說這個Metrics Server會去蒐集Pod之中的CPU使用量。

有了Metrics Server之後，就能在Deployment之上建議一個HPA，這邊特別要注意的是，每一個HPA資源都會針對一個特定的Deployment去監控其底下的Pod。而HPA會跟Metrics Server去拿它想要監控的東西。

例如說這次要監控的是Pod的CPU使用量，它就會持續監控，如果某時某刻突然發現CPU的使用量過高，代表目前Pod數量不夠去應付當前流量，HPA就會主動更新Deployment底下Replica Set所需要的數量，例如說從2個提升到3個，去應付現在的高流量。

Pod啟動完後，Metrics Server一樣會去監控這個新產生出來的Pod，繼續監控它的CPU，讓CPU維持在特定的目標底下。如果CPU使用量持續穩定著，HPA就不會再做任何更動。

假設過了三天後，HPA資源在監控CPU使用量時，發現流量非常少，它就會根據這個狀況，去將Deployment下面Replica Set設定的數量，從3個減少到2個或1個，有了這個設定後，Replica Set就會將原本的Pod給刪掉，最後讓整個部署只剩下一個Pod，來應付當前低流量請求。

Metrics Server是一個K8s內建一個Metrics蒐集的一個應用程式，但它蒐集的東西是非常有限的例如CPU跟Memory使用量。
如果想要蒐集更客製化或是更全面的Metrics，在實務上大家更常使用的是一個叫做「Prometheus」的Metrics蒐集應用程式。
#### K8s進階運算部署：Horizontal Pod AutoScaling(HPA)
`resources:limits:
            cpu: 300m
          requests:
            cpu: 200m`
limits表示這一個Container最多只能用到300CPU單位。
requests表示每一個Container最少會用到200CPU單位。
```yaml=
apiVersion: v1
kind: Namespace
metadata:
  name: app-ns
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: beta-app-deployment
  namespace: app-ns
spec:
  replicas: 1
  selector:
    matchLabels:
      app: beta-app-pod
  template:
    metadata:
      labels:
        app: beta-app-pod
    spec:
      containers:
      - name: beta-app-container
        image: uopsdod/k8s-hostname-amd64-beta:v1
        ports:
        - containerPort: 80
        resources:
          limits:
            cpu: 300m
          requests:
            cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
  name: beta-app-service-clusterip
  namespace: app-ns
spec:
  type: ClusterIP
  selector:
    app: beta-app-pod
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 80
```

首先記得，HPA要進行監控，要先有Metrics Server，在K8s中有提供一個簡易的Metrics Server快速部署，去拿到Pod裡面的CPU效能資訊。
`wget http://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml` 下載安裝檔
會有一個yaml被下載下來。

`kubectl autoscale deployment beta-app-deployment --cpu-percent=10 --min=1 --max=10 -n app-ns` 部署HPA
`--cpu-percent=10`:指定想維持的CPU使用比率為10%
`--min=1`:需要的Pod數量至少為一個
`--max=10`:需要的Pod數量最多為一個
`-n app-ns`:指定HPA這個資源放到"app-ns"這個namespace底下
```yaml=
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    k8s-app: metrics-server
  name: metrics-server
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    k8s-app: metrics-server
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
    rbac.authorization.k8s.io/aggregate-to-view: "true"
  name: system:aggregated-metrics-reader
rules:
- apiGroups:
  - metrics.k8s.io
  resources:
  - pods
  - nodes
  verbs:
  - get
  - list
  - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    k8s-app: metrics-server
  name: system:metrics-server
rules:
- apiGroups:
  - ""
  resources:
  - nodes/metrics
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - pods
  - nodes
  verbs:
  - get
  - list
  - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    k8s-app: metrics-server
  name: metrics-server-auth-reader
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: extension-apiserver-authentication-reader
subjects:
- kind: ServiceAccount
  name: metrics-server
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    k8s-app: metrics-server
  name: metrics-server:system:auth-delegator
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
- kind: ServiceAccount
  name: metrics-server
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    k8s-app: metrics-server
  name: system:metrics-server
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:metrics-server
subjects:
- kind: ServiceAccount
  name: metrics-server
  namespace: kube-system
---
apiVersion: v1
kind: Service
metadata:
  labels:
    k8s-app: metrics-server
  name: metrics-server
  namespace: kube-system
spec:
  ports:
  - name: https
    port: 443
    protocol: TCP
    targetPort: https
  selector:
    k8s-app: metrics-server
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    k8s-app: metrics-server
  name: metrics-server
  namespace: kube-system
spec:
  selector:
    matchLabels:
      k8s-app: metrics-server
  strategy:
    rollingUpdate:
      maxUnavailable: 0
  template:
    metadata:
      labels:
        k8s-app: metrics-server
    spec:
      containers:
      - args:
        - --cert-dir=/tmp
        - --secure-port=10250
        - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
        - --kubelet-use-node-status-port
        - --metric-resolution=15s
        - --kubelet-insecure-tls  
        image: registry.k8s.io/metrics-server/metrics-server:v0.7.1
        imagePullPolicy: IfNotPresent
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /livez
            port: https
            scheme: HTTPS
          periodSeconds: 10
        name: metrics-server
        ports:
        - containerPort: 10250
          name: https
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /readyz
            port: https
            scheme: HTTPS
          initialDelaySeconds: 20
          periodSeconds: 10
        resources:
          requests:
            cpu: 100m
            memory: 200Mi
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: 1000
          seccompProfile:
            type: RuntimeDefault
        volumeMounts:
        - mountPath: /tmp
          name: tmp-dir
      nodeSelector:
        kubernetes.io/os: linux
      priorityClassName: system-cluster-critical
      serviceAccountName: metrics-server
      volumes:
      - emptyDir: {}
        name: tmp-dir
---
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
  labels:
    k8s-app: metrics-server
  name: v1beta1.metrics.k8s.io
spec:
  group: metrics.k8s.io
  groupPriorityMinimum: 100
  insecureSkipTLSVerify: true
  service:
    name: metrics-server
    namespace: kube-system
  version: v1beta1
  versionPriority: 100
```
# 資料來源
網際威信內部訓練課程
https://ithelp.ithome.com.tw/articles/10315780
2023 iThome 鐵人賽
https://ithelp.ithome.com.tw/articles/10340019
書籍：K8s自學聖經624元
