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 configmap.yaml
使用文本文件創建, 不需要進行base64編碼
MYSQL_USER=rootMYSQL_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。平台設計與引擎的具體的實現解耦
- 多容器共享網絡|存儲|進程 空間, 支持的業務場景更加靈活
# yamlapiVersion: v1kind: Pod # 資源類型metadata: # 基本配置 name: POD_NAME namespace: MY_NS labels: # 給pod打標簽, 方便管理, 后面用到會有介紹 component: POD_NAMEspec: 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"
# 創建podkubectl create -f pod.yamlkubectl apply -f pod.yamlkubectl 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完整的yamlkubectl 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 xxxxxkubectl -n default logs -f --tail=10 nginx -c nginx# 刪除容器kubectl delete -f pod.yamlkubectl 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: INIT1630456398: START1630456398: POST-START1630456398: READINESS1630456406: LIVENESS1630456406: READINESS1630456408: 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/v1kind: Deploymentmetadata: name: nginx namespace: defaultspec: 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
# 創建Deploymentkubectl create -f nginx.yaml kubectl create deploy --image=nginx nginx# 通過試運行獲取基礎yamlkubectl create deploy --image=nginx nginx --dry-run -o yaml# 查看Deployment, -n 可加命名空間kubectl get deploy# 會自動創建replicaSet, 可以查看kubectl get replicaset(rs) # 使用創建文件刪除Deploymentkubectl delete -f nginx.yaml # 更新--修改配置文件后執行applykubectl 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 podNAME READY STATUS RESTARTS AGEnginx-6799fc88d8-7rppv 1/1 Running 0 9m5snginx-6799fc88d8-fnwqr 1/1 Running 0 56s[root@k8s-n-1 ~]# kubectl delete pod nginx-6799fc88d8-7rppvpod "nginx-6799fc88d8-7rppv" deleted[root@k8s-n-1 ~]# kubectl get podNAME READY STATUS RESTARTS AGEnginx-6799fc88d8-fnwqr 1/1 Running 0 97snginx-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.yamlkubectl 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 nginxdeployment.apps/nginx REVISION CHANGE-CAUSE1 kubectl apply --filename=nginx.yaml --record=true2 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=1deployment.apps/nginx rolled back[root@k8s-n-1 ~]# kubectl rollout history deploy nginxdeployment.apps/nginx REVISION CHANGE-CAUSE2 kubectl set image deploy nginx nginx=nginx:1.21.1 --record=true3 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: v1kind: Servicemetadata: name: nginx namespace: defaultspec: 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=tomcatkubectl 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.confnameserver 10.1.0.10search default.svc.cluster.local svc.cluster.local cluster.localoptions ndots:5
# 上面的10.1.0.10是dns的ip[root@k8s-n-1 ~]# kubectl get svc -n kube-systemNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEkube-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 nginxcurl nginx.default # 可實現跨命名空間通訊curl nginx.default.svccurl nginx.default.svc.cluster.local
5.3 Service負載均衡之NodePort
cluster-ip為虛擬地址,只能在k8s集群內部進行訪問,集群外部如果訪問內部服務,實現方式之一為使用NodePort方式。NodePort會默認在 30000-32767 ,不指定的會隨機使用其中一個
apiVersion: v1kind: Servicemetadata: name: nginx namespace: defaultspec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: nginx type: NodePort
# 創建kubectl apply -f svc-nginx.yamlkubectl expose deployment nginx2 --port=80 --type=NodePort
# 啟動后就可用機器ip(任意node都可以)+port訪問nginx服務了curl 192.169.8.1:31429curl 192.169.8.2:31429curl 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