Add Cassandra StatefulSet Chart (#667)

* Add Cassandra StatefulSet Chart

* Change name truncate size to 21 cause StatefulSet will append 3 characters at the end of each name

* Change storage class annotation from alpha to beta

* Add seeds size

* Add liveness probe and node selector

* Rename template files

* Update Readme

* Remove configmap and update liveness probe

* Add readiness probe

* Update description and add icon

* Comment out nodeSelector as an optional value
Add default persistence storage class

* Change default image tag from “latest” to “3”
This commit is contained in:
Siri
2017-05-10 14:38:47 -07:00
committed by Vic Iglesias
parent 308718d827
commit 8a0f96c824
8 changed files with 378 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
+13
View File
@@ -0,0 +1,13 @@
name: cassandra
version: 0.1.0
description: Apache Cassandra is a free and open-source distributed database management system designed to handle large amounts of data across many commodity servers, providing high availability with no single point of failure.
icon: https://upload.wikimedia.org/wikipedia/commons/5/5e/Cassandra_logo.svg
keywords:
- cassandra
- database
- nosql
home: http://cassandra.apache.org
maintainers:
- name: Siri C
email: goonohc@gmail.com
engine: gotpl
+112
View File
@@ -0,0 +1,112 @@
# Cassandra
A Cassandra Chart for Kubernetes
## Install Chart
To install the Cassandra Chart into your Kubernetes cluster (This Chart requires persistent volume by default, you may need to create a storage class before install chart. To create storage class, see [Persist data](#persist_data) section)
```bash
helm install --namespace "cassandra" -n "cassandra" incubator/cassandra
```
After installation succuess, you can get a status of Chart
```bash
helm status "cassandra"
```
If you want to delete your Chart, use this command
```bash
helm delete --purge "cassandra"
```
## Persist data
You need to create `StorageClass` before able to persist data in persistent volume.
To create a `StorageClass` on Google Cloud, run the following
```bash
kubectl create -f sample/create-storage-gce.yaml
```
And set the following values in `values.yaml`
```yaml
persistence:
enabled: true
```
If you want to create a `StorageClass` on other platform, please see documentation here [https://kubernetes.io/docs/user-guide/persistent-volumes/](https://kubernetes.io/docs/user-guide/persistent-volumes/)
## Install Chart with specific cluster size
By default, this Chart will create a cassandra with 3 nodes. If you want to change the cluster size during installation, you can use `--set config.cluster_size={value}` argument. Or edit `values.yaml`
For example:
Set cluster size to 5
```bash
helm install --namespace "cassandra" -n "cassandra" --set config.cluster_size=5 incubator/cassandra/
```
## Install Chart with specific resource size
By default, this Chart will create a cassandra with CPU 2 vCPU and 4Gi of memery which is suitable for development environment.
If you want to use this Chart for production, I would recommend to update the CPU to 4 vCPU and 16Gi. Also increase size of `max_heap_size` and `heap_new_size`.
To update the settings, edit `values.yaml`
## Install Chart with specific node
Sometime you may need to deploy your cassandra to specific nodes to allocate resources. You can use node selector by edit `nodes.enabled=true` in `values.yaml`
For example, you have 6 vms in node pools and you want to deploy cassandra to node which labeled as `cloud.google.com/gke-nodepool: pool-db`
Set the following values in `values.yaml`
```yaml
nodes:
enabled: true
selector:
nodeSelector:
cloud.google.com/gke-nodepool: pool-db
```
## Scale cassandra
When you want to change the cluser size of your cassandra, you can use the helm upgrade command.
```bash
helm upgrade --set config.cluster_size=5 cassandra incubator/cassandra
```
## Get cassandra status
You can get your cassandra cluster status by running the command
```bash
kubectl exec -it --namespace cassandra $(kubectl get pods --namespace cassandra -l app=cassandra-cassandra -o jsonpath='{.items[0].metadata.name}') nodetool status
```
Output
```bash
Datacenter: asia-east1
======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns (effective) Host ID Rack
UN 10.8.1.11 108.45 KiB 256 66.1% 410cc9da-8993-4dc2-9026-1dd381874c54 a
UN 10.8.4.12 84.08 KiB 256 68.7% 96e159e1-ef94-406e-a0be-e58fbd32a830 c
UN 10.8.3.6 103.07 KiB 256 65.2% 1a42b953-8728-4139-b070-b855b8fff326 b
```
## Benchmark
You can use [cassandra-stress](https://docs.datastax.com/en/cassandra/3.0/cassandra/tools/toolsCStress.html) tool to run the benchmark on the cluster by the following command
```bash
kubectl exec -it --namespace cassandra $(kubectl get pods --namespace cassandra -l app=cassandra-cassandra -o jsonpath='{.items[0].metadata.name}') cassandra-stress
```
Example of `cassandra-stress` argument
- Run both read and write with ration 9:1
- Operator total 1 million keys with uniform distribution
- Use QUORUM for read/write
- Generate 50 threads
- Generate result in graph
- Use NetworkTopologyStrategy with replica factor 2
```bash
cassandra-stress mixed ratio\(write=1,read=9\) n=1000000 cl=QUORUM -pop dist=UNIFORM\(1..1000000\) -mode native cql3 -rate threads=50 -log file=~/mixed_autorate_r9w1_1M.log -graph file=test2.html title=test revision=test2 -schema "replication(strategy=NetworkTopologyStrategy, factor=2)"
```
@@ -0,0 +1,7 @@
kind: StorageClass
apiVersion: storage.k8s.io/v1beta1
metadata:
name: generic
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd
+27
View File
@@ -0,0 +1,27 @@
Cassandra CQL can be accessed via port {{ .Values.config.ports.cql }} on the following DNS name from within your cluster:
Cassandra Thrift can be accessed via port {{ .Values.config.ports.thrift }} on the following DNS name from within your cluster:
If you want to connect to the remote instance with your local Cassandra CQL cli. To forward the API port to localhost:9042 run the following:
- kubectl port-forward --namespace {{ .Release.Namespace }} $(kubectl get pods --namespace {{ .Release.Namespace }} -l app={{ template "fullname" . }} -o jsonpath='{ .items[0].metadata.name }') 9042:{{ .Values.config.ports.cql }}
If you want to connect to the Cassandra CQL run the following:
{{- if contains "NodePort" .Values.service.type }}
- export CQL_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "fullname" . }})
- export CQL_HOST=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
- cqlsh $CQL_HOST $CQL_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
Watch the status with: 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "fullname" . }}'
- export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
- echo cqlsh $SERVICE_IP
{{- else if contains "ClusterIP" .Values.service.type }}
- kubectl port-forward --namespace {{ .Release.Namespace }} $(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "fullname" . }}" -o jsonpath="{.items[0].metadata.name}") 9042:{{ .Values.config.ports.cql }}
echo cqlsh 127.0.0.1 9042
{{- end }}
You can also see the cluster status by run the following:
- kubectl exec -it --namespace {{ .Release.Namespace }} $(kubectl get pods --namespace {{ .Release.Namespace }} -l app={{ template "fullname" . }} -o jsonpath='{.items[0].metadata.name}') nodetool status
To tail the logs for the Cassandra pod run the following:
- kubectl logs -f --namespace {{ .Release.Namespace }} $(kubectl get pods --namespace {{ .Release.Namespace }} -l app={{ template "fullname" . }} -o jsonpath='{ .items[0].metadata.name }')
@@ -0,0 +1,17 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 21 -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 21 chars because Kubernetes name fields are limited to 24 (by the DNS naming spec)
and Statefulset will append -xx at the end of name.
*/}}
{{- define "fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 21 -}}
{{- end -}}
@@ -0,0 +1,128 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "fullname" . }}
labels:
app: {{ template "fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
spec:
clusterIP: None
type: {{ .Values.service.type }}
ports:
- name: intra
port: 7000
targetPort: 7000
- name: tls
port: 7001
targetPort: 7001
- name: jmx
port: 7199
targetPort: 7199
- name: cql
port: {{ default 9042 .Values.config.ports.cql }}
targetPort: {{ default 9042 .Values.config.ports.cql }}
- name: thrift
port: {{ default 9160 .Values.config.ports.thrift }}
targetPort: {{ default 9160 .Values.config.ports.thrift }}
selector:
app: {{ template "fullname" . }}
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: {{ template "fullname" . }}
labels:
app: {{ template "fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
spec:
serviceName: {{ template "fullname" . }}
replicas: {{ .Values.config.cluster_size }}
template:
metadata:
labels:
app: {{ template "fullname" . }}
spec:
{{- if .Values.selector }}
{{ toYaml .Values.selector | indent 6 }}
{{- end }}
containers:
- name: {{ template "fullname" . }}
image: "{{ .Values.image.repo }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
resources:
{{ toYaml .Values.resources | indent 10 }}
env:
{{- $seed_size := default 1 .Values.config.seed_size | int -}}
{{- $global := . }}
- name: CASSANDRA_SEEDS
value: "{{- range $i, $e := until $seed_size }}{{ template "fullname" $global }}-{{ $i }}.{{ template "fullname" $global }}.{{ $global.Release.Namespace }}.svc.cluster.local,{{- end }}"
- name: MAX_HEAP_SIZE
value: {{ default "8192M" .Values.config.max_heap_size | quote }}
- name: HEAP_NEWSIZE
value: {{ default "200M" .Values.config.heap_new_size | quote }}
- name: CASSANDRA_ENDPOINT_SNITCH
value: {{ default "SimpleSnitch" .Values.config.endpoint_snitch | quote }}
- name: CASSANDRA_CLUSTER_NAME
value: {{ default "Cassandra" .Values.config.cluster_name | quote }}
- name: CASSANDRA_DC
value: {{ default "DC1" .Values.config.dc_name | quote }}
- name: CASSANDRA_RACK
value: {{ default "RAC1" .Values.config.rack_name | quote }}
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
livenessProbe:
exec:
command: [ "/bin/sh", "-c", "nodetool status | grep -E \"^UN\\s+${POD_IP}\"" ]
initialDelaySeconds: 90
periodSeconds: 30
readinessProbe:
exec:
command: [ "/bin/sh", "-c", "nodetool status | grep -E \"^UN\\s+${POD_IP}\"" ]
initialDelaySeconds: 90
periodSeconds: 30
ports:
- name: intra
containerPort: 7000
- name: tls
containerPort: 7001
- name: jmx
containerPort: 7199
- name: cql
containerPort: {{ default 9042 .Values.config.ports.cql }}
- name: thrift
containerPort: {{ default 9160 .Values.config.ports.thrift }}
volumeMounts:
- name: data
{{- if .Values.persistence.enabled }}
mountPath: /var/lib/cassandra
{{- else }}
emptyDir: {}
{{- end }}
{{- if .Values.persistence.enabled }}
volumeClaimTemplates:
- metadata:
name: data
labels:
app: {{ template "fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
annotations:
{{- if .Values.persistence.storageClass }}
volume.beta.kubernetes.io/storage-class: {{ .Values.persistence.storageClass | quote }}
{{- else }}
volume.alpha.kubernetes.io/storage-class: default
{{- end }}
spec:
accessModes:
- {{ .Values.persistence.accessMode | quote }}
resources:
requests:
storage: {{ .Values.persistence.size | quote }}
{{- end }}
+58
View File
@@ -0,0 +1,58 @@
## Cassandra image version
## ref: https://hub.docker.com/r/library/cassandra/
image:
repo: "cassandra"
tag: "3"
pullPolicy: IfNotPresent
## Specify a service type
## ref: http://kubernetes.io/docs/user-guide/services/
service:
type: ClusterIP
## Persist data to a persitent volume
persistence:
enabled: true
## If defined, volume.beta.kubernetes.io/storage-class: <storageClass>
## Default: volume.alpha.kubernetes.io/storage-class: default
# storageClass: generic
accessMode: ReadWriteOnce
size: 10Gi
## Configure resource requests and limits
## ref: http://kubernetes.io/docs/user-guide/compute-resources/
## Minimum memory for development is 4GB and 2 CPU cores
## Minimum memory for production is 8GB and 4 CPU cores
## ref: http://docs.datastax.com/en/archived/cassandra/2.0/cassandra/architecture/architecturePlanningHardware_c.html
resources:
requests:
memory: 4Gi
cpu: 2
limits:
memory: 4Gi
cpu: 2
## Change cassandra configuration paramaters below:
## ref: http://docs.datastax.com/en/cassandra/3.0/cassandra/configuration/configCassandra_yaml.html
## Recommended max heap size is 1/2 of system memory
## Recommeneed heap new size is 1/4 of max heap size
## ref: http://docs.datastax.com/en/cassandra/3.0/cassandra/operations/opsTuneJVM.html
config:
cluster_name: cassandra
cluster_size: 3
seed_size: 2
num_tokens: 256
dc_name: DC1
rack_name: RAC1
endpoint_snitch: SimpleSnitch
max_heap_size: 2048M
heap_new_size: 512M
ports:
cql: 9042
thrift: 9160
## Configure node selector. Edit code below for adding selector to pods
## ref: https://kubernetes.io/docs/user-guide/node-selection/
# selector:
# nodeSelector:
# cloud.google.com/gke-nodepool: pool-db