Closed4

CustomResourceでPostgresのDatabaseとUser(とPassword)を作成したい

dekimasoondekimasoon

PostgreSQLやMySQLを使っていると、アプリケーションごとにDatabaseやUser、Passwordを作成して権限制御したい場合が多々あると思う。特にKubernetesを利用している場合は、アプリケーションのリソース定義と一緒に、PostgreSQLのDatabaseやUserも合わせて定義できたら大変便利である。

Kubernetes上でDBクラスタ自体を管理するOperatorの場合は、上記のような仕組みを併せ持っている場合も多いと思うが、今回はDBクラスタはAWS上のAurora Serverless v2で運用し、Kubernetes上で管理するのはあくまでクラスタ内のDatabseとUser, Passwordになる。

使えそうなライブラリとしては3点見つけた。

  1. https://github.com/crossplane-contrib/provider-sql
  2. https://github.com/movetokube/postgres-operator
  3. https://github.com/EasyMile/postgresql-operator

1はcrossplaneのプラグインの1つで、PostgreSQLに限らすMySQLとかも管理できそう。2と3はPostgreSQL専用。2の方がstarが多いが、ソースコードとかは3のほうがキレイに見える(ロクにGo書けないので直感)。

1はcrossplane本体をいれるのがちょっと面倒というか大げさな感じがする。
というわけでスターは少ないが企業が管理している3を採用してみる。

dekimasoondekimasoon

Operatorを入れてみる(EasyMile/postgresql-operator)

Helm ChartがGithub内に用意されているが、Chart用の公開リポジトリの記載がない。
ローカルに落として来る必要がありそうだ。面倒だぞ。Issueを立ててみた。時間がないのだ許してくれ。

https://github.com/EasyMile/postgresql-operator/issues/24

Operatorを入れてみる(movetokube/postgres-operator)

というわけで急遽Operatorを変更する。なんて技術力、解決力のなさ。
以下で入った気がする。valuesのところにAWSで立てたAuroraに接続するための情報を渡す。
なお、接続情報をもたせたSecretを作っておいて、そのSecretの名前を渡す方式も可能らしい。

import * as k8s from "@pulumi/kubernetes"
import * as pulumi from "@pulumi/pulumi"

export type KubernetesPostgresOperatorArgs = {
  host: pulumi.Input<string>
  user: pulumi.Input<string>
  password: pulumi.Input<string>
  defaultDatabase: pulumi.Input<string>
}

export class KubernetesPostgresOperator extends pulumi.ComponentResource {
  public opts: pulumi.ResourceOptions
  public namespace: k8s.core.v1.Namespace
  public release: k8s.helm.v3.Release

  constructor(
    name: string,
    args: KubernetesPostgresOperatorArgs,
    opts?: pulumi.ResourceOptions,
  ) {
    super("stack8:kubernetes:PostgresOperator", name, undefined, opts)

    this.opts = { ...opts, parent: this }

    this.namespace = new k8s.core.v1.Namespace(
      "namespace",
      {
        metadata: {
          name: "postgres-operator",
        },
      },
      this.opts,
    )

    this.release = new k8s.helm.v3.Release(
      "release",
      {
        chart: "ext-postgres-operator",
        namespace: this.namespace.metadata.name,
        version: "1.2.6",
        repositoryOpts: {
          repo: "https://movetokube.github.io/postgres-operator/",
        },
        values: {
          postgres: {
            host: args.host,
            user: args.user,
            password: args.password,
            cloud_provider: "AWS",
            default_database: args.defaultDatabase,
          },
        },
      },
      {
        ...this.opts,
        // FIXME:
        // It always comes up as a diff, so we'll make it an ignore target once.
        // We want to investigate.
        ignoreChanges: ["checksum"],
      },
    )
  }
}
dekimasoondekimasoon

Databaseを作ってみる

以下のようなCustomResourceを定義することで、PostgresのDatabaseとUser(Role)、Passwordが生成されるらしい。Pulumi(TypeScriptなのは許して欲しい)からの抜粋なので見づらいのはご勘弁。

his.postgresDatabse = new k8s.apiextensions.CustomResource(
  "postgres-database",
  {
    apiVersion: "db.movetokube.com/v1alpha1",
    kind: "Postgres",
    metadata: {
      namespace: this.namespace.metadata.name,
      name: "example-app",
    },
    spec: {
      database: "example-app",
      dropOnDelete: true,
      extensions: ["pgcrypto", "pg_bigm"],
    },
  },
  this.opts,
)

this.postgresUser = new k8s.apiextensions.CustomResource(
  "postgres-user",
  {
    apiVersion: "db.movetokube.com/v1alpha1",
    kind: "PostgresUser",
    metadata: {
      namespace: this.namespace.metadata.name,
      name: "example-app",
    },
    spec: {
      role: "example-app",
      database: "example-app",
      secretName: "secret",
      privileges: "OWNER",
    },
  },
  this.opts,
)

適応すると接続情報を持ったSecretが生成されるっぽい。
HOST名が含まれるとこだけ念の為マスクしている。

✗ kubectl get secrets -n example-app secret-example-app -o yaml
apiVersion: v1
data:
  DATABASE_NAME: ZXhhbXBsZS1hcHA=
  HOST: xxx
  LOGIN: ZXhhbXBsZS1hcHAtdUpFMGZR
  PASSWORD: VkdzNU5kSmxaSEx5UXkx
  POSTGRES_DOTNET_URL: xxx
  POSTGRES_JDBC_URL: xxx
  POSTGRES_URL: xxx
  ROLE: ZXhhbXBsZS1hcHAtdUpFMGZR
kind: Secret
metadata:
  creationTimestamp: "2024-03-15T05:30:43Z"
  labels:
    app: example-app
  name: secret-example-app
  namespace: example-app
type: Opaque

試しに接続してみる。
指定したpgcryptopg_bigmが入っていることも確認できた。
なぜplpgsqlが入っているのかは不明。Auroraでは必須なのか、ParameterGroupとかで指定しているのか。きっと何かしら理由があるだろうが、いまは一旦良し。

✗ kubectl run -it --rm --restart=Never postgres-cli --image=postgres -- sh
# psql postgresql://example.com/example-app
psql (16.2 (Debian 16.2-1.pgdg120+2), server 15.4)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
Type "help" for help.
example-app=> select * from pg_extension;
  oid  | extname  | extowner | extnamespace | extrelocatable | extversion | extconfig | extcondition
-------+----------+----------+--------------+----------------+------------+-----------+--------------
 14498 | plpgsql  |       10 |           11 | f              | 1.0        |           |
 24736 | pgcrypto |       10 |         2200 | t              | 1.3        |           |
 24773 | pg_bigm  |       10 |         2200 | t              | 1.2        |           |
(3 rows)

schemaは指定しなかったが以下のような感じになっている。
デフォルトではpublicのみらしい。

example-app=> \dn
      List of schemas
  Name  |       Owner
--------+-------------------
 public | pg_database_owner
(1 row)

以下のように指定することで作成されるSchemaを指定できるらしい。
変更して適用してみる。

this.postgresDatabse = new k8s.apiextensions.CustomResource(
      "postgres-database",
      {
        apiVersion: "db.movetokube.com/v1alpha1",
        kind: "Postgres",
        metadata: {
          namespace: this.namespace.metadata.name,
          name: "example-app",
        },
        spec: {
         database: "example-app",
          dropOnDelete: true,
+         schemas: ["public", "dev", "test"],
          extensions: ["pgcrypto", "pg_bigm"],
        },
      },
      this.opts,
    )

適応後Schemaを確認すると増えている。
Ownerはpg_database_ownerではなく、直接example-app-groupになっていた。

example-app=> \dn
      List of schemas
  Name  |       Owner
--------+-------------------
 dev    | example-app-group
 public | pg_database_owner
 test   | example-app-group
このスクラップは2024/03/15にクローズされました