From 7fe1376fe659bdf654f3bb66370d835382516bc4 Mon Sep 17 00:00:00 2001 From: stackops Date: Thu, 9 Apr 2026 13:26:46 +0300 Subject: [PATCH] add infrastructure manifests --- 00-dns/cloudflare-records.sh | 16 +++++ 01-k3s/install.sh | 16 +++++ 02-core/clusterissuer.yaml | 14 +++++ 04-gitea/values.yaml | 44 ++++++++++++++ 05-monitoring/values-loki.yaml | 11 ++++ 05-monitoring/values-prometheus.yaml | 34 +++++++++++ 06-stackops/deployment.yaml | 87 ++++++++++++++++++++++++++++ 06-stackops/rbac.yaml | 44 ++++++++++++++ 07-test-app/stackfile.toml | 14 +++++ Makefile | 78 +++++++++++++++++++++++++ 10 files changed, 358 insertions(+) create mode 100644 00-dns/cloudflare-records.sh create mode 100644 01-k3s/install.sh create mode 100644 02-core/clusterissuer.yaml create mode 100644 04-gitea/values.yaml create mode 100644 05-monitoring/values-loki.yaml create mode 100644 05-monitoring/values-prometheus.yaml create mode 100644 06-stackops/deployment.yaml create mode 100644 06-stackops/rbac.yaml create mode 100644 07-test-app/stackfile.toml create mode 100644 Makefile diff --git a/00-dns/cloudflare-records.sh b/00-dns/cloudflare-records.sh new file mode 100644 index 0000000..da8101a --- /dev/null +++ b/00-dns/cloudflare-records.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Load credentials +source "$(dirname "$0")/../../.cloudflare" + +IP="82.114.226.118" + +for name in "@" "app" "git" "grafana" "prom" "*.app"; do + echo "Creating DNS record: $name -> $IP" + curl -s -X POST "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records" \ + -H "Authorization: Bearer ${CF_API_TOKEN}" \ + -H "Content-Type: application/json" \ + --data "{\"type\":\"A\",\"name\":\"${name}\",\"content\":\"${IP}\",\"ttl\":1,\"proxied\":false}" \ + | python3 -c "import sys,json; r=json.load(sys.stdin); print(' OK' if r['success'] else f' FAIL: {r[\"errors\"]}')" +done diff --git a/01-k3s/install.sh b/01-k3s/install.sh new file mode 100644 index 0000000..9e6c026 --- /dev/null +++ b/01-k3s/install.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +VPS="root@82.114.226.118" + +echo "Installing prerequisites..." +ssh $VPS 'apt-get update -qq && apt-get install -y -qq curl' + +echo "Installing k3s (traefik disabled)..." +ssh $VPS 'curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable=traefik" sh -' + +echo "Copying kubeconfig..." +mkdir -p ~/.kube +ssh $VPS 'cat /etc/rancher/k3s/k3s.yaml' | sed 's/127.0.0.1/82.114.226.118/' > ~/.kube/config-nodeup + +echo "Done. Use: export KUBECONFIG=~/.kube/config-nodeup" diff --git a/02-core/clusterissuer.yaml b/02-core/clusterissuer.yaml new file mode 100644 index 0000000..cd7c8a7 --- /dev/null +++ b/02-core/clusterissuer.yaml @@ -0,0 +1,14 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-prod +spec: + acme: + server: https://acme-v02.api.letsencrypt.org/directory + email: admin@nodeup.ru + privateKeySecretRef: + name: letsencrypt-prod-key + solvers: + - http01: + ingress: + class: nginx diff --git a/04-gitea/values.yaml b/04-gitea/values.yaml new file mode 100644 index 0000000..93dff67 --- /dev/null +++ b/04-gitea/values.yaml @@ -0,0 +1,44 @@ +gitea: + admin: + username: stackops + password: stackops-admin-2026 + config: + server: + DOMAIN: git.nodeup.ru + ROOT_URL: https://git.nodeup.ru + SSH_DOMAIN: git.nodeup.ru + database: + DB_TYPE: sqlite3 + session: + PROVIDER: memory + cache: + ADAPTER: memory + queue: + TYPE: level + packages: + ENABLED: true + +persistence: + enabled: true + size: 10Gi + storageClass: local-path + +ingress: + enabled: true + className: nginx + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + hosts: + - host: git.nodeup.ru + paths: + - path: / + pathType: Prefix + tls: + - secretName: gitea-tls + hosts: + - git.nodeup.ru + +postgresql-ha: + enabled: false +redis-cluster: + enabled: false diff --git a/05-monitoring/values-loki.yaml b/05-monitoring/values-loki.yaml new file mode 100644 index 0000000..c4a000d --- /dev/null +++ b/05-monitoring/values-loki.yaml @@ -0,0 +1,11 @@ +loki: + persistence: + enabled: true + storageClassName: local-path + size: 10Gi + config: + limits_config: + retention_period: 168h + +promtail: + enabled: true diff --git a/05-monitoring/values-prometheus.yaml b/05-monitoring/values-prometheus.yaml new file mode 100644 index 0000000..e2cc8cd --- /dev/null +++ b/05-monitoring/values-prometheus.yaml @@ -0,0 +1,34 @@ +grafana: + adminPassword: stackops-grafana-2026 + ingress: + enabled: true + ingressClassName: nginx + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + hosts: + - grafana.nodeup.ru + tls: + - secretName: grafana-tls + hosts: + - grafana.nodeup.ru + +prometheus: + prometheusSpec: + retention: 15d + storageSpec: + volumeClaimTemplate: + spec: + storageClassName: local-path + resources: + requests: + storage: 20Gi + resources: + requests: + cpu: 100m + memory: 512Mi + limits: + cpu: 1000m + memory: 1Gi + +alertmanager: + enabled: false diff --git a/06-stackops/deployment.yaml b/06-stackops/deployment.yaml new file mode 100644 index 0000000..44c72b5 --- /dev/null +++ b/06-stackops/deployment.yaml @@ -0,0 +1,87 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: stackops-api + namespace: stackops +spec: + replicas: 1 + selector: + matchLabels: + app: stackops-api + template: + metadata: + labels: + app: stackops-api + spec: + serviceAccountName: stackops-api + containers: + - name: api + image: stackops-api:v1 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8080 + env: + - name: PORT + value: "8080" + - name: STACKOPS_API_TOKEN + valueFrom: + secretKeyRef: + name: stackops-secrets + key: api-token + - name: DB_PATH + value: /data/stackops.db + volumeMounts: + - name: data + mountPath: /data + livenessProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 5 + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 256Mi + volumes: + - name: data + persistentVolumeClaim: + claimName: stackops-data +--- +apiVersion: v1 +kind: Service +metadata: + name: stackops-api + namespace: stackops +spec: + selector: + app: stackops-api + ports: + - port: 8080 + targetPort: 8080 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: stackops-api + namespace: stackops + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod +spec: + ingressClassName: nginx + tls: + - hosts: [app.nodeup.ru] + secretName: stackops-tls + rules: + - host: app.nodeup.ru + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: stackops-api + port: + number: 8080 diff --git a/06-stackops/rbac.yaml b/06-stackops/rbac.yaml new file mode 100644 index 0000000..5a4282c --- /dev/null +++ b/06-stackops/rbac.yaml @@ -0,0 +1,44 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: stackops +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: stackops-api + namespace: stackops +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: stackops-deployer +rules: + - apiGroups: ["*"] + resources: ["*"] + verbs: ["*"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: stackops-deployer +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: stackops-deployer +subjects: + - kind: ServiceAccount + name: stackops-api + namespace: stackops +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: stackops-data + namespace: stackops +spec: + accessModes: [ReadWriteOnce] + storageClassName: local-path + resources: + requests: + storage: 1Gi diff --git a/07-test-app/stackfile.toml b/07-test-app/stackfile.toml new file mode 100644 index 0000000..c0d7509 --- /dev/null +++ b/07-test-app/stackfile.toml @@ -0,0 +1,14 @@ +[app] +name = "whoami" +image = "traefik/whoami:latest" +replicas = 1 +port = 80 + +[ingress] +host = "whoami.app.nodeup.ru" +path = "/" +tls = true + +[dependencies.postgres] +version = "15" +storage = "1Gi" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..dc2e3ea --- /dev/null +++ b/Makefile @@ -0,0 +1,78 @@ +KUBECONFIG ?= $(HOME)/.kube/config-nodeup +export KUBECONFIG + +.PHONY: all dns k3s core kubevela gitea monitoring stackops test-app + +all: dns k3s core kubevela gitea monitoring stackops test-app + +dns: + bash 00-dns/cloudflare-records.sh + +k3s: + bash 01-k3s/install.sh + +core: nginx-ingress cert-manager clusterissuer + +nginx-ingress: + helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \ + --namespace ingress-nginx --create-namespace \ + --set controller.service.type=LoadBalancer \ + --set controller.kind=DaemonSet \ + --set controller.hostPort.enabled=true \ + --set controller.service.externalTrafficPolicy=Local \ + --timeout 10m --wait + +cert-manager: + helm upgrade --install cert-manager jetstack/cert-manager \ + --namespace cert-manager --create-namespace \ + --set crds.enabled=true \ + --timeout 10m --wait + +clusterissuer: + @source ../../.cloudflare && \ + sed "s|\$${CF_API_TOKEN}|$$CF_API_TOKEN|g" 02-core/clusterissuer.yaml | \ + kubectl apply -f - + +kubevela: + helm upgrade --install kubevela kubevela/vela-core \ + --namespace vela-system --create-namespace \ + --timeout 8m --wait + kubectl apply --server-side \ + -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.23/releases/cnpg-1.23.0.yaml + helm upgrade --install redis-operator ot-helm/redis-operator \ + --namespace redis-operator --create-namespace --wait + kubectl apply -f ../kubevela/components/ + +gitea: + helm repo add gitea-charts https://dl.gitea.com/charts/ || true + helm repo update gitea-charts + helm upgrade --install gitea gitea-charts/gitea \ + --namespace gitea --create-namespace \ + -f 04-gitea/values.yaml \ + --timeout 10m --wait + +monitoring: + helm repo add prometheus-community https://prometheus-community.github.io/helm-charts || true + helm repo add grafana https://grafana.github.io/helm-charts || true + helm repo update prometheus-community grafana + helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \ + --namespace monitoring --create-namespace \ + -f 05-monitoring/values-prometheus.yaml \ + --timeout 10m --wait + helm upgrade --install loki grafana/loki-stack \ + --namespace monitoring \ + -f 05-monitoring/values-loki.yaml \ + --timeout 5m --wait + +stackops: + kubectl apply -f 06-stackops/rbac.yaml + kubectl create secret generic stackops-secrets \ + --namespace stackops \ + --from-literal=api-token=dev-secret-token \ + --dry-run=client -o yaml | kubectl apply -f - + kubectl apply -f 06-stackops/deployment.yaml + +test-app: + @echo "Deploy test app via StackOps API or manually:" + @echo " cd ../toml-converter && go run ./cmd/main.go -f ../infra/07-test-app/stackfile.toml -o /tmp/whoami.yaml" + @echo " kubectl create namespace whoami && kubectl apply -f /tmp/whoami.yaml -n whoami"