在 Kubernetes 中运行有状态的 Cassandra 应用
在现代的分布式系统中,Kubernetes 已经成为了容器编排的事实标准,而 Cassandra 作为一款强大的分布式数据库,在处理大规模数据时表现出色。将 Cassandra 与 Kubernetes 集成,可以充分发挥两者的优势,实现高效、可靠的数据存储和管理。本文将详细介绍如何在 Kubernetes 中运行有状态的 Cassandra 应用,包括环境配置、容器启动脚本、与 Kubernetes 的集成以及不同的部署方式。
1. 配置 Cassandra 容器
首先,我们需要配置 Cassandra 容器,确保其能够在 Kubernetes 环境中正常运行。以下是一些关键步骤:
-
登录并设置权限
:登录到 Cassandra 容器,并设置
ready-probe.sh脚本的权限。
login cassandra \
&& chown cassandra: /ready-probe.sh \
- 定义数据卷 :为 Cassandra 数据定义一个卷,确保数据的持久化存储。
VOLUME ["/$CASSANDRA_DATA"]
- 暴露端口 :暴露 Cassandra 节点之间通信以及外部访问所需的重要端口。
# 7000: 节点间通信
# 7001: TLS 节点间通信
# 7199: JMX
# 9042: CQL
# 9160: thrift 服务
EXPOSE 7000 7001 7199 9042 9160
-
启动脚本
:使用
dumb-init作为容器的初始化系统,最终运行run.sh脚本。
CMD ["/sbin/dumb-init", "/bin/bash", "/run.sh"]
2. 解析 run.sh 脚本
run.sh
脚本负责设置 Cassandra 的运行环境和配置。以下是脚本的主要步骤:
- 设置本地变量 :为 Cassandra 配置文件设置本地变量。
set -e
CASSANDRA_CONF_DIR=/etc/cassandra
CASSANDRA_CFG=$CASSANDRA_CONF_DIR/cassandra.yaml
-
设置主机名
:如果未指定
CASSANDRA_SEEDS,则设置主机名。
if [ -z "$CASSANDRA_SEEDS" ]; then
HOSTNAME=$(hostname -f)
fi
- 设置环境变量 :设置一系列环境变量,并为其提供默认值。
CASSANDRA_RPC_ADDRESS="${CASSANDRA_RPC_ADDRESS:-0.0.0.0}"
CASSANDRA_NUM_TOKENS="${CASSANDRA_NUM_TOKENS:-32}"
CASSANDRA_CLUSTER_NAME="${CASSANDRA_CLUSTER_NAME:='Test Cluster'}"
# 其他环境变量设置...
- 打印变量信息 :打印一些重要的变量信息,方便调试。
echo Starting Cassandra on ${CASSANDRA_LISTEN_ADDRESS}
echo CASSANDRA_CONF_DIR ${CASSANDRA_CONF_DIR}
- 配置 Snitch :根据数据中心和机架信息,配置 Snitch 以优化网络拓扑。
if [[ $CASSANDRA_DC && $CASSANDRA_RACK ]]; then
echo "dc=$CASSANDRA_DC" > $CASSANDRA_CONF_DIR/cassandra-rackdc.properties
echo "rack=$CASSANDRA_RACK" >> $CASSANDRA_CONF_DIR/cassandra-rackdc.properties
CASSANDRA_ENDPOINT_SNITCH="GossipingPropertyFileSnitch"
fi
- 内存管理 :控制 Cassandra 的最大堆大小,避免内存交换。
if [ -n "$CASSANDRA_MAX_HEAP" ]; then
sed -ri "s/^(#)?-Xmx[0-9]+.*/-Xmx$CASSANDRA_MAX_HEAP/" "$CASSANDRA_CONF_DIR/jvm.options"
sed -ri "s/^(#)?-Xms[0-9]+.*/-Xms$CASSANDRA_MAX_HEAP/" "$CASSANDRA_CONF_DIR/jvm.options"
fi
- 更新配置文件 :根据环境变量更新 Cassandra 的配置文件。
for yaml in \
broadcast_address \
broadcast_rpc_address \
# 其他配置项...
; do
var="CASSANDRA_${yaml^^}"
val="${!var}"
if [ "$val" ]; then
sed -ri 's/^(# )?('"$yaml"':).*/\2 '"$val"'/' "$CASSANDRA_CFG"
fi
done
echo "auto_bootstrap: ${CASSANDRA_AUTO_BOOTSTRAP}" >> $CASSANDRA_CFG
- 设置种子节点 :根据部署方案设置种子节点或种子提供者。
if [[ $CASSANDRA_SEEDS == 'false' ]]; then
sed -ri 's/- seeds:.*/- seeds: "'"$POD_IP"'"/' $CASSANDRA_CFG
else
sed -ri 's/- seeds:.*/- seeds: "'"$CASSANDRA_SEEDS"'"/' $CASSANDRA_CFG
fi
sed -ri 's/- class_name: SEED_PROVIDER/- class_name: '"$CASSANDRA_SEED_PROVIDER"'/' $CASSANDRA_CFG
- 远程管理和 JMX 监控 :设置远程管理和 JMX 监控选项。
if [[ $CASSANDRA_GC_STDOUT == 'true' ]]; then
sed -ri 's/ -Xloggc:\/var\/log\/cassandra\/gc\.log//' $CASSANDRA_CONF_DIR/cassandra-env.sh
fi
echo "JVM_OPTS=\"\$JVM_OPTS -Djava.rmi.server.hostname=$POD_IP\"" >> $CASSANDRA_CONF_DIR/cassandra-env.sh
# 其他 JMX 相关设置...
- 启动 Cassandra :设置类路径并以 Cassandra 用户身份启动 Cassandra。
export CLASSPATH=/kubernetes-cassandra.jar
su cassandra -c "$CASSANDRA_HOME/bin/cassandra -f"
3. 连接 Kubernetes 和 Cassandra
将 Kubernetes 和 Cassandra 集成需要一些额外的工作,主要涉及种子提供者和 Snitch 的配置。
-
Cassandra 配置解析
:Cassandra 的配置文件
cassandra.yaml中有两个重要的设置:种子提供者和 Snitch。-
种子提供者
:负责发布集群中节点的 IP 地址列表,节点启动时会连接到这些种子节点以发现整个集群。默认的种子提供者是一个静态的 IP 地址列表。
```yaml
seed_provider:-
class_name: SEED_PROVIDER
parameters:-
seeds: “127.0.0.1”
```
-
seeds: “127.0.0.1”
-
class_name: SEED_PROVIDER
-
Snitch
:教 Cassandra 了解网络拓扑,以便高效路由请求,并将副本分布在不同的数据中心和机架上,避免相关故障。默认的 Snitch 是
SimpleSnitch。
yaml endpoint_snitch: SimpleSnitch
-
种子提供者
:负责发布集群中节点的 IP 地址列表,节点启动时会连接到这些种子节点以发现整个集群。默认的种子提供者是一个静态的 IP 地址列表。
- 自定义种子提供者 :在 Kubernetes 中运行 Cassandra 节点时,Kubernetes 可能会移动节点,包括种子节点。因此,需要一个自定义的种子提供者与 Kubernetes API 服务器交互。以下是一个简单的 Java 类示例:
public class KubernetesSeedProvider implements SeedProvider {
/**
* 调用 Kubernetes API 收集种子提供者列表
* @return 种子提供者列表
*/
public List<InetAddress> getSeeds() {
String host = getEnvOrDefault("KUBERNETES_PORT_443_TCP_ADDR", "kubernetes.default.svc.cluster.local");
String port = getEnvOrDefault("KUBERNETES_PORT_443_TCP_PORT", "443");
String serviceName = getEnvOrDefault("CASSANDRA_SERVICE", "cassandra");
String podNamespace = getEnvOrDefault("POD_NAMESPACE", "default");
String path = String.format("/api/v1/namespaces/%s/endpoints/", podNamespace);
String seedSizeVar = getEnvOrDefault("CASSANDRA_SERVICE_NUM_SEEDS", "8");
Integer seedSize = Integer.valueOf(seedSizeVar);
String accountToken = getEnvOrDefault("K8S_ACCOUNT_TOKEN", "/var/run/secrets/kubernetes.io/serviceaccount/token");
List<InetAddress> seeds = new ArrayList<InetAddress>();
try {
String token = getServiceAccountToken(accountToken);
SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(null, trustAll, new SecureRandom());
String PROTO = "https://";
URL url = new URL(PROTO + host + ":" + port + path + serviceName);
logger.info("Getting endpoints from " + url);
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(ctx.getSocketFactory());
conn.addRequestProperty("Authorization", "Bearer " + token);
ObjectMapper mapper = new ObjectMapper();
Endpoints endpoints = mapper.readValue(conn.getInputStream(), Endpoints.class);
// 其他处理...
} catch (Exception e) {
// 异常处理...
}
return Collections.unmodifiableList(seeds);
}
}
- 创建 Cassandra 无头服务 :无头服务允许 Kubernetes 集群中的客户端通过标准的 Kubernetes 服务连接到 Cassandra 集群,而无需跟踪节点的网络标识。以下是配置文件示例:
apiVersion: v1
kind: Service
metadata:
labels:
app: cassandra
name: cassandra
spec:
clusterIP: None
ports:
- port: 9042
selector:
app: Cassandra
4. 使用 StatefulSet 创建 Cassandra 集群
StatefulSet 是 Kubernetes 中用于管理有状态应用的资源,它可以确保每个 Pod 都有一个稳定的网络标识和持久化存储。以下是一个创建三节点 Cassandra 集群的 StatefulSet 配置文件示例:
apiVersion: "apps/v1"
kind: StatefulSet
metadata:
name: cassandra
spec:
serviceName: cassandra
replicas: 3
template:
metadata:
labels:
app: cassandra
spec:
containers:
- name: cassandra
image: gcr.io/google-samples/cassandra:v12
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
env:
- name: MAX_HEAP_SIZE
value: 512M
- name: CASSANDRA_SEEDS
value: "cassandra-0.cassandra.default.svc.cluster.local"
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
readinessProbe:
exec:
command:
- /bin/bash
- -c
- /ready-probe.sh
initialDelaySeconds: 15
timeoutSeconds: 5
volumeMounts:
- name: cassandra-data
mountPath: /cassandra_data
volumeClaimTemplates:
- metadata:
name: cassandra-data
annotations:
volume.beta.kubernetes.io/storage-class: fast
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
5. 使用复制控制器部署 Cassandra
除了 StatefulSet,我们还可以使用复制控制器来部署 Cassandra。复制控制器更符合 Cassandra 的语义,因为它直接使用节点上的存储,而不是依赖于持久化存储声明。以下是复制控制器的配置文件示例:
apiVersion: v1
kind: ReplicationController
metadata:
name: cassandra
spec:
replicas: 3
template:
metadata:
labels:
app: Cassandra
spec:
containers:
- command:
- /run.sh
image: gcr.io/google-samples/cassandra:v11
name: cassandra
resources:
limits:
cpu: 0.5
env:
- name: MAX_HEAP_SIZE
value: 512M
- name: HEAP_NEWSIZE
value: 100M
- name: CASSANDRA_SEED_PROVIDER
value: "io.k8s.cassandra.KubernetesSeedProvider"
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
ports:
- containerPort: 7000
name: intra-node
- containerPort: 7001
name: tls-intra-node
- containerPort: 7199
name: jmx
- containerPort: 9042
name: cql
volumeMounts:
- mountPath: /cassandra_data
name: data
volumes:
- name: data
emptyDir: {}
总结
通过以上步骤,我们可以在 Kubernetes 中成功运行有状态的 Cassandra 应用。无论是使用 StatefulSet 还是复制控制器,都可以根据实际需求选择合适的部署方式。同时,通过自定义种子提供者和 Snitch,我们可以确保 Cassandra 集群在 Kubernetes 环境中高效、稳定地运行。
下面是一个简单的 mermaid 流程图,展示了在 Kubernetes 中部署 Cassandra 的主要步骤:
graph LR
A[配置 Cassandra 容器] --> B[解析 run.sh 脚本]
B --> C[连接 Kubernetes 和 Cassandra]
C --> D{选择部署方式}
D --> |StatefulSet| E[使用 StatefulSet 创建集群]
D --> |复制控制器| F[使用复制控制器部署]
表格总结不同部署方式的特点:
| 部署方式 | 优点 | 缺点 | 适用场景 |
| ---- | ---- | ---- | ---- |
| StatefulSet | 提供稳定的网络标识和持久化存储 | 配置相对复杂,依赖于持久化存储声明 | 需要严格的状态管理和持久化存储的场景 |
| 复制控制器 | 更符合 Cassandra 语义,直接使用节点存储 | 节点故障时数据恢复可能较复杂 | 对存储要求不高,更注重节点本地存储的场景 |
在 Kubernetes 中运行有状态的 Cassandra 应用
6. 不同部署方式的深入分析
6.1 StatefulSet 部署方式
-
稳定性优势
:StatefulSet 为每个 Pod 提供了稳定的网络标识和持久化存储。在 Cassandra 集群中,这意味着每个节点都有一个固定的名称和存储卷,即使 Pod 被重新调度,也能挂载回原来的存储卷,保证数据的一致性。例如,在
cassandra-0.cassandra.default.svc.cluster.local这样的 DNS 名称中,cassandra-0是固定的,方便节点之间的通信和数据恢复。 -
配置复杂度
:StatefulSet 的配置相对复杂,需要定义服务名称、副本数量、Pod 模板和卷声明模板等。例如,在配置文件中需要明确指定
serviceName、replicas、template和volumeClaimTemplates等字段,对于初学者来说可能有一定的难度。 - 适用场景 :适用于对数据一致性和稳定性要求较高的场景,如金融交易系统、医疗数据存储等。这些场景需要确保数据不会丢失,并且在节点故障时能够快速恢复。
6.2 复制控制器部署方式
- 符合 Cassandra 语义 :复制控制器直接使用节点上的存储,更符合 Cassandra 设计初衷。Cassandra 本身就是为了在节点本地存储数据而设计的,通过复制控制器部署可以充分利用节点的本地存储,减少对网络存储的依赖。
-
数据恢复挑战
:当节点发生故障时,由于使用的是
emptyDir存储,数据恢复可能会比较复杂。因为emptyDir是临时存储,当 Pod 被删除或重新调度时,数据会丢失。需要依赖 Cassandra 的复制机制来恢复数据。 - 适用场景 :适用于对存储要求不高,更注重节点本地存储性能的场景,如实时数据分析、日志存储等。这些场景对数据的实时性要求较高,而对数据的持久化要求相对较低。
7. 性能优化建议
7.1 内存管理
-
设置最大堆大小
:通过设置
CASSANDRA_MAX_HEAP环境变量,可以控制 Cassandra 的最大堆大小。例如,在run.sh脚本中使用sed命令修改jvm.options文件:
if [ -n "$CASSANDRA_MAX_HEAP" ]; then
sed -ri "s/^(#)?-Xmx[0-9]+.*/-Xmx$CASSANDRA_MAX_HEAP/" "$CASSANDRA_CONF_DIR/jvm.options"
sed -ri "s/^(#)?-Xms[0-9]+.*/-Xms$CASSANDRA_MAX_HEAP/" "$CASSANDRA_CONF_DIR/jvm.options"
fi
这样可以避免 Cassandra 因为内存不足而进行频繁的交换,提高性能。
-
调整新生代大小
:可以通过设置
HEAP_NEWSIZE
环境变量来调整新生代的大小,优化垃圾回收性能。
7.2 磁盘优化
- 使用 SSD 存储 :建议使用 SSD 驱动器作为 Cassandra 的存储介质,特别是对于日志文件。SSD 的读写速度比传统硬盘快很多,可以显著提高 Cassandra 的性能。
- 预留足够的磁盘空间 :Cassandra 在数据处理过程中会进行大量的数据洗牌、压缩和重新平衡操作,因此需要预留足够的磁盘空间。一般建议至少预留 50% 的磁盘空间,以确保数据处理的顺利进行。
7.3 网络优化
- 合理配置种子节点 :种子节点是 Cassandra 集群中非常重要的节点,它们负责向新节点提供集群信息。合理配置种子节点的数量和位置,可以减少节点之间的通信延迟,提高集群的性能。
- 使用高效的 Snitch :根据实际的网络拓扑和数据中心布局,选择合适的 Snitch 可以优化 Cassandra 的请求路由和副本分布,减少跨数据中心的通信。
8. 监控和故障处理
8.1 监控指标
- JMX 监控 :Cassandra 支持 Java Management Extensions (JMX) 标准,可以通过 JMX 监控 Cassandra 的各种指标,如内存使用情况、磁盘 I/O、请求响应时间等。可以使用工具如 Cassandra OpsCenter 来监控这些指标。
- 日志监控 :通过监控 Cassandra 的日志文件,可以及时发现异常情况,如节点故障、数据不一致等。可以使用日志收集工具如 Fluentd 或 Logstash 来收集和分析日志。
8.2 故障处理
- 节点故障恢复 :当节点发生故障时,Cassandra 可以通过复制机制自动恢复数据。但是,在某些情况下,可能需要手动干预,如重新启动节点、调整配置等。
-
集群扩容和缩容
:在集群扩容或缩容时,需要注意数据的重新平衡和节点的加入或退出顺序。可以使用 Cassandra 的工具如
nodetool来进行这些操作。
9. 总结和展望
通过本文的介绍,我们了解了如何在 Kubernetes 中运行有状态的 Cassandra 应用,包括容器配置、脚本解析、与 Kubernetes 的集成以及不同的部署方式。同时,我们也探讨了性能优化、监控和故障处理等方面的内容。
在未来,随着 Kubernetes 和 Cassandra 的不断发展,我们可以期待更多的优化和改进。例如,Kubernetes 可能会提供更强大的状态管理功能,而 Cassandra 可能会进一步优化其分布式架构,提高性能和可靠性。
下面是一个 mermaid 流程图,展示了 Cassandra 集群的监控和故障处理流程:
graph LR
A[监控指标] --> B{是否异常}
B --> |是| C[故障处理]
B --> |否| D[继续监控]
C --> E{故障类型}
E --> |节点故障| F[节点故障恢复]
E --> |集群扩容/缩容| G[集群调整操作]
F --> D
G --> D
表格总结性能优化的关键要点:
| 优化方面 | 具体措施 |
| ---- | ---- |
| 内存管理 | 设置最大堆大小和新生代大小 |
| 磁盘优化 | 使用 SSD 存储,预留足够磁盘空间 |
| 网络优化 | 合理配置种子节点,使用高效 Snitch |
超级会员免费看
23

被折叠的 条评论
为什么被折叠?



