Closed6

dragonfly-operatorを試してみる

dekimasoondekimasoon

dragonflyはRedis互換の早いやつ、というイメージ。
Webサイトの宣伝文句だと25倍くらいのスループットを叩き出すらしい。
https://www.dragonflydb.io/

dragonfly-operatorはKubernetesでdragonflyを簡単に建てられるようにするOperator。
Redisは決定的なOperatorが無い認識(Redis Enterpriseは有料)なので、結構期待している。
https://github.com/dragonflydb/dragonfly-operator

試したいこと

  • RedisのクラスタをCustomResourceでたてられるようにしたい
  • パスワード認証を有効にしたい(できればExternal Secret Operatorとかでパスワードを管理したい)
dekimasoondekimasoon

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
dekimasoondekimasoon

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というパラメーターで指定可能らしい。
https://github.com/dragonflydb/dragonfly/issues/1491

最終的に以下のようにしてみた。

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
dekimasoondekimasoon

立てた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
dekimasoondekimasoon

認証を設定する

メモリの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を参照してみた。

https://zenn.dev/dekimasoon/scraps/de38714e094a24

生成された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"

大成功!

dekimasoondekimasoon

まとめ

dragonfly-operatorは普通に使えた。
最低256MiBのメモリを要求し、limitsで256MiB以下にすると起動しなくなってしまうのだけがトラップ。
いろいろパラメーターがあるっぽいので、もう少しメモリー上限を絞れるかもしれない。詳しい方教えて欲しい。

RedisはRDBとは違って、1つのクラスタを複数のアプリケーションで共有するハードルが高い(ACLでの権限制御は管理コストが高すぎる。databse番号を変えても権限制御はできてないなど)。

なので、アプリケーションごとにRedis(Dragonfly)をたてられるのは非常に嬉しい。
Redis運用に悩んでいる方はぜひ色々試して知見を公開していただけると助かる。

このスクラップは2024/03/14にクローズされました