diff --git a/k8s/base/ci-rbac.yaml b/k8s/base/ci-rbac.yaml new file mode 100644 index 0000000..006a550 --- /dev/null +++ b/k8s/base/ci-rbac.yaml @@ -0,0 +1,52 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: ci-deployer + labels: + app.kubernetes.io/name: buzz-sheet + app.kubernetes.io/component: ci +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: ci-deployer + labels: + app.kubernetes.io/name: buzz-sheet + app.kubernetes.io/component: ci +rules: + - apiGroups: [""] + resources: ["configmaps"] + resourceNames: ["buzz-sheet-config"] + verbs: ["get", "patch", "update"] + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["pods/log"] + verbs: ["get"] + - apiGroups: ["apps"] + resources: ["deployments"] + resourceNames: ["buzz-sheet", "buzz-sheet-worker"] + verbs: ["get", "patch", "update"] + - apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get", "list", "watch"] + - apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["create", "get", "list", "watch", "delete"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: ci-deployer + labels: + app.kubernetes.io/name: buzz-sheet + app.kubernetes.io/component: ci +subjects: + - kind: ServiceAccount + name: ci-deployer + namespace: buzz-sheet +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: ci-deployer diff --git a/k8s/base/configmap.yaml b/k8s/base/configmap.yaml new file mode 100644 index 0000000..b389ebb --- /dev/null +++ b/k8s/base/configmap.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: buzz-sheet-config + labels: + app.kubernetes.io/name: buzz-sheet +data: + BETTER_AUTH_URL: https://sheet.sudloh.com + BUZZ_DEMO_MODE: "false" + DATABASE_POOL_SIZE: "10" + HEALTHCHECK_TIMEOUT_MS: "2500" + NEXT_DEPLOYMENT_ID: replace-me + OUTBOX_BATCH_SIZE: "20" + OUTBOX_LEASE_MS: "30000" + OUTBOX_POLL_MS: "1000" + REDIS_CACHE_MAX_ENTRY_BYTES: "5242880" + REDIS_CACHE_PREFIX: buzz:next-cache + REDIS_EVENT_PREFIX: buzz:events + S3_REGION: auto + S3_VIRTUAL_HOSTED_STYLE: "false" diff --git a/k8s/base/deployment.yaml b/k8s/base/deployment.yaml new file mode 100644 index 0000000..13780a8 --- /dev/null +++ b/k8s/base/deployment.yaml @@ -0,0 +1,92 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: buzz-sheet + labels: + app.kubernetes.io/name: buzz-sheet + app.kubernetes.io/component: web +spec: + replicas: 2 + revisionHistoryLimit: 3 + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + selector: + matchLabels: + app.kubernetes.io/name: buzz-sheet + app.kubernetes.io/component: web + template: + metadata: + labels: + app.kubernetes.io/name: buzz-sheet + app.kubernetes.io/component: web + spec: + automountServiceAccountToken: false + terminationGracePeriodSeconds: 30 + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + containers: + - name: app + image: buzz-sheet-app + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 3000 + protocol: TCP + envFrom: + - configMapRef: + name: buzz-sheet-config + - secretRef: + name: buzz-sheet-env + env: + - name: NEXT_DEPLOYMENT_ID + valueFrom: + configMapKeyRef: + name: buzz-sheet-config + key: NEXT_DEPLOYMENT_ID + resources: + requests: + cpu: 500m + memory: 512Mi + limits: + cpu: "1" + memory: 1Gi + startupProbe: + httpGet: + path: /api/health + port: http + failureThreshold: 30 + periodSeconds: 2 + readinessProbe: + httpGet: + path: /api/health?ready=1 + port: http + failureThreshold: 3 + periodSeconds: 10 + timeoutSeconds: 4 + livenessProbe: + httpGet: + path: /api/health + port: http + failureThreshold: 3 + periodSeconds: 20 + timeoutSeconds: 3 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + readOnlyRootFilesystem: true + volumeMounts: + - name: next-cache + mountPath: /app/.next/cache + - name: temporary + mountPath: /tmp + volumes: + - name: next-cache + emptyDir: {} + - name: temporary + emptyDir: {} diff --git a/k8s/base/hpa.yaml b/k8s/base/hpa.yaml new file mode 100644 index 0000000..04e9f67 --- /dev/null +++ b/k8s/base/hpa.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: buzz-sheet + labels: + app.kubernetes.io/name: buzz-sheet +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: buzz-sheet + minReplicas: 2 + maxReplicas: 6 + behavior: + scaleDown: + stabilizationWindowSeconds: 300 + scaleUp: + stabilizationWindowSeconds: 30 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 diff --git a/k8s/base/ingress.yaml b/k8s/base/ingress.yaml new file mode 100644 index 0000000..cb3db73 --- /dev/null +++ b/k8s/base/ingress.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: buzz-sheet + labels: + app.kubernetes.io/name: buzz-sheet + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" +spec: + ingressClassName: traefik + tls: + - hosts: + - sheet.sudloh.com + secretName: sheet-sudloh-com-tls + rules: + - host: sheet.sudloh.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: buzz-sheet + port: + number: 3000 diff --git a/k8s/base/kustomization.yaml b/k8s/base/kustomization.yaml new file mode 100644 index 0000000..03bfc99 --- /dev/null +++ b/k8s/base/kustomization.yaml @@ -0,0 +1,17 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: buzz-sheet +resources: + - namespace.yaml + - configmap.yaml + - deployment.yaml + - worker-deployment.yaml + - service.yaml + - ingress.yaml + - hpa.yaml + - pdb.yaml + - ci-rbac.yaml +images: + - name: buzz-sheet-app + newName: registry.neko-piranha.ts.net/astral/buzz-sheet + newTag: replace-me diff --git a/k8s/base/namespace.yaml b/k8s/base/namespace.yaml new file mode 100644 index 0000000..1bd410a --- /dev/null +++ b/k8s/base/namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: buzz-sheet + labels: + app.kubernetes.io/name: buzz-sheet diff --git a/k8s/base/pdb.yaml b/k8s/base/pdb.yaml new file mode 100644 index 0000000..34fee53 --- /dev/null +++ b/k8s/base/pdb.yaml @@ -0,0 +1,12 @@ +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: buzz-sheet + labels: + app.kubernetes.io/name: buzz-sheet +spec: + minAvailable: 1 + selector: + matchLabels: + app.kubernetes.io/name: buzz-sheet + app.kubernetes.io/component: web diff --git a/k8s/base/service.yaml b/k8s/base/service.yaml new file mode 100644 index 0000000..904f5eb --- /dev/null +++ b/k8s/base/service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: buzz-sheet + labels: + app.kubernetes.io/name: buzz-sheet + app.kubernetes.io/component: web +spec: + type: ClusterIP + selector: + app.kubernetes.io/name: buzz-sheet + app.kubernetes.io/component: web + ports: + - name: http + port: 3000 + targetPort: http + protocol: TCP diff --git a/k8s/base/worker-deployment.yaml b/k8s/base/worker-deployment.yaml new file mode 100644 index 0000000..1793297 --- /dev/null +++ b/k8s/base/worker-deployment.yaml @@ -0,0 +1,65 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: buzz-sheet-worker + labels: + app.kubernetes.io/name: buzz-sheet + app.kubernetes.io/component: worker +spec: + replicas: 1 + revisionHistoryLimit: 3 + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + selector: + matchLabels: + app.kubernetes.io/name: buzz-sheet + app.kubernetes.io/component: worker + template: + metadata: + labels: + app.kubernetes.io/name: buzz-sheet + app.kubernetes.io/component: worker + spec: + automountServiceAccountToken: false + terminationGracePeriodSeconds: 35 + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + containers: + - name: worker + image: buzz-sheet-app + imagePullPolicy: IfNotPresent + command: ["bun", "worker/outbox-worker.js"] + envFrom: + - configMapRef: + name: buzz-sheet-config + - secretRef: + name: buzz-sheet-env + env: + - name: NEXT_DEPLOYMENT_ID + valueFrom: + configMapKeyRef: + name: buzz-sheet-config + key: NEXT_DEPLOYMENT_ID + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + readOnlyRootFilesystem: true + volumeMounts: + - name: temporary + mountPath: /tmp + volumes: + - name: temporary + emptyDir: {} diff --git a/k8s/kustomization.yaml b/k8s/kustomization.yaml new file mode 100644 index 0000000..bc0249a --- /dev/null +++ b/k8s/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - base + - migration diff --git a/k8s/migration/job.yaml b/k8s/migration/job.yaml new file mode 100644 index 0000000..86f0e7a --- /dev/null +++ b/k8s/migration/job.yaml @@ -0,0 +1,50 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: buzz-sheet-migrate + labels: + app.kubernetes.io/name: buzz-sheet + app.kubernetes.io/component: migration +spec: + backoffLimit: 3 + activeDeadlineSeconds: 600 + ttlSecondsAfterFinished: 86400 + template: + metadata: + labels: + app.kubernetes.io/name: buzz-sheet + app.kubernetes.io/component: migration + spec: + restartPolicy: Never + automountServiceAccountToken: false + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + containers: + - name: migration + image: buzz-sheet-migrate + imagePullPolicy: IfNotPresent + envFrom: + - configMapRef: + name: buzz-sheet-config + - secretRef: + name: buzz-sheet-env + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + readOnlyRootFilesystem: true + volumeMounts: + - name: temporary + mountPath: /tmp + volumes: + - name: temporary + emptyDir: {} diff --git a/k8s/migration/kustomization.yaml b/k8s/migration/kustomization.yaml new file mode 100644 index 0000000..3b30021 --- /dev/null +++ b/k8s/migration/kustomization.yaml @@ -0,0 +1,9 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: buzz-sheet +resources: + - job.yaml +images: + - name: buzz-sheet-migrate + newName: registry.neko-piranha.ts.net/astral/buzz-sheet + newTag: migrate-replace-me