Kubernetes部署Cassandra为何选用StatefulSet而非Deployment?附StatefulSet转Deployment请求
为什么Cassandra更适合用StatefulSet而不是Deployment?
这得从Cassandra的分布式有状态特性,以及Kubernetes中StatefulSet和Deployment的核心差异说起:
稳定的网络身份需求:Cassandra集群的节点依赖固定的标识符和DNS地址进行通信(比如你的配置里的
CASSANDRA_SEEDS用了cassandra-0.cassandra.default.svc.cluster.local)。StatefulSet会给每个Pod分配固定的、可预测的名称(如cassandra-0、cassandra-1),对应的DNS记录也会长期稳定;而Deployment管理的Pod是无状态的,名称是随机生成的(比如cassandra-xyzab),重启或重建后身份会完全改变,直接导致Cassandra集群的种子节点失效、节点间通信中断。独立持久化存储:Cassandra每个节点需要专属的数据存储目录,不能和其他节点共享。StatefulSet的
volumeClaimTemplates会为每个Pod自动创建独立的PVC,即使Pod被删除,对应的PVC和存储的数据也会保留;如果用Deployment,要么所有Pod共享同一个PVC(会导致数据冲突、损坏),要么手动创建多个PVC但无法和Pod一一绑定,管理起来非常混乱。有序的部署/扩缩容/终止:Cassandra集群要求节点的启动、下线必须有序进行——启动时要确保种子节点先就绪,新节点才能加入同步数据;下线时要先执行
nodetool drain排空流量再终止。StatefulSet会严格按照顺序(启动0→1→2,终止2→1→0)操作Pod,而Deployment是并行处理所有Pod的,很容易引发集群状态不稳定、数据丢失的问题。
把StatefulSet转成Deployment的尝试(不推荐!)
虽然技术上可以把你的StatefulSet改成Deployment,但这完全违背了Cassandra的运行需求,会导致集群无法稳定工作(比如节点身份混乱、数据共享冲突、扩缩容时集群崩溃等)。如果只是用来做临时测试,下面是修改后的Deployment配置,但请务必注意其局限性:
apiVersion: apps/v1 kind: Deployment metadata: name: cassandra labels: app: cassandra spec: replicas: 3 selector: matchLabels: app: cassandra template: metadata: labels: app: cassandra spec: terminationGracePeriodSeconds: 1800 containers: - name: cassandra image: gcr.io/google-samples/cassandra:v13 imagePullPolicy: Always ports: - containerPort: 7000 name: intra-node - containerPort: 7001 name: tls-intra-node - containerPort: 7199 name: jmx - containerPort: 9042 name: cql resources: limits: cpu: "500m" memory: 1Gi requests: cpu: "500m" memory: 1Gi securityContext: capabilities: add: - IPC_LOCK lifecycle: preStop: exec: command: - /bin/sh - -c - nodetool drain env: - name: MAX_HEAP_SIZE value: 512M - name: HEAP_NEWSIZE value: 100M # 注意:这里的种子节点无法用固定Pod地址,只能用Service地址,会导致集群发现问题 - name: CASSANDRA_SEEDS value: "cassandra.default.svc.cluster.local" - name: CASSANDRA_CLUSTER_NAME value: "K8Demo" - name: CASSANDRA_DC value: "DC1-K8Demo" - name: CASSANDRA_RACK value: "Rack1-K8Demo" - name: POD_IP valueFrom: fieldRef: fieldPath: status.podIP readinessProbe: exec: command: - /bin/bash - -c - /ready-probe.sh initialDelaySeconds: 15 timeoutSeconds: 5 # 注意:这里只能用共享存储或者emptyDir,emptyDir会在Pod删除时丢失数据 volumeMounts: - name: cassandra-data mountPath: /cassandra_data volumes: # 这里用emptyDir仅作测试,生产或长期测试必须用PersistentVolume,但多个Pod共享会出问题 - name: cassandra-data emptyDir: {} --- kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: fast provisioner: k8s.io/minikube-hostpath parameters: type: pd-ssd
⚠️ 关键局限性说明:
- 种子节点只能用Service地址,Cassandra集群无法稳定识别节点身份,容易出现脑裂或节点无法加入的情况。
- 存储用了
emptyDir,Pod删除后数据直接丢失;如果换成共享PVC,多个Cassandra节点会同时读写同一目录,必然导致数据损坏。 - Deployment的Pod是并行启动/终止的,没有有序性,很容易引发集群状态异常。
所以强烈建议你还是使用StatefulSet来部署Cassandra,这是Kubernetes针对有状态分布式服务的最佳实践。
备注:内容来源于stack exchange,提问作者best_of_man

