owned this note
owned this note
Published
Linked with GitHub
# Container 管理
## Monolithic to Microservice
1. 當你的服務不需要在內部維持請求之間的狀態時就是 `stateless service` ,反之就是 `statefule service` 。
2. 微服務應該將狀態資訊儲存在外部而非內部,這才有利於服務的擴展。
![](https://i.imgur.com/enSwKWw.png)
![](https://i.imgur.com/fSUyZRB.png)
[stateful vs stateless](https://stackoverflow.com/questions/58696684/stateless-vs-stateful-microservices)
## 優點
- 開發上的 decouple
- Scaling
- 不同的微服務可以獨立維護部署
## 困難點
- 😂 重新抽象軟體架構
- 😂 API 要變成 stateless
- 😍 由於系統變得龐大且複雜,所以在除錯上會更加困難 [Zipkin](https://zipkin.io/)
- 😍😎 當有大量的微服務 **相互依賴** 時會讓你要去決定誰去要一起被部署
- 😎 微服務造成環境的多樣性更高,在開發和部署環境上更不容易維護
- 😎 環境需求的不同,程式想要不同版本的 shared libs
## 解決
- 😂 依賴軟體團隊的經驗設計 (練家軍) ...
- 😍 服務的[顆粒度(Granularity)](https://www.fiorano.com/blogs/microservices)應該以獨立的業務邏輯來解決之間因 **相互依賴** 導致 **除錯困難** 的問題
- 😎 **We ease the pain with the help of docker and kubernetes.**
# 介紹
## Container management solutions
> FIXME: why (openshift, k8s, docker-compose 差別 (什麼時候用到)
- `docker-compose` 適用在 **單節點** 的環境上,可以快速部署多個容器。
## What's the Kubernetes?
> FIXME: what (他是什麼以及歷史), how(怎麼達成)....
### 歷史
1. Kubernetes(又稱為 k8s)是一個開放原始碼的 **容器編排管理系統**,用於自動化應用程序的部署,擴展和管理。
2. Google 於 2014 年中首次宣布 Kubernetes ,現由 [Cloud Native Computing Foundation (CNCF)](https://www.cncf.io/) 負責維護。
3. 原以 C++ 編寫,後以 Golang 重構。
### 叢集架構
![](https://i.imgur.com/g8e8Gft.png)
1. `Pod` 最基本的部署單位。
2. `Service` 代表一組功能相同的 Pods 群體。訪問 Service 時會自動 load balance 到該群體底下任意的 Pod 上。
3. `Label` 可以將任意物件 **貼上標籤** ,方便將物件進行分組還有分配任務
4. `Ingress` 用於管理從外部對叢集中的 `Service` 進行訪問(HTTP, HTTPS)。必須部署到 master node 上面。
### 系統架構
![](https://i.imgur.com/zpnOfVC.png)
- `master node` : 負責管理和控制所有 `worker node` 的主機
- `Worker node` : 運行微服務的主機。
- `API server` : 提供 Kubernetes API ,讓使用者可以透過它控制整個系統。
- `etcd` : 儲存整個 k8s 系統中的狀態。
- `Scheduler` : 負責決定 Pod 要部署到哪一個節點上。
- `Kubelets` : **視為每個節點的大腦**。 負責從 master node 的 API 獲取指令,然後對其進行處理。
- 負責管理 Pod,包括在 Pod 發生故障時創建新的 Pod。
- `cAdvisor` : 搜集該節點上所有資源的資訊。
- `Kube-Proxy` : 維護節點上的網絡規則。負責網路流量的傳送。
<!-- ### 網路架構
![](https://i.imgur.com/EP0tmsd.png)
-->
### 特性
Velocity 不只是部署的速度,更是強調系統的穩定
- Immutability 不變性
- 當軟體版本更新時
- ✅ (**immutable**) 重新 `build` 一個更新的 `image`
- ❌ (**mutable**) 進入 `container` 更新
- Declarative configuration 宣告式設定
- `命令式命令 (imperative commands)` 定義了 **行為** 。「執行A,執行B,然後再執行C...」
- `宣告式設定 (declarartive configurations)` 定義了 **狀態** 。「我需要三個A程式」
> FIXME: 一個完整的服務或網站在 docker-compose 或 k8s 環境底下長什麼樣子。 ([圖片支援](https://www.nakivo.com/blog/docker-vs-kubernetes/))
# kubernetes Component
## k8s 物件的概念
k8s 常見的物件像是 `Pod`、`Service`、`deployment`、`ingress` 等等
**物件在 Kubernetes API 是由 JSON 或 YAML 檔案來表示的。**
<!-- - 這些檔案不是由伺服器因應 query 的請求而返回,又或是作為請求 api 的一部分傳到伺服器上。 -->
```yaml=
# This's a yaml file to describe the spec and state of Pod objects.
apiVersion: v1
kind: Pod
metadata:
name: kubia-manual
labels:
app: kubia
spec:
containers:
- image: oliwave/kubia
name: kubia
ports:
- containerPort: 8080
protocol: TCP
```
## k8s 物件和 RESTful 的關係
- 任何資源在 k8s 當中都可以用 **RESTful 來表示**。
```
https://your-k8s.com/api/v1/namespaces/default/pods/my-pod
```
- 這些資源在 k8s 當中都叫做 Kubernetes objects 。
- 每一個 kubernetes objects 都存在一個 **唯一的 HTTP 路徑**。
取得 Kubernetes objects 最基本的 kubectl 指令是 **get**
> kubectl get <resource-name\>
列出在目前 namespaces 底下的所有資源,然而若想要取得特定資源
> kubectl get <resource-name\> <obj-name\>
想要知道該物件的詳細資訊
> kubectl describe <resource-name\> <obj-name\>
:::info
### -o 參數的意義
在預設的情況下, kubectl 的返回結果是 human-readable ,但這會導致許多詳細的資訊被省略掉。如果想要得到完整的 **物件** 可以加上 -o 參數並指定想要的格式
> -o json/yaml
更進階的做法還可以從返回的物件中指定想要看到的 field
> kubectl get pods my-pod -o jsonpath --template={.status.podIP}
:::
### Creating, Updating, 和 Destroying Kubernetes 物件
#### 範例
假設有一個簡單的物件在 `obj.yaml` 中定義好了。可以透過 kubectl 在 Kubernetes 中 **創建和更新** 該物件
> kubectl apply -f obj.yaml
<!-- 可以注意到不需要特別指定物件的資源類型,因為它可以從 obj file 中得到。同樣的也可以用 `apply` 指令來更新該物件
> kubectl apply -f obj.yaml -->
<!-- 當然也可以用 `edit` 指令直接修改該物件在 Kubernetes 裡面最新的狀態
> kubectl edit <resource-name\> <obj-name\> -->
<!-- 不僅如此, `apply` 指令還會幫你記錄物件的修改歷史紀錄
> kubectl apply -f myobj.yaml view-last-applied -->
# Namespaces
Kubernetes 支持由同一 physical cluster 支持的多個 virtual clusters。這些 virtual clusters 稱為 **Namespaces**。
![](https://i.imgur.com/A16s2k9.png)
namespaces 提供了名稱範圍。資源名稱在名稱空間內必須唯一,但在各個名稱空間之間是不用的。namespaces 不能彼此嵌套,並且每個 Kubernetes 資源只能位於一個namespaces 中。
:::success
- `namespaces` 用來組織在叢集中的 **物件**
- 可以把每一個 *`namespaces`* 都想像成一個 **資料夾** 管理自己的 **物件**
- `kubectl` 預設是和 `default namespaces` 互動
- 如果想列出叢集中所有的 `pods` ,可以用 `--all-namespaces` 作為參數
- 不同的使用者應該有不同的 `namespaces` ,也需要有權限的管理
:::
#### 創建 Namespace
```yaml=
apiVersion: v1
kind: Namespace # 資源的類型
metadata:
name: custom-namespace # Namespace 的名字
```
在不同的 `namespace` 創建資源
> kubectl create -f kubia-manual.yaml -n custom-namespace
# Pods
## Pod 基本概念
1. K8s 將多個 container 變成一組單位 `Pod` (Pod 有一群鯨魚的意思)
2. Pod 代表一個在相同環境底下執行的 application containers 和 volume 的集合
3. Pod 是 K8s 中可部署的最小單位,也就是所有在 Pod 裡的 containers 都運行在同一個機器上
4. **每一個在 Pod 裡面的 container 都擁有自己的 cgroup ,但卻共用一定數量的 namespaces**
5. 應用程式在同個 Pod 中共享同個 `Network` 、 `UTS` (hostname)、 `IPC` namespaces ,因此可以用 `http://localhost:\<port_num>` 來彼此溝通 (IPC)。相反的應用程式在不同的 Pod 是隔離的,所以有不同的 IP 、 hostname 等等。
:::info
## Container 的概念
使用 Linux 的兩個技術
- **Namespaces** 確保每一個 process 都是由自己的觀點去看整個系統 (files, processes, network interfaces, hostname, and so on) **軟體**
- **Cgroups** 限制每一個 process 可以使用的資源 (CPU, memory, network bandwidth, and so on) **硬體**
有許多種 Namespaces 而每一種都是在隔離不同種的資源
:::
## Pod IP
對 K8s 來說,不管 Pod 在哪一個 Node 上都可以把它們想像成在同一個 LAN 底下。因此彼此是可以訪問對方的,這是透過 **軟體定義網路 SDN** 來實現的。
![](https://i.imgur.com/uBwYrHV.png)
<!-- **在同一個 Pod 底下的 containers 就像一台實體主機或虛擬機上跑一些 processes 一樣** -->
## 如何創建一個 Pod
事實上可以透過 kubectl 用以下幾種 K8s 提供的方式來創造物件
1. `Imperative command` 立即指令
- > kubectl run kuard --generator=run-pod/v1 \ --image=gcr.io/kuar-demo/kuard-amd64:blue
2. `Declarative configuation` => **撰寫 Pod manifest**
### 什麼是 Pod Manifest
- Pod Manifest 就只是一個用 json 或 yaml 格式來描述 **Pod 物件** 的文字檔案
- 該文件描述 Pod 的 **ideal state** ,因此**這是一種 declarative configuration 而不是 imperative configuration**
- 事實上撰寫 Pod Manifest (或其他 K8s 物件) 是比較推薦的
- 當 source code 在維護
- 更容易閱讀
1. 撰寫簡易的 Pod Manifest
```yaml=
apiVersion: v1
kind: Pod
metadata:
name: kuard
spec:
containers:
- image: gcr.io/kuar-demo/kuard-amd64:blue
name: kuard
ports:
- containerPort: 8080
name: http
protocol: TCP
```
2. 把撰寫好的 Pod Manifest 透過 kubectl 丟到 K8s API server
- > kubectl apply -f kuard-pod.yaml
3. K8s API server 會做兩件事:
- 透過 **Kube Scheduler** 會決定要放在哪個 worker node 上。
- **Kubelet** 屆時會負責把相對應寫在 Pod manifest 裡的 container 都運行起來,並且監控。
以上都步驟都完成後,可以查看所有 Pod 的狀況
> kubectl get pods
![](https://i.imgur.com/i29gYZY.png)
或是檢查單一 Pod 的詳細資訊
> kubectl describe pods kuard
### 還有許多可以對 Pod 操作的事情...
#### Grace Period
事實上當我們刪除一個 Pod 時 K8s 會做以下事情
- 讓 Pod 拒絕接收新的服務請求,並消化當前還有的請求
- 在預設時間 30 秒過後才把 Pod 刪除掉
以上的 30 秒就是所謂的 **Grace period** ,這麼做是為了確保服務的**可靠性**。我們不希望有任何請求是因為要刪除 Pod 而無法回應到的
#### Health Check
最簡易的 health check 就是 **process health check** 。也就是直接判斷該程序是否還繼續執行著。但這會有一個很大的問題就是萬一程序進入 dead lock ,事實上該程序仍然運行的但是卻無法再提供服務了。
因此在 K8s 引進了幾種方法來解決
##### Liveness Probe
也**就是執行一個任務,如果通過了他就還健在**。常見的就是發 http request 檢查在 Pod 裡的 container 是否健康~
```yaml=
apiVersion: v1
kind: Pod
metadata:
name: kuard
spec:
containers:
- image: gcr.io/kuar-demo/kuard-amd64:blue
name: kuard
livenessProbe:
httpGet:
path: /healthy # 測試該容器是否健康的請求
port: 8080
initialDelaySeconds: 5 # 在 Pod 被初始化後的 5 秒才開始測試
timeoutSeconds: 1 # 健康測試的回應時間始有 1 秒
periodSeconds: 10 # 每 10 秒一次健康測試
failureThreshold: 3 # 超過三次失敗就重起該 container
ports:
- containerPort: 8080
name: http
protocol: TCP
```
##### Readiness Probe
readiness probe 和 liveness probe 最大的不一樣就是 liveness probe 確認應用程式是否正確執行,而它則是 **檢查該應用程式是不是已經準備好可以開始服務了**。
##### 其他的 K8s 所提供的 Health Check
除了 Http 以外,還有 **tcpSocket health checks** 或是讓容器執行一段 script 來檢查。這麼做是因為不是每一個服務都是 http-base appliction.
##### MISC
1. 取得 Pod 的 Logs (fluentd and elasticsearch)
2. 在 container 裡面執行指令
3. 從 container 裡面複製檔案到外面,反之亦然
以上都是不建議直接使用 kubectl 去操作的。因為這違反 declarative configuration 的精神。
都有更適當的工具或方法去完成這些任務。
#### Resource Management
Pod 在資源(cpu, mem, gpu)的請求上在 K8s 有兩種方式
- **minimum** 該容器最少需要多少該種類型的資源 (情況允許,可以多給)
- **limit** 該容器最多可以擁有該種類型的資源
```yaml=
apiVersion: v1
kind: Pod
metadata:
name: kuard
spec:
containers:
- image: gcr.io/kuar-demo/kuard-amd64:blue
name: kuard
resources:
requests:
cpu: "500m"
memory: "128Mi"
limits:
cpu: "1000m"
memory: "256Mi"
ports:
- containerPort: 8080
name: http
protocol: TCP
```
::: info
1. **記住資源的請求是以 container 為單位,因為不同服務需要的資源種類和數量都不一樣**
2. **一個 Pod 所請求的資源就是其所有 containers 資源請求的總和**
3. **記憶體與 CPU 資源的分配不一樣,因為記憶體一旦分配給一個程序就不能再收回。因此當系統消耗完記憶體的資源時,就會把取得過多的 contaienr 重啟以達重新分配記憶體資源**
:::
#### 在 Pod 中使用不同種 volume 的方式
- 該 volume 的生命週期與 Pod 相同 (**emptyDir**)
1. Communication/synchronization
- 容器間可以共用一些資料
3. Cache
- 有時容器需要或被重啟,那就可以緩衝一些運算昂貴的資料
- 長期維持的 volume
3. Persistent data
- 不管是 private 或 public 的 provider , k8s 都有提供 api
- e.g. NFS, iSCSI, Amazon’s Elastic Block Store, Azure’s Files and Disk Storage, Google’s Persistent Disk
- ```yaml=
volumes:
- name: "kuard-data"
nfs:
server: my.nfs.server.local
path: "/exports"
```
5. Mounting the host filesystem (hostPath)
- 掛載在 Node 上面
- ```yaml=
apiVersion: v1
kind: Pod
metadata:
name: kuard
spec:
volumes:
- name: "kuard-data"
hostPath:
path: "/var/lib/kuard"
containers:
- image: gcr.io/kuar-demo/kuard-amd64:blue
name: kuard
volumeMounts:
- mountPath: "/data"
name: "kuard-data"
ports:
- containerPort: 8080
name: http
protocol: TCP
```
# Labels
- Labels 是 **鍵值對**
- Labels 提供了為 **物件分組** 的基礎(該物件可以是 pod, replicaSet ...)
## 概念
**Labels 提供一群物件可被識別的標記**,這為之後分組、操作、查看等動作帶來方便。
在 K8s 中物件要尋找或參考到其他物件的時候就需要用到 selector 。
```yaml=
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: kubia
spec:
replicas: 3
selector: # ReplicaSet 用 label 來尋找它管理的物件
matchLabels:
app: test
template: # PodTemplate
metadata:
labels: # 將 pod 貼上 `app: test` 這個 label
app: test
spec:
containers:
- name: kubia
image: luksa/kubia
```
![](https://i.imgur.com/mSLyrq7.png)
:::warning
只有 `Job`、`Deployment`、`ReplicaSet` 還有 `DaemonSet` 有支援 **matchLabels**
:::
# ReplicationController
> FIXME: 不重要縮小,放進 pod 裡面
:::danger
請使用 ReplicaSet 而不是用 ReplicationController
:::
## ReplicationController 的工作
*確保資源都保持存在,當有消失、不足或過多時都會加以調控*
以下為 ReplicationController 的協調迴圈
![](https://i.imgur.com/IxZhngr.png)
## 基本功能
ReplicationController 是由三個基本的元素組成的
1. `label selector` 決定哪些 Pods 是在該 ReplicationController 管理之下
2. `replica count` 決定理想的複製數目
3. `pod template` 設定要被複製的 Pod
![](https://i.imgur.com/4GxLGEY.png)
### 修改 ReplicationController 會對既有的 Pods 有哪些影響?
- `label selector` 的修改只表示 ReplicationController 不再 **關心** 原有產生的 Pods 而已,所以**並不會影響**已經運行的 Pods
- `replica count` **會影響**。因為數量的增減可能會導致既有的 Pods 被刪掉
- `pod template` **不會影響**。 ReplicationController 不管 Pod 的**內容** 只管對他複製的數量
### 創建 ReplicationController
```yaml=
apiVersion: v1
kind: ReplicationController
metadata:
name: kubia
spec:
replicas: 3 # Pod 複製的數量
selector: # 表示該 RC 所要管理的 Pod group
app: kubia
template: # Pod 模板,用來創建新的 Pod
metadata:
labels:
app: kubia
spec:
containers:
- name: kubia
image: luksa/kubia
ports:
- containerPort: 8080
```
*可以不用明確寫出 RC 的 selector 因為預設是會使用 Pod 的 label*
*由 RC 創建的 PO 並不意味著被 RC 綁定,儘管如此還是可以透過 PO 的 `ownerReferences` 來查看該 PO 是屬於哪個 RC*
### 刪除 ReplicationController
在這裏 `cascade` 參數為 false 是為了防止刪除 RC 時 **級聯** 旗下管理的 Pods
> kubectl delete rc kubia --cascade=false
![](https://i.imgur.com/DxmEIi3.png)
# ReplicaSet & DaemonSet
![](https://i.imgur.com/7cq15KY.png)
- **RS** 定義一個 Pod 數量讓他們隨機散佈在 K8s cluster 中
- **DS** **特殊需求** ,要讓 cluster 中的每個 worker node 都有一個該服務的 Pod 。例如,像是 log collector 或是 memory monitor 的程式需要在每個節點上。在 K8s 系統中 kube-proxy Pod 也是運行在每一個節點上。
## ReplicaSet
> FIXME: 放進 pod 裡面,記得看 deployment
1. ReplicaSet 是用來取代 ReplicatinController 的 (最終 RC 是會被棄用的)
2. 請使用 ReplicaSet
3. 通常不會直接使用 ReplicaSet 而是用更高層次的 Deployment
4. 基本上兩者功能都相同,但是在 label selector 上 ReplicaSet **可以同時比較兩個 labels 以上,還有直接判斷是否有該 key 而不管它的 value (e.g. env=\*)**
```yaml=
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: kubia
spec:
replicas: 3
selector:
matchLabels:
app: kubia
#matchExpressions:
#- key: app # 鍵 必須為 'app'
# operator: In # 值 必須為 'kubia'
# values:
# - kubia
template:
metadata:
labels:
app: kubia
spec:
containers:
- name: kubia
image: luksa/kubia
```
詳情可見 [label selector](https://hackmd.io/exfgC8MzSfqKsJgkWIpkJA?both#Labels-Selectors)
## DaemonSet
**DS 只會在每一個 Node 上執行一個 Pod**
因此 DaemonSet 不需要透過 K8s scheduler 來安排 Pod 在哪個節點上
### 將 Pod 部署到特定的節點上
> FIXME: node 以標籤搜尋 (用資源?)
比較簡單的做法是直接在 Pod 上定義 `nodeSelector`。 DS 也提供類似的功能。那為啥不直接在 Pod 上定義呢?
:::danger
在更進階的觀念中,節點可以是 **unschedulable** ,然而 DS 是不需要經過 K8s-scheduler 的,所以由 DS 管理的 Pod 都可以繞過它直接部署在 unschedulable node 上
:::
![](https://i.imgur.com/ksxUhIn.png)
> FIXME: selector 底下可不可以其他方式 select
```yaml=
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ssd-monitor
spec:
selector:
matchLabels:
app: ssd-monitor # 管理 label 為 app:ssd-monitor 的 Pod
template:
metadata:
labels:
app: ssd-monitor
spec:
nodeSelector:
disk: ssd # 把 Pod 部署到有 ssd 的節點上
containers:
- name: main
image: luksa/ssd-monitor
```
*如果 nodeSelector 沒有成功找到符合的節點,就不會創建 Pod*
*刪除 RC RS DS 都會連帶刪除旗下的 Pods*
# Service
## Service Discovery
> FIXME: 簡單一句話
由於 K8s 是一個非常動態的系統,所以必須要確保 Node 上的 Pod 在不管是被**建立執行或是重新排程都可以輕易的被找到**。會有這樣的問題是因為重啟或新建立一個 Pod 都會造成 IP 位址的改變,而要確保 fontend 可以隨時的使用這些 Pod 就需要 **Service** 作為中間人來找到相應的 Pod。這也**幫助 client 和 Pod 之間有個 service 來解耦合 (decouple)**。
![](https://i.imgur.com/QxLEOmG.png)
## 基本 Serivce 物件
```yaml=
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
# sessionAffinity: ClientIP # 根據需要來設定
ports:
- port: 80 # service 的 port
targetPort: 8080 # service 會將封包轉送的 port
selector:
app: kubia
```
:::info
`sessionAffinity` 只有 ClientIP 和 None 兩種模式,**並沒有 cookie-based** 。因為 K8 是在 L3 而非 L4 所以只有 TCP 和 UDP
:::
## 多埠 Service 物件
```yaml=
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
- name: http # 外部 80 轉送到內部 8080
port: 80
targetPort: 8080
- name: https # 443 轉送到 8443
port: 443
targetPort: 8443
selector:
app: kubia
```
## 使用命名埠
*好處是萬一不是一個常見的 port 號,可以透過命名的方式讓結構更清晰*
*可以隨時更改底層 (Pod) 的 port 號而不會影響到上層的服務 (Service)*
首先,先為 Pod 建立好 port 的名字
```yaml=
apiVersion: v1
kind: Pod
spec:
containers:
- name: kubia
ports:
- name: http # 容器的 port 號 8080 命名為 http
containerPort: 8080
- name: https # 容器的 port 號 8443 命名為 https
containerPort: 8443
```
再來是為 Pod 建立好 Service
```yaml=
apiVersion: v1
kind: Service
spec:
ports:
- name: http
port: 80
targetPort: http
- name: https
port: 443
targetPort: https
```
> FIXME: cluster ip 和 service 的關係
:::info
為什麼不能 ping cluster IP ?
因為 cluster ip 是一個虛擬 IP 本身不提供服務,所以當有人訪問這組 IP 就會被 iptables 修改並且 forward 掉
:::
# 連接 cluster 外部的 Service
## 介紹 Service endpoints
### 基本概念
事實上 Service 和 Pod 並不是直接有所連結的,而是有一個叫做 **endpoints** 的資源在中間。
透過以下指令可以看到 Service 有一個 Endpoints 的資訊
> kubectl describe svc kubia
![](https://i.imgur.com/XfkaV9c.png)
**Endpoints 資源(複數)是一組組 Pod 的 IP 和 Port 向 Service expose 的清單**
根據以上概念,既然它也是資源那也可以直接 kubctl 查看
> kubectl get endpoints kubia
![](https://i.imgur.com/1HNBfID.png)
::: info
### Service -> Endpoints -> Pod 的終極概念
- 雖然已經在 Serivce 的 `spec` 上定義了 pod selector,但在重新導向新進來的連線時它並沒有被直接的使用。與此相反的事 selector 是被用來建立一個 IP 和 Port 的清單,並且被儲存到 Endpoints 這個資源當中。
- 所以當用戶連線到 Service 時, Service 代理會選擇其中一組已經在 Endpoints 定義的 IP 和 Port 並且將連線重新導向到那個位址。
:::
## 手動的配置 service endpoints
有 pod selector , Service 就會自動幫你建立 Endpoints
### 創建一個沒有 selector 的 Service
沒有 selector 就必須手動建立 **Service + Endpoints**
```yaml=
apiVersion: v1
kind: Service
metadata:
name: external-service # 注意!! Service 的名字必須和 Endpoints 一樣
spec: # 無需定義 selector
ports:
- port: 80
```
### 為沒有 selector 的 Service 創建 Endpoints
```yaml=
apiVersion: v1
kind: Endpoints
metadata:
name: external-service # Endpoints 的名字必須和 Service 的一樣
subsets:
- addresses:
- ip: 11.11.11.11 # Service 將連接轉發到的端點的 IP
- ip: 22.22.22.22
ports:
- port: 80
```
Service 和 Endpoints 建立起來後,就可以讓 Pods 消費具有兩個外部端點的 Service
> FIXME: Pod 是要用 hostname or IP 連到 Service ?
![](https://i.imgur.com/ejnt4Nc.png)
:::info
> FIXME: 查閱
### 內部與外部 Service 的移動
如果想要把外部 Service 到 Pod 的服務跑在 cluster 裡面只需在 Service 上加入 selector 就會自動產生 Endpoints 並且自動管理。反之,就是上面所講的。**也就是 Service 的 IP 是維持不變的但實際服務的內容可以不停改變**
:::
## 為外部 Service 創建 alias
除了上述所及手動創建 Service 和 Endpoints,也可以使用 `FQDN` 來讓 cluster 存取外部的服務
### 創建 ExternalName Service
```yaml=
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
type: ExternalName # Service 的類型為 ExternalName
externalName: someapi.somecompany.com # 真實服務的 FQDN
ports:
- port: 80
```
執行上面的 ExternalName Service 後可以讓 cluster 的 Pod 透過訪問 `external-service.default.svc.cluster.local` (FQDN) 來存取外部的服務。**這樣的方式可以向 Pod 隱藏呼叫服務的名字和位址**,這樣的好處有 :
1. 不影響 Pod 的情況下,在 ExternalName Service 上改變存取的外部服務的位址
2. 不影響 Pod 的情況下,也可以把 Service 改回成 ClusterIP
:::info
### ExternalName Service 沒有 cluster IP ?
YES!!!
這種類型的 Service 只會有 **[CNAME](https://support.dnsimple.com/articles/differences-a-cname-records/)** ,因此 Pod 連線該 Service 時會直接連線到外部的服務,而不會經過 Service proxy。
:::
# 讓外部的用戶連線到 cluster 的 Service
![](https://i.imgur.com/eonnWmB.png)
讓外部來連線 Service 有以下幾種方式
1. 將 Service 的類型設為 `NodePort`
- 每一個在 cluster 裡面的 Node 都會開一個 Port ,然後把流量轉送到底層的 Service
- 該 Service 不僅可以透過 ClusterIP 和 Port 來訪問,也可以透過每個 Node 上的 Port
3. 將 Service 的類型設為 `LoadBalancer`
- 是 NodePort 的擴充版
- 是根據不同 cloud infrastructure 而定
- 用戶通過 load balancer 的 IP 來訪問 service
4. 建立 `Ingress` 資源,一種根本不同的機制,用於通過單個 IP 公開多個服務
- 它是在 Layer 7 上運作的
## NodePort
```yaml=
apiVersion: v1
kind: Service
metadata:
name: kubia-nodeport
spec:
type: NodePort
ports:
- port: 80 # 這是內部 cluster IP 的 port
targetPort: 8080 # 這是 Pod 的 port
nodePort: 30123 # Service 可以從 Node 的 port 來被存取
selector:
app: kubia
```
可以使用以下指令來檢查 NodePort Service
> kubectl get svc kubia-nodeport
![](https://i.imgur.com/WTTVeNc.png)
可以看到 `EXTERNAL-IP` 是 `<nodes>` ,也就是說該 Service 可以在每一個 node 上被存取透過 30123 port
![](https://i.imgur.com/frEDH1l.png)
## LocadBalancer
![](https://i.imgur.com/rjN3ViF.png)
### externalTrafficPolicy 在 NodePort Service 上的設定
為什麼需要 externalTrafficPolicy ?
**情況** 當連線通過 NodePort 進來會丟給 Service 來統一分配到所有可以接收的 Pod ,所以這樣有可能有額外的消耗(需要丟到不同 Node 上的 Pod)
因此可以透過設定 externalTrafficPolicy 將連線留在本地的 node 。
> FIXME: 對 node 還是對 Pod 做 loadbalancer
```yaml=
spec:
externalTrafficPolicy: Local
...
```
**缺點**
1. 當本地 node 沒有可接受連線的 Pod 時就會一直 pending,所以必須確保該 node 上面一定有這個 pod
2. 也有可能不會達到真正的附載平衡
- ![](https://i.imgur.com/TWjEkhJ.png)
## Ingress
### 為什麼一定需要 Ingress ?
每一個 LoadBalancer Service 都需要有自己的 IP,而 Ingress 只需要一個 IP。
![](https://i.imgur.com/vaXiJZd.png)
> FIXME: rephrase
**Ingress 是在 Layer 7 上面運作,所以可以提供更多功能比起 Service 來說**
### Ingress Controller
必須記得 K8s cluster 中必須要有 Ingress controller 才可以讓 Ingress 正常運作。不同的的 K8s 環境在 controller 實作上也不同。
### 創建 Ingress
```yaml=
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: kubia
spec:
rules:
- host: kubia.example.com
http:
paths:
- path: /
backend:
serviceName: kubia-nodeport
servicePort: 80
# - path: /foo
# backend:
# serviceName: bar
# servicePort: 80
# - host: bar.example.com
# http:
# paths:
# - path: /
# backend:
# serviceName: bar
# servicePort: 80
```
:::info
在 GKE 中,要求 service 必須為 NodePort ,但在 k8s 系統中是沒有這個要求的
:::
一個 client 向 K8s cluster 請求的基本流程
> FIXME: ingress 流量直接倒倒 Pod ?
![](https://i.imgur.com/5rSHMf8.png)
# Service Mesh
## 傳統 Microservice 不同服務間的交互
![](https://i.imgur.com/l1QNyrQ.png)
### 缺點
1. 在公有雲的部署上,**較不安全**
2. 在交錯複雜的系統中,必須個別實現安全機制,**不易開發和維護**
### 定義
:::info
用來描述組成應用程式的微服服網路之間的交互
The term service mesh is used to describe the network of microservices that make up such applications and the interactions between them.
:::
### 說明
將 Microservices 的 `ISC` 給獨立抽象出來,也就是主要負則 service 間的網路通訊。
> FIXME: sidecar 是如何做到的 (把什麼 inject ;如何攔截流量)
### Sidecar 模式
> FIXME: sidecar for pod-to-pod communication? 塞去哪?
![](https://i.imgur.com/ScdWAxL.png)
輕鬆簡單地為現有已經部署的微服務進行 `load-balancing`, `service-to-service authentication`, `monitoring` 等等,而 **無需改動現有的服務**
# Istio
## 架構
> FIXME: pod 要連到其他的 service 時需要經由自己的 service 嗎?
![](https://i.imgur.com/7iwg60B.png)
Istio service mesh 在邏輯上可以分為 `Control plane` 和 `Data plane`
1. `Data plane` 是由一組 proxies (**envoy**) 所組成,並以 **Sidecar** 的方式來部署
2. `Control plane` 專門用來管理和設定這些 *路由流量* 的 proxies
#### Pilot
**為 Envoy Sidecar 提供 Service Discovery 的服務**
![](https://i.imgur.com/0cNCHJi.png)
#### Citadel
> FIXME: 怎麼認證?
Enables strong service-to-service and end-user authentication with built-in identity and credential management.
:::info
#### Sidecar deployment 的效果
1. 使 Istio 可以將大量有關流量的訊號抽取出來成為不同的 *屬性*。Istio 可以通過這些屬性來執行不同的 *政策決定 (policy decisions)* ,並將他們送到監控系統來提供當前 service mesh 的行為資訊。
2. 可以將不同的功能加入到現有 Istio 而無需重構架構或是程式碼。
:::
:::warning
#### Traffic
流量在 Istio 的定義中可以分為 `Data plane traffic` 和 `Control plane traffic` 。 Envoy proxies 是 Istio 架構中唯一與 Data plane traffic 有關的組件。
:::
## 流量管理
> FIXME: 流量管理是在管理什麼
Istio 的流量管理模組是使用 **envoy proxies** ,隨著你的服務一起部署上去。**所有的流量不論是由 service mesh 接收或送出都會由 envoy 來 proxy**。
:::info
`Envoy` 是一種 L7 proxy 和 communication bus (**API gateway**) 被設計用來在大型 **`SOA` Service-oriented Architectures** 上。
:::
透過簡單規則配置和流量路由控制服務之間的流量和API調用的流量。簡化了 Service 一些屬性上的配置。
1. [Ciciut breaker](https://ithelp.ithome.com.tw/articles/10194912)
- 服務自我檢查的機制
- 避免單一服務崩潰,造成整體服務連鎖崩壞
2. A/B Testing
- ![](https://i.imgur.com/epUV9qO.png)
3. Canary rollouts
4. Failure recovery
### Kubernetes 就有做了為什麼還需要 Istio ?
#### 舉例: Version-based Rollout (Canary deployment)
> FIXME: 用一句話解釋
無論我們使用一種部署還是兩種部署,使用容器編排平台(例如Docker,Mesos / Marathon或Kubernetes)的部署功能進行的金絲雀管理都存在一個基本問題:使用實例擴展來管理流量;流量版本分發和副本部署在這些系統中不是獨立的。在kube-proxy循環池中,所有副本Pod(無論版本如何)都被視為相同,因此,管理特定版本接收的流量的唯一方法是控制副本比率。將金絲雀流量保持在很小的百分比就需要許多副本(例如,如果1%至少需要100個副本)。即使我們忽略了這個問題,部署方法仍然非常有限,因為它僅支持簡單(隨機百分比)canary方法。相反,如果我們希望基於某些特定條件將金絲雀的可見性限制為請求,則仍然需要另一種解決方案。
:::success
#### Using an API Gateway
**API Gateway** 統一集中管理分配來自 client 端的請求路徑,而不是讓 client 自己跟不同的微服務來進行互動。
唯一缺點 : 微服務開發好時, **API Gateway** 仍然沒有開發好像對應的路徑而造成工作延宕。所以 **API Gateway** 的程式碼儘量要夠輕量。
![](https://i.imgur.com/2uFlMQ4.png)
:::
## 安全
1. Istio 讓開發人員專注在開發本身而不是安全上。 Istio 提供基礎安全的通訊管道,並大規模管理服務通訊的身份驗證,授權和加密。
2. 預設所有 **`ISC` Inter-service communication** 都是安全的
3. 包含 `pod-to-pod` 和 `service-to-service`
# Volume
```yaml=
volumes:
- name: html
emptyDir:
medium: Memory # 可以指定將資料儲存在 Mem
```
---
<!-- # Job
與 ReplicationController、ReplicaSet、DaemonSet 不同, Job 專門執行會結束任務的 Pod
如果在 Job 執行的過程中不論是任何意外,只要沒有執行完成都會被 reschedule
![](https://i.imgur.com/9tw2XNi.png)
```yaml=
apiVersion: batch/v1
kind: Job
metadata:
name: batch-job
spec:
# 可以不用特別指定 pod selector 。
# 其實預設會直接以 template 裡面定義的 label 來做 seletor
completions: 5 # 按照順序地執行 5 個 Pods
parallelism: 2 # 可以同時讓兩個 Pods 執行t
template:
metadata:
labels:
app: batch-job
spec:
restartPolicy: OnFailure # Job 不能使用預設的 重啟政策 always
containers:
- name: main
image: luksa/batch-job
``` -->
---
<!-- # 安裝
[Set up K8s with rpi](/SyIAnTSeTiubhEirO4Ezfg)
[Set up Microk8s on rpi](https://microk8s.io/docs/)
-->
---
<!-- ### Context
如果想改變 `default namespaces` 可以使用其他的 `default namespaces` 來創建 `context`
> kubectl config set-context my-context --namespace=mystuff
不僅如此,還需要下該指令來讓 k8s 開始使用該 `default namespaces`
> kubectl config use-context my-context
---
:::info
### config
以上的紀錄都寫在 `$HOME/.kube/config` 檔案當中。該檔案還可以幫助我們 **找到且授權** 我們的叢集
:::
-->
---
<!-- ### 哪些 containers 的組合適合成為一個 Pod ?
每當撰寫 Pod 的時候都應該問自己這樣一個問題,**這些 containers 在不同機器上還可以被正確的運作嗎?** 如果可以那就分成不同 Pod,如果不行就應該寫在同一個 Pod 中。
:::warning
### WordPress 需要和 MySQL 放在同一個 Pod 嗎?
NO !!!
這違反了兩點
1. WP 和 mysql 是可以獨立運行在不同機器上來互相溝通的
2. WP 和 mysql 如果在同一個 Pod 中的話,在服務的擴展上會有很多個 mysql 出現。而我們真正需要的是擴展更多的 WP
![](https://i.imgur.com/VBcWjc4.png)
**一個 container 不應該去執行多個 process 。一個 pod 也不應該包含多個 container 如果他們不需要在同一台機器上執行的話。**
::: -->
<!-- ## Labels
> FIXME: 濃縮
**Labels 提供一群物件一個可識別的 metadata**,這為之後分組、操作、查看等動作帶來方便。
Labels value 的規格和 key 是一樣的
Labels key 的規格可選擇性用 `/` 拆成兩部分
1. 前綴 : 必須為 DNS sub‐domain
2. 名字(必要): 必須為字母數字符號
![](https://i.imgur.com/0MYBwc7.png)
## Labels Selectors
Labels Selectors 是用來過濾出含有特定 labels 的一群 K8s 物件
在篩選中可以用 boolean 語法來挑選想要的 Pod
- > kubectl get pods --selector="key=value,key=value, ..."
- > kubectl get pods --selector="key in (value1,valu2)"
當然我們也可以查看該 label 是否有被設定過
> kubectl get deployments --selector="key"
![](https://i.imgur.com/4jvucpG.png)
## Label Selectors in API Objects
在 K8s 部分物件參考到部分物件的時候就需要用到 Labels selector 。以上所描述 imperative command 可以在 K8s object 的 manifest 中定義
### 範例
> app=alpaca,ver in (1, 2)
```yaml=
selector:
matchLabels:
app: alpaca
matchExpressions:
- {key: ver, operator: In, values: [1, 2]} # Compact yaml syntax
```
> app=alpaca,ver=1
```yaml=
selector:
app: alpaca
ver: 1
```
## 小節 : Labels 在 K8s 的架構中
**背景** : K8s 是一個有目的性去耦合的系統。因此每一個元件不會有階層關係也是獨立運作的。
然而在現實中,如果想要連繫不同物件的關係就必需要用到 label 的方式來搭建。例如 :
- `ReplicaSets` 用來創造和維護 Pod 的多個副本的元件。必須透過 selector 來找到他所管理的 Pod 群
- `service load balancer` 也是透過 selector 找到需將流量帶入的 Pod 群
- 在 pod 的 `nodeSelector` 中指定 label 來將該 pod 分配到特定的 node 上
- ....
:::info
### Label vs Annotation
**Labels** 用於識別 Kubernetes cluster 中的物件並有選擇地對其進行分組。Labels 還用於 selector 查詢中,以提供物件(例如Pod)的靈活運行時分組。
**Annotation** 提供了物件範圍的 metadata 的鍵/值存儲,可以由自動化工具和客戶端庫使用。 Annotation 也可以用於保存外部工具(例如第三方調度程序和監視工具)的配置數據。 **Annotation 不是資料庫,只是簡單的記錄一些資料** 。
:::
-->
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>