commit 589cadd8fcf2697227a2ab787e78a4aea4a6c84c Author: Alexander Rogov Date: Fri Jun 12 17:55:07 2026 +0300 init diff --git a/BOOTSTRAP.md b/BOOTSTRAP.md new file mode 100644 index 0000000..8d89927 --- /dev/null +++ b/BOOTSTRAP.md @@ -0,0 +1,308 @@ +# Yandex Cloud Production Cluster — Bootstrap Guide + +## Prerequisites + +- [ ] `kubeconfig` file placed at `~/infra/yandex-prod/kubeconfig` +- [ ] `kubectl` context pointing to the new `yc-prod` cluster +- [ ] Domain `prod.t01tt.tech` DNS managed (can be updated later in Phase 5) +- [ ] `git` and `helm` installed locally + +--- + +## Phase 0: Verify Cluster Access + +```bash +export KUBECONFIG=~/infra/yandex-prod/kubeconfig + +kubectl get nodes +# Expected: 3 nodes Ready, 2CPU/8GB each + +kubectl get sc +# Expected: yc-network-hdd (default), yc-network-ssd, yc-network-nvme, ... +``` + +--- + +## Phase 1: Bootstrap Gitea (internal access only) + +Gitea hosts the Git repo that ArgoCD reads. Deploy it first, but without ingress — we access it via port-forward. + +```bash +kubectl apply -f bootstrap/gitea/namespace.yaml +kubectl apply -f bootstrap/gitea/pvc.yaml +kubectl apply -f bootstrap/gitea/deployment.yaml +kubectl apply -f bootstrap/gitea/service.yaml +# NOTE: Do NOT apply ingress.yaml yet — no Traefik or cert-manager exists +``` + +Wait for Gitea to be ready, then port-forward and configure: + +```bash +kubectl wait deploy/gitea -n gitea --for=condition=available --timeout=120s + +# Port-forward in a separate terminal: +kubectl port-forward svc/gitea 3000:3000 -n gitea +``` + +1. Open **http://localhost:3000** in a browser +2. Fill out the install form: + - Database: **SQLite3** (default) + - Site Title: **Gitea** + - Domain: **git.prod.t01tt.tech** + - Application URL: **https://git.prod.t01tt.tech** + - Create admin account (username/password/email — save these) +3. Click "Install Gitea" +4. Create a new repository: **`yandex-prod`** (must be **public**, owned by admin) +5. Close the port-forward (Ctrl+C) + +--- + +## Phase 2: Push Repository to Gitea + +```bash +cd ~/infra/yandex-prod + +git init +git remote add origin http://localhost:3000//yandex-prod.git +# Or, once Gitea ingress works later, use: +# git remote add origin https://git.prod.t01tt.tech//yandex-prod.git + +git add -A +git commit -m "initial bootstrap: infrastructure manifests" +git push -u origin main +# Enter Gitea admin credentials when prompted +``` + +--- + +## Phase 3: Install ArgoCD (internal access only) + +```bash +bash bootstrap/argocd/install.sh +# Saves the admin password — copy it +``` + +Add the Gitea repository to ArgoCD: + +```bash +# Via port-forward: +kubectl port-forward svc/argocd-server 8080:80 -n argocd & +sleep 2 + +# Login and add repo: +ARGOCD_PASS=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d) +argocd login localhost:8080 --username admin --password "${ARGOCD_PASS}" --insecure + +argocd repo add http://gitea.gitea.svc.cluster.local:3000//yandex-prod.git \ + --name yandex-prod \ + --type git +``` + +Deploy the root app: + +```bash +kubectl apply -f argocd/app-of-apps.yaml +``` + +ArgoCD will now sync child apps according to their sync waves. You can watch progress: + +```bash +argocd app list +``` + +--- + +## Phase 4: Let the Sync Waves Run + +Sync order (automated by ArgoCD via `argocd.argoproj.io/sync-wave` annotations): + +| Wave | App | What happens | +|------|-----|-------------| +| **-2** | `traefik` | DaemonSet deployed on all 3 nodes. NLB created → external IP provisioned | +| **-1** | `cert-manager` | cert-manager operator + CRDs installed | +| **0** | `cert-manager-issuers` | `letsencrypt-production` + `letsencrypt-staging` ClusterIssuers created | +| **0** | `monitoring` | VM k8s-stack (metrics) + Grafana ingress deployed | +| **0** | `loki` | Loki single-binary deployed | +| **0** | `cnpg-operator` | CloudNativePG operator installed | +| **1** | `cnpg-cluster` | `shared-pg` 3-node PostgreSQL cluster + 8 databases created | + +**Verify Traefik IP:** + +```bash +kubectl get svc traefik -n traefik -w +# Wait for EXTERNAL-IP to appear. Example output: +# traefik LoadBalancer 10.x.x.x 80:3xxxx/TCP,443:3xxxx/TCP 30s +# traefik LoadBalancer 10.x.x.x 158.160.x.x 80:3xxxx/TCP,443:3xxxx/TCP 60s +``` + +Take the EXTERNAL-IP — this is your NLB IP. You'll need it in Phase 5. + +**Verify state:** + +```bash +kubectl get pods -A +# Expected running pods: +# traefik: traefik-xxxxx (3 pods, DaemonSet) +# cert-manager: cert-manager-xxxxx, cert-manager-cainjector-xxxxx, cert-manager-webhook-xxxxx +# metrics: vm-k8s-stack-* pods (vmsingle, alertmanager, grafana, node-exporter, kube-state-metrics, vmagent) +# metrics: loki-0 +# cnpg-system: cnpg-operator-xxxxx +# cnpg: shared-pg-1, shared-pg-2, shared-pg-3 (may take a minute to start) + +kubectl get clusterissuer +# Expected: letsencrypt-production (True), letsencrypt-staging (True) + +kubectl get cluster -n cnpg +# Expected: shared-pg (3/3 instances ready) +``` + +--- + +## Phase 5: DNS + Expose Gitea & ArgoCD + +Now that Traefik has an external IP and cert-manager is running, we can: +1. Point DNS at the NLB IP +2. Create the Gitea and ArgoCD ingress resources (with TLS) + +### 5.1 Update DNS + +Point the following records to the Traefik NLB IP (from Phase 4): + +``` +git.prod.t01tt.tech → +argocd.prod.t01tt.tech → +grafana.prod.t01tt.tech → +``` + +Also create a wildcard for future hosts: +``` +*.prod.t01tt.tech → +``` + +### 5.2 Apply Ingresses + +```bash +kubectl apply -f bootstrap/gitea/ingress.yaml +kubectl apply -f bootstrap/argocd/ingress.yaml +``` + +### 5.3 Wait for TLS Certificates + +```bash +kubectl get certificate -A -w +# Wait for all to show Ready=True: +# gitea gitea-tls True +# argocd argocd-tls True +# metrics grafana-tls True +``` + +**Troubleshooting:** If certificates are stuck in `Pending`: +- Check DNS resolves: `dig git.prod.t01tt.tech` — must return the NLB IP +- Check cert-manager logs: `kubectl logs -n cert-manager deploy/cert-manager` +- Check challenge: `kubectl get challenges -A` + +--- + +## Phase 6: Verify Everything + +### Gitea +``` +https://git.prod.t01tt.tech +``` +Login with the admin credentials from Phase 1. Verify the `yandex-prod` repo exists. + +### ArgoCD +``` +https://argocd.prod.t01tt.tech +``` +Login with `admin` + password from Phase 3. All apps should show green (`Synced` + `Healthy`). + +The Ingress health may show `Healthy` immediately (by design — see `values.yaml` customization). + +### Grafana +``` +https://grafana.prod.t01tt.tech +``` +Login with `admin` / `change-me`. Check that VM k8s-stack dashboards are available. + +### PostgreSQL +```bash +kubectl get databases -n cnpg +# Expected: 8 Database resources, one per homeserver + +kubectl get pods -n cnpg +# Expected: shared-pg-1, shared-pg-2, shared-pg-3 (Running) +``` + +### ArgoCD Repo Connection +```bash +argocd repo list +# Expected: the Gitea repo with status "Successful" +``` + +If not connected, re-add via ArgoCD CLI: +```bash +argocd repo add http://gitea.gitea.svc.cluster.local:3000//yandex-prod.git \ + --name yandex-prod \ + --type git +``` + +Or in the ArgoCD UI: Settings → Repositories → Connect repo. + +--- + +## Phase 7: Post-Bootstrap Checklist + +- [ ] All ArgoCD apps `Synced` and `Healthy` +- [ ] `https://git.prod.t01tt.tech` — Gitea accessible, SSL valid +- [ ] `https://argocd.prod.t01tt.tech` — ArgoCD accessible, SSL valid +- [ ] `https://grafana.prod.t01tt.tech` — Grafana accessible, SSL valid, datasources working +- [ ] `kubectl get pv` — PVCs bound for all stateful components +- [ ] CNPG `shared-pg` cluster status: `kubectl get cluster -n cnpg` shows 3/3 ready +- [ ] Certificates all `Ready`: `kubectl get certificate -A | grep False` (should return nothing) + +--- + +## Quick Reference: Service URLs + +| Service | URL | Auth | +|---------|-----|------| +| Gitea | `https://git.prod.t01tt.tech` | Admin user from Phase 1 | +| ArgoCD | `https://argocd.prod.t01tt.tech` | `admin` / password from Phase 3 | +| Grafana | `https://grafana.prod.t01tt.tech` | `admin` / `change-me` | +| Traefik dashboard | `kubectl port-forward -n traefik daemonset/traefik 9000:9000` | Internal only | + +--- + +## Troubleshooting + +### Traefik NLB stuck in `` +Yandex Cloud NLB provisioning can take a few minutes. Check: +```bash +kubectl describe svc traefik -n traefik +``` +If it's stuck for >5 minutes, verify the Yandex annotations are correct. + +### Certificates stuck in `Pending` +1. Verify DNS: `dig git.prod.t01tt.tech` → must return the NLB IP +2. Check Traefik is listening: `curl -k https:// -H "Host: git.prod.t01tt.tech"` → should return 404 (expected, just verifying Traefik responds) +3. Check orders: `kubectl get orders -A` + +### CNPG cluster not becoming ready +```bash +kubectl describe cluster shared-pg -n cnpg +kubectl logs -n cnpg-system deploy/cnpg-controller-manager +``` +Common issue: pods can't schedule due to `podAntiAffinityType: required`. Ensure all 3 nodes exist and PVCs can bind. + +### Gitea UI shows wrong URL after first login +Gitea caches the ROOT_URL from the `deployment.yaml` env vars. If you change the domain, update: +```bash +kubectl set env deploy/gitea -n gitea \ + GITEA__server__DOMAIN=git.prod.t01tt.tech \ + GITEA__server__ROOT_URL=https://git.prod.t01tt.tech +kubectl rollout restart deploy/gitea -n gitea +``` + +### ArgoCD apps showing "Unknown" health +This is normal for Ingress resources — the custom health check in `bootstrap/argocd/values.yaml` marks all Ingresses as `Healthy` once synced. For other resources, check the app details in ArgoCD UI for the specific error. diff --git a/argocd/app-of-apps.yaml b/argocd/app-of-apps.yaml new file mode 100644 index 0000000..29d4cd5 --- /dev/null +++ b/argocd/app-of-apps.yaml @@ -0,0 +1,25 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: root-app + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + source: + repoURL: http://gitea.gitea.svc.cluster.local:3000/gitea/yandex-prod.git + targetRevision: main + path: argocd/apps + directory: + recurse: true + include: "*.yaml" + destination: + server: https://kubernetes.default.svc + namespace: argocd + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/argocd/apps/cert-manager-issuers.yaml b/argocd/apps/cert-manager-issuers.yaml new file mode 100644 index 0000000..3c16c58 --- /dev/null +++ b/argocd/apps/cert-manager-issuers.yaml @@ -0,0 +1,25 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: cert-manager-issuers + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "0" + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + source: + repoURL: http://gitea.gitea.svc.cluster.local:3000/gitea/yandex-prod.git + targetRevision: main + path: manifests/cert-manager + directory: + recurse: true + include: "*.yaml" + destination: + server: https://kubernetes.default.svc + namespace: cert-manager + syncPolicy: + automated: + prune: true + selfHeal: true diff --git a/argocd/apps/cert-manager.yaml b/argocd/apps/cert-manager.yaml new file mode 100644 index 0000000..5c8bcd2 --- /dev/null +++ b/argocd/apps/cert-manager.yaml @@ -0,0 +1,24 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: cert-manager + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "-1" + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + source: + repoURL: https://charts.jetstack.io + chart: cert-manager + targetRevision: ">=1.18.0" + helm: + values: | + installCRDs: true + destination: + server: https://kubernetes.default.svc + namespace: cert-manager + syncPolicy: + syncOptions: + - CreateNamespace=true diff --git a/argocd/apps/cnpg-cluster.yaml b/argocd/apps/cnpg-cluster.yaml new file mode 100644 index 0000000..1656175 --- /dev/null +++ b/argocd/apps/cnpg-cluster.yaml @@ -0,0 +1,27 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: cnpg-cluster + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "1" + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + source: + repoURL: http://gitea.gitea.svc.cluster.local:3000/gitea/yandex-prod.git + targetRevision: main + path: manifests/cnpg + directory: + recurse: true + include: "*.yaml" + destination: + server: https://kubernetes.default.svc + namespace: cnpg + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/argocd/apps/cnpg-operator.yaml b/argocd/apps/cnpg-operator.yaml new file mode 100644 index 0000000..12f2372 --- /dev/null +++ b/argocd/apps/cnpg-operator.yaml @@ -0,0 +1,19 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: cnpg-operator + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + source: + repoURL: https://cloudnative-pg.github.io/charts + chart: cloudnative-pg + targetRevision: ">=0.23.0" + destination: + server: https://kubernetes.default.svc + namespace: cnpg-system + syncPolicy: + syncOptions: + - CreateNamespace=true diff --git a/argocd/apps/loki.yaml b/argocd/apps/loki.yaml new file mode 100644 index 0000000..4387317 --- /dev/null +++ b/argocd/apps/loki.yaml @@ -0,0 +1,56 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: loki + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "0" + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + source: + repoURL: https://grafana.github.io/helm-charts + chart: loki + targetRevision: ">=6.0.0" + helm: + values: | + deploymentMode: SingleBinary + loki: + auth_enabled: false + commonConfig: + replication_factor: 1 + storage: + type: filesystem + schemaConfig: + configs: + - from: "2025-01-01" + store: tsdb + objectStore: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + limits_config: + retention_period: 30d + reject_old_samples: true + reject_old_samples_max_age: 168h + + singleBinary: + replicas: 1 + persistence: + enabled: true + size: 20Gi + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 1Gi + destination: + server: https://kubernetes.default.svc + namespace: metrics + syncPolicy: + syncOptions: + - CreateNamespace=true diff --git a/argocd/apps/monitoring.yaml b/argocd/apps/monitoring.yaml new file mode 100644 index 0000000..3df93d2 --- /dev/null +++ b/argocd/apps/monitoring.yaml @@ -0,0 +1,119 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: monitoring + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "0" + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + sources: + - repoURL: https://victoriametrics.github.io/helm-charts/ + chart: victoria-metrics-k8s-stack + targetRevision: ">=0.30.0" + helm: + values: | + fullnameOverride: vm-k8s-stack + namespaceOverride: metrics + + vmsingle: + enabled: true + spec: + retentionPeriod: "30d" + replicaCount: 1 + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 1Gi + storage: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 30Gi + + alertmanager: + enabled: true + spec: + replicaCount: 1 + resources: + requests: + cpu: 50m + memory: 128Mi + limits: + cpu: 200m + memory: 512Mi + storage: + volumeClaimTemplate: + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + config: + route: + receiver: blackhole + receivers: + - name: blackhole + + grafana: + enabled: true + adminUser: admin + adminPassword: change-me + persistence: + enabled: true + size: 2Gi + resources: + requests: + cpu: 50m + memory: 256Mi + limits: + cpu: 200m + memory: 512Mi + + prometheus-node-exporter: + enabled: true + + kube-state-metrics: + enabled: true + + kubelet: + enabled: true + + kubeApiServer: + enabled: false + + kubeControllerManager: + enabled: false + + kubeScheduler: + enabled: false + + kubeProxy: + enabled: false + + kubeEtcd: + enabled: false + + - repoURL: http://gitea.gitea.svc.cluster.local:3000/gitea/yandex-prod.git + targetRevision: main + path: manifests/metrics/grafana + directory: + recurse: true + include: "*.yaml" + + destination: + server: https://kubernetes.default.svc + namespace: argocd + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/argocd/apps/traefik.yaml b/argocd/apps/traefik.yaml new file mode 100644 index 0000000..0f041a3 --- /dev/null +++ b/argocd/apps/traefik.yaml @@ -0,0 +1,84 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: traefik + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "-2" + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + source: + repoURL: https://traefik.github.io/charts + chart: traefik + targetRevision: ">=37.0.0" + helm: + values: | + deployment: + kind: DaemonSet + + ingressClass: + enabled: true + isDefaultClass: true + + additionalArguments: + - "--api.dashboard=true" + - "--ping=true" + - "--metrics.prometheus=true" + - "--metrics.prometheus.entrypoint=metrics" + - "--providers.kubernetesingress.ingressclass=traefik" + - "--providers.kubernetesingress.ingressendpoint.publishedservice=traefik/traefik" + - "--accesslog=true" + - "--log.level=INFO" + + ports: + web: + port: 8080 + exposedPort: 80 + redirectTo: websecure + websecure: + port: 8443 + exposedPort: 443 + metrics: + port: 9100 + expose: false + traefik: + port: 9000 + expose: false + + service: + type: LoadBalancer + annotations: + service.beta.kubernetes.io/yandex-load-balancer-name: traefik + service.beta.kubernetes.io/yandex-load-balancer-specification: '{"type": "network-load-balancer"}' + service.beta.kubernetes.io/yandex-load-balancer-type: external + + podAnnotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9100" + + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 256Mi + + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + + destination: + server: https://kubernetes.default.svc + namespace: traefik + syncPolicy: + syncOptions: + - CreateNamespace=true diff --git a/bootstrap/argocd/ingress.yaml b/bootstrap/argocd/ingress.yaml new file mode 100644 index 0000000..e6e29a1 --- /dev/null +++ b/bootstrap/argocd/ingress.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: argocd + namespace: argocd + annotations: + cert-manager.io/cluster-issuer: letsencrypt-production + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" +spec: + ingressClassName: traefik + tls: + - hosts: + - argocd.prod.t01tt.tech + secretName: argocd-tls + rules: + - host: argocd.prod.t01tt.tech + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: argocd-server + port: + number: 80 diff --git a/bootstrap/argocd/install.sh b/bootstrap/argocd/install.sh new file mode 100755 index 0000000..fed1032 --- /dev/null +++ b/bootstrap/argocd/install.sh @@ -0,0 +1,29 @@ +#!/bin/bash +set -e + +KUBECONFIG="/home/mrt0rtikize/infra/yandex-prod/kubeconfig" +KCTL="kubectl --kubeconfig ${KUBECONFIG}" + +echo "=== Installing ArgoCD ===" + +helm repo add argo https://argoproj.github.io/argo-helm 2>/dev/null || true +helm repo update + +helm upgrade --install argocd argo/argo-cd \ + --namespace argocd \ + --create-namespace \ + --values "$(dirname "$0")/values.yaml" \ + --wait \ + --timeout 300s + +echo "" +echo "=== ArgoCD installed ===" +echo "" +echo "To access ArgoCD UI:" +echo " kubectl --kubeconfig ${KUBECONFIG} port-forward svc/argocd-server -n argocd 8080:80" +echo "" +echo "Admin password:" +kubectl --kubeconfig ${KUBECONFIG} -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d +echo "" +echo "" +echo "Login with username: admin" diff --git a/bootstrap/argocd/values.yaml b/bootstrap/argocd/values.yaml new file mode 100644 index 0000000..46f17fe --- /dev/null +++ b/bootstrap/argocd/values.yaml @@ -0,0 +1,20 @@ +server: + extraArgs: + - --insecure + +configs: + params: + server.insecure: true + cm: + timeout.reconciliation: 180s + helm.timeoutSeconds: "300" + resource.customizations.health.networking.k8s.io_Ingress: | + hs = {} + hs.status = "Healthy" + hs.message = "Ingress is synced" + return hs + +redis: + image: + repository: docker.io/library/redis + tag: 7.4-alpine diff --git a/bootstrap/gitea/deployment.yaml b/bootstrap/gitea/deployment.yaml new file mode 100644 index 0000000..cec0658 --- /dev/null +++ b/bootstrap/gitea/deployment.yaml @@ -0,0 +1,64 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gitea + namespace: gitea +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: gitea + template: + metadata: + labels: + app: gitea + spec: + containers: + - name: gitea + image: gitea/gitea:1.24 + ports: + - containerPort: 3000 + name: http + - containerPort: 22 + name: ssh + env: + - name: GITEA__database__DB_TYPE + value: sqlite3 + - name: GITEA__server__DOMAIN + value: git.prod.t01tt.tech + - name: GITEA__server__ROOT_URL + value: https://git.prod.t01tt.tech + - name: GITEA__server__HTTP_PORT + value: "3000" + - name: GITEA__server__SSH_PORT + value: "22" + - name: GITEA__service__DISABLE_REGISTRATION + value: "true" + volumeMounts: + - name: data + mountPath: /data + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + livenessProbe: + httpGet: + path: / + port: 3000 + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: 3000 + initialDelaySeconds: 5 + periodSeconds: 5 + volumes: + - name: data + persistentVolumeClaim: + claimName: gitea-data diff --git a/bootstrap/gitea/ingress.yaml b/bootstrap/gitea/ingress.yaml new file mode 100644 index 0000000..e99f3e6 --- /dev/null +++ b/bootstrap/gitea/ingress.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: gitea + namespace: gitea + annotations: + cert-manager.io/cluster-issuer: letsencrypt-production + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" +spec: + ingressClassName: traefik + tls: + - hosts: + - git.prod.t01tt.tech + secretName: gitea-tls + rules: + - host: git.prod.t01tt.tech + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: gitea + port: + number: 3000 diff --git a/bootstrap/gitea/namespace.yaml b/bootstrap/gitea/namespace.yaml new file mode 100644 index 0000000..09a988f --- /dev/null +++ b/bootstrap/gitea/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: gitea diff --git a/bootstrap/gitea/pvc.yaml b/bootstrap/gitea/pvc.yaml new file mode 100644 index 0000000..ea94e6e --- /dev/null +++ b/bootstrap/gitea/pvc.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: gitea-data + namespace: gitea +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi diff --git a/bootstrap/gitea/service.yaml b/bootstrap/gitea/service.yaml new file mode 100644 index 0000000..378a351 --- /dev/null +++ b/bootstrap/gitea/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: gitea + namespace: gitea +spec: + selector: + app: gitea + ports: + - name: http + port: 3000 + targetPort: 3000 + - name: ssh + port: 22 + targetPort: 22 diff --git a/kubeconfig b/kubeconfig new file mode 100644 index 0000000..7e7625e --- /dev/null +++ b/kubeconfig @@ -0,0 +1,5 @@ +# Placeholder for Yandex Cloud Managed Kubernetes kubeconfig. +# Copy your kubeconfig here before running bootstrap. +# +# Example: +# yc managed-kubernetes cluster get-credentials --id --external --kubeconfig kubeconfig diff --git a/manifests/cert-manager/cluster-issuers.yaml b/manifests/cert-manager/cluster-issuers.yaml new file mode 100644 index 0000000..8e27ea0 --- /dev/null +++ b/manifests/cert-manager/cluster-issuers.yaml @@ -0,0 +1,29 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-production +spec: + acme: + email: i_am@rogov.al + privateKeySecretRef: + name: letsencrypt-production-account-key + server: https://acme-v02.api.letsencrypt.org/directory + solvers: + - http01: + ingress: + class: traefik +--- +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-staging +spec: + acme: + email: i_am@rogov.al + privateKeySecretRef: + name: letsencrypt-staging-account-key + server: https://acme-staging-v02.api.letsencrypt.org/directory + solvers: + - http01: + ingress: + class: traefik diff --git a/manifests/cnpg/cluster.yaml b/manifests/cnpg/cluster.yaml new file mode 100644 index 0000000..cbee07c --- /dev/null +++ b/manifests/cnpg/cluster.yaml @@ -0,0 +1,30 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: shared-pg + namespace: cnpg +spec: + instances: 3 + imageName: ghcr.io/cloudnative-pg/postgresql:16 + + storage: + size: 20Gi + storageClass: yc-network-ssd + + affinity: + podAntiAffinityType: required + + bootstrap: + initdb: + database: postgres + owner: postgres + + postgresql: + parameters: + shared_buffers: "256MB" + effective_cache_size: "768MB" + maintenance_work_mem: "64MB" + max_connections: "200" + + monitoring: + enablePodMonitor: true diff --git a/manifests/cnpg/databases.yaml b/manifests/cnpg/databases.yaml new file mode 100644 index 0000000..596f199 --- /dev/null +++ b/manifests/cnpg/databases.yaml @@ -0,0 +1,87 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Database +metadata: + name: synapse-mrt0rtikize + namespace: cnpg +spec: + name: synapse_mrt0rtikize + owner: synapse_mrt0rtikize + clusterRef: + name: shared-pg +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Database +metadata: + name: mas-mrt0rtikize + namespace: cnpg +spec: + name: mas_mrt0rtikize + owner: mas_mrt0rtikize + clusterRef: + name: shared-pg +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Database +metadata: + name: synapse-t0rt1k + namespace: cnpg +spec: + name: synapse_t0rt1k + owner: synapse_t0rt1k + clusterRef: + name: shared-pg +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Database +metadata: + name: mas-t0rt1k + namespace: cnpg +spec: + name: mas_t0rt1k + owner: mas_t0rt1k + clusterRef: + name: shared-pg +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Database +metadata: + name: synapse-roglog + namespace: cnpg +spec: + name: synapse_roglog + owner: synapse_roglog + clusterRef: + name: shared-pg +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Database +metadata: + name: mas-roglog + namespace: cnpg +spec: + name: mas_roglog + owner: mas_roglog + clusterRef: + name: shared-pg +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Database +metadata: + name: synapse-uretra + namespace: cnpg +spec: + name: synapse_uretra + owner: synapse_uretra + clusterRef: + name: shared-pg +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Database +metadata: + name: mas-uretra + namespace: cnpg +spec: + name: mas_uretra + owner: mas_uretra + clusterRef: + name: shared-pg diff --git a/manifests/cnpg/namespace.yaml b/manifests/cnpg/namespace.yaml new file mode 100644 index 0000000..6b7aad5 --- /dev/null +++ b/manifests/cnpg/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: cnpg diff --git a/manifests/cnpg/secrets.yaml b/manifests/cnpg/secrets.yaml new file mode 100644 index 0000000..00fa72d --- /dev/null +++ b/manifests/cnpg/secrets.yaml @@ -0,0 +1,63 @@ +apiVersion: v1 +kind: Secret +metadata: + name: mrt0rtikize-pg-creds + namespace: cnpg + labels: + cnpg.io/reload: "" +type: kubernetes.io/basic-auth +stringData: + synapse: | + username: synapse_mrt0rtikize + password: change-me-synapse + mas: | + username: mas_mrt0rtikize + password: change-me-mas +--- +apiVersion: v1 +kind: Secret +metadata: + name: t0rt1k-pg-creds + namespace: cnpg + labels: + cnpg.io/reload: "" +type: kubernetes.io/basic-auth +stringData: + synapse: | + username: synapse_t0rt1k + password: change-me-synapse + mas: | + username: mas_t0rt1k + password: change-me-mas +--- +apiVersion: v1 +kind: Secret +metadata: + name: roglog-pg-creds + namespace: cnpg + labels: + cnpg.io/reload: "" +type: kubernetes.io/basic-auth +stringData: + synapse: | + username: synapse_roglog + password: change-me-synapse + mas: | + username: mas_roglog + password: change-me-mas +--- +apiVersion: v1 +kind: Secret +metadata: + name: uretra-pg-creds + namespace: cnpg + labels: + cnpg.io/reload: "" +type: kubernetes.io/basic-auth +stringData: + synapse: | + username: synapse_uretra + password: change-me-synapse + mas: | + username: mas_uretra + password: change-me-mas diff --git a/manifests/metrics/grafana/ingress.yaml b/manifests/metrics/grafana/ingress.yaml new file mode 100644 index 0000000..32ec704 --- /dev/null +++ b/manifests/metrics/grafana/ingress.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: grafana + namespace: metrics + annotations: + cert-manager.io/cluster-issuer: letsencrypt-production + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" +spec: + ingressClassName: traefik + tls: + - hosts: + - grafana.prod.t01tt.tech + secretName: grafana-tls + rules: + - host: grafana.prod.t01tt.tech + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: vm-k8s-stack-grafana + port: + number: 80 diff --git a/manifests/metrics/namespace.yaml b/manifests/metrics/namespace.yaml new file mode 100644 index 0000000..6d57933 --- /dev/null +++ b/manifests/metrics/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: metrics