diff --git a/README.md b/README.md index 0e2fac6..ef4f09d 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ headlamp が 1 文字なのは、規則どおりだと head + lamp でも hubble | [`k8up/`](apps/k8up/) | バックアップ(restic → Cloudflare R2)。**Talos でホストのスクリプトが使えなくなる**ぶんの受け皿 | | [`infisical/`](apps/infisical/) [`infisical-operator/`](apps/infisical-operator/) [`infisical-push-bridge/`](apps/infisical-push-bridge/) | 秘密の配布。本体は `bootstrap/` にあり、ここには公開経路と operator と即時反映のブリッジ | | [`headlamp/`](apps/headlamp/) | Kubernetes の Web UI(`h.doany.io`、Portainer の置き換え)。認証は Entra、権限は `bootstrap/apiserver/` | +| [`rybbit/`](apps/rybbit/) | Rybbit(`rb.doany.io`、アクセス解析。クッキー無し)+ ClickHouse + PostgreSQL + Redis。トドロクの離脱をファネル・ジャーニー・セッションリプレイで見る。**使い始めは [apps/rybbit/README.md](apps/rybbit/README.md)** | **アプリの多くはこのリポジトリに無い。** 各アプリのリポジトリの `deploy/` に置いてあり、 `bootstrap/argocd/repos.yaml` の ApplicationSet が拾う(上の「仕組み」)。 diff --git a/apps/k8up/README.md b/apps/k8up/README.md index 15837af..0c94296 100644 --- a/apps/k8up/README.md +++ b/apps/k8up/README.md @@ -194,6 +194,7 @@ sudo k3s kubectl -n k8up logs -f job/k8up-retire | --- | --- | --- | | mattermost | postgres の `pg_dump` | [../mattermost/postgres.yaml](../mattermost/postgres.yaml) | | forgejo | postgres の `pg_dump` + リポジトリの PVC(ファイル) | [../forgejo/postgres.yaml](../forgejo/postgres.yaml) | +| rybbit | postgres の `pg_dump` + ClickHouse の PVC(ファイル。パートは追記だけなので動いたまま取る) | [../rybbit/postgres.yaml](../rybbit/postgres.yaml) / [../rybbit/clickhouse.yaml](../rybbit/clickhouse.yaml) | | erpnext | mariadb の `mariadb-dump` | 上流 chart の `worker.gunicorn.podAnnotations`([application.yaml](../erpnext/application.yaml)) | | infisical | postgres の `pg_dump` | `bootstrap/infisical/helmchart.yaml` の `postgresql.primary.podAnnotations`(**SOPS 済みなので編集は `sops set`**) | | lgtm / xool / worklog / denpa / blog / todoroku | SQLite を `serialize()` した 1 ファイル | 各アプリのリポジトリの `deploy/`(denpa と yosegaki は chart) | diff --git a/apps/k8up/schedules.yaml b/apps/k8up/schedules.yaml index fe0607c..1425992 100644 --- a/apps/k8up/schedules.yaml +++ b/apps/k8up/schedules.yaml @@ -329,6 +329,29 @@ spec: keepMonthly: 6 tags: [k8up] --- +apiVersion: k8up.io/v1 +kind: Schedule +metadata: + name: rybbit + namespace: rybbit + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + # 15:00〜17:45 の枠は埋まっているので、その前の 14:30(14:45 も既に使っている) + backup: + schedule: "30 14 * * *" + tags: [k8up] + podSecurityContext: {runAsUser: 0, runAsGroup: 0} + failedJobsHistoryLimit: 2 + successfulJobsHistoryLimit: 1 + prune: + schedule: "45 23 * * 0" + retention: + keepDaily: 7 + keepWeekly: 4 + keepMonthly: 6 + tags: [k8up] + # リポジトリの健全性チェック。これだけ 1 本。 # ホストの `backup/k3s-backup` が `restic check --read-data-subset=5%` を回していたぶんで、 # Talos に移るとあれが無くなるので k8up 側に置いておく。 diff --git a/apps/rybbit/README.md b/apps/rybbit/README.md new file mode 100644 index 0000000..ab8af74 --- /dev/null +++ b/apps/rybbit/README.md @@ -0,0 +1,44 @@ +# Rybbit(rb.doany.io) + +アクセス解析。Google Analytics の置き換えで、クッキーを使わない(同意バナーが要らない)。 +トドロク(tk.doany.io)の LP から購入までのどこで離脱するかを、ファネル・ユーザージャーニー・ +セッションリプレイで見るために置いた(2026-09-19)。AGPL-3.0、自分で動かす分には縛りはない。 + +上流: 。構成は上流の docker-compose.yml をそのまま k8s に写した。 + +| ファイル | 中身 | +| --- | --- | +| [rybbit.yaml](rybbit.yaml) | backend(Fastify、`/api`)と client(Next.js)。`ghcr.io/rybbit-io/rybbit-{backend,client}:vX.Y.Z` を 2 つ同じタグで | +| [clickhouse.yaml](clickhouse.yaml) | イベントの倉庫。上流の設定 4 本を ConfigMap に、メモリの上限だけこの箱向けに下げた(limit 3 Gi、1 クエリ 2 GB) | +| [postgres.yaml](postgres.yaml) | ユーザー・サイト・設定。forgejo と同じ 17 系、`pg_dump` を k8up に | +| [redis.yaml](redis.yaml) | セッション追跡のカウンタ。PVC は持たない | +| [rybbit-secrets.yaml](rybbit-secrets.yaml) | Infisical `/rybbit/rybbit` の 4 キー → Secret `rybbit` | +| [httproute.yaml](httproute.yaml) | `rb.doany.io`。`/api` は backend、ほかは client。上流の Caddyfile にある `/.well-known/oauth-*`(MCP 向け)は使わないので置かない | + +バックアップは [../k8up/schedules.yaml](../k8up/schedules.yaml) の `rybbit`(ClickHouse の PVC をファイルとして、PostgreSQL は `pg_dump`)。 + +## 使い始め + +1. Infisical(il.doany.io)に フォルダ `/rybbit/rybbit` を作り、`clickhouse-password` / `postgres-password` / + `redis-password` / `better-auth-secret` を英数字のランダム(32 文字くらい)で入れる。 + 入れるまで Pod は `CreateContainerConfigError` で待つ(壊れてはいない) +2. を開き、最初のアカウントを作る(これが管理者) +3. **すぐに** [rybbit.yaml](rybbit.yaml) の `DISABLE_SIGNUP` と `NEXT_PUBLIC_DISABLE_SIGNUP` を `"true"` にして + PR を出す。false のままだと誰でもこのインスタンスにアカウントを作れる +4. 画面で「サイトを追加」→ `tk.doany.io`。出てくるサイト ID をトドロクの `PUBLIC_RYBBIT_SITE_ID` + (todoroku の `deploy/deployment.yaml`)に入れる。トドロクの root layout が + `https://rb.doany.io/api/script.js` を読み、購入完了と車両の登録をイベントで送る +5. Rybbit のファネルに「LP → ログイン → 台帳 → 車両の登録 → 料金 → 購入」を作る + +## 気にしておくこと + +- **訪問者の IP と国。** Cloudflare の後ろにいるので、backend に届く `X-Forwarded-For` の先頭が + 訪問者の IP かどうかを最初に確かめる(Rybbit の「国」が Cloudflare のデータセンターの所在地ばかりなら + ずれている)。上流の Caddyfile は Cloudflare の範囲を `trusted_proxies` にして対処している。 + Cilium Gateway(Envoy)は既定で XFF を追記するので、たぶんそのままで正しい +- **メモリ。** ClickHouse は limit 3 Gi。1 サイトなら実測 数百 MB で足りるはず。足りなければ + clickhouse.yaml の limit と `resource_limits.xml` は比率なので limit だけ上げればよい +- **版。** GitHub のリリースの `vX.Y.Z` を 2 つのイメージに同じタグで。Renovate が PR を出す。 + ClickHouse・PostgreSQL・Redis は上流の compose が使っている版に合わせる +- **PSA は baseline。** privileged にした他の namespace と違い、hostPort も特権も要らない。 + Talos に移ったときそのまま通る diff --git a/apps/rybbit/clickhouse.yaml b/apps/rybbit/clickhouse.yaml new file mode 100644 index 0000000..0fa3992 --- /dev/null +++ b/apps/rybbit/clickhouse.yaml @@ -0,0 +1,167 @@ +# Rybbit のイベントを溜める ClickHouse。上流の docker-compose.yml の設定(network / logging / resource_limits / +# user_settings)をそのまま ConfigMap にし、メモリの上限だけこの箱に合わせて下げた。 +# +# 版は上流の compose に合わせる(26.3 系)。Renovate が PR を出す。 +apiVersion: v1 +kind: Service +metadata: + name: clickhouse + namespace: rybbit +spec: + selector: + app: clickhouse + ports: + - name: http + port: 8123 + targetPort: 8123 +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: clickhouse-data + namespace: rybbit + annotations: + # ファイルとして取る。MergeTree のパートは書き終わると変わらないディレクトリなので、 + # 動いたままコピーしても壊れにくい(書きかけは tmp_ の下)。アクセス解析なので、 + # 最悪でも直近の数時間が欠けるだけ。論理バックアップ(BACKUP 文)は S3 先が要るので使わない + k8up.io/backup: "true" +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-path-retain + resources: + requests: + storage: 20Gi +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: clickhouse-config + namespace: rybbit +data: + network.xml: | + + 0.0.0.0 + + logging_rules.xml: | + + + warning + true + + + + + + + + + + + + + + + + + resource_limits.xml: | + + + 0.80 + 1 + 0.25 + + # プロファイル(ユーザー側)の設定は users.d からしか読まない + user_settings.xml: | + + + + 1 + 1 + 1 + 0 + 0 + 0 + + 2000000000 + 4 + + + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: clickhouse + namespace: rybbit + annotations: + secrets.infisical.com/auto-reload: "true" +spec: + replicas: 1 + # ReadWriteOnce なので古い Pod が消えてから新しい Pod が付ける + strategy: + type: Recreate + selector: + matchLabels: + app: clickhouse + template: + metadata: + labels: + app: clickhouse + spec: + # イメージは uid 101(clickhouse)。作りたての PV は root のものなので、グループで書けるようにする + securityContext: + fsGroup: 101 + fsGroupChangePolicy: OnRootMismatch + containers: + - name: clickhouse + image: clickhouse/clickhouse-server:26.3.17.4 + ports: + - containerPort: 8123 + env: + - name: CLICKHOUSE_DB + value: analytics + - name: CLICKHOUSE_USER + value: default + - name: CLICKHOUSE_PASSWORD + valueFrom: + secretKeyRef: + name: rybbit + key: clickhouse-password + # backend が起動時に SQL で最小権限の rybbit_query ユーザーを作れるように + - name: CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT + value: "1" + readinessProbe: + httpGet: + path: /ping + port: 8123 + initialDelaySeconds: 10 + periodSeconds: 10 + resources: + requests: + cpu: 100m + memory: 512Mi + limits: + memory: 3Gi + volumeMounts: + - name: data + mountPath: /var/lib/clickhouse + - name: config + mountPath: /etc/clickhouse-server/config.d/network.xml + subPath: network.xml + - name: config + mountPath: /etc/clickhouse-server/config.d/logging_rules.xml + subPath: logging_rules.xml + - name: config + mountPath: /etc/clickhouse-server/config.d/resource_limits.xml + subPath: resource_limits.xml + - name: config + mountPath: /etc/clickhouse-server/users.d/user_settings.xml + subPath: user_settings.xml + volumes: + - name: data + persistentVolumeClaim: + claimName: clickhouse-data + - name: config + configMap: + name: clickhouse-config diff --git a/apps/rybbit/httproute.yaml b/apps/rybbit/httproute.yaml new file mode 100644 index 0000000..cd67d57 --- /dev/null +++ b/apps/rybbit/httproute.yaml @@ -0,0 +1,32 @@ +# 公開経路。証明書は Gateway(bootstrap/gateway/)のワイルドカードなので指定しない。 +# 振り分けは /api が backend、ほかは client。上流の Caddyfile はほかに /.well-known/oauth-* と +# /.well-known/openid-configuration(MCP クライアント向けの OAuth の案内。Rybbit が OAuth サーバになる側)も +# backend に流すが、MCP は使わないので置かない。Gateway API の PathPrefix は / 区切りの要素単位で、 +# `/.well-known/oauth-` のような途中までの前方一致は書けない(要るなら Exact でフルパスを並べる)。 +# 計測のスクリプトは https://rb.doany.io/api/script.js、イベントの送り先は /api/track。 +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: rybbit + namespace: rybbit +spec: + parentRefs: + - name: doany + namespace: kube-system + sectionName: https + hostnames: ["rb.doany.io"] + rules: + - matches: + - path: + type: PathPrefix + value: /api + backendRefs: + - name: backend + port: 3001 + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: client + port: 3002 diff --git a/apps/rybbit/namespace.yaml b/apps/rybbit/namespace.yaml new file mode 100644 index 0000000..76f789b --- /dev/null +++ b/apps/rybbit/namespace.yaml @@ -0,0 +1,10 @@ +# PSA は baseline。hostPort も privileged も無い(ClickHouse は uid 101、postgres は起動時に root から落とすだけ)。 +# 一覧と背景は docs/talos.md「PSA のラベル」(k3s では効かない。Talos のために書いておく)。 +apiVersion: v1 +kind: Namespace +metadata: + name: rybbit + labels: + pod-security.kubernetes.io/enforce: baseline + pod-security.kubernetes.io/audit: baseline + pod-security.kubernetes.io/warn: baseline diff --git a/apps/rybbit/postgres.yaml b/apps/rybbit/postgres.yaml new file mode 100644 index 0000000..b2a7f48 --- /dev/null +++ b/apps/rybbit/postgres.yaml @@ -0,0 +1,87 @@ +# Rybbit のユーザー・サイト・設定の DB。forgejo/postgres.yaml と同じ形(17 系、pg_dump を k8up に取らせる)。 +apiVersion: v1 +kind: Service +metadata: + name: postgres + namespace: rybbit +spec: + selector: + app: postgres + ports: + - name: postgres + port: 5432 + targetPort: 5432 +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-data + namespace: rybbit + annotations: + # DB はファイルとして取らない。下の k8up.io/backupcommand の pg_dump で足りる。 + k8up.io/backup: "false" +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-path-retain + resources: + requests: + storage: 2Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres + namespace: rybbit +spec: + replicas: 1 + # ReadWriteOnce なので古い Pod が消えてから新しい Pod が付ける。RollingUpdate だと詰まる。 + strategy: + type: Recreate + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + annotations: + k8up.io/backupcommand: /bin/sh -c 'pg_dump -U rybbit -d analytics --clean --if-exists' + k8up.io/file-extension: .sql + spec: + containers: + - name: postgres + image: postgres:17.11 + ports: + - containerPort: 5432 + env: + - name: POSTGRES_DB + value: analytics + - name: POSTGRES_USER + value: rybbit + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: rybbit + key: postgres-password + # initdb は空でないディレクトリを嫌うので、マウント先の下の段に置く + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + readinessProbe: + exec: + command: [pg_isready, -U, rybbit, -d, analytics] + initialDelaySeconds: 10 + periodSeconds: 10 + resources: + requests: + cpu: 20m + memory: 128Mi + limits: + memory: 512Mi + volumeMounts: + - name: postgres-data + mountPath: /var/lib/postgresql/data + volumes: + - name: postgres-data + persistentVolumeClaim: + claimName: postgres-data diff --git a/apps/rybbit/redis.yaml b/apps/rybbit/redis.yaml new file mode 100644 index 0000000..b978550 --- /dev/null +++ b/apps/rybbit/redis.yaml @@ -0,0 +1,65 @@ +# Rybbit のセッション追跡・ボット判定のカウンタ。上流の compose どおり noeviction + AOF。 +# セッションの鍵は 30 分で消えるのでメモリは増え続けない。データは PVC を持たない(emptyDir)。 +# 再起動で消えるのは進行中のセッションの継ぎ目だけで、集計(ClickHouse)には残っている。 +apiVersion: v1 +kind: Service +metadata: + name: redis + namespace: rybbit +spec: + selector: + app: redis + ports: + - name: redis + port: 6379 + targetPort: 6379 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis + namespace: rybbit + annotations: + secrets.infisical.com/auto-reload: "true" +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + containers: + - name: redis + image: redis:8.6.4-alpine + ports: + - containerPort: 6379 + env: + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: rybbit + key: redis-password + command: + - /bin/sh + - -c + - exec redis-server --requirepass "$REDIS_PASSWORD" --appendonly yes --appendfsync everysec --maxmemory-policy noeviction + readinessProbe: + exec: + command: [/bin/sh, -c, 'redis-cli -a "$REDIS_PASSWORD" --no-auth-warning ping | grep -q PONG'] + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + requests: + cpu: 10m + memory: 32Mi + limits: + memory: 256Mi + volumeMounts: + - name: data + mountPath: /data + volumes: + - name: data + emptyDir: {} diff --git a/apps/rybbit/rybbit-secrets.yaml b/apps/rybbit/rybbit-secrets.yaml new file mode 100644 index 0000000..e849233 --- /dev/null +++ b/apps/rybbit/rybbit-secrets.yaml @@ -0,0 +1,43 @@ +# Secret rybbit は Infisical (il.doany.io) のフォルダ /rybbit/rybbit にあり、 +# Infisical 純正 operator (bootstrap/infisical-operator) が Secret にする(mattermost と同じ形)。 +# +# | キー | 何に使うか | +# | --- | --- | +# | clickhouse-password | ClickHouse の default ユーザー。backend も同じ値で入る | +# | postgres-password | PostgreSQL の rybbit ユーザー(最初の起動後は変えない。initdb のときだけ効く) | +# | redis-password | Redis の requirepass | +# | better-auth-secret | ログインのセッション署名。変えると全員ログアウト | +# +# 4 つとも英数字だけのランダム(32 文字くらい)。**同期する前に**入れる。無いと Pod が +# CreateContainerConfigError で待つ(入れれば operator が Secret を作って自然に上がる)。 +# 値は Infisical の UI で書き換えるだけ(push-bridge が数秒で同期。auto-reload 注釈のある +# Deployment は自動で入れ替わる)。 +apiVersion: secrets.infisical.com/v1alpha1 +kind: InfisicalSecret +metadata: + name: rybbit + namespace: rybbit + # 2 つとも必須。前者は operator が作る Secret を prune させないため、後者は + # CRD がまだ無いクラスタで apps/ の同期が丸ごと落ちるのを防ぐため。 + # bootstrap/README.md「InfisicalSecret には sync-options を 2 つ付けること」。 + annotations: + argocd.argoproj.io/sync-options: Prune=false,SkipDryRunOnMissingResource=true +spec: + hostAPI: http://infisical.infisical.svc:8080/api + authentication: + kubernetesAuth: + identityId: f2bc3416-f196-4626-9bf3-c11693b32cc6 + autoCreateServiceAccountToken: true + serviceAccountRef: + name: infisical-auth + namespace: infisical + secretsScope: + projectSlug: doa + envSlug: prod + secretsPath: /rybbit/rybbit + recursive: false + managedKubeSecretReferences: + - secretName: rybbit + secretNamespace: rybbit + # Orphan: CR を消しても Secret は残す(移行や再作成で稼働中の Pod を巻き込まない) + creationPolicy: Orphan diff --git a/apps/rybbit/rybbit.yaml b/apps/rybbit/rybbit.yaml new file mode 100644 index 0000000..15ccb2e --- /dev/null +++ b/apps/rybbit/rybbit.yaml @@ -0,0 +1,172 @@ +# Rybbit 本体。rb.doany.io。backend(Fastify、/api)と client(Next.js、画面)の 2 つ。 +# 版は GitHub のリリースに合わせて 2 つ同じタグで上げる(タグは v 付き。latest は追わない)。 +# +# 環境変数は上流の docker-compose.yml の backend / client と同じ。DISABLE_SIGNUP は +# 最初のアカウントを作ったら true にする(README「使い始め」)。テレメトリは切る。 +apiVersion: v1 +kind: Service +metadata: + name: backend + namespace: rybbit +spec: + selector: + app: backend + ports: + - name: http + port: 3001 + targetPort: 3001 +--- +apiVersion: v1 +kind: Service +metadata: + name: client + namespace: rybbit +spec: + selector: + app: client + ports: + - name: http + port: 3002 + targetPort: 3002 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend + namespace: rybbit + annotations: + # 参照している Secret を Infisical operator が同期したら、自動でローリング再起動する + secrets.infisical.com/auto-reload: "true" +spec: + replicas: 1 + selector: + matchLabels: + app: backend + template: + metadata: + labels: + app: backend + spec: + initContainers: + # backend は DB を一定回数しか待たずに落ちるので、初回(initdb 中・ClickHouse の起動中)は先に待つ + - name: wait-for-db + image: busybox:1.37.0 + command: + - sh + - -c + - | + until wget -q -O- http://clickhouse:8123/ping >/dev/null 2>&1; do echo "ClickHouse を待っている"; sleep 5; done + until nc -z postgres 5432; do echo "PostgreSQL を待っている"; sleep 5; done + until nc -z redis 6379; do echo "Redis を待っている"; sleep 5; done + containers: + - name: backend + image: ghcr.io/rybbit-io/rybbit-backend:v2.9.0 + ports: + - containerPort: 3001 + env: + - name: NODE_ENV + value: production + - name: BASE_URL + value: https://rb.doany.io + - name: CLICKHOUSE_HOST + value: http://clickhouse:8123 + - name: CLICKHOUSE_DB + value: analytics + - name: CLICKHOUSE_PASSWORD + valueFrom: + secretKeyRef: + name: rybbit + key: clickhouse-password + # 上流の既定は CLICKHOUSE_PASSWORD と同じ値。backend が rybbit_query ユーザーをこの値で作る + - name: CLICKHOUSE_QUERY_PASSWORD + valueFrom: + secretKeyRef: + name: rybbit + key: clickhouse-password + - name: POSTGRES_HOST + value: postgres + - name: POSTGRES_PORT + value: "5432" + - name: POSTGRES_DB + value: analytics + - name: POSTGRES_USER + value: rybbit + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: rybbit + key: postgres-password + - name: REDIS_HOST + value: redis + - name: REDIS_PORT + value: "6379" + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: rybbit + key: redis-password + - name: BETTER_AUTH_SECRET + valueFrom: + secretKeyRef: + name: rybbit + key: better-auth-secret + # 最初のアカウントを作るまで false。作ったら true にする(誰でも登録できる状態にしない) + - name: DISABLE_SIGNUP + value: "false" + - name: DISABLE_TELEMETRY + value: "true" + # 上流の既定は 4。1 サイトなので 2 + - name: CLUSTER_WORKERS + value: "2" + readinessProbe: + httpGet: + path: /api/health + port: 3001 + initialDelaySeconds: 10 + periodSeconds: 10 + resources: + requests: + cpu: 50m + memory: 256Mi + limits: + memory: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: client + namespace: rybbit +spec: + replicas: 1 + selector: + matchLabels: + app: client + template: + metadata: + labels: + app: client + spec: + containers: + - name: client + image: ghcr.io/rybbit-io/rybbit-client:v2.9.0 + ports: + - containerPort: 3002 + env: + - name: NODE_ENV + value: production + - name: NEXT_PUBLIC_BACKEND_URL + value: https://rb.doany.io + - name: NEXT_PUBLIC_DISABLE_SIGNUP + value: "false" + readinessProbe: + httpGet: + path: / + port: 3002 + initialDelaySeconds: 10 + periodSeconds: 15 + resources: + requests: + cpu: 20m + memory: 128Mi + limits: + memory: 512Mi