From 718820daecddf030989cff7cdcf090f36508761c Mon Sep 17 00:00:00 2001 From: syui Date: Wed, 4 Feb 2026 22:52:06 +0900 Subject: [PATCH] add k8s --- .gitignore | 3 + k8s/bgs.yaml | 78 ++++++++++++++++ k8s/bsky.yaml | 199 ++++++++++++++++++++++++++++++++++++++++ k8s/feed.yaml | 71 ++++++++++++++ k8s/gen-secrets.sh | 26 ++++++ k8s/jetstream.yaml | 67 ++++++++++++++ k8s/kustomization.yaml | 27 ++++++ k8s/namespace.yaml | 4 + k8s/ozone-web.yaml | 47 ++++++++++ k8s/ozone.yaml | 96 +++++++++++++++++++ k8s/pds.yaml | 121 ++++++++++++++++++++++++ k8s/plc.yaml | 53 +++++++++++ k8s/postgres.yaml | 89 ++++++++++++++++++ k8s/redis.yaml | 58 ++++++++++++ k8s/secrets.env.example | 10 ++ k8s/social-app.yaml | 52 +++++++++++ 16 files changed, 1001 insertions(+) create mode 100644 k8s/bgs.yaml create mode 100644 k8s/bsky.yaml create mode 100644 k8s/feed.yaml create mode 100755 k8s/gen-secrets.sh create mode 100644 k8s/jetstream.yaml create mode 100644 k8s/kustomization.yaml create mode 100644 k8s/namespace.yaml create mode 100644 k8s/ozone-web.yaml create mode 100644 k8s/ozone.yaml create mode 100644 k8s/pds.yaml create mode 100644 k8s/plc.yaml create mode 100644 k8s/postgres.yaml create mode 100644 k8s/redis.yaml create mode 100644 k8s/secrets.env.example create mode 100644 k8s/social-app.yaml diff --git a/.gitignore b/.gitignore index d85a12e..eff7c05 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ deploy.yml claude.md embedded.mobileprovision .env +k8s/secrets.env +k8s/deploy.yml web/dist node_modules package-lock.json + diff --git a/k8s/bgs.yaml b/k8s/bgs.yaml new file mode 100644 index 0000000..6b3f7fc --- /dev/null +++ b/k8s/bgs.yaml @@ -0,0 +1,78 @@ +apiVersion: v1 +kind: Service +metadata: + name: bgs + namespace: atproto +spec: + selector: + app: bgs + ports: + - port: 2470 + targetPort: 2470 +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: bgs-data + namespace: atproto +spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 10Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bgs + namespace: atproto +spec: + replicas: 1 + selector: + matchLabels: + app: bgs + template: + metadata: + labels: + app: bgs + spec: + containers: + - name: bgs + image: registry/bgs + ports: + - containerPort: 2470 + env: + - name: DATABASE_URL + value: "postgres://postgres:postgres@database/bgs" + - name: CARSTORE_DATABASE_URL + value: "postgres://postgres:postgres@database/carstore" + - name: DATA_DIR + value: "/data" + - name: ATP_PLC_HOST + value: "https://plc.syu.is" + - name: BGS_NEW_PDS_PER_DAY_LIMIT + value: "1000" + - name: BGS_ADMIN_KEY + valueFrom: + secretKeyRef: + name: atproto-secrets + key: bgs-admin-key + volumeMounts: + - name: data + mountPath: /data + livenessProbe: + httpGet: + path: /xrpc/_health + port: 2470 + initialDelaySeconds: 15 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /xrpc/_health + port: 2470 + initialDelaySeconds: 5 + periodSeconds: 5 + volumes: + - name: data + persistentVolumeClaim: + claimName: bgs-data diff --git a/k8s/bsky.yaml b/k8s/bsky.yaml new file mode 100644 index 0000000..6460ac9 --- /dev/null +++ b/k8s/bsky.yaml @@ -0,0 +1,199 @@ +apiVersion: v1 +kind: Service +metadata: + name: bsky + namespace: atproto +spec: + selector: + app: bsky + ports: + - name: api + port: 2584 + targetPort: 2584 + - name: dataplane + port: 3001 + targetPort: 3001 + - name: bsync + port: 3002 + targetPort: 3002 +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: bsky-data + namespace: atproto +spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 5Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bsky + namespace: atproto +spec: + replicas: 1 + selector: + matchLabels: + app: bsky + template: + metadata: + labels: + app: bsky + spec: + securityContext: + runAsUser: 0 + containers: + - name: bsky + image: registry/bsky + command: ["node", "--enable-source-maps", "api.js"] + ports: + - containerPort: 2584 + - containerPort: 3001 + - containerPort: 3002 + env: + - name: BSKY_PORT + value: "2584" + - name: BSKY_BLOB_CACHE_LOC + value: "/data/" + - name: BSKY_BSYNC_HTTP_VERSION + value: "1.1" + - name: BSKY_BSYNC_PORT + value: "3002" + - name: BSKY_BSYNC_URL + value: "http://localhost:3002" + - name: BSKY_COURIER_URL + value: "http://fake-courier.example.invalid/" + - name: BSKY_DATAPLANE_HTTP_VERSION + value: "1.1" + - name: BSKY_DATAPLANE_PORT + value: "3001" + - name: BSKY_DATAPLANE_URLS + value: "http://localhost:3001" + - name: BSKY_DB_POSTGRES_URL + value: "postgres://postgres:postgres@database/bsky" + - name: BSKY_DID_PLC_URL + value: "https://plc.syu.is" + - name: BSKY_PUBLIC_URL + value: "https://bsky.syu.is" + - name: BSKY_REPO_PROVIDER + value: "ws://bgs:2470" + - name: BSKY_SERVER_DID + value: "did:web:bsky.syu.is" + - name: MOD_SERVICE_DID + value: "did:web:ozone.syu.is" + - name: BSKY_ADMIN_PASSWORDS + valueFrom: + secretKeyRef: + name: atproto-secrets + key: bsky-admin-passwords + - name: BSKY_SERVICE_SIGNING_KEY + valueFrom: + secretKeyRef: + name: atproto-secrets + key: bsky-service-signing-key + volumeMounts: + - name: data + mountPath: /data + livenessProbe: + httpGet: + path: /xrpc/_health + port: 2584 + initialDelaySeconds: 15 + periodSeconds: 10 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /xrpc/_health + port: 2584 + initialDelaySeconds: 5 + periodSeconds: 5 + volumes: + - name: data + persistentVolumeClaim: + claimName: bsky-data +--- +## bsky subscription monitor +## subscriptionが停止していたらPodを再起動する +apiVersion: batch/v1 +kind: CronJob +metadata: + name: bsky-subscription-watchdog + namespace: atproto +spec: + schedule: "*/5 * * * *" + jobTemplate: + spec: + template: + spec: + containers: + - name: watchdog + image: postgres:16-alpine + command: + - /bin/sh + - -c + - | + # BGSの最新seqを取得 + LATEST_SEQ=$(psql -t -A "$DB_URL" -c "SELECT COALESCE(MAX(seq),0) FROM repo_event_records") + # bskyのsubscription cursorを取得 + BSKY_CURSOR=$(psql -t -A "$BSKY_DB_URL" -c "SELECT COALESCE(state,0) FROM subscription WHERE service='ws://bgs:2470' LIMIT 1") + LAG=$((LATEST_SEQ - BSKY_CURSOR)) + echo "BGS seq=$LATEST_SEQ, bsky cursor=$BSKY_CURSOR, lag=$LAG" + if [ "$LAG" -gt 50 ]; then + echo "WARN: bsky subscription lag=$LAG, restarting bsky pod" + # Podを削除すればDeploymentが再作成する + apk add --no-cache curl > /dev/null 2>&1 + APISERVER=https://kubernetes.default.svc + TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace) + # bsky podを取得して削除 + POD=$(curl -s -k -H "Authorization: Bearer $TOKEN" \ + "$APISERVER/api/v1/namespaces/$NAMESPACE/pods?labelSelector=app=bsky" \ + | grep -o '"name":"bsky-[^"]*"' | head -1 | cut -d'"' -f4) + if [ -n "$POD" ]; then + curl -s -k -X DELETE -H "Authorization: Bearer $TOKEN" \ + "$APISERVER/api/v1/namespaces/$NAMESPACE/pods/$POD" + echo "Deleted pod $POD" + fi + else + echo "OK: subscription is healthy" + fi + env: + - name: DB_URL + value: "postgres://postgres:postgres@database/bgs" + - name: BSKY_DB_URL + value: "postgres://postgres:postgres@database/bsky" + restartPolicy: OnFailure + serviceAccountName: bsky-watchdog +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: bsky-watchdog + namespace: atproto +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: bsky-watchdog + namespace: atproto +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "delete"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: bsky-watchdog + namespace: atproto +subjects: + - kind: ServiceAccount + name: bsky-watchdog + namespace: atproto +roleRef: + kind: Role + name: bsky-watchdog + apiGroup: rbac.authorization.k8s.io diff --git a/k8s/feed.yaml b/k8s/feed.yaml new file mode 100644 index 0000000..ec5d558 --- /dev/null +++ b/k8s/feed.yaml @@ -0,0 +1,71 @@ +apiVersion: v1 +kind: Service +metadata: + name: feed + namespace: atproto +spec: + selector: + app: feed + ports: + - port: 3000 + targetPort: 3000 +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: feed-data + namespace: atproto +spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 2Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: feed + namespace: atproto +spec: + replicas: 1 + selector: + matchLabels: + app: feed + template: + metadata: + labels: + app: feed + spec: + containers: + - name: feed + image: registry/feed + ports: + - containerPort: 3000 + env: + - name: FEEDGEN_PORT + value: "3000" + - name: FEEDGEN_LISTENHOST + value: "0.0.0.0" + - name: FEEDGEN_SQLITE_LOCATION + value: "/data/db.sqlite" + - name: FEEDGEN_HOSTNAME + value: "feed.syu.is" + - name: FEEDGEN_PUBLISHER_DID + value: "did:plc:6qyecktefllvenje24fcxnie" + - name: FEEDGEN_SERVICE_DID + value: "did:web:feed.syu.is" + - name: FEEDGEN_JETSTREAM_URL + value: "ws://jetstream:6008/subscribe" + volumeMounts: + - name: data + mountPath: /data + livenessProbe: + httpGet: + path: / + port: 3000 + initialDelaySeconds: 10 + periodSeconds: 10 + volumes: + - name: data + persistentVolumeClaim: + claimName: feed-data diff --git a/k8s/gen-secrets.sh b/k8s/gen-secrets.sh new file mode 100755 index 0000000..3609e99 --- /dev/null +++ b/k8s/gen-secrets.sh @@ -0,0 +1,26 @@ +#!/bin/bash +## envs/ から k8s/secrets.env を生成する +## usage: cd k8s && bash gen-secrets.sh + +ENVS_DIR="${1:-../envs}" +OUT="secrets.env" + +get_val() { + local file="$1" key="$2" + grep "^${key}=" "$file" 2>/dev/null | head -1 | cut -d'=' -f2- +} + +cat > "$OUT" <