k8ssandra v1.0を試す + Stargate

2021/03/10に公開

k8ssandra v1.0

前回の記事では、公開されたばかりのk8ssandraを動かして、3ノードのCassandraクラスタをローカル環境のk8s(kind)上で動かしました。

そして先月末、安定版となるv1.0がリリースされました。

https://k8ssandra.io/blog/2021/02/26/k8ssandra-1.0-stable-release-and-whats-next/

上の記事を見てみると、以前セットアップしたときと比べて以下のような変更があるようです。

  • 一つのhelmチャートでのインストール
    • 前回は k8ssandra-toolsk8ssandra の二つの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.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ファイルを用意し、構築する環境に合わせてカスタマイズします。

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ノードで、他にもgrafanaprometheus、および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ファイルを用意し、

k8ssandra-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-1k8ssandra-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

https://stargate.io

さて、ここで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コマンドでインストールします。

traefik.values.yaml
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を設定します。

ingress.yaml
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リファレンスはこちら。

https://stargate.io/docs/stargate/1.0/developers-guide/api_ref/openapi_document_ref.html

まとめ

v1.0がリリースされたk8ssandraを使ってCassandraクラスタを構築してみました。また、デフォルトで起動するStargateを用いて、CassandraにAPIでアクセスできるようにもなっています。

機会があればStargateについてもっと触っていきたいと思います。

Discussion