dragonfly-operatorを試してみる
dragonflyはRedis互換の早いやつ、というイメージ。
Webサイトの宣伝文句だと25倍くらいのスループットを叩き出すらしい。
dragonfly-operatorはKubernetesでdragonflyを簡単に建てられるようにするOperator。
Redisは決定的なOperatorが無い認識(Redis Enterpriseは有料)なので、結構期待している。
試したいこと
- RedisのクラスタをCustomResourceでたてられるようにしたい
- パスワード認証を有効にしたい(できればExternal Secret Operatorとかでパスワードを管理したい)
Operatorのインストール
CustomResource定義をいれるためのYAMLが用意されているっぽい。
バージョンを指定したいので、作業時点で最新のv1.1.1を指定する。
例によってPulumi(TypeScript)でいれる場合は以下。
import * as k8s from "@pulumi/kubernetes"
import * as pulumi from "@pulumi/pulumi"
export type KubernetesDragonflyArgs = object
export class KubernetesDragonfly extends pulumi.ComponentResource {
public opts: pulumi.ResourceOptions
public configFile: k8s.yaml.ConfigFile
constructor(
name: string,
args: KubernetesDragonflyArgs,
opts?: pulumi.ResourceOptions,
) {
super("stack8:Kubernetess:Dragonfly", name, undefined, opts)
this.opts = { ...opts, parent: this }
this.configFile = new k8s.yaml.ConfigFile(
"config-file",
{
file: "https://raw.githubusercontent.com/dragonflydb/dragonfly-operator/v1.1.1/manifests/dragonfly-operator.yaml",
},
this.opts,
)
}
}
yamlの中身をみるとnamespaceまで作成するみたいだ。
namespaceは指定したかった感があるが、許容範囲。
とりあえず適応させてdragonfly-operator-system
のnamespaceにpodなどが作成されていることを確認。
kubectl get po -n dragonfly-operator-system
NAME READY STATUS RESTARTS AGE
dragonfly-operator-controller-manager-5d687479ff-njntc 2/2 Running 0 16h
Dragonflyのインスタンスを立ててみる
以下のようなCustomResourceを定義すればたてられるっぽい。yaml書式でないのはご勘弁。
レプリカを2で、リソースはlimitだけ定義して立ててみる。
this.sample = new k8s.apiextensions.CustomResource(
"sample",
{
apiVersion: "dragonflydb.io/v1alpha1",
kind: "Dragonfly",
metadata: {
name: "sample",
},
spec: {
replicas: 2,
resources: {
limits: {
cpu: "100m",
memory: "128Mi",
},
},
},
},
this.opts,
)
するとCrashLoopBackOff
が発生。
原因は自動的に設定されているMaxmermoryが足りないことっぽい。
✗ kubectl get po -n default
NAME READY STATUS RESTARTS AGE
sample-0 0/1 CrashLoopBackOff 4 (44s ago) 2m48s
✗ kubectl logs -n default pod/sample-0
I20240313 00:32:40.020349 1 init.cc:70] dragonfly running in opt mode.
I20240313 00:32:40.020452 1 dfly_main.cc:621] Starting dragonfly df-v1.14.5-9ed5513be71ae0451cbaf0d6d59ebb04e35cec8f
* Logs will be written to the first available of the following paths:
/tmp/dragonfly.*
./dragonfly.*
* For the available flags type dragonfly [--help | --helpfull]
* Documentation can be found at: https://www.dragonflydb.io/docs
I20240313 00:32:40.020622 1 dfly_main.cc:665] maxmemory has not been specified. Deciding myself....
I20240313 00:32:40.020637 1 dfly_main.cc:674] Found 128.00MiB available memory. Setting maxmemory to 102.40MiB
I20240313 00:32:40.024246 8 uring_proactor.cc:154] IORing with 1024 entries, allocated 102720 bytes, cq_entries is 2048
I20240313 00:32:40.802258 1 proactor_pool.cc:146] Running 4 io threads
E20240313 00:32:40.802305 1 dfly_main.cc:151] There are 4 threads, so 1.00GiB are required. Exiting...
Githubをぐぐったら、以下のIssueが出てきた。
Dragonflyは1threadにつき、最低256MiBのメモリを要求するらしい。
KubernetesのNode(t3.xlarge)が4VPCなので、自動的に4thread割り当てられて、1GiB必要だけどmaxmemoryが102.40MiBで足りねーぞ、というエラーっぽい。
thread数は--proactor_threads=X
というパラメーターで指定可能らしい。
最終的に以下のようにしてみた。
this.sample = new k8s.apiextensions.CustomResource(
"sample",
{
apiVersion: "dragonflydb.io/v1alpha1",
kind: "Dragonfly",
metadata: {
namespace: "dragonfly-operator-system",
name: "sample",
},
spec: {
replicas: 2,
args: ["--maxmemory", "256mb", "--proactor_threads", "1"],
resources: {
limits: {
cpu: "100m",
},
},
},
},
this.opts,
)
本番環境などではrequestsも定義してあげればよいと思う。
topで見たときのリソース使用量は非常に少ない。立ち上げて使ってないからか。
✗ kubectl top pod -n dragonfly-operator-system
NAME CPU(cores) MEMORY(bytes)
dragonfly-operator-controller-manager-5d687479ff-njntc 2m 21Mi
sample-0 5m 4Mi
sample-1 5m 3Mi
立てたDragonflyインスタンスに接続してみる
以下のコマンドでredisのpodを立てて接続してみる(公式より)。
-h
はホストの指定で${name}.${namespace}
の形式になっている。
実際以下を実行したらターミナルが固まってしまった。podは立ち上がっていて、接続するときの何かでうまく行ってない感じだった。別のとこから立ち上がったpodにkubectl exec -it redis-cli sh
で普通に接続できた。
kubectl run -it --rm --restart=Never redis-cli --image=redis:7.0.10 -- redis-cli -h sample.dragonfly-operator-system
以下のように問題なく動いていそう。やったね!
# redis-cli -h sample.dragonfly-operator-system
sample.dragonfly-operator-system:6379> GET 1
(nil)
sample.dragonfly-operator-system:6379> SET 1 2
OK
sample.dragonfly-operator-system:6379> GET 1
"2"
接続後にtopを見てみても変化なし。
✗ kubectl top pod -n dragonfly-operator-system
NAME CPU(cores) MEMORY(bytes)
dragonfly-operator-controller-manager-5d687479ff-njntc 2m 21Mi
sample-0 5m 3Mi
sample-1 6m 3Mi
認証を設定する
メモリの256MiBに戸惑ったが、普通に起動させることには成功。
Dragonflyにはパスワードでの認証と、TLSによる認証が用意されているらしい。たぶんRedisと同様なのだろう。おすすめはTLSらしいのだけど、管理が面倒なので今回はパスワード認証を試したい。
こちらは非常にお手軽で、Secretを作成して、それを以下のように参照するだけで良いらしい。
apiVersion: dragonflydb.io/v1alpha1
kind: Dragonfly
metadata:
name: dragonfly-auth
spec:
authentication:
passwordFromSecret:
name: dragonfly-auth
key: password
replicas: 2
Dragonflyに設定するパスワードを開発者が知る必要はなく、接続する各Podが知れればいいので、以下で試したようにExternal Secrets OperatorのGenaratorを使ってSecretを生成。生成されたSecretを参照してみた。
生成されたSecretは以下(抜粋)。
apiVersion: v1
data:
password: eTI+R0tXKlQsfUI4bzM3QzQxfmdudUQj
kind: Secret
metadata:
name: dragonfly-secret
namespace: example-app
type: Opaque
passwordをデコードするとy2>GKW*T,}B8o37C41~gnuD#
らしい。結構記号が多いな。
最後に接続してみる。
✗ kubectl run -it --rm --restart=Never redis-cli --image=redis:7.0.10 -- sh
If you don't see a command prompt, try pressing enter.
# redis-cli -h dragonfly.example-app
dragonfly.example-app:6379> GET 1
(error) NOAUTH Authentication required.
dragonfly.example-app:6379> AUTH y2>GKW*T,}B8o37C41~gnuD#
OK
dragonfly.example-app:6379> GET 1
(nil)
dragonfly.example-app:6379> SET 1 2
OK
dragonfly.example-app:6379> GET 1
"2"
大成功!
まとめ
dragonfly-operatorは普通に使えた。
最低256MiBのメモリを要求し、limitsで256MiB以下にすると起動しなくなってしまうのだけがトラップ。
いろいろパラメーターがあるっぽいので、もう少しメモリー上限を絞れるかもしれない。詳しい方教えて欲しい。
RedisはRDBとは違って、1つのクラスタを複数のアプリケーションで共有するハードルが高い(ACLでの権限制御は管理コストが高すぎる。databse番号を変えても権限制御はできてないなど)。
なので、アプリケーションごとにRedis(Dragonfly)をたてられるのは非常に嬉しい。
Redis運用に悩んでいる方はぜひ色々試して知見を公開していただけると助かる。