# sema-stack(full-cluster 形态,Phase 1b as-built;cluster-up.sh 渲染 __占位符__ 后 apply)
# 占位符:__MINIO_PW__ __SVC_TOKEN__ __MODEL_KEY__ __MODEL_GATEWAY__ __MODEL_ID__
#         __SERVER_IMAGE__ __SERVER_PULL_POLICY__(tar 预灌=Never / registry 拉取=IfNotPresent)
# 前置(cluster-up.sh 负责):Longhorn 默认 SC、kata-deploy(runtimeclass kata-qemu)、CNPG operator 1.30、
#         runner token 跨 ns 复制成 sema/sema-runner-token-copy、MinIO 建桶 sema-pg-backup
apiVersion: v1
kind: Namespace
metadata: { name: sema }
---
apiVersion: v1
kind: Namespace
metadata: { name: sema-sandbox }
---
# ── 数据面:CNPG 3 实例 HA(REV-2 §3c 调优参数)──────────────────────────────
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata: { name: sema-pg, namespace: sema }
spec:
  instances: __PG_INSTANCES__
  # 反亲和(>=3 机 required,不然调度器把多实例挤同节点——复审实抓 pg-2/pg-3 同机,HA 名不副实;
  # <3 机 preferred,required 会让副本永久 Pending——单节点 dev 真踩)
  affinity:
    enablePodAntiAffinity: true
    topologyKey: kubernetes.io/hostname
    podAntiAffinityType: __PG_ANTI_AFFINITY__
  storage: { size: 100Gi, storageClass: longhorn }
  bootstrap:
    initdb: { database: sema, owner: sema }
  postgresql:
    parameters:
      shared_buffers: "8GB"
      effective_cache_size: "86GB"
      maintenance_work_mem: "2GB"
      random_page_cost: "1.1"
      effective_io_concurrency: "200"
      checkpoint_completion_target: "0.9"
      max_parallel_workers: "8"
  # ⚠ in-tree barman 在 CNPG 1.31 移除——operator 钉 1.30.0;升 operator 前必须先迁 barman-cloud plugin(需 cert-manager)
  backup:
    retentionPolicy: 7d
    barmanObjectStore:
      destinationPath: s3://sema-pg-backup/
      endpointURL: http://sema-minio.sema.svc:9000
      s3Credentials:
        accessKeyId: { name: sema-minio, key: rootUser }
        secretAccessKey: { name: sema-minio, key: rootPassword }
      wal: { compression: gzip }
---
# 每日 03:00 UTC 基备(WAL 连续归档另在跑,支持 PITR);bucket sema-pg-backup 需先建(mc mb)
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata: { name: sema-pg-daily, namespace: sema }
spec:
  schedule: "0 0 3 * * *"
  backupOwnerReference: self
  cluster: { name: sema-pg }
---
# ── MinIO(单副本 on Longhorn=盘冗余在下层;集群 EC 形态后续档)────────────────
apiVersion: v1
kind: Secret
metadata: { name: sema-minio, namespace: sema }
stringData: { rootUser: sema, rootPassword: "__MINIO_PW__" }
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: sema-minio-data, namespace: sema }
spec: { accessModes: [ReadWriteOnce], storageClassName: longhorn, resources: { requests: { storage: 200Gi } } }
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: sema-minio, namespace: sema }
spec:
  replicas: 1
  selector: { matchLabels: { app: sema-minio } }
  template:
    metadata: { labels: { app: sema-minio } }
    spec:
      containers:
        - name: minio
          image: minio/minio:latest
          args: ["server", "/data", "--console-address", ":9001"]
          env:
            - { name: MINIO_ROOT_USER, valueFrom: { secretKeyRef: { name: sema-minio, key: rootUser } } }
            - { name: MINIO_ROOT_PASSWORD, valueFrom: { secretKeyRef: { name: sema-minio, key: rootPassword } } }
          ports: [{ containerPort: 9000 }, { containerPort: 9001 }]
          volumeMounts: [{ name: data, mountPath: /data }]
      volumes: [{ name: data, persistentVolumeClaim: { claimName: sema-minio-data } }]
---
apiVersion: v1
kind: Service
metadata: { name: sema-minio, namespace: sema }
spec: { selector: { app: sema-minio }, ports: [{ name: s3, port: 9000 }, { name: console, port: 9001 }] }
---
# ── 沙箱 lane RBAC(k8s+kata;测试机同款五动词)────────────────────────────────
apiVersion: v1
kind: ServiceAccount
metadata: { name: sema-runner, namespace: sema-sandbox }
---
apiVersion: v1
kind: Secret
metadata:
  name: sema-runner-token
  namespace: sema-sandbox
  annotations: { kubernetes.io/service-account.name: sema-runner }
type: kubernetes.io/service-account-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata: { name: sema-runner, namespace: sema-sandbox }
rules:
  - apiGroups: [""]
    resources: [pods]
    verbs: [get, list, watch, create, delete]
  - apiGroups: [""]
    resources: [pods/exec, pods/attach]
    verbs: [create, get]
  - apiGroups: [""]
    resources: [pods/log]
    verbs: [get]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata: { name: sema-runner, namespace: sema-sandbox }
roleRef: { apiGroup: rbac.authorization.k8s.io, kind: Role, name: sema-runner }
subjects: [{ kind: ServiceAccount, name: sema-runner, namespace: sema-sandbox }]
---
# ── 引擎(3 副本跨机打散;镜像=节点预灌 sema-server:local,imagePullPolicy Never
#    ⚠ k3s-uninstall.sh 会连 containerd 镜像一起清,重装过的节点必须补灌)──────────
apiVersion: v1
kind: Secret
metadata: { name: sema-server-env, namespace: sema }
stringData:
  SERVICE_AUTH_TOKEN: "__SVC_TOKEN__"
  MODEL_API_KEY: "__MODEL_KEY__"
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: sema-server, namespace: sema }
spec:
  replicas: __SERVER_REPLICAS__
  selector: { matchLabels: { app: sema-server } }
  template:
    metadata: { labels: { app: sema-server } }
    spec:
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: ScheduleAnyway
          labelSelector: { matchLabels: { app: sema-server } }
      containers:
        - name: server
          image: "__SERVER_IMAGE__"
          imagePullPolicy: __SERVER_PULL_POLICY__
          ports: [{ containerPort: 8090 }]
          env:
            - { name: PORT, value: "8090" }
            - { name: DB_BACKEND, value: pg }
            - { name: SESSION_BACKEND, value: auto }
            - { name: PG_HOST, value: sema-pg-rw.sema.svc }
            - { name: PG_PORT, value: "5432" }
            - { name: PG_USER, value: sema }
            - { name: PG_DATABASE, value: sema }
            - { name: PG_PASSWORD, valueFrom: { secretKeyRef: { name: sema-pg-app, key: password } } }
            - { name: MINIO_ENDPOINT, value: "http://sema-minio.sema.svc:9000" }
            - { name: MINIO_ACCESS_KEY, valueFrom: { secretKeyRef: { name: sema-minio, key: rootUser } } }
            - { name: MINIO_SECRET_KEY, valueFrom: { secretKeyRef: { name: sema-minio, key: rootPassword } } }
            - { name: SERVICE_AUTH_TOKEN, valueFrom: { secretKeyRef: { name: sema-server-env, key: SERVICE_AUTH_TOKEN } } }
            - { name: MODEL_GATEWAY_BASEURL, value: "__MODEL_GATEWAY__" }
            - { name: MODEL_ID, value: "__MODEL_ID__" }
            - { name: MODEL_API_KEY, valueFrom: { secretKeyRef: { name: sema-server-env, key: MODEL_API_KEY } } }
            - { name: MODEL_VISION, value: "false" }
            - { name: SANDBOX_PKG_SOURCE, value: "__PKG_SOURCE__" }
            - { name: REMOTE_EXEC, value: k8s }
            - { name: K8S_API_URL, value: "https://kubernetes.default.svc" }
            - { name: K8S_INSECURE_TLS, value: "true" }
            - { name: K8S_NAMESPACE, value: sema-sandbox }
            - { name: K8S_RUNTIME_CLASS, value: kata-qemu }
            # 默认=公开池 code-node(小巧 git+node+python;clay 拍 2026-07-13,重任务 per-task profile 按需选)——裸 OS 镜像无 pkg-source hook,别退回
            - { name: K8S_SANDBOX_IMAGE, value: "docker.io/claybobby/code-node:20260707-749ab84f5fdd-786f5ef10ffd" }
            - { name: K8S_TOKEN, valueFrom: { secretKeyRef: { name: sema-runner-token-copy, key: token } } }
          readinessProbe: { httpGet: { path: /health, port: 8090 }, initialDelaySeconds: 5, periodSeconds: 5 }
---
apiVersion: v1
kind: Service
metadata: { name: sema-server, namespace: sema }
spec:
  type: NodePort
  selector: { app: sema-server }
  ports: [{ port: 8090, targetPort: 8090, nodePort: 30890 }]
