1. 基礎
本文實操基於k8s 1.22.1
# 可以檢視資源分配情況
kubectl describe node
# 全域性資源情況檢視
kubectl api-resources
1.1 apply
apply 命令可以使用配置檔案建立資源
- -f 使用yaml或json建立資源, 也是比較常用的方式
kubectl apply -f ./my1.yaml # 建立資源
kubectl apply -f ./my1.yaml -f ./my2.yaml # 使用多個檔案建立
kubectl apply -f ./dir # 基於目錄下的所有清單檔案建立資源
kubectl apply -f https://git.io/vPieo # 從 URL 中建立資源
從標準輸出流建立
# 從標準輸入建立多個Pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: busybox-sleep
spec:
containers:
- name: busybox
image: busybox
args:
- sleep
- "1000000"
---
apiVersion: v1
kind: Pod
metadata:
name: busybox-sleep-less
spec:
containers:
- name: busybox
image: busybox
args:
- sleep
- "1000"
EOF
# 建立有多個 key 的 Secret
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
password: $(echo -n "s33msi4" | base64 -w0)
username: $(echo -n "jane" | base64 -w0)
EOF
1.2 get
列出一個或多個資源
型別 | 說明 |
---|---|
pods, pod, po | 列出pod資訊 |
replicationcontroller, rc | 列出副本控制器 |
services, svc | 列出服務 |
ds | 列出守護程式集 |
引數 | 預設值 | 說明 |
---|---|---|
-A, --all-namespace | false | 返回所有名稱空間的資源, 即使指定-n也無效 |
--chunk-size | 500 | 以塊的形式返回大列表,而不是一次返回所有列表。傳遞0以禁用。 |
--ignore-not-found | false | true: 查詢不到資源時不報錯 |
-o, --output | '' | json,yaml,name,go-template,go-template-file,template,templatefile,jsonpath,jsonpath-as-json,jsonpath-file,custom-columns-file,custom-columns,wide yaml: 以yaml格式輸出詳細配置 wide: 以列表輸出較詳細的資訊 name: 僅列印資源名稱 json: 以json格式輸出詳細資訊 |
--server-print | true | 是否列印從伺服器接收的特定列資訊, 比如: STATUS(執行狀態), RESTARTS(重啟次數) |
--show-labels | false | 檢視對應資源設定的標籤 |
--sort-by | '' | 接受 jsonpath 表示式, 比如按名稱排序的pod列表 kubectl get pods --sort-by=.metadata.name |
--template | '' | |
-w, --watch | false | 監控資源變化, 開啟一個常駐任務, 可使用pod name監控單個pod |
-n, --namespace | 名稱空間 | |
1.3 describe
顯示一個或多個資源的詳細狀態,預設情況下包括未初始化的資源。
2. 依賴
2.1 名稱空間 namespace
- namespace的寫法: namespaces, namespace, ns
將資源歸類, 相關的服務放在同一個名稱空間下方便管理
# 建立名稱空間
kubectl create namespace
# 查詢名稱空間
kubectl get ns
# 刪除名稱空間
kubectl delete namespace NAME
# yaml 配置
apiVersion: v1
kind: Namespace
metadata:
name: repo-nexus
labels:
name: repo-nexus
k8s叢集預設擁有如下幾個namespace
NAME STATUS AGE
default Active 2d3h
kube-node-lease Active 2d3h
kube-public Active 2d3h
kube-system Active 2d3h
-
所有NAMESPACED的資源,在建立的時候都需要指定namespace,若不指定,預設會在default名稱空間下
-
相同namespace下的同類資源不可以重名,不同型別的資源可以重名
-
不同namespace下的同類資源可以重名
-
通常在專案使用的時候,我們會建立帶有業務含義的namespace來做邏輯上的整合
2.2 apiVersion
apiVersion | 含義 |
---|---|
alpha | 進入K8s功能的早期候選版本,可能包含Bug,最終不一定進入K8s |
beta | 已經過測試的版本,最終會進入K8s,但功能、物件定義可能會發生變更。 |
stable | 可安全使用的穩定版本 |
v1 | stable 版本之後的首個版本,包含了更多的核心物件 |
apps/v1 | 使用最廣泛的版本,像Deployment、ReplicaSets都已進入該版本 |
資源型別與apiVersion對照表(版本不同也會不太一樣, 具體以官方文件為準)
Kind | apiVersion |
---|---|
ClusterRoleBinding | rbac.authorization.k8s.io/v1 |
ClusterRole | rbac.authorization.k8s.io/v1 |
ConfigMap | v1 |
CronJob | batch/v1beta1 |
DaemonSet | extensions/v1beta1 |
Node | v1 |
Namespace | v1 |
Secret | v1 |
PersistentVolume | v1 |
PersistentVolumeClaim | v1 |
Pod | v1 |
Deployment | v1、apps/v1、apps/v1beta1、apps/v1beta2 |
Service | v1 |
Ingress | extensions/v1beta1 |
ReplicaSet | apps/v1、apps/v1beta2 |
Job | batch/v1 |
StatefulSet | apps/v1、apps/v1beta1、apps/v1beta2 |
快速獲得資源和版本
kubectl explain pod
kubectl explain Pod.apiVersion
2.3 叢集node label
# 查詢node上已有的標籤資訊
kubectl get nodes --show-labels
# 為節點打標籤
kubectl label node k8s-n-1 component=mysql
2.4 configmap
通常用來管理應用的配置檔案或者環境變數, 放一些不是特別敏感的資訊
apiVersion: v1
kind: ConfigMap
metadata:
name: xxxx
namespace: default
data:
# 配置 key: value
MYSQL_HOST: "172.168.23.11"
MYSQL_PORT: "3306"
kubectl create -f configmap.yaml
使用文字檔案建立
MYSQL_HOST=172.168.23.11
MYSQL_PORT=3306
kubectl create configmap xxx --form-env-file=xxx.txt -n default
2.5 secret
管理敏感類資訊, 預設會base64編碼儲存, 有三種型別
- Service Account: 用來訪問Kubernetes API, 有Kubernetes自動建立, 並且會自動掛載到Pod的/run/secrets/kubernetes.io/serviceaccount目錄中; 建立ServiceAccount後, pod中指定serviceAccount後, 自動建立該ServiceAccount對應的secret
- Opaque: base64編碼格式的Secret, 用來儲存密碼, 金鑰等;
- kubernetes.io/dockerconfigjson: 用來儲存私有docker registry的認證資訊
apiVersion: v1
kind: Secret
metadata:
name: xxxx
namespace: default
type: Opaque
data:
# 配置 key: base64(value)
MYSQL_USER: cm9vdA==
MYSQL_PASSWD: MTIzNDU2
kubectl create -f secret.yaml
使用文字檔案建立, 不需要進行base64編碼
MYSQL_USER=root
MYSQL_PASSWD=123456
kubectl create secret generic xxx --form-env-file=xxx.txt -n default
3. Pod
- pod支援的寫法: pods, pod, po
docker排程的是容器,在k8s叢集中,最小的排程單元是Pod
- 與容器引擎解耦 Docker、Rkt。平臺設計與引擎的具體的實現解耦
- 多容器共享網路|儲存|程式 空間, 支援的業務場景更加靈活
# yaml
apiVersion: v1
kind: Pod # 資源型別
metadata: # 基本配置
name: POD_NAME
namespace: MY_NS
labels: # 給pod打標籤, 方便管理, 後面用到會有介紹
component: POD_NAME
spec:
containers: # 容器配置
- name: NAME1 # 容器名
image: IMAGE # 映象倉庫地址
env: # 環境變數
- name: MYSQL_HOST # 指定root使用者的使用者名稱
value: "127.0.0.1"
- name: MYSQL_PASSWD
value: "123456"
ports: # 容器暴漏的埠
- containerPort: 8002
- name: mysql # 一個pod可以擁有多個容器
image: mysql
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
- name: MYSQL_DATABASE
value: "myblog"
# 建立pod
kubectl create -f pod.yaml
kubectl apply -f pod.yaml
kubectl run --image=IMAGE POD_NAME # 更多kubectl run 用法參考kubeclt run -h
# 可以通過試執行獲取基礎yaml檔案
kubectl run --image=nginx --dry-run -o yaml nginx
# 更新服務版本
kubectl apply -f pod.yaml
# 檢視pod, -n 指定名稱空間, -o wide 顯示更多資訊(可以看到排程節點)
kubectl get pods -o wide -n xxx
# 檢視pod的明細資訊及事件
kubectl describe pods -n xxx POD_NAME
# 檢視pod完整的yaml
kubectl get pods -o yaml -n xxx POD_NAME
# 檢視Pod內容器日誌,顯示標準或者錯誤輸出日誌
kubectl logs -n xxx -f POD_NAME -c CONTAINER_NAME
# --tail限定行數, 類似於docker命令 docker logs --tail=10 -f xxxxx
kubectl -n default logs -f --tail=10 nginx -c nginx
# 刪除容器
kubectl delete -f pod.yaml
kubectl delete pod -n xxx POD_NAME
# 進入容器, -c 指定進入哪個容器
kubectl -n xxx exec -it POD_NAME -c NAME1 bash
# 使用 -o wide 我們可以看到pod在哪臺機器, 那麼對應的容器也就是在那臺機器
# 我們可以在相應機器上執行 docker ps 檢視容器資訊
docker ps -a | grep NAME1
# pod 中的容器命名格式為: k8s_<container_name>_<pod_name>_<pod_uuid>
3.1 節點選擇器
- nodeSelector 節點選擇器, 可將pod排程到有某個label的node上, 如果node不存在, pod將不會正常建立, 直到node被打上相應label將會自動部署, 而不用重新建立
spec:
nodeSelector: # 使用節點選擇器將Pod排程到指定label的節點
component: mysql # 指定選擇有mysql標籤的node
3.2 資料持久化
類似於docker的資料掛載, 但我們之前掛載都是在單機上進行, 現在我們在叢集中使用volumes掛載其也僅會掛載在pod所在的機器上, 當我們重新上線時pod被排程到其它機器就依然會存在找不到資料的問題, 在這裡我們使用nodeSelector解決, 當然生產上一般不會用這種方法
...
spec:
volumes:
- name: mysql-data
hostPath:
path: /opt/mysql/data
nodeSelector:
component: mysql
containers:
- name: mysql
image: mysql
...
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
使用PV+PVC連線分散式儲存解決方案
- ceph
- glusterfs
- nfs
3.3 服務健康檢查
兩種探針
- LivenessProbe探針: 存活性探測, 用於判斷容器是否存活, 即Pod是否為running狀態, 如果LivenessProbe探針探測到容器不健康, 則kubelet將kill掉容器, 並根據容器的重啟策略是否重啟, 如果一個不包含LivenessProbe探針, 則Kubelet認為容器的LivenessProbe探針返回值永遠成功
- ReadinessProbe探針: 可用性探測, 用於探測容器是否正常提供服務, 即容器的Ready是否為True, 是否可以接收請求, 如果ReadinessProbe探測失敗, 則容器的Ready將為False, Endpoint Controller控制器將此Pod的Endpoint從對應的serviceEndpoint列表中移除, 不再將任何請求排程此Pod上, 直到下次探測成功. (剔除此Pod不參與接收請求不會將流量轉發給此Pod)
三種型別
-
exec: 執行一個命令, 返回狀態值為0則表示容器健康
-
httpGet: 傳送一個http請求, 返回200-399表示容器健康
-
tcpSocket: 通過容器的IP和Port執行TCP檢查, 如果能建立連線, 則表示容器健康
引數及含義
- initialDelaySeconds: 啟動後第一次執行探測需要等待多少秒
- perioSeconds: 執行探測的頻率. 預設10秒, 最小1秒
- timeoutSeconds: 探測超時時間. 預設1秒, 最小1秒
- successThreshold: 探測失敗後, 最少連續探測成功多少次才被認定為成功. 預設1
- failureThreshold: 探測成功後, 最少連續探測失敗多少次才被認定為失敗. 預設3, 最小1
spec:
containers:
- image: xxx
name: xxx
livenessProbe: # 指定探針, 此處也可指定 readinessProbe
# 使用http請求, 請求容器80埠來判斷容器是否存活
httpGet:
path: /blog/index
port: 80
scheme: HTTP
initialDelaySeconds: 10 # 容器啟動後第一次執行探測需要等待多少秒
perioSeconds: 10 # 執行探測的頻率
timeoutSeconds: 2 # 探測超時時間
3.4 重啟策略
Pod的重啟策略(RestartPolicy)應用於Pod內所有容器, 並僅在Pod所處的Node上由kubelet進行判斷和重啟操作. 當某個容器異常退出或健康檢查失敗時, kubelet將根據RestartPolicy的設定來進行相應的操作. Pod的重啟策略包括:Always、OnFailure和Never,預設值為Always
- Always:當容器失效時(無論是否正常退出),由kubelet自動重啟該容器。
- OnFailure:當容器終止執行且退出碼不為0時,由kubelet自動重啟該容器。
- Never:不論容器執行狀態如何,kubelet都不會重啟該容器。
spec:
restartPolicy: Always
containers:
- name: xxx
image: xxx
args: # 模擬異常退出, 容器引數
- /bin/sh
- -c
- sleep 10 && exit 1
3.5 映象拉取策略
spec:
containers:
- name: xxx
image: xxx
imagePullPolicy: IfNotPresent
映象拉取策略: 預設IfNotPresent
- Always: 總是拉取映象, 即使本地有也從倉庫拉取
- IfNotPresent: 優先使用本地, 本地沒有則去倉庫拉取
- Never: 僅使用本地映象, 本地沒有則報錯
3.6 Pod資源限制
注意:若記憶體使用超出限制,會引發系統的OOM機制,因CPU是可壓縮資源,不會引發Pod退出或重建
spec:
containers:
- name: xxx
image: xxx
resources:
requests:
memory: 100Mi
cpu: 50m
limits:
memory: 100Mi
cpu: 50m
requests:
- 容器使用的最小資源需求, 作用與schedule階段, 作為容器排程時資源分配的判斷依賴
- 只有當節點上可分配的資源量>=request時才允許將容器排程到該節點
- request引數不限制容器的最大可使用資源
- requests.cpu被轉成docker的--cpu-shares引數, 與cgroup cpu.shares功能相同(無論宿主機有多少個cpu或核心, --cpu-shares選項都會按照比例分配cpu資源)
- requests.memory沒有對應的docker引數, 僅作為k8s排程依據
limits:
- 容器能使用的資源最大量
- 設定為0表示對使用的資源不限制, 可無限使用
- 當pod記憶體超過limit時, 會被oom
- 當cpu超過limit時, 不會被kill, 但是會限制不超過limit值
- limits.cpu會被轉為docker的-cpu-qiota引數. 與cgroup cpu.cfs_quota_us功能相同
- limits.memory會被轉成docker的-memory引數. 用來限制容器最大記憶體
3.7 網路
如果在POD中使用"hostNetwork: true"配置網路,pod中執行的應用程式可以直接看到宿主主機的網路介面,宿主機所在的區域網上所有網路介面都可以訪問到該應用程式及埠。宣告pod的網路模式為host模式,效果同docker run --net=host
spec:
hostNetwork: true
3.8 引用配置
3.8.1 引用configmap
spec:
containers:
- name: xxx
image: xxx
env:
- name: MYSQL_HOST
valueFrom:
configMapKeyRef:
name: xxx
key: MYSQL_HOST
- name: MYSQL_PORT
valueFrom:
configMapKeyRef:
name: xxx
key: MYSQL_PORT
- name: MYSQL_DATABASE
value: "xxx"
3.8.2 引用secret
spec:
containers:
- name: xxx
image: xxx
env:
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: xxx
key: MYSQL_USER
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: xxx
key: MYSQL_PASSWD
- name: MYSQL_DATABASE
value: "xxx"
3.9 pod 生命週期
Pod狀態值描述
- Pending: API Server已經建立該Pod,等待排程器排程。
- ContainerCreating: 拉取映象啟動容器中
- Runnung: Pod內所有容器均已建立,且至少有一個容器處於執行狀態、正在啟動狀態或正在重啟狀態。
- Succeeded| Completed: Pod內所有容器均成功執行後退出,且不會再重啟。
- Failed | Error: Pod內所有容器均已退出,但至少有一個容器退出為失敗狀態。
- CrashLoopBackOff: Pod內有容器啟動失敗,比如配置檔案丟失導致主程式啟動失敗
- Unknown: 由於某種原因無法獲取該Pod的狀態,可能由於網路通訊不暢導致。
初始化容器(init)
- 驗證業務應用依賴的元件是否均已啟動
- 修改目錄的許可權
- 調整系統引數
驗證Pod生命週期
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: default
labels:
component: nginx
spec:
# 在業務pod啟動前啟動, 可用於處理一些業務pod啟動前的一些工作
initContainers:
- name: nginx1
image: nginx
# 容器啟動後寫入檔案時間和標記
command: ['sh', '-c', 'echo $(date +%s): INIT >> /loap/timing']
volumeMounts:
- mountPath: /loap
name: timing
containers:
- name: nginx2
image: nginx
command: ['sh', '-c', 'echo $(date +%s): START >> /loap/timing; sleep 10; echo $(date +%s): END >> /loap/timing;']
volumeMounts:
- mountPath: /loap
name: timing
livenessProbe: # 存活性探測探針
exec:
command: ['sh', '-c', 'echo $(date +%s): LIVENESS >> /loap/timing']
readinessProbe: # 可用性探測探針
exec:
command: ['sh', '-c', 'echo $(date +%s): READINESS >> /loap/timing']
lifecycle: # 生命週期
postStart: # 容器啟動的時候觸發
exec:
command: ['sh', '-c', 'echo $(date +%s): POST-START >> /loap/timing']
# 須主動殺掉 Pod 才會觸發 pre-stop hook,如果是 Pod 自己 Down 掉,則不會執行 pre-stop hook
preStop:
exec:
command: ['sh', '-c', 'echo $(date +%s): PRE-STOP >> /loap/timing']
volumes:
- name: timing
hostPath:
path: /tmp/loap
kubectl apply -f nginx.yaml
# 檢視pod所在機器
kubectl get pod -o wide
# 在pod所在機器檢視檔案
cat /tmp/loap/timing
1630456396: INIT
1630456398: START
1630456398: POST-START
1630456398: READINESS
1630456406: LIVENESS
1630456406: READINESS
1630456408: END
# 刪除測試pod, 否則由於沒有常駐程式, pod會一直重啟
kubectl delete -f nginx.yaml
4. 控制器
控制器又稱工作負載是用於實現管理pod的中間層,確保pod資源符合預期的狀態,pod的資源出現故障時,會嘗試 進行重啟,當根據重啟策略無效,則會重新新建pod的資源。
- ReplicaSet: 代使用者建立指定數量的pod副本數量,確保pod副本數量符合預期狀態,並且支援滾動式自動擴容和縮容功能
- Deployment:工作在ReplicaSet之上,用於管理無狀態應用,目前來說最好的控制器。支援滾動更新和回滾功能,提供宣告式配置
- DaemonSet:用於確保叢集中的每一個節點只執行特定的pod副本,通常用於實現系統級後臺任務。比如EFK服務
- Job:只要完成就立即退出,不需要重啟或重建
- Cronjob:週期性任務控制,不需要持續後臺執行
- StatefulSet:管理有狀態應用
4.1 Deployment
Deployment支援的寫法: deployment, deploy
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: default
spec:
replicas: 1 # 指定Pod副本數
selector: # 指定Pod的選擇器
matchLabels:
app: nginx
template: # temlate中內容與建立pod基本一致
metadata:
labels: # 給Pod打label
app: nginx
spec:
containers:
- image: nginx
name: nginx
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
# 建立Deployment
kubectl create -f nginx.yaml
kubectl create deploy --image=nginx nginx
# 通過試執行獲取基礎yaml
kubectl create deploy --image=nginx nginx --dry-run -o yaml
# 檢視Deployment, -n 可加名稱空間
kubectl get deploy
# 會自動建立replicaSet, 可以檢視
kubectl get replicaset(rs)
# 使用建立檔案刪除Deployment
kubectl delete -f nginx.yaml
# 更新--修改配置檔案後執行apply
kubectl apply -f nginx.yaml
# 線上更新, 類似於vim編輯器, 儲存後自動更新
kubectl edit deploy nginx
# 直接使用命令更新
kubectl set image deploy nginx nginx=nginx:1.21.1 --record
4.2 副本數
controller(控制器)實時檢測pod狀態,並保障副本數一直處於期望的值
spec:
replicas: 1 # 指定Pod副本數
# 修改副本數
kubectl scale deploy nginx --replicas=2
# 最好是通過修改配置檔案中replicas引數重新apply來修改副本數(保持配置檔案與實際執行狀況一致)
# 我們刪除一個pod, 控制器會重新建立一個
kubectl delete pod nginx-6799fc88d8-7rppv
[root@k8s-n-1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-6799fc88d8-7rppv 1/1 Running 0 9m5s
nginx-6799fc88d8-fnwqr 1/1 Running 0 56s
[root@k8s-n-1 ~]# kubectl delete pod nginx-6799fc88d8-7rppv
pod "nginx-6799fc88d8-7rppv" deleted
[root@k8s-n-1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-6799fc88d8-fnwqr 1/1 Running 0 97s
nginx-6799fc88d8-grxfs 0/1 ContainerCreating 0 2s
4.3 Pod驅逐策略
K8S 有個特色功能叫 pod eviction,它在某些場景下如節點 NotReady,或者資源不足時,把 pod 驅逐至其它節點,這也是出於業務保護的角度去考慮的
4.4 更新策略
spec:
replicas: 2 # 指定Pod副本數
selector: # 指定Pod的選擇器
matchLabels:
app: myblog
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate # 指定更新方式為滾動更新,預設策略,通過kubectl get deploy -o yaml檢視
策略控制:
- maxSurge:最大激增數, 指更新過程中, 最多可以比replicas預先設定值多出的pod數量, 可以為固定值或百分比,預設為desired Pods數的25%。計算時向上取整(比如3.4,取4),更新過程中最多會有replicas + maxSurge個pod
- maxUnavailable: 指更新過程中, 最多有幾個pod處於無法服務狀態 , 可以為固定值或百分比,預設為desired Pods數的25%。計算時向下取整(比如3.6,取3)
# 我們修改一下nginx映象版本kubectl set image deploy nginx nginx=nginx:1.21.1 --record# 檢視滾動更新事件kubectl describe deploy nginx
Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 35s deployment-controller Scaled up replica set nginx-54f48578cf to 1 Normal ScalingReplicaSet 32s (x2 over 5m5s) deployment-controller Scaled down replica set nginx-6799fc88d8 to 2 Normal ScalingReplicaSet 32s deployment-controller Scaled up replica set nginx-54f48578cf to 2 Normal ScalingReplicaSet 29s deployment-controller Scaled down replica set nginx-6799fc88d8 to 1 Normal ScalingReplicaSet 29s deployment-controller Scaled up replica set nginx-54f48578cf to 3 Normal ScalingReplicaSet 27s deployment-controller Scaled down replica set nginx-6799fc88d8 to 0
- 我們有三個節點, 所以最多可以增加1個pod, 最多可以有0個pod處於無法服務狀態(即至少要保持三個pod在執行中)
- 3 old pod, 增加的策略觸發成功增加一個pod, 減少的策略觸發失敗
- 3 old pod + 1 new pod, 增加的策略觸發失敗, 減少的策略觸發成功, 減少一箇舊pod
- 2 old pod + 1 new pod, 增加1減少0
- 2 old pod + 2 new pod, 增加0減少1
- 1 old pod + 2 new pod, 增加1減少0
- 1 old pod + 3 new pod, 新的pod數滿足需求, 舊的pod減少1
- 3 new pod 完成更新
4.5 服務回滾
通過滾動升級的策略可以平滑的升級Deployment,若升級出現問題,需要最快且最好的方式回退到上一次能夠提供正常工作的版本。為此K8S提供了回滾機制
revision:更新應用時,K8S都會記錄當前的版本號,即為revision,當升級出現問題時,可通過回滾到某個特定的revision,預設配置下,K8S只會保留最近的幾個revision,可以通過Deployment配置檔案中的spec.revisionHistoryLimit屬性增加revision數量,預設是10。
# 檢視當前版本及版本歷史, 如果是<none>是因為建立和更新的時候沒有加--record
kubectl rollout history deploy nginx
# 我們刪除服務重建一下
kubectl delete -f nginx.yaml
kubectl apply -f nginx.yaml --record
# 然後修改一下
kubectl set image deploy nginx nginx=nginx:1.21.1 --record
# 重新檢視歷史版本
kubectl rollout history deploy nginx
[root@k8s-n-1 ~]# kubectl rollout history deploy nginx
deployment.apps/nginx
REVISION CHANGE-CAUSE
1 kubectl apply --filename=nginx.yaml --record=true
2 kubectl set image deploy nginx nginx=nginx:1.21.1 --record=true
# 回滾到具體的版本
kubectl rollout undo deploy nginx --to-revision=1
[root@k8s-n-1 ~]# kubectl rollout undo deploy nginx --to-revision=1
deployment.apps/nginx rolled back
[root@k8s-n-1 ~]# kubectl rollout history deploy nginx
deployment.apps/nginx
REVISION CHANGE-CAUSE
2 kubectl set image deploy nginx nginx=nginx:1.21.1 --record=true
3 kubectl apply --filename=nginx.yaml --record=true
我們看到本該再增加一條記錄的歷史記錄卻只有兩條記錄, 是因為1和3是同一個版本, 則只保留一條
5. Service
service別名: services, service, svc
通過上面,能夠通過Deployment來建立一組Pod來提供具有高可用性的服務。雖然每個Pod都會分配一個單獨的Pod IP,然而卻存在如下兩個問題:
- Pod IP僅僅是叢集內可見的虛擬IP,外部無法訪問
- Pod IP會隨著Pod的銷燬而消失,當ReplicaSet對Pod進行動態伸縮時,Pod IP可能隨時隨地都會變化,這樣對於訪問這個服務帶來了難度
5.1 Service負載均衡之Cluster IP
service是一組pod的服務抽象,相當於一組pod的LB,負責將請求分發給對應的pod。service會為這個LB提供一個IP,一般稱為cluster IP 。使用Service物件,通過selector進行標籤選擇,找到對應的Pod
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: default
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: ClusterIP
# 建立
kubectl apply -f svc-nginx.yaml
kubectl expose deployment nginx --port=80 --type=ClusterIP
# 檢視svc
kubectl get svc
# 刪除
kubectl delete svc nginx
# 檢視svc詳情
kubectl describe svc nginx
[root@k8s-n-1 ~]# kubectl describe svc nginx
Name: nginx
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=nginx # 根據此label篩選其可操控的pod
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.1.156.133
IPs: 10.1.156.133
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.2.1.12:80,10.2.2.7:80
Session Affinity: None
Events: <none>
我們現在就可以在叢集內部用 10.1.156.133 來訪問服務了
5.1.1 Endpoint
service物件建立的同時,會建立同名的endpoints物件,若服務設定了readinessProbe, 當readinessProbe檢測失敗時,endpoints列表中會剔除掉對應的pod_ip,這樣流量就不會分發到健康檢測失敗的Pod中
# 檢視endpoint, 與service中的Endpoints內容一致
kubectl get endpoints nginx
5.1.2 存在的問題
目前使用hostNetwork部署, 需通過宿主機ip+port訪問, 就會有以下問題
- 服務使用hostNetwork,使得宿主機的埠大量暴漏,存在安全隱患
- 容易引發埠衝突
服務均屬於k8s叢集,儘可能使用k8s的網路訪問,因此可以對目前myblog訪問mysql的方式做改造:
- 為mysql建立一個固定clusterIp的Service,把clusterIp配置在myblog的環境變數中
- 利用叢集服務發現的能力,元件之間通過service name來訪問
5.2 服務發現
在k8s叢集中,元件之間可以通過定義的Service名稱實現通訊
雖然podip和clusterip都不固定,但是service name是固定的,而且具有完全的跨叢集可移植性,因此元件之間呼叫的同時,完全可以通過service name去通訊,這樣避免了大量的ip維護成本,使得服務的yaml模板更加簡單。
# 再起一個服務, 來驗證一下
kubectl create deployment tomcat --image=tomcat
kubectl expose deployment tomcat --port=8080 --type=ClusterIP
# 再tomcat容器中請求nginx服務
kubectl exec -it tomcat-7d987c7694-qgfxv -- bash
# 可以直接使用svc name訪問服務
curl nginx
# 檢視pod解析配置
cat /etc/resolv.conf
root@tomcat-7d987c7694-qgfxv:~# cat /etc/resolv.conf
nameserver 10.1.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
# 上面的10.1.0.10是dns的ip
[root@k8s-n-1 ~]# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.1.0.10 <none> 53/UDP,53/TCP,9153/TCP 10d
啟動pod的時候,會把kube-dns服務的cluster-ip地址注入到pod的resolve解析配置中,同時新增對應的namespace的search域。 因此跨namespace通過service name訪問的話,需要新增對應的namespace名稱
# 根據resolv.conf的配置, 下述地址都可請求nginx服務
curl nginx
curl nginx.default # 可實現跨名稱空間通訊
curl nginx.default.svc
curl nginx.default.svc.cluster.local
5.3 Service負載均衡之NodePort
cluster-ip為虛擬地址,只能在k8s叢集內部進行訪問,叢集外部如果訪問內部服務,實現方式之一為使用NodePort方式。NodePort會預設在 30000-32767 ,不指定的會隨機使用其中一個
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: default
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: NodePort
# 建立
kubectl apply -f svc-nginx.yaml
kubectl expose deployment nginx2 --port=80 --type=NodePort
# 啟動後就可用機器ip(任意node都可以)+port訪問nginx服務了
curl 192.169.8.1:31429
curl 192.169.8.2:31429
curl 192.169.8.3:31429
我們也可以在同一區域網內的瀏覽器訪問到nginx服務
5.4 kube-proxy
執行在每個節點上,監聽 API Server 中服務物件的變化,再通過建立流量路由規則來實現網路的轉發。官方文件
有三種模式:
- User space, 讓 Kube-Proxy 在使用者空間監聽一個埠,所有的 Service 都轉發到這個埠,然後 Kube-Proxy 在內部應用層對其進行轉發 , 所有報文都走一遍使用者態,效能不高,k8s v1.2版本後廢棄
- Iptables, 當前預設模式,完全由 IPtables 來實現, 通過各個node節點上的iptables規則來實現service的負載均衡,但是隨著service數量的增大,iptables模式由於線性查詢匹配、全量更新等特點,其效能會顯著下降
- IPVS, 與iptables同樣基於Netfilter,但是採用的hash表,因此當service數量達到一定規模時,hash查表的速度優勢就會顯現出來,從而提高service的服務效能。 k8s 1.8版本開始引入,1.11版本開始穩定,需要開啟宿主機的ipvs模組
iptables模式示意圖:
[root@k8s-n-1 ~]# iptables-save | grep "default/nginx"
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/nginx" -m tcp --dport 31429 -j KUBE-SVC-2CMXP7HKUVJN7L6M
-A KUBE-SEP-QEWF37NALXUNKCQB -s 10.2.1.13/32 -m comment --comment "default/nginx" -j KUBE-MARK-MASQ
-A KUBE-SEP-QEWF37NALXUNKCQB -p tcp -m comment --comment "default/nginx" -m tcp -j DNAT --to-destination 10.2.1.13:80
-A KUBE-SEP-THS5ZWJX3A5H6VN4 -s 10.2.1.12/32 -m comment --comment "default/nginx" -j KUBE-MARK-MASQ
-A KUBE-SEP-THS5ZWJX3A5H6VN4 -p tcp -m comment --comment "default/nginx" -m tcp -j DNAT --to-destination 10.2.1.12:80
-A KUBE-SERVICES -d 10.1.162.20/32 -p tcp -m comment --comment "default/nginx cluster IP" -m tcp --dport 80 -j KUBE-SVC-2CMXP7HKUVJN7L6M
-A KUBE-SVC-2CMXP7HKUVJN7L6M ! -s 10.2.0.0/16 -d 10.1.162.20/32 -p tcp -m comment --comment "default/nginx cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SVC-2CMXP7HKUVJN7L6M -p tcp -m comment --comment "default/nginx" -m tcp --dport 31429 -j KUBE-MARK-MASQ
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-THS5ZWJX3A5H6VN4
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx" -j KUBE-SEP-QEWF37NALXUNKCQB
6. Kubernetes服務訪問之Ingress
對於Kubernetes的Service,無論是Cluster-Ip和NodePort均是四層的負載,叢集內的服務如何實現七層的負載均衡,這就需要藉助於Ingress,Ingress控制器的實現方式有很多,比如nginx, Contour, Haproxy, trafik, Istio。
Ingress-nginx是7層的負載均衡器 ,負責統一管理外部對k8s cluster中Service的請求。主要包含:
- ingress-nginx-controller:根據使用者編寫的ingress規則(建立的ingress的yaml檔案),動態的去更改nginx服務的配置檔案,並且reload過載使其生效(是自動化的,通過lua指令碼來實現);
- Ingress資源物件:將Nginx的配置抽象成一個Ingress物件
實現邏輯
-
ingress controller通過和kubernetes api互動,動態的去感知叢集中ingress規則變化
-
然後讀取ingress規則(規則就是寫明瞭哪個域名對應哪個service),按照自定義的規則,生成一段nginx配置
-
再寫到nginx-ingress-controller的pod裡,這個Ingress controller的pod裡執行著一個Nginx服務,控制器把生成的nginx配置寫入/etc/nginx/nginx.conf檔案中
-
然後reload一下使配置生效。以此達到域名分別配置和動態更新的問題
6.1 安裝
# 一般多試幾次就能下載下來
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.0/deploy/static/provider/baremetal/deploy.yaml
sed -i "s#k8s.gcr.io/ingress-nginx/controller:v1.0.0.*#registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:v1.0.0#" deploy.yaml
sed -i "s#k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.0.*#hzde0128/kube-webhook-certgen:v1.0#g" deploy.yaml
kubectl apply -f deploy.yaml
kubectl get pod -n ingress-nginx -owide
kubectl describe pod -n ingress-nginx
# 檢視webhook
kubectl get validatingwebhookconfigurations
# 刪除ingress-nginx-admission
kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
6.2 使用
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
# 可通過kubectl get ingressclass 檢視ingressClassName
ingressClassName: nginx
rules:
- host: nginx.openstudy.space
http:
paths:
- path: /
pathType: Prefix
backend:
service:
# 服務的service name
name: nginx
port:
# 服務開放的埠
number: 80
kubectl apply -f ingress.yaml
# 沒有域名可以新增hosts測試, 換成自己的ip
vi /etc/hosts
# 192.169.8.1 nginx.openstudy.space
yum -y install
ifconfig cni0 down
ifconfig flannel.1 down
ifconfig del flannel.1
ifconfig del cni0
ip link del flannel.1
ip link del cni0
yum -y install bridge-utils
brctl delbr flannel.1
kubectl delete -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
ip link del flannel.1
ip link del cni0
rm -rf /var/lib/cni/
rm -f /etc/cni/net.d/*
systemctl restart kubelet
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml