Kubernetesを動かす(4) Misskey本体+PostgresOperator+Pgpool+Redis Operator編

完成しました!!

こちらはちょっと複雑な感じですが、あとでシンプルな構成なk8s+misskey鯖の立て方を別記事で載せるかも?

MisskeyはMisskey本体(web)+PostgreSQL(データーベース)+Redisで動いているのでデーターベースやRedisを冗長化しつつ構成してみます。

続きを読む

データーベースの作成

まずk8s上でPostgreSQLを冗長化してくれるPostgres Operatorを導入します。こちらが自動フェイルオーバー(データーベースが落ちたとき自動で復旧する)やDBのバックアップなどを担当してくれます。

Zalando Postgres Operatorのデプロイ

Percona Operatorを使用する予定でしたがPostgreSQL本体にpgroonga拡張を導入したい関係でZalandoになりました。使用するPostgreSQLイメージがSpiloというPostgreSQL+patroniで自分でビルドできるので、Dockerfileを弄ればpgroongaがインストールできます。

Percona Operatorを弄ったときの備忘録はこちらへ

公式のリポジトリをcloneします。

git clone https://github.com/zalando/postgres-operator.git
cd postgres-operator

manifests/configmap.yamlにバックアップ先の設定などを記述するので編集します。

パラメータの説明はこちら

MiniOを使用する場合WAL-Gを使用する必要があるとのこと

# デフォルトのmanifests/configmap.yamlに下記を追加
  WAL_S3_BUCKET: test
  WAL_BUCKET_SCOPE_PREFIX: ""
  WAL_BUCKET_SCOPE_SUFFIX: ""
  USE_WALG_BACKUP: "true"
  USE_WALG_RESTORE: "true"
  BACKUP_SCHEDULE: '00 10 * * *'
  AWS_ACCESS_KEY_ID: アクセスキー
  AWS_SECRET_ACCESS_KEY: シークレットキー
  AWS_S3_FORCE_PATH_STYLE: "true"
  AWS_ENDPOINT: https://img.estampie.work
  AWS_REGION: "us-east-1"
  WALG_DISABLE_S3_SSE: "true"
  BACKUP_NUM_TO_RETAIN: "5"
  CLONE_USE_WALG_RESTORE: "true"

できたら下記でデプロイ

kubectl create -f manifests/configmap.yaml
kubectl create -f manifests/operator-service-account-rbac.yaml
kubectl create -f manifests/postgres-operator.yaml
kubectl create -f manifests/api-service.yaml

さらに下記でダッシュボードもインストールできます。NodePortサービスを作成するかkubectl port-forward --address 0.0.0.0 svc/postgres-operator-ui 8081:80を実行中にhttp://マスターノードのIP:8081/でアクセスできます。

kubectl apply -f ui/manifests/

PostgreSQLデーターベースのデプロイ

データーベースを作成するためにpostgres.yamlを作成します。(名前は適当)
最低限必要な設定はminimal-postgres-manifest.yamlに、他に設定出来る項目はcomplete-postgres-manifest.yamlに書かれているので参考にします。

apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
  name: mypg
spec:
  teamId: "misskey"
  volume:
    size: 5Gi # DBサイズ
  numberOfInstances: 2 # レプリカ数
  users:
    DBユーザー名:
    - superuser # この辺はroleを付与
    - createdb
  databases:
    misskeydb: DBユーザー名
  postgresql:
    version: "15"
    parameters:  # Expert section
      shared_buffers: "32MB"
      max_connections: "10"
      log_statement: "all"

できたら下記でデプロイ

kubectl create -f postgres.yaml

DBパスワードはランダムに生成されるので

kubectl get secret DBユーザー名.mypg.credentials.postgresql.acid.zalan.do -o jsonpath="{.data.password}" | base64 -d

でデコードしたものを使用するか、自分で設定したい場合はkubectl exec -it pod名 -- /bin/bashでmypg-0の中に入ってpostgresユーザーでDBにログインして設定します。

# postgresユーザーのパスワードを取得
kubectl get secret postgres.mypg.credentials.postgresql.acid.zalan.do -o jsonpath="{.data.password}" | base64 -d
# pod内部で行う
psql -h mypg -U postgres
ALTER USER DBユーザー名 WITH PASSWORD 'DBパスワード';

自分で設定した場合はSecretも更新します。

kubectl patch secret DBユーザー名.mypg.credentials.postgresql.acid.zalan.do -p '{"stringData":{"password":"DBパスワード"}}'

Pgpool-IIのデプロイ

負荷分散はPgpool-IIにやってもらいます。pgpoolを通すと自動で書き込みをプライマリに、リードレプリカとしてreplicaにアクセスを分散させられます。

公式ドキュメント

pgpool.yaml

pgpool本体です。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pgpool
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pgpool
  template:
    metadata:
      labels:
        app: pgpool
    spec:
      containers:
      - name: pgpool
        image: pgpool/pgpool:4.4.3
        env:
        - name: PGPOOL_PARAMS_BACKEND_HOSTNAME0
          value: "mypg"
        - name: PGPOOL_PARAMS_BACKEND_PORT0
          value: "5432"
        - name: PGPOOL_PARAMS_BACKEND_WEIGHT0
          value: "1"
        - name: PGPOOL_PARAMS_BACKEND_FLAG0
          value: "ALWAYS_PRIMARY|DISALLOW_TO_FAILOVER"
        - name: PGPOOL_PARAMS_BACKEND_HOSTNAME1
          value: "mypg-repl"
        - name: PGPOOL_PARAMS_BACKEND_PORT1
          value: "5432"
        - name: PGPOOL_PARAMS_BACKEND_WEIGHT1
          value: "1"
        - name: PGPOOL_PARAMS_BACKEND_FLAG1
          value: "DISALLOW_TO_FAILOVER"
        - name: PGPOOL_PARAMS_FAILOVER_ON_BACKEND_ERROR
          value: "off"
        - name: PGPOOL_PARAMS_ENABLE_POOL_HBA
          value: "off"
        - name: PGPOOL_PARAMS_SSL
          value: "on"
        - name: POSTGRES_USERNAME 
          valueFrom:
            secretKeyRef:
              name: DBユーザー名.mypg.credentials.postgresql.acid.zalan.do
              key: username
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: DBユーザー名.mypg.credentials.postgresql.acid.zalan.do
              key: password
        - name: PGPOOL_ENABLE_POOL_PASSWD
          value: "true"
        - name: PGPOOL_SKIP_PASSWORD_ENCRYPTION
          value: "false"
---
apiVersion: v1
kind: Service
metadata:
  name: pgpool
spec:
  selector:
    app: pgpool
  ports:
  - name: pgpool-port
    protocol: TCP
    port: 9999
    targetPort: 9999

kubectl get serviceコマンドでプライマリとレプリカのサービス名がわかるのでそれぞれPGPOOL_PARAMS_BACKEND_HOSTNAME0,PGPOOL_PARAMS_BACKEND_HOSTNAME1に設定します。

できたらデプロイ

kubectl apply -f pgpool.yaml

Redis

Redisをを作成します。Misskey Hubのスケールアウトを参考にPub/Subとジョブキューを別podにしています。

redis.yaml

# Main
apiVersion: v1
kind: Service
metadata:
  name: redis
spec:
  selector:
    app: redis
  ports:
    - protocol: TCP
      port: 6379
      targetPort: 6379
---
# Pub/Sub
apiVersion: v1
kind: Service
metadata:
  name: redis-pubsub
spec:
  selector:
    app: redis-pubsub
  ports:
  - protocol: TCP
    port: 6379
    targetPort: 6379
---
# ジョブキュー
apiVersion: v1
kind: Service
metadata:
  name: redis-jobqueue
spec:
  selector:
    app: redis-jobqueue
  ports:
  - protocol: TCP
    port: 6379
    targetPort: 6379
---
# main
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
spec:
  replicas: 2
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:7-alpine
        volumeMounts:
        - name: redis-data
          mountPath: /data
  volumeClaimTemplates:
  - metadata:
      name: redis-data
    spec:
      storageClassName: rook-ceph-block
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
---
# Pub/Sub
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-pubsub
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis-pubsub
  template:
    metadata:
      labels:
        app: redis-pubsub
    spec:
      containers:
      - name: redis-pubsub
        image: redis:7-alpine
        volumeMounts:
        - name: redis-pubsub-data
          mountPath: /data
  volumeClaimTemplates:
  - metadata:
      name: redis-pubsub-data
    spec:
      storageClassName: rook-ceph-block
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
---
# ジョブキュー
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-jobqueue
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis-jobqueue
  template:
    metadata:
      labels:
        app: redis-jobqueue
    spec:
      containers:
      - name: redis-jobqueue
        image: redis:7-alpine
        volumeMounts:
        - name: redis-jobqueue-data
          mountPath: /data
  volumeClaimTemplates:
  - metadata:
      name: redis-jobqueue-data
    spec:
      storageClassName: rook-ceph-block
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
kubectl create -f redis.yaml

Misskey本体のデプロイ

とりあえず公式イメージで

misskey-config.yaml

設定ファイルです。元々の/misskey/.config/default.ymlの内容をここに書きます

apiVersion: v1
kind: ConfigMap
metadata:
  name: misskey-config
data:
  default.yml: |
    # Misskey configuration
    url: https://mymiss.local/
    port: 3000
    db:
      host: pgpool
      port: 9999
      db: misskeydb
      user: DBユーザー名
      pass: DBパスワード
    dbReplications: false # pgpoolを使用する場合pgpookが行うので不要
    redis:
      host: redis
      port: 6379
    redisForPubsub:
      host: redis-pubsub
      port: 6379
    redisForJobQueue:
      host: redis-jobqueue
      port: 6379
    id: 'aid'
    proxyBypassHosts:
      - api.deepl.com
      - api-free.deepl.com
      - www.recaptcha.net
      - hcaptcha.com
      - challenges.cloudflare.com
    signToActivityPubGet: true

misskey.yaml

misskeyは最終的には自分でビルドしたものを使いますがとりあえず作成済みイメージで

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: NodePort
  selector:
    app: web
  ports:
  - name: http
    protocol: TCP
    port: 3000
    targetPort: 3000
    nodePort: 30100
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: misskey-web
spec:
  replicas: 1
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: "50%"
      maxSurge: "50%"
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      securityContext:
        fsGroup: 1000
      containers:
      - name: web
        image: misskey/misskey:latest
        envFrom:
        - configMapRef:
            name: misskey-config
        env:
        volumeMounts:
        - mountPath: /misskey/files
          name: misskey-files
        - mountPath: /misskey/.config
          name: misskey-config
        ports:
        - containerPort: 3000
        resources:
          limits:
            cpu: "2"
        command: ["pnpm", "run", "migrateandstart"]
      volumes:
      - name: misskey-config
        configMap:
          name: misskey-config  
      - name: misskey-files
        persistentVolumeClaim:
          claimName: pvc-misskey-files
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-misskey-files
spec:
  storageClassName: rook-ceph-block
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

できたらデプロイ

kubectl apply -f misskey-config.yaml
kubectl apply -f misskey.yaml

完成

kubectl logs pod名でログを確認しつつ、データーベースのマイグレーションが終了したらマスターノードのマシンからcurlコマンドでアクセスしてみます。

やった~👍

ホストOS側のブラウザでも確認してみます。

デプロイできました。やったね🎉

終わりに

DB接続周りで結構躓きましたがとりあえずデプロイできました。

次はPostgreSQLイメージのSpiloのDockerfileにPgroonga拡張インストールを追加してカスタムイメージにして読み込ませます。また、pgroonga拡張を導入した検索にMisskey本体も書き換えます。その他ローカルでテスト予定です。

お疲れさまでした!