k8ssandra v1.0を試す + Stargate
k8ssandra v1.0
前回の記事では、公開されたばかりのk8ssandraを動かして、3ノードのCassandraクラスタをローカル環境のk8s(kind)上で動かしました。
そして先月末、安定版となるv1.0がリリースされました。
上の記事を見てみると、以前セットアップしたときと比べて以下のような変更があるようです。
- 一つのhelmチャートでのインストール
- 前回は
k8ssandra-tools
とk8ssandra
の二つのChartを利用しましたが、これがk8ssandra
のみになりました。
- 前回は
- サブプロジェクトには専用のhelmチャートを用意
- バックアップや復元がそれ用のhelmチャートになっています。
-
Stargate の統合
- 後ほど解説します。
- デフォルトでユーザー認証を有効
- 前回はCassandraへのアクセスに認証がかかっていませんでしたが、デフォルトで有効になりました。
- rootを利用しないコンテナ
- kube-prometheus-stack の利用
- ドキュメントの充実
- HTTPワークロードにK8sネイティブのIngressリソースを利用
- C*設定可能項目の追加(トポロジー、ヒープサイズ、ストレージクラス)
- 名前空間内のリソース
それでは Getting startedガイド、開発者向けガイド、SRE向けガイドを参考に構築してみましょう。
Getting started
k8sの準備
k8s環境は、前回同様、ローカルのdocker上で動く kind
を利用します。
❯ kind version
kind v0.10.0 go1.15.7 windows/amd64
3台のノードを持ち、ホストからアクセス可能になるように、以下のような kind-cluster.yaml
ファイルを用意します。
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 32080
hostPort: 80
protocol: TCP
- containerPort: 32443
hostPort: 443
protocol: TCP
- role: worker
- role: worker
- role: worker
そして、kindクラスタを作成します。
❯ kind create cluster --config=kind-cluster.yaml
Creating cluster "kind" ...
• Ensuring node image (kindest/node:v1.20.2) 🖼 ...
✓ Ensuring node image (kindest/node:v1.20.2) 🖼
• Preparing nodes 📦 📦 📦 📦 ...
✓ Preparing nodes 📦 📦 📦 📦
• Writing configuration 📜 ...
✓ Writing configuration 📜
• Starting control-plane 🕹️ ...
✓ Starting control-plane 🕹️
• Installing CNI 🔌 ...
✓ Installing CNI 🔌
• Installing StorageClass 💾 ...
✓ Installing StorageClass 💾
• Joining worker nodes 🚜 ...
✓ Joining worker nodes 🚜
Set kubectl context to "kind-kind"
You can now use your cluster with:
kubectl cluster-info --context kind-kind
Have a nice day! 👋
❯ kubectl get nodes
NAME STATUS ROLES AGE VERSION
kind-control-plane Ready control-plane,master 12m v1.20.2
kind-worker Ready <none> 11m v1.20.2
kind-worker2 Ready <none> 11m v1.20.2
kind-worker3 Ready <none> 11m v1.20.2
kind
クラスタは以下のストレージクラスがデフォルトで利用可能です。
❯ kubectl get storageclasses
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
standard (default) rancher.io/local-path Delete WaitForFirstConsumer false 12m
k8ssandraの準備
k8ssandra
はhelmチャートで配布されています。v1.0からは、安定版を配布するstable
と、開発版のnext
の二つでリリースされるようになりました。
あらためて、stable
のレポジトリをhelmに追加します。
❯ helm repo add k8ssandra https://helm.k8ssandra.io/stable
"k8ssandra" has been added to your repositories
❯ helm repo list
NAME URL
stable https://charts.helm.sh/stable
traefik https://helm.traefik.io/traefik
datastax https://datastax.github.io/charts
k8ssandra https://helm.k8ssandra.io/stable
❯ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "datastax" chart repository
...Successfully got an update from the "k8ssandra" chart repository
...Successfully got an update from the "traefik" chart repository
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈ Happy Helming!⎈
Cassandraクラスタを構築する
v1.0から、任意の名前空間に構築できるようになったので、試してみたいと思います。
cassandra
という名前空間を作成し、そこにk8ssandraでCassandraクラスタを構築します。
❯ kubectl create namespace cassandra
namespace/cassandra created
k8ssandraはhelmを用いてCassandraクラスタを構築しますが、その際に様々なパラメータをカスタマイズできます(https://k8ssandra.io/docs/reference/k8ssandra/)。
以下のようなyamlファイルを用意し、構築する環境に合わせてカスタマイズします。
cassandra:
version: "3.11.10"
cassandraLibDirVolume:
# kubectl get storageclassにある利用可能なストレージクラス名を指定する
storageClass: standard
size: 5Gi
allowMultipleNodesPerWorker: true
# ローカルで動かすのでリソースを少な目に
heap:
size: 256M
newGenSize: 256M
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 500m
memory: 1Gi
datacenters:
- name: dc1
size: 1
racks:
- name: default
kube-prometheus-stack:
grafana:
adminUser: admin
adminPassword: admin123
stargate:
enabled: true
replicas: 1
heapMB: 256
cpuReqMillicores: 200
cpuLimMillicores: 1000
そして、helmを用いてCassandraクラスタを構築します。
❯ helm install -n cassandra -f k8ssandra.yaml k8ssandra k8ssandra/k8ssandra
NAME: k8ssandra
LAST DEPLOYED: Fri Mar 5 11:16:22 2021
NAMESPACE: cassandra
STATUS: deployed
REVISION: 1
cassandra
名前空間を確認すると、いろいろなポッドが自動的に作成されています。
❯ kubectl -n cassandra get pods
NAME READY STATUS RESTARTS AGE
k8ssandra-cass-operator-6ffc65b9f6-h4lfj 1/1 Running 0 9m10s
k8ssandra-dc1-default-sts-0 2/2 Running 0 8m36s
k8ssandra-dc1-stargate-794d647b98-dw4wp 1/1 Running 0 9m10s
k8ssandra-grafana-5ff75568d5-v2zhd 2/2 Running 0 9m10s
k8ssandra-kube-prometheus-operator-8487f5df79-2pz22 1/1 Running 0 9m10s
k8ssandra-reaper-7bb77d575c-p5f75 1/1 Running 0 2m55s
k8ssandra-reaper-operator-766696f6d4-lhqgz 1/1 Running 0 9m10s
k8ssandra-reaper-schema-rh4j5 0/1 Completed 0 3m50s
prometheus-k8ssandra-kube-prometheus-prometheus-0 2/2 Running 1 8m57s
STATUS
が上記のようになると完了です。k8ssandra-dc1-default-sts-0
がCasasndraノードで、他にもgrafana
、prometheus
、およびcassandra-reaper
が起動しています。
Cassandraデータベースにアクセスする
起動が確認できたら、cqlsh
を用いてデータベースにアクセスしてみましょう。v1.0からはデフォルトでユーザー認証が行われるようになったため、まずは自動的に作成されたスーパーユーザーの認証情報を取得する必要があります。
認証情報は、kubernetesのシークレットとして登録されています。Base64でエンコードされているので、デコードも必要です。
❯ kubectl -n cassandra get secret k8ssandra-superuser -o jsonpath="{.data.username}" | openssl base64 -d
k8ssandra-superuser
❯ kubectl -n cassandra get secret k8ssandra-superuser -o jsonpath="{.data.password}" | openssl base64 -d
s98FDUbDnRzDvW8KrGJR
デフォルトのスーパーユーザー名はk8ssandra-superuser
で、パスワードはこの場合、s98FDUbDnRzDvW8KrGJR
です。
では、この認証情報を用いてcqlsh
でアクセスしてみましょう。k8ssandra-dc1-default-sts-0
ポッド内のcassandra
コンテナでcqlsh
を実行します。
❯ kubectl -n cassandra exec -it k8ssandra-dc1-default-sts-0 -c cassandra -- cqlsh -u k8ssandra-superuser -p s98FDUbDnRzDvW8KrGJR
Warning: Cannot create directory at `/home/cassandra/.cassandra`. Command history will not be saved.
Connected to k8ssandra at 127.0.0.1:9042.
[cqlsh 5.0.1 | Cassandra 3.11.10 | CQL spec 3.4.4 | Native protocol v4]
Use HELP for help.
k8ssandra-superuser@cqlsh>
ログインできました。
同じように、スーパーユーザーの認証情報を用いて、nodetool status
コマンドでクラスタの状態を確認します。
❯ kubectl -n cassandra exec -it k8ssandra-dc1-default-sts-0 -c cassandra -- nodetool -u k8ssandra-superuser -pw s98FDUbDnRzDvW8KrGJR status
Datacenter: dc1
===============
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns Host ID Rack
UN 10.244.3.6 320.93 KiB 256 ? 5ffc5d00-bd41-4f4c-817e-0aaebb7f4188 default
Note: Non-system keyspaces don't have the same replication settings, effective ownership information is meaningless
1ノードのCassandraで構築されたクラスタが確認できました。
3ノードに拡張する
クラスタの拡張は、helm upgrade
コマンドを用いて行います。
以下のような拡張用のyamlファイルを用意し、
cassandra:
datacenters:
- name: dc1
size: 3
helm upgrade
コマンドを実行します。
❯ helm upgrade -n cassandra -f k8ssandra-upgrade.yaml k8ssandra k8ssandra/k8ssandra
Release "k8ssandra" has been upgraded. Happy Helming!
NAME: k8ssandra
LAST DEPLOYED: Fri Mar 5 16:45:31 2021
NAMESPACE: cassandra
STATUS: deployed
REVISION: 2
kubectl get pod
を見てみると
❯ kubectl -n cassandra get pod
NAME READY STATUS RESTARTS AGE
k8ssandra-cass-operator-6ffc65b9f6-dlvlp 1/1 Running 0 4h
k8ssandra-dc1-default-sts-0 2/2 Running 0 4h
k8ssandra-dc1-default-sts-1 2/2 Running 0 6m56s
k8ssandra-dc1-default-sts-2 2/2 Running 0 6m55s
k8ssandra-dc1-stargate-794d647b98-mj2bl 1/1 Running 0 4h
k8ssandra-grafana-74db456fdb-w6gb8 2/2 Running 0 6m56s
k8ssandra-kube-prometheus-operator-8487f5df79-dhv5r 1/1 Running 0 4h
k8ssandra-reaper-7bb77d575c-xvf7w 1/1 Running 0 4h
k8ssandra-reaper-operator-766696f6d4-twrh2 1/1 Running 0 4h
k8ssandra-reaper-schema-4ch7g 0/1 Completed 0 4h
prometheus-k8ssandra-kube-prometheus-prometheus-0 2/2 Running 1 4h
k8ssandra-dc1-default-sts-1
とk8ssandra-dc1-default-sts-2
が新たに追加されました。再度nodetool status
を確認すると、
❯ kubectl -n cassandra exec -it k8ssandra-dc1-default-sts-0 -c cassandra -- nodetool -u k8ssandra-superuser -pw s98FDUbDnRzDvW8KrGJR status
Datacenter: dc1
===============
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns Host ID Rack
UN 10.244.3.6 312.49 KiB 256 ? 5ffc5d00-bd41-4f4c-817e-0aaebb7f4188 default
UN 10.244.1.7 110.06 KiB 256 ? 3f3b3f11-902f-4d35-b1c4-36014f91aa65 default
UN 10.244.2.8 90.42 KiB 256 ? be05809e-7d6b-4a4a-8c11-09f812071bcc default
Note: Non-system keyspaces don't have the same replication settings, effective ownership information is meaningless
3ノードのクラスタに拡張されています。
認証用キースペースの設定と開発用ユーザーの作成
3ノードに増やしたので、認証用キースペースの調整と、開発用ユーザー(dev
)を作成しておきます。またdev
ユーザーが任意のCassandraキースペースを作成できるようにしています。
❯ kubectl -n cassandra exec -it k8ssandra-dc1-default-sts-0 -c cassandra -- cqlsh -u k8ssandra-superuser -p s98FDUbDnRzDvW8KrGJR
Warning: Cannot create directory at `/home/cassandra/.cassandra`. Command history will not be saved.
Connected to k8ssandra at 127.0.0.1:9042.
[cqlsh 5.0.1 | Cassandra 3.11.10 | CQL spec 3.4.4 | Native protocol v4]
Use HELP for help.
k8ssandra-superuser@cqlsh> ALTER KEYSPACE system_auth WITH replication = {'class': 'NetworkTopologyStrategy', 'dc1': 3};
k8ssandra-superuser@cqlsh> CREATE ROLE dev WITH PASSWORD = 'dev' AND LOGIN = true;
k8ssandra-superuser@cqlsh> GRANT CREATE ON ALL KEYSPACES TO dev;
k8ssandra-superuser@cqlsh> LIST ALL OF dev;
role | username | resource | permission
------+----------+-----------------+------------
dev | dev | <all keyspaces> | CREATE
(1 rows)
Stargate
さて、ここでk8ssandraが自動的に起動するStargateについて見ていきましょう。
Stargateは、従来Cassandraが提供するCQLバイナリプロトコル以外に、Web API(REST API/Document API/GraphQL)を通じてデータベースアクセスできるようにするテクノロジーです。Stargateが提供するAPIを用いて、開発者が容易にCassandraアプリを開発できるようになります。例えば、Document APIを用いると、ネストしたJSONをそのまま登録することができます。
ここでは、StargateのDocument API Quick Startを参考に、Document APIを用いてデータの読み書きを行ってみます。
準備
前回の記事と同様、HTTP経由でkind内の各種APIにアクセスできるように、Traefik Ingressコントローラーを追加します。
Traefikのインストール
以下のようなyamlファイルを用意し、helmコマンドでインストールします。
ports:
web:
nodePort: 32080
websecure:
nodePort: 32443
service:
type: NodePort
❯ helm install -n cassandra -f traefik.values.yaml traefik traefik/traefik
NAME: traefik
LAST DEPLOYED: Wed Mar 10 13:56:47 2021
NAMESPACE: cassandra
STATUS: deployed
REVISION: 1
TEST SUITE: None
Stargateは様々なポートでAPIを公開しています。
- 9042: CassandraのCQLバイナリプロトコル
- 8080: Stargate GraphQL API
- 8081: Stargate 認証用REST API
- 8082: Stargate REST/Document API
以下のようなyamlファイルを用意し、認証APIとREST/Document APIにアクセスできるようにTraefikを設定します。
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: traefik-k8ssandra-route
spec:
entryPoints:
- web
routes:
- match: Host(`127.0.0.1`)
kind: Rule
services:
- name: k8ssandra-dc1-stargate-service
port: 8081
- match: Host(`127.0.0.2`)
kind: Rule
services:
- name: k8ssandra-dc1-stargate-service
port: 8082
❯ kubectl -n cassandra apply -f ingress.yaml
ingressroute.traefik.containo.us/traefik-k8ssandra-route created
これで127.0.0.1
で認証APIに、127.0.0.2
でDocument APIにアクセスできるようになりました。
ユーザー認証
各種APIを利用するには、認証APIを用いて認証トークンを取得する必要があります。作成したdev
ユーザーで認証トークンを取得してみます。
❯ curl -L -X POST 'http://127.0.0.1/v1/auth' -H 'Content-Type: application/json' --data-raw '{"username": "dev", "password": "dev"}'
実行結果:
{"authToken":"3e24dfc9-e184-49e1-a2db-9cbf15f9f2b3"}
このアクセストークンを控えておきます。
ネームスペースの作成
Document APIを使うには、データが格納されるネームスペースを作成する必要があります。これはCassandraでいうキースペースに相当します。
/v2/schemas/namespaces
に対してPOST
することでネームスペースを作成できます。この時、ユーザー認証のため、X-Cassandra-Token
ヘッダーで先に取得したトークンを送信する必要があります。
❯ curl --location --request POST 'http://127.0.0.2/v2/schemas/namespaces' \
--header "X-Cassandra-Token: 3e24dfc9-e184-49e1-a2db-9cbf15f9f2b3" \
--header 'Content-Type: application/json' \
--data '{"name": "myworld"}'
実行結果:
{"name":"myworld"}
確認してみましょう。同じURLに対してGET
でアクセスしてみます。
❯ curl -L -X GET 'http://127.0.0.2/v2/schemas/namespaces' \
-H "X-Cassandra-Token: 3e24dfc9-e184-49e1-a2db-9cbf15f9f2b3" \
-H 'Content-Type: application/json'
ネームスペースの一覧がJSON形式で取得できました。作成したmyworld
ネームスペースが含まれています。
{"data":[{"name":"system_distributed","datacenters":[{"name":"dc1","replicas":3}]},{"name":"system"},{"name":"data_endpoint_auth"},{"name":"system_schema"},{"name":"myworld"},{"name":"stargate_system"},{"name":"reaper_db","datacenters":[{"name":"dc1","replicas":3}]},{"name":"system_auth","datacenters":[{"name":"dc1","replicas":3}]},{"name":"system_traces","datacenters":[{"name":"dc1","replicas":3}]}]}
JSONドキュメントの読み書き
Stargateでは、ドキュメントはコレクションの中に蓄積されます(Cassandraのテーブルに相当)。コレクション内にドキュメントを作成するには、/v2/namespaces/{ネームスペース名}/collections/{コレクション名}
のURLにJSONをPOST
します。
fitness
コレクションにJSONドキュメントを追加してみましょう。
❯ curl --location \
--request POST 'http://127.0.0.2/v2/namespaces/myworld/collections/fitness' \
--header "X-Cassandra-Token: 7c64c9f4-df1a-4416-8936-e8a3ac3a13da" \
--header 'Content-Type: application/json' \
--data '{"id": "some-stuff", "other": "This is nonsensical stuff."}'
実行すると、やや時間がかかった後、以下の結果が返ってきました。
{"documentId":"bb2a0c28-b848-4090-8fce-faeb199bcc30"}
APIの実行結果はCassandraテーブルに格納されますが、そのためのテーブルは自動的に作成されます。そのため、あらかじめテーブルが存在しない場合、作成のための時間がかかります。
(StargateがどのようなテーブルにJSONドキュメントを保持するか、詳しくは https://stargate.io/2020/10/19/the-stargate-cassandra-documents-api.html に解説があります。)
作成したドキュメントのIDが自動的に生成され、実行結果として返却されます。ではこのIDを用いて登録されているドキュメントを取得してみましょう。
❯ curl --location \
--request GET 'http://127.0.0.2/v2/namespaces/myworld/collections/fitness/bb2a0c28-b848-4090-8fce-faeb199bcc30' \
--header "X-Cassandra-Token: 7c64c9f4-df1a-4416-8936-e8a3ac3a13da" \
--header 'Content-Type: application/json'
以下のように登録したドキュメントが取得できました。
{"documentId":"bb2a0c28-b848-4090-8fce-faeb199bcc30","data":{"id":"some-stuff","other":"This is nonsensical stuff."}}
もちろん、更新や削除のためのAPIも存在します。APIリファレンスはこちら。
まとめ
v1.0がリリースされたk8ssandraを使ってCassandraクラスタを構築してみました。また、デフォルトで起動するStargateを用いて、CassandraにAPIでアクセスできるようにもなっています。
機会があればStargateについてもっと触っていきたいと思います。
Discussion