talos deploy
This commit is contained in:
parent
82c06a5963
commit
e9220a6a69
14 changed files with 212 additions and 64 deletions
97
.github/workflows/deploy.yml
vendored
97
.github/workflows/deploy.yml
vendored
|
|
@ -4,9 +4,17 @@ on:
|
|||
push:
|
||||
branches: [ "main" ]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
deploy_prod:
|
||||
description: "Nach erfolgreichem Test auch auf Produktion (Talos) deployen?"
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
# ---------------------------------------------------------------------------
|
||||
# 1. Build + Test (k3s) – wie bisher
|
||||
# ---------------------------------------------------------------------------
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-latest
|
||||
steps:
|
||||
|
|
@ -22,53 +30,96 @@ jobs:
|
|||
cp nginx_ssl/nginx.crt /usr/local/share/ca-certificates/nginx-sandbox.crt 2>/dev/null || echo "Nutze Workspace-Zertifikat"
|
||||
update-ca-certificates
|
||||
|
||||
# Nutzt dein Personal Access Token (PAT) für den schnellen Push über den Socket-Port
|
||||
- name: Log in to Local Registry
|
||||
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login localhost:3005 -u ${{ github.actor }} --password-stdin
|
||||
|
||||
# Baut das Django-Image und taggt es mit dem eindeutigen Git-SHA
|
||||
# Ein Build, zwei Tags
|
||||
- name: Build Django Image
|
||||
run: |
|
||||
docker build -t localhost:3005/${{ github.repository }}:${{ github.sha }} .
|
||||
docker build -t localhost:3005/${{ github.repository }}:latest .
|
||||
docker build \
|
||||
-t localhost:3005/${{ github.repository }}:${{ github.sha }} \
|
||||
-t localhost:3005/${{ github.repository }}:latest .
|
||||
|
||||
- name: Push Django Image
|
||||
run: |
|
||||
docker push localhost:3005/${{ github.repository }}:${{ github.sha }}
|
||||
docker push localhost:3005/${{ github.repository }}:latest
|
||||
|
||||
# Holt die echten K3s-Schlüssel aus dem automatischen Host-Synchronisations-Secret
|
||||
- name: Prepare Kubernetes Access
|
||||
- name: Prepare Kubernetes Access (k3s)
|
||||
run: |
|
||||
echo "${{ secrets.KUBECONFIG_DATA }}" > ./kind_kubeconfig
|
||||
chmod 600 ./kind_kubeconfig
|
||||
|
||||
# Führt direkt das installierte, performante Zentral-Binary aus
|
||||
- name: Terraform Deploy
|
||||
- name: Terraform Deploy (test / k3s)
|
||||
run: |
|
||||
terraform --version
|
||||
terraform init
|
||||
terraform apply -auto-approve -var="app_version=${{ github.sha }}"
|
||||
terraform init -reconfigure -backend-config=envs/test.backend.hcl
|
||||
terraform apply -auto-approve \
|
||||
-var-file=envs/test.tfvars \
|
||||
-var="app_version=${{ github.sha }}"
|
||||
|
||||
# Überprüft das Deployment im internen Container-Netzwerk und folgt dem Django-Redirect (-L)
|
||||
- name: Verify App Health
|
||||
- name: Verify App Health (test)
|
||||
run: |
|
||||
echo "Warte 15 Sekunden auf die Django-Zertifikate und Migrationen..."
|
||||
echo "DEBUG: Sende Request an local-k3s-platform..."
|
||||
sleep 15
|
||||
curl --fail -L --connect-timeout 5 -H "Host: boday-it.ch" http://local-k3s-platform:80/ || exit 1
|
||||
echo "Erfolg: Die Boday-IT Django-Webseite läuft perfekt via Helm im GitOps-Standard!"
|
||||
echo "Test (k3s) OK"
|
||||
|
||||
# DAS FINALE DEINES PRODUKTS: Generiert das anonyme JSON und pusht es mit dem cloudmon-Host-Header
|
||||
- name: Publish Map to cloudmon Dashboard
|
||||
- name: Publish Map to cloudmon Dashboard (test)
|
||||
run: |
|
||||
terraform show -json > plan.json
|
||||
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer boday-cloudmon-super-token-2026" \
|
||||
-H "Authorization: Bearer ${{ secrets.CLOUDMON_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Host: cloudmon.local" \
|
||||
--data-binary @plan.json \
|
||||
"http://local-k3s-platform:80/api/v1/push/?repo=${{ github.repository }}"
|
||||
|
||||
echo "Infrastruktur-Map erfolgreich an cloudmon übermittelt!"
|
||||
"http://local-k3s-platform:80/api/v1/push/?repo=${{ github.repository }}&env=test"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. Produktion (Talos) – nur wenn Test grün ist; gleiches Image (Git-SHA)
|
||||
# ---------------------------------------------------------------------------
|
||||
deploy-prod:
|
||||
needs: build-and-test
|
||||
if: github.event_name == 'push' || inputs.deploy_prod
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare Kubernetes Access (Talos)
|
||||
run: |
|
||||
echo "${{ secrets.KUBECONFIG_TALOS }}" > ./talos_kubeconfig
|
||||
chmod 600 ./talos_kubeconfig
|
||||
|
||||
- name: Terraform Deploy (prod / Talos)
|
||||
env:
|
||||
TF_VAR_django_secret_key: ${{ secrets.PROD_DJANGO_SECRET_KEY }}
|
||||
TF_VAR_postgres_password: ${{ secrets.PROD_POSTGRES_PASSWORD }}
|
||||
run: |
|
||||
terraform init -reconfigure -backend-config=envs/prod.backend.hcl
|
||||
terraform apply -auto-approve \
|
||||
-var-file=envs/prod.tfvars \
|
||||
-var="app_version=${{ github.sha }}"
|
||||
|
||||
# Erster Lauf: Longhorn-Volume + initdb dauern länger -> mit Retries
|
||||
- name: Verify App Health (prod)
|
||||
run: |
|
||||
for i in $(seq 1 12); do
|
||||
if curl --fail -sL --connect-timeout 5 -o /dev/null -H "Host: boday-it.ch" http://${{ vars.TALOS_INGRESS_IP }}/; then
|
||||
echo "Prod (Talos) OK"; exit 0
|
||||
fi
|
||||
echo "Noch nicht erreichbar, Versuch $i/12"; sleep 10
|
||||
done
|
||||
exit 1
|
||||
|
||||
- name: Publish Map to cloudmon Dashboard (prod)
|
||||
run: |
|
||||
# show -json enthält sensitive Werte im Klartext -> Prod-Secrets vor dem Versand entfernen
|
||||
terraform show -json | jq 'del(.. | .set_sensitive?)' > plan.json
|
||||
! grep -qF "${{ secrets.PROD_POSTGRES_PASSWORD }}" plan.json || { echo "Secret im JSON gefunden, Abbruch"; exit 1; }
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer ${{ secrets.CLOUDMON_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Host: cloudmon.local" \
|
||||
--data-binary @plan.json \
|
||||
"http://local-k3s-platform:80/api/v1/push/?repo=${{ github.repository }}&env=prod"
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -8,3 +8,4 @@ davenv
|
|||
.DS_Store
|
||||
notes.txt
|
||||
runserver.sh
|
||||
talos_kubeconfig
|
||||
|
|
|
|||
|
|
@ -26,6 +26,11 @@ spec:
|
|||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-secrets
|
||||
key: postgres-password
|
||||
{{- if .Values.postgres.pgdataSubdir }}
|
||||
# Longhorn-Volumes haben ein lost+found im Root -> initdb braucht ein Unterverzeichnis
|
||||
- name: PGDATA
|
||||
value: /var/lib/postgresql/data/pgdata
|
||||
{{- end }}
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
name: dbport
|
||||
|
|
@ -37,6 +42,9 @@ spec:
|
|||
name: db-data
|
||||
spec:
|
||||
accessModes: [ "ReadWriteOnce" ]
|
||||
{{- with .Values.postgres.storageClassName }}
|
||||
storageClassName: {{ . | quote }}
|
||||
{{- end }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.postgres.storageSize }}
|
||||
|
|
|
|||
|
|
@ -1,43 +1,32 @@
|
|||
# KORRIGIERT: Der offizielle, moderne API-Pfad für Ingress in K3s
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-ingress
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "nginx"
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.ingress.className }}
|
||||
ingressClassName: {{ . | quote }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- host: boday-it.ch
|
||||
{{- range .Values.ingress.hosts }}
|
||||
- host: {{ . | quote }}
|
||||
http:
|
||||
paths:
|
||||
- path: /static
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ .Release.Name }}-web
|
||||
name: {{ $.Release.Name }}-web
|
||||
port:
|
||||
name: static
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ .Release.Name }}-web
|
||||
port:
|
||||
name: django
|
||||
- host: www.boday-it.ch
|
||||
http:
|
||||
paths:
|
||||
- path: /static
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ .Release.Name }}-web
|
||||
port:
|
||||
name: static
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ .Release.Name }}-web
|
||||
name: {{ $.Release.Name }}-web
|
||||
port:
|
||||
name: django
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ spec:
|
|||
- name: collectstatic
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
command:
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
|
|
|
|||
10
charts/boday-it/values-prod.yaml
Normal file
10
charts/boday-it/values-prod.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Talos – Produktion
|
||||
postgres:
|
||||
storageSize: 5Gi
|
||||
storageClassName: longhorn
|
||||
pgdataSubdir: true
|
||||
|
||||
ingress:
|
||||
className: traefik
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: null # alte Annotation entfernen
|
||||
1
charts/boday-it/values-test.yaml
Normal file
1
charts/boday-it/values-test.yaml
Normal file
|
|
@ -0,0 +1 @@
|
|||
# k3s – Testumgebung: rendert identisch zum bisherigen Stand
|
||||
|
|
@ -9,7 +9,7 @@ django:
|
|||
debug: "0"
|
||||
allowedHosts: "boday-it.ch,www.boday-it.ch,localhost,127.0.0.1"
|
||||
|
||||
# Diese Werte werden im Secret verschlüsselt
|
||||
# Defaults für den Test. In Prod setzt Terraform echte Werte aus Forgejo-Secrets.
|
||||
secrets:
|
||||
djangoSecretKey: "aldhguroqugaplslljdgzlshfz"
|
||||
postgresPassword: "asafdksdf"
|
||||
|
|
@ -18,3 +18,13 @@ postgres:
|
|||
database: "djangodb"
|
||||
user: "djangouser"
|
||||
storageSize: 1Gi
|
||||
storageClassName: "" # leer = Default-StorageClass des Clusters
|
||||
pgdataSubdir: false
|
||||
|
||||
ingress:
|
||||
className: ""
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "nginx"
|
||||
hosts:
|
||||
- boday-it.ch
|
||||
- www.boday-it.ch
|
||||
|
|
|
|||
1
envs/prod.backend.hcl
Normal file
1
envs/prod.backend.hcl
Normal file
|
|
@ -0,0 +1 @@
|
|||
path = "terraform/states/boday_tf_prod"
|
||||
5
envs/prod.tfvars
Normal file
5
envs/prod.tfvars
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Talos – Produktion
|
||||
environment = "prod"
|
||||
kubeconfig_path = "./talos_kubeconfig"
|
||||
kube_insecure = false
|
||||
namespace = "boday-it"
|
||||
1
envs/test.backend.hcl
Normal file
1
envs/test.backend.hcl
Normal file
|
|
@ -0,0 +1 @@
|
|||
path = "terraform/states/boday_tf"
|
||||
6
envs/test.tfvars
Normal file
6
envs/test.tfvars
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# k3s – Testumgebung (Verhalten wie bisher)
|
||||
environment = "test"
|
||||
kubeconfig_path = "./kind_kubeconfig"
|
||||
kube_host = "https://local-k3s-platform:6443"
|
||||
kube_insecure = true
|
||||
namespace = "default"
|
||||
99
main.tf
99
main.tf
|
|
@ -10,42 +10,107 @@ terraform {
|
|||
}
|
||||
}
|
||||
|
||||
# Der State-Pfad pro Umgebung wird beim init gesetzt:
|
||||
# terraform init -backend-config=envs/test.backend.hcl (k3s, terraform/states/boday_tf – unverändert)
|
||||
# terraform init -backend-config=envs/prod.backend.hcl (Talos, terraform/states/boday_tf_prod)
|
||||
backend "consul" {
|
||||
address = "consul:8500"
|
||||
path = "terraform/states/boday_tf"
|
||||
}
|
||||
}
|
||||
|
||||
# Variable für den dynamischen Git-SHA-Tag deiner Pipeline
|
||||
# --- Variablen ---------------------------------------------------------------
|
||||
|
||||
variable "app_version" {
|
||||
type = string
|
||||
default = "latest"
|
||||
description = "Image-Tag (Git-SHA aus der Pipeline)"
|
||||
type = string
|
||||
default = "latest"
|
||||
}
|
||||
|
||||
variable "environment" {
|
||||
description = "test (k3s) oder prod (Talos)"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "kubeconfig_path" {
|
||||
type = string
|
||||
default = "./kind_kubeconfig"
|
||||
}
|
||||
|
||||
# Nur für k3s nötig (überschreibt den Server aus der Kubeconfig). Bei Talos null lassen.
|
||||
variable "kube_host" {
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
# k3s-Test: true. Talos: false – die Kubeconfig von talosctl enthält die CA.
|
||||
variable "kube_insecure" {
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "namespace" {
|
||||
type = string
|
||||
default = "default"
|
||||
}
|
||||
|
||||
# Leer = Wert aus values.yaml bleibt (k3s-Test). Für Prod als Forgejo-Secret übergeben.
|
||||
variable "django_secret_key" {
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "postgres_password" {
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
# --- Provider ----------------------------------------------------------------
|
||||
|
||||
provider "kubernetes" {
|
||||
config_path = "./kind_kubeconfig"
|
||||
host = "https://local-k3s-platform:6443"
|
||||
insecure = true
|
||||
config_path = var.kubeconfig_path
|
||||
host = var.kube_host
|
||||
insecure = var.kube_insecure
|
||||
}
|
||||
|
||||
provider "helm" {
|
||||
kubernetes {
|
||||
config_path = "./kind_kubeconfig"
|
||||
host = "https://local-k3s-platform:6443"
|
||||
insecure = true
|
||||
config_path = var.kubeconfig_path
|
||||
host = var.kube_host
|
||||
insecure = var.kube_insecure
|
||||
}
|
||||
}
|
||||
|
||||
# DAS FINALE DJANGO HELM-CHART DEPLOYMENT
|
||||
resource "helm_release" "boday_website" {
|
||||
name = "boday-it"
|
||||
chart = "./charts/boday-it" # Zeigt direkt auf den lokalen Chart-Ordner im Repo
|
||||
namespace = "default"
|
||||
# --- Deployment --------------------------------------------------------------
|
||||
|
||||
locals {
|
||||
secret_overrides = {
|
||||
"secrets.djangoSecretKey" = var.django_secret_key
|
||||
"secrets.postgresPassword" = var.postgres_password
|
||||
}
|
||||
}
|
||||
|
||||
resource "helm_release" "boday_website" {
|
||||
name = "boday-it"
|
||||
chart = "./charts/boday-it"
|
||||
namespace = var.namespace
|
||||
create_namespace = var.namespace != "default"
|
||||
|
||||
# Umgebungsspezifische Werte (Ingress-Klasse, StorageClass, ...)
|
||||
values = [file("${path.module}/charts/boday-it/values-${var.environment}.yaml")]
|
||||
|
||||
# Überschreibt das Image-Tag dynamisch mit der aktuellen Pipeline-Version
|
||||
set {
|
||||
name = "image.tag"
|
||||
value = var.app_version
|
||||
}
|
||||
}
|
||||
|
||||
# Secrets nur setzen, wenn sie übergeben wurden
|
||||
dynamic "set_sensitive" {
|
||||
for_each = { for k, v in local.secret_overrides : k => v if v != "" }
|
||||
content {
|
||||
name = set_sensitive.key
|
||||
value = set_sensitive.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue