概述
在傳統的虛機/物理機環境里, 如果我們想要對一個有狀態應用擴容, 我們需要做哪些步驟?
- 申請虛機/物理機
- 安裝依賴
- 下載安裝包
- 按規范配置主機名, hosts
- 配置網路: 包括域名, DNS, 虛 ip, 防火墻...
- 配置監控
今天虛機環境上出現了問題, 是因為 RabbitMQ 資源不足. 手動擴容的程序中花費了較長的時間.
但是在 K8S 上, 有狀態應用的擴容就很簡單, YAML 里改一下replicas副本數, 等不到 1min 就擴容完畢.
當然, 最基本的: 下鏡像, 啟動 pod(相當于上邊的前 3 步), 就不必多提. 那么, 還有哪些因素, 讓有狀態應用可以在 k8s 上快速擴容甚至自動擴容呢?
原因就是這兩點:
- peer discovery +peer discovery 的 相關實作(通過 hostname, dns, k8s api 或其他)
- 可觀察性 + 自動伸縮
我們今天選擇幾個典型的有狀態應用, 一一梳理下:
- Eureka
- Nacos
- Redis
- RabbitMQ
- Kafka
- TiDB
K8S 上有狀態應用擴容
在 Kubernetes 上, 有狀態應用快速擴容甚至自動擴容很容易. 這得益于 Kubernetes 優秀的設計以及良好的生態. Kubernetes 就像是一個云原生時代的作業系統. 它自身就具有:
- 自動化工具;
- 內部服務發現 + 負載均衡
- 內部 DNS
- 和 Prometheus 整合
- 統一的宣告式 API
- 標準, 開源的生態環境.
所以, 需要擴容, 一個 yaml 搞定全部. 包括上邊提到的: 下載, 安裝, 存盤配置, 節點發現, 加入集群, 監控配置...
Eureka 擴容

?? 備注:
有狀態擴容第一層:
StatefulSet + Headless Service
eureka 的擴容在 K8S 有狀態應用中是最簡單的, 就是:
headless service + statefulset
Eureka 要擴容, 只要 eureka 實體彼此能相互發現就可以. headless service 在這種情況下就派上用場了, 就是讓彼此發現.
Eureka 的一個完整集群 yaml, 如下:詳細說明如下:
apiVersion: v1
kind: Service
metadata:
name: eureka
namespace: ms
spec:
clusterIP: None
ports:
- name: eureka
port: 8888
selector:
project: ms
app: eureka
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: eureka
namespace: ms
spec:
serviceName: eureka
replicas: 3
selector:
matchLabels:
project: ms
app: eureka
template:
metadata:
labels:
project: ms
app: eureka
spec:
terminationGracePeriodSeconds: 10
imagePullSecrets:
- name: registry-pull-secret
containers:
- name: eureka
image: registry.example.com/kubernetes/eureka:latest
ports:
- protocol: TCP
containerPort: 8888
env:
- name: APP_NAME
value: "eureka"
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: APP_OPTS
value: "
--eureka.instance.hostname=${POD_NAME}.${APP_NAME}
--registerWithEureka=true
--fetchRegistry=true
--eureka.instance.preferIpAddress=false
--eureka.client.serviceUrl.defaultZone=http://eureka-0.${APP_NAME}:8888/eureka/,http://eureka-1.${APP_NAME}:8888/eureka/,http://eureka-2.${APP_NAME}:8888/eureka/
- 配置名為
eureka的 Service - 在名為
eureka的 statefulset 配置下,共 3 個 eureka 副本, 每個 eureka 的 HOSTNAME 為:${POD_NAME}.${SERVICE_NAME}. 如:eureka-0.eureka - 彼此通過
--registerWithEureka=true --fetchRegistry=true --eureka.instance.preferIpAddress=false --eureka.client.serviceUrl.defaultZone=http://eureka-0.${APP_NAME}:8888/eureka/,http://eureka-1.${APP_NAME}:8888/eureka/,http://eureka-2.${APP_NAME}:8888/eureka/通過 HOSTNAME 相互注冊, 完成了集群的創建.
那么, 如果要快速擴容到 5 個:
- 調整 StatefulSet:
replicas: 5 - 在環境變數
APP_OPTS中加入新增的 2 個副本 hostname:http://eureka-3.${APP_NAME}:8888/eureka/,http://eureka-4.${APP_NAME}:8888/eureka/
即可完成.
Headless Service
有時不需要或不想要負載均衡,以及單獨的 Service IP, 遇到這種情況,可以通過指定 Cluster IP(spec.clusterIP)的值為 None 來創建 Headless Service,
您可以使用無頭 Service 與其他服務發現機制進行介面,而不必與 Kubernetes 的實作捆綁在一起,
對這無頭 Service 并不會分配 Cluster IP,kube-proxy 不會處理它們, 而且平臺也不會為它們進行負載均衡和路由, DNS 如何實作自動配置,依賴于 Service 是否定義了選擇算符,
Nacos

?? 備注:
有狀態擴容第二層:
StatefulSet + Headless Service + Init Container(自動化發現) + PVC
相比 Eureka, nacos 通過一個init container,(這個 init container, 就是一個自動化的 peer discovery 腳本.) , 實作了一行命令快速擴容:
kubectl scale sts nacos --replicas=3
腳本鏈接為: https://github.com/nacos-group/nacos-k8s/tree/master/plugin/peer
擴容的相關自動化操作為:
- 從 Headless Service 自動發現所有的 replicas 的 HOSTNAME;
- 并將 HOSTNAME 寫入到:
${CLUSTER_CONF}這個檔案下. ${CLUSTER_CONF}這個檔案就是 nacos 集群的所有 member 資訊. 將新寫入 HOSTNAME 的實體加入到 nacos 集群中.
在這里, 通過 Headless Service 和 PV/PVC(存盤 nacos 插件或其他資料),實作了對 Pod 的拓撲狀態和存盤狀態的維護,從而讓用戶可以在 Kubernetes 上運行有狀態的應用,
然而 Statefullset 只能提供受限的管理,通過 StatefulSet 我們還是需要撰寫復雜的腳本(如 nacos 的peer-finder相關腳本), 通過判斷節點編號來區別節點的關系和拓撲,需要關心具體的部署作業,
RabbitMQ

?? 備注:
有狀態擴容第三層:
StatefulSet + Headless Service + 插件(自動化發現和監控) + PVC
RabbitMQ 的集群可以參考這邊官方檔案: Cluster Formation and Peer Discovery
這里提到的, 動態的發現機制需要依賴外部的服務, 如: DNS, API(AWS 或 K8S).
對于 Kubernetes, 使用的動態發現機制是基于rabbitmq-peer-discovery-k8s插件 實作的.
通過這種機制,節點可以使用一組配置的值從 Kubernetes API 端點獲取其對等方的串列:URI 模式,主機,埠以及令牌和證書路徑,
另外, rabbitmq 鏡像也默認集成了監控的插件 - rabbitmq_prometheus.
當然, 通過Helm Chart也能一鍵部署和擴容.
Helm Chart
一句話概括, Helm 之于 Kubernetes, 相當于 yum 之于 centos. 解決了依賴的問題. 將部署 rabbitmq 這么復雜的軟體所需要的一大堆 yaml, 通過引數化抽象出必要的引數(并且提供默認引數)來快速部署.
Redis

?? 備注:
有狀態擴容第四層:
通過 Operator 統一編排和管理:
Deployment(哨兵) + StatefulSet + Headless Service + Sidecar Container(監控) + PVC
這里以 UCloud 開源的: redis-operator 為例. 它是基于 哨兵模式 的 redis 集群.
于之前的 StatefulSet + Headless 不同, 這里用到了一項新的 K8S 技術: operator.
Operator 原理
?? 說明:
解釋 Operator 不得不提 Kubernetes 中兩個最具價值的理念:“宣告式 API” 和 “控制器模式”,“宣告式 API”的核心原理就是當用戶向 Kubernetes 提交了一個 API 物件的描述之后,Kubernetes 會負責為你保證整個集群里各項資源的狀態,都與你的 API 物件描述的需求相一致,Kubernetes 通過啟動一種叫做“控制器模式”的無限回圈,WATCH 這些 API 物件的變化,不斷檢查,然后調諧,最后確保整個集群的狀態與這個 API 物件的描述一致,
比如 Kubernetes 自帶的控制器:Deployment,如果我們想在 Kubernetes 中部署雙副本的 Nginx 服務,那么我們就定義一個 repicas 為 2 的 Deployment 物件,Deployment 控制器 WATCH 到我們的物件后,通過控制回圈,最侄訓幫我們在 Kubernetes 啟動兩個 Pod,
Operator 是同樣的道理,以我們的 Redis Operator 為例,為了實作 Operator,我們首先需要將自定義物件的說明注冊到 Kubernetes 中,這個物件的說明就叫 CustomResourceDefinition(CRD),它用于描述我們 Operator 控制的應用:redis 集群,這一步是為了讓 Kubernetes 能夠認識我們應用,然后需要實作自定義控制器去 WATCH 用戶提交的 redis 集群實體,這樣當用戶告訴 Kubernetes 我想要一個 redis 集群實體后,Redis Operator 就能夠通過控制回圈執行調諧邏輯達到用戶定義狀態,
簡單說, operator 可以翻譯為: 運維人(操作員) . 就是將高級原廠運維專家多年的經驗, 濃縮為一個: operator. 那么, 我們所有的 運維打工人 就不需要再苦哈哈的"從零開始搭建 xxx 集群", 而是通過這個可擴展、可重復、標準化、甚至全生命周期運維管理的operator, 來完成復雜軟體的安裝,擴容,監控, 備份甚至故障恢復,
Redis Operator
使用 Redis Operator 我們可以很方便的起一個哨兵模式的集群,集群只有一個 Master 節點,多個 Slave 節點,假如指定 Redis 集群的 size 為 3,那么 Redis Operator 就會幫我們啟動一個 Master 節點,兩個 Salve 節點,同時啟動三個 Sentinel 節點來管理 Redis 集群:

Redis Operator 通過 Statefulset 管理 Redis 節點,通過 Deployment 來管理 Sentinel 節點,這比管理裸 Pod 要容易,節省實作成本,同時創建一個 Service 指向所有的哨兵節點,通過 Service 對客戶端提供查詢 Master、Slave 節點的服務,最終,Redis Operator 控制回圈會調諧集群的狀態,設定集群的拓撲,讓所有的 Sentinel 監控同一個 Master 節點,監控相同的 Salve 節點,Redis Operator 除了會 WATCH 實體的創建、更新、洗掉事件,還會定時檢測已有的集群的健康狀態,實時把集群的狀態記錄到 spec.status.conditions 中.
同時, 還提供了快速持久化, 監控, 自動化 redis 集群配置的能力. 只需一個 yaml 即可實作:
apiVersion: redis.kun/v1beta1
kind: RedisCluster
metadata:
name: redis
spec:
config: # redis集群配置
maxmemory: 1gb
maxmemory-policy: allkeys-lru
password: sfdfghc56s # redis密碼配置
resources: # redis資源配置
limits:
cpu: '1'
memory: 1536Mi
requests:
cpu: 250m
memory: 1Gi
size: 3 # redis副本數配置
storage: # 持久化存盤配置
keepAfterDeletion: true
persistentVolumeClaim:
metadata:
name: redis
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: nfs
volumeMode: Filesystem
sentinel: # 哨兵配置
image: 'redis:5.0.4-alpine'
exporter: # 啟用監控
enabled: true
要擴容也很簡單, 將上邊的size: 3按需調整即可. 調整后, 自動申請資源, 擴容, 加存盤, 改 redis 配置, 加入 redis 集群, 并且自動添加監控.
Kafka

?? 備注:
有狀態擴容第五層:
通過 Operator 統一編排和管理多個有狀態組件的:
StatefulSet + Headless Service + ... + 監控
這里以 Strimzi 為例 - Strimzi Overview guide (0.20.0). 這是一個 Kafka 的 Operator.
提供了 Apache Kafka 組件以通過 Strimzi 發行版部署到 Kubernetes, Kafka 組件通常以集群的形式運行以提高可用性,
包含 Kafka 組件的典型部署可能包括:
- Kafka 代理節點集群集群
- ZooKeeper - ZooKeeper 實體的集群
- Kafka Connect 集群用于外部資料連接
- Kafka MirrorMaker 集群可在第二個集群中鏡像 Kafka 集群
- Kafka Exporter 提取其他 Kafka 指標資料以進行監控
- Kafka Bridge 向 Kafka 集群發出基于 HTTP 的請求
Kafka 的組件架構比較復雜, 具體如下:

通過 Operator, 一個 YAML 即可完成一套復雜的部署:
- 資源請求(CPU /記憶體)
- 用于最大和最小記憶體分配的 JVM 選項
- Listeners (和身份驗證)
- 認證
- 存盤
- Rack awareness
- 監控指標
apiVersion: kafka.strimzi.io/v1beta1
kind: Kafka
metadata:
name: my-cluster
spec:
kafka:
replicas: 3
version: 0.20.0
resources:
requests:
memory: 64Gi
cpu: "8"
limits:
memory: 64Gi
cpu: "12"
jvmOptions:
-Xms: 8192m
-Xmx: 8192m
listeners:
- name: plain
port: 9092
type: internal
tls: false
useServiceDnsDomain: true
- name: tls
port: 9093
type: internal
tls: true
authentication:
type: tls
- name: external
port: 9094
type: route
tls: true
configuration:
brokerCertChainAndKey:
secretName: my-secret
certificate: my-certificate.crt
key: my-key.key
authorization:
type: simple
config:
auto.create.topics.enable: "false"
offsets.topic.replication.factor: 3
transaction.state.log.replication.factor: 3
transaction.state.log.min.isr: 2
ssl.cipher.suites: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" (17)
ssl.enabled.protocols: "TLSv1.2"
ssl.protocol: "TLSv1.2"
storage:
type: persistent-claim
size: 10000Gi
rack:
topologyKey: topology.kubernetes.io/zone
metrics:
lowercaseOutputName: true
rules:
# Special cases and very specific rules
- pattern : kafka.server<type=(.+), name=(.+), clientId=(.+), topic=(.+), partition=(.*)><>Value
name: kafka_server_$1_$2
type: GAUGE
labels:
clientId: "$3"
topic: "$4"
partition: "$5"
# ...
zookeeper:
replicas: 3
resources:
requests:
memory: 8Gi
cpu: "2"
limits:
memory: 8Gi
cpu: "2"
jvmOptions:
-Xms: 4096m
-Xmx: 4096m
storage:
type: persistent-claim
size: 1000Gi
metrics:
# ...
entityOperator:
topicOperator:
resources:
requests:
memory: 512Mi
cpu: "1"
limits:
memory: 512Mi
cpu: "1"
userOperator:
resources:
requests:
memory: 512Mi
cpu: "1"
limits:
memory: 512Mi
cpu: "1"
kafkaExporter:
# ...
cruiseControl:
# ...
當然, 由于 Kafka 的特殊性, 如果要將新增的 brokers 添加到現有集群, 還需要重新磁區, 這里邊涉及的更多操作詳見: Scaling Clusters - Using Strimzi
TiDB

?? 備注:
有狀態擴容第六層:
通過 Operator 統一編排和管理多個有狀態組件的:
StatefulSet + Headless Service + ... + 監控 + TidbClusterAutoScaler(類似 HPA 的實作)
甚至能做到備份和災難恢復.
TiDB 更進一步, 可以實作 有狀態應用自動擴容.
具體見這里: Enable TidbCluster Auto-scaling | PingCAP Docs
Kubernetes 提供了Horizontal Pod Autoscaler ,這是一種基于 CPU 利用率的原生 API, TiDB 4.0 基于 Kubernetes,實作了彈性調度機制,
只需要啟用此功能即可使用:
features:
- AutoScaling=true
TiDB 實作了一個TidbClusterAutoScaler CR 物件用于控制 TiDB 集群中自動縮放的行為, 如果您使用過Horizontal Pod Autoscaler ,大概是您熟悉 TidbClusterAutoScaler 概念, 以下是 TiKV 中的自動縮放示例,
apiVersion: pingcap.com/v1alpha1
kind: TidbClusterAutoScaler
metadata:
name: auto-scaling-demo
spec:
cluster:
name: auto-scaling-demo
namespace: default
monitor:
name: auto-scaling-demo
namespace: default
tikv:
minReplicas: 3
maxReplicas: 4
metrics:
- type: "Resource"
resource:
name: "cpu"
target:
type: "Utilization"
averageUtilization: 80
需要指出的是: 需要向TidbClusterAutoScaler 提供指標收集和查詢(監控)服務,因為它通過指標收集組件捕獲資源使用情況, monitor 屬性參考TidbMonitor 物件(其實就是自動化地配置 TiDB 的 prometheus 監控和展示等), 有關更多資訊,請參見使用TidbMonitor監視TiDB群集,
總結
通過 6 個有狀態軟體, 我們見識到了層層遞進的 K8S 上有狀態應用的快速擴容甚至是自動擴容:
- 最簡單實作: StatefulSet + Headless Service -- Eureka
- 腳本/Init Container 自動化實作: StatefulSet + Headless Service + Init Container(自動化發現) + PVC -- Nacos
- 通過插件實作擴容和監控:StatefulSet + Headless Service + 插件(自動化發現和監控) + PVC -- RabbitMQ
- 通過 Operator 統一編排和管理: -- Redis
- 對于復雜有狀態, 是需要通過 Operator 統一編排和管理多個有狀態組件的: -- Kafka
- 通過 Operator 統一編排和管理多個有狀態組件的: -- TiDB
?????? 解放開發和運維打工人, 是時候在 K8S 上部署有狀態軟體了! ??????
三人行, 必有我師; 知識共享, 天下為公. 本文由東風微鳴技術博客 EWhisper.cn 撰寫.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/539418.html
標籤:其他
上一篇:快來給你的寵物視頻加個表情特效吧
