Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09f485b5ac | ||
|
|
dfff276de6 | ||
|
|
4fd1f14a3b | ||
|
|
c8556e6e74 | ||
|
|
62c4e4824a | ||
|
|
35e24b4209 | ||
|
|
e1338c2314 | ||
|
|
8b3b15a9ce | ||
|
|
16f5a5e7b1 | ||
|
|
51379cf1fc |
No files matched your search
@@ -0,0 +1,440 @@
|
||||
Alles klar – ich führe den Prompt jetzt aus und entwerfe dir die Architektur.
|
||||
|
||||
---
|
||||
|
||||
## 1. Überblick & Annahmen
|
||||
|
||||
**Annahmen (konkretisiert):**
|
||||
|
||||
* Branche: reguliertes Finanzinstitut oder KRITIS-Betreiber in der EU.
|
||||
* Daten: überwiegend **personenbezogene Daten** (inkl. special categories möglich), daher **DSGVO hoch relevant**.
|
||||
* KI-System eurer Lösung gilt als **„High-Risk AI System“** nach EU AI Act (z. B. Kreditentscheidungen, Risiko-Scoring).([Künstliche Intelligenz Gesetz EU][1])
|
||||
* Alle produktiven Workloads laufen in **EU-Azure-Regionen** (z. B. *West Europe*, *North Europe*).
|
||||
* Nutzung von **Azure OpenAI (EU Data Zone / EU-Residency-Option)** bzw. ausschließlich EU-datenresidente Endpunkte für Foundation Models.([Microsoft Learn][2])
|
||||
* Ziel: Architektur soll **technisch belegbar** Anforderungen aus
|
||||
|
||||
* EU AI Act (insb. Logging, Risikomanagement, Human Oversight)([digital-strategy.ec.europa.eu][3])
|
||||
* DSGVO,
|
||||
* DORA, NIS 2 (Resilienz, Monitoring, Incident Handling),
|
||||
* ISO 27001 (ISMS-Kontrollen)
|
||||
adressieren.
|
||||
|
||||
---
|
||||
|
||||
## 2. Zielarchitektur (High Level)
|
||||
|
||||
### 2.1 Schichtenmodell (verbale Übersicht)
|
||||
|
||||
1. **User & Clients**
|
||||
|
||||
* interne Nutzer (Mitarbeitende), ggf. externe Kunden
|
||||
* Zugriff über Browser / interne Portale / APIs
|
||||
|
||||
2. **Edge / Ingress / Security**
|
||||
|
||||
* **Azure Front Door mit WAF** (global, optional)
|
||||
* **Azure Application Gateway mit WAF** (regional, TLS-Terminierung)([Microsoft Learn][4])
|
||||
* DDoS Protection, Azure Firewall, Private Endpoints
|
||||
|
||||
3. **API & Orchestration Layer**
|
||||
|
||||
* **Azure API Management (APIM)** als API-Gateway
|
||||
* „Prompt Firewall“ & Policies in APIM + Azure AI Content Safety
|
||||
* LLM-Router als Microservice (AKS) hinter APIM
|
||||
|
||||
4. **KI-/Compute-Layer**
|
||||
|
||||
* **AKS-Cluster** mit GPU-Nodepools für KI-Workloads
|
||||
* Agentic AI-Orchestrator (z. B. LangChain-/Semantic-Kernel-basiert) als Services auf AKS
|
||||
* Azure OpenAI / Azure AI Foundry / Self-hosted Models auf AKS
|
||||
|
||||
5. **Data & Event Layer**
|
||||
|
||||
* Azure SQL, Cosmos DB, Blob Storage (Data Lake), Azure AI Search (Vektor)
|
||||
* Event Hubs (Kafka-kompatibel), Service Bus, Stream Analytics / Data Explorer
|
||||
|
||||
6. **Observability & Security-Layer**
|
||||
|
||||
* Azure Monitor, Log Analytics, Application Insights
|
||||
* Azure Managed Grafana für Metrik-Dashboards([Microsoft Learn][5])
|
||||
* Microsoft Sentinel + Splunk-Anbindung als SIEM/SOC
|
||||
* Defender for Cloud (inkl. Defender for Containers, Defender for APIs)
|
||||
|
||||
7. **DevOps / MLOps / IaC**
|
||||
|
||||
* Git-Repos (GitHub Enterprise oder Azure DevOps), CI/CD-Pipelines
|
||||
* Terraform für IaC (State in verschlüsseltem Azure Storage)
|
||||
* Azure ML / AI Studio / AI Foundry für MLOps (je nach Stack)
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Textuelles Architekturdiagramm (Mermaid)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Clients
|
||||
U[User / Apps]
|
||||
end
|
||||
|
||||
subgraph Edge
|
||||
AFD[Azure Front Door + WAF]
|
||||
AGW[Application Gateway WAF + TLS]
|
||||
AFW[Azure Firewall]
|
||||
end
|
||||
|
||||
subgraph API
|
||||
APIM[Azure API Management\n(API Gateway + Policies)]
|
||||
PF[Prompt Firewall\n(Content Safety + APIM Policies)]
|
||||
end
|
||||
|
||||
subgraph AI_Compute[AI & Compute Layer (AKS)]
|
||||
AKS[AKS Cluster\nGPU/CPU Node Pools]
|
||||
LLMR[LLM Router Service]
|
||||
AGENTS[Agentic AI Orchestrator\n(AI Agents)]
|
||||
FE[Frontend Container]
|
||||
end
|
||||
|
||||
subgraph AI_Services
|
||||
AOAI[Azure OpenAI / Azure AI Foundry\n(EU Data Residency)]
|
||||
MODELS[Self-hosted Models on AKS]
|
||||
end
|
||||
|
||||
subgraph Data[Data & Storage Layer]
|
||||
SQL[Azure SQL DB / MI]
|
||||
COSMOS[Azure Cosmos DB\n(NoSQL + Graph)]
|
||||
BLOB[Azure Data Lake / Blob Storage]
|
||||
VECTOR[Azure AI Search\n(Vektorindex)]
|
||||
REDIS[Azure Cache for Redis]
|
||||
KUSTO[Azure Data Explorer / Synapse]
|
||||
end
|
||||
|
||||
subgraph Events
|
||||
EH[Azure Event Hubs (Kafka Endpoint)]
|
||||
SB[Azure Service Bus (Queues/Topics)]
|
||||
SA[Azure Stream Analytics]
|
||||
end
|
||||
|
||||
subgraph Observability[Observability & Security]
|
||||
AM[Azure Monitor + Log Analytics]
|
||||
APPINS[Application Insights]
|
||||
GRAF[Azure Managed Grafana]
|
||||
SENT[Microsoft Sentinel]
|
||||
SPL[SPLUNK (VM/AKS)]
|
||||
DEF[Microsoft Defender for Cloud]
|
||||
end
|
||||
|
||||
subgraph Identity_Sec[Identity & Access]
|
||||
ENTRA[Azure Entra ID\n(RBAC, Conditional Access)]
|
||||
PIM[Entra Privileged Identity Mgmt]
|
||||
KV[Azure Key Vault + Managed HSM]
|
||||
BAST[Azure Bastion]
|
||||
end
|
||||
|
||||
subgraph DevOps[DevOps / MLOps / IaC]
|
||||
GIT[GitHub / Azure DevOps Repos]
|
||||
PIPE[CI/CD Pipelines\n(GitHub Actions / Azure Pipelines)]
|
||||
TF[Terraform\n(State in Storage)]
|
||||
end
|
||||
|
||||
U --> AFD --> AGW --> APIM --> PF --> LLMR --> AGENTS
|
||||
AGENTS --> AOAI
|
||||
AGENTS --> MODELS
|
||||
LLMR --> AOAI
|
||||
LLMR --> MODELS
|
||||
|
||||
AGENTS --> SQL
|
||||
AGENTS --> COSMOS
|
||||
AGENTS --> BLOB
|
||||
AGENTS --> VECTOR
|
||||
AGENTS --> REDIS
|
||||
|
||||
AGENTS --> EH
|
||||
EH --> SA --> KUSTO
|
||||
AGENTS --> SB
|
||||
|
||||
AKS --> AM
|
||||
AKS --> APPINS
|
||||
AM --> GRAF
|
||||
AM --> SENT
|
||||
AM --> SPL
|
||||
DEF --> SENT
|
||||
|
||||
ENTRA --> APIM
|
||||
ENTRA --> AKS
|
||||
ENTRA --> GIT
|
||||
PIM --> ENTRA
|
||||
KV --> AGW
|
||||
KV --> APIM
|
||||
KV --> AKS
|
||||
BAST --> AKS
|
||||
|
||||
GIT --> PIPE --> AKS
|
||||
PIPE --> TF
|
||||
TF --> AKS
|
||||
TF --> Data
|
||||
TF --> Networking((VNet/NSG/PE))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Azure-Service-Mapping pro Baustein
|
||||
|
||||
### 3.1 KI- und Compute-Layer
|
||||
|
||||
| Baustein | Azure-Service(s) | Zweck / Begründung |
|
||||
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Kubernetes-Cluster mit KI-Workern | **Azure Kubernetes Service (AKS)** | Managed Kubernetes mit Integration in Azure RBAC, Managed Identities, Auto-Scaling, GPU-Nodepools für KI/LLM-Workloads. |
|
||||
| Container-/Cluster-Management | AKS + **Azure Monitor for Containers**, **Azure Policy** | Zentrales Management, Configuration Enforcement (PodSecurity, Gatekeeper), Ressourcenkontrolle, Compliance. |
|
||||
| Agentic AI mit LLM-Adapter | Microservices auf AKS (z. B. .NET/Python), **Azure OpenAI** / Azure AI Foundry | Orchestrierung von Agents, Aufruf verschiedener Modelle (Azure OpenAI, lokale Modelle). EU-Residency-Optionen helfen bei DSGVO/AI-Act-Konformität.([Microsoft Learn][2]) |
|
||||
| Build, Test, Deploy von AI Agents | **GitHub Enterprise / Azure DevOps Repos**, **GitHub Actions / Azure Pipelines**, **AKS**, optional **Azure Machine Learning** (MLOps) | End-to-End-CI/CD-Pipelines inkl. Build, Tests (unit, integration, eval), Deployment auf AKS; ML-spezifische Pipelines mit Modellversionierung. |
|
||||
| LLM-Router | Custom Service auf AKS + **Azure API Management** (Routing/Policies) | Policy-gesteuertes Routing zwischen Modellen (Azure OpenAI, Self-hosted, ggf. Drittanbieter), A/B-Tests, Fallback-Strategien. |
|
||||
|
||||
---
|
||||
|
||||
### 3.2 Security & Governance
|
||||
|
||||
| Baustein | Azure-Service(s) | Zweck / Begründung |
|
||||
| --------------------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| WAF | **Azure Front Door WAF** und/oder **Application Gateway WAF** | Zentrale L7-Firewall gegen OWASP-Top-10, Bot-Protection, Geo-Blocking; unterstützt Compliance-Anforderungen.([Microsoft Learn][4]) |
|
||||
| Prompt Firewall / Prompt Security | **Azure API Management Policies** + **Azure AI Content Safety** + Azure OpenAI Safety Filters | Vor- und Nachschaltung von Content-Filtern, Blockieren schädlicher Prompts/Outputs; wichtig für AI-Act-Risikomitigation und Grundrechtsschutz.([Microsoft Learn][2]) |
|
||||
| AI-Model Scanning | **Microsoft Defender for Cloud** (Container/VM/Registry-Scanning), **Azure ML Responsible AI-Tools** | Scans von Container-Images/Abhängigkeiten, Modell-Governance (Bias, Fairness, Explainability) als Beitrag zu EU-AI-Act-Risikomanagement.([21medien.de][6]) |
|
||||
| AI-Interface Scanning | **Microsoft Defender for APIs**, WAF-Logs, APIM Diagnostics | Schutz der API-Interfaces gegen Missbrauch, Anomalie-Erkennung, Angriffserkennung auf AI-Endpunkte. |
|
||||
| Vulnerability Management | **Microsoft Defender for Cloud**, Defender for Containers, Qualys-Integration | Kontinuierliches Schwachstellenmanagement auf VMs, Containern, PaaS-Ressourcen – relevant für DORA/NIS2/ISO 27001. |
|
||||
| IAM (RBAC, LDAP) | **Azure Entra ID**, Azure RBAC, Azure AD Connect / Entra Connect | Zentrale Identitäten, Rollen, Conditional Access; Integration mit On-Prem-LDAP/AD; Grundlage für least privilege & Zero Trust. |
|
||||
| PAM | **Azure Entra Privileged Identity Management (PIM)** | Zeitlich begrenzte, genehmigungspflichtige Admin-Rollen, Governance & Justification-Logging – wichtig für ISO 27001, DORA. |
|
||||
| Bastion Host | **Azure Bastion** | Sichere, browserbasierte RDP/SSH-Verbindungen ohne Public IPs auf VMs (z. B. Admin-Knoten, Jump Hosts). |
|
||||
| Zertifikate & Secrets | **Azure Key Vault** (Secrets, Keys, Certificates) + ggf. **Managed HSM** | Zentrales Management von TLS-Zertifikaten, Schlüsseln (inkl. HSM-backed), Secret Rotation; Integrationen mit App Gateway, AKS, APIM, etc. |
|
||||
|
||||
---
|
||||
|
||||
### 3.3 Networking & Ingress
|
||||
|
||||
| Baustein | Azure-Service(s) | Zweck / Begründung |
|
||||
| ---------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Load Balancer mit TLS-Terminierung | **Application Gateway** (L7, TLS-Offload) + interne **Azure Load Balancer** | TLS-Termination, SNI, Path-based Routing; interne Lastverteilung zwischen AKS-Knoten/Pods. |
|
||||
| API-Gateway | **Azure API Management** | Throttling, AuthN/AuthZ (JWT/OAuth2/Entra), Versionierung, Request/Response-Transformation, zentraler Audit-Log-Punkt. |
|
||||
| Netzwerksegmentierung | **Azure Virtual Network**, **NSG**, **Azure Firewall**, **Private Endpoints**, **Hub-Spoke-Architektur** | Trennung nach Zonen (Edge, App, Data, Admin), erzwungener Zugriff über Firewall, private Kommunikation zu PaaS-Services. |
|
||||
|
||||
---
|
||||
|
||||
### 3.4 Daten- & Storage-Layer
|
||||
|
||||
| Baustein | Azure-Service(s) | Zweck / Begründung |
|
||||
| -------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| SQL-Datenbanken | **Azure SQL Database** / **Azure SQL Managed Instance** | Transaktionale Workloads, Konfigurationsdaten, Audit-Tables, referentielle Integrität. |
|
||||
| NoSQL | **Azure Cosmos DB (Core API)** | Hochskalierbare, globale Dokument-/Key-Value-DB für Sessions, Konfiguration, Agent-States. |
|
||||
| Object Store | **Azure Blob Storage / Data Lake Storage Gen2** | S3-ähnliche Object-Speicherung für Dokumente, Logs, Model-Checkpoints, Trainingsdaten. |
|
||||
| Vektor-Datenbank | **Azure AI Search mit Vektorindex** | Verwaltete Vektorsuche für RAG, semantische Suche, Retrieval-Orchestrierung. |
|
||||
| Redis Cache | **Azure Cache for Redis** | In-Memory-Cache für Sessions, Prompt-Kontext, Throttling, Rate-Limiting-Counter. |
|
||||
| GraphDB | **Cosmos DB (Gremlin API)** oder **Azure SQL Graph** | Modellierung von Beziehungsnetzen (Entitäten, Events, Policies). |
|
||||
| Big Data / Data Lake / DWH | **Data Lake Storage Gen2**, **Azure Synapse Analytics** oder **Fabric** | Historisierung, Advanced Analytics, Reporting, KI-Trainingsdaten. |
|
||||
| Real-Time Analytics | **Azure Event Hubs**, **Stream Analytics**, **Azure Data Explorer / Synapse Data Explorer** | near real-time Monitoring, Fraud-Detection, Telemetrie-Auswertung. |
|
||||
| Apache Kafka Router | **Azure Event Hubs (Kafka-kompatibler Endpoint)** | Kafka-API-kompatibles Event-Streaming ohne eigenen Kafka-Cluster. |
|
||||
| Queues / MQ | **Azure Service Bus** (Queues/Topics) | Zuverlässige, transaktionale Nachrichten zwischen Services, Entkopplung. |
|
||||
|
||||
---
|
||||
|
||||
### 3.5 Frontend & Applikation
|
||||
|
||||
| Baustein | Azure-Service(s) | Zweck / Begründung |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| Frontend als Container | Container auf **AKS** (separate Namespace) oder **Azure App Service for Containers** | Isoliertes Deployment der UI, Integration mit WAF/APIM, Identity via Entra ID (OIDC). |
|
||||
| Git-Repos, Runner, CI/CD | **GitHub Enterprise** oder **Azure DevOps** (Repos, Pipelines), self-hosted Runner auf AKS/VM | Vollständige Code-Versionierung, Build/Deploy-Pipelines, Policy-Gates (Code-Scanning, Secrets-Scanning). |
|
||||
| IDE-Integration | **GitHub Codespaces** / **Dev Box**, VS Code Remote | Sichere, cloudbasierte Dev- und Debug-Umgebungen mit Policy-Control. |
|
||||
|
||||
---
|
||||
|
||||
### 3.6 Observability & Operations
|
||||
|
||||
| Baustein | Azure-Service(s) | Zweck / Begründung |
|
||||
| ------------------ | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Logging | **Azure Monitor Logs + Log Analytics**, Diagnostics Settings, Export zu Blob/Event Hub | Zentrale Sammlung von Infrastruktur-, App-, API- und Sicherheits-Logs; Grundlage für EU-AI-Act-Logging-Pflichten und ISO 27001-Kontrollen.([iks.fraunhofer.de][7]) |
|
||||
| Monitoring | **Azure Monitor**, **Application Insights**, Container Insights | Verfügbarkeit, Performance, End-to-End-Traceability. |
|
||||
| Metriken / Grafana | **Azure Managed Grafana** | Visualisierung von Kennzahlen (Latenz, Request-Fehler, Modell-Auslastung, GPU-Nutzung) – vollständig verwalteter Dienst.([Microsoft Learn][5]) |
|
||||
| SIEM / SOC | **Microsoft Sentinel** + Anbindung an **Splunk** | Korrelation sicherheitsrelevanter Events, Detection-Regeln, Playbooks; Integration in bestehende SOC-Landschaft. |
|
||||
|
||||
---
|
||||
|
||||
### 3.7 Infrastructure as Code (IaC)
|
||||
|
||||
| Baustein | Azure-Service(s) | Zweck / Begründung |
|
||||
| -------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| Terraform Deployment | **Terraform OSS/Cloud** + Backend in **Azure Storage** (verschlüsselt, private Endpoint) | Reproduzierbare Provisionierung von Netz, AKS, Datenbanken, Monitor, Sicherheitskomponenten. |
|
||||
| Secrets für IaC | **Azure Key Vault** | Sichere Ablage von Service Principals, SSH-Keys etc.; Zugriff per Managed Identity der Pipelines. |
|
||||
|
||||
---
|
||||
|
||||
## 4. Compliance-Mapping (Tabelle – Auszug)
|
||||
|
||||
**Hinweis:** Stark vereinfacht; in einem echten Projekt würdest du pro Regulation eine viel granularere Kontrollliste pflegen.
|
||||
|
||||
| Regulierung / Anforderung | Architekturbaustein(e) | Technischer Beitrag |
|
||||
| ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **EU AI Act – Logging & Traceability (Art. 12, 26)**([Künstliche Intelligenz Gesetz EU][8]) | Azure Monitor, Log Analytics, APIM-Logs, AKS Audit Logs, Data Lake für Log-Archiv | Vollständige Protokollierung von Input/Output, Modellaufrufen, Entscheidungen; Aufbewahrung > 6 Monate; revisionssichere Ablage. |
|
||||
| **EU AI Act – Human Oversight (Art. 14)**([Künstliche Intelligenz Gesetz EU][9]) | Frontend Controls, Agent-Orchestrator, RBAC, Workflow-Engine (z. B. Logic Apps) | Manuelle Freigaben, Vier-Augen-Prinzip, Override- und Stop-Funktionen für KI-Entscheidungen. |
|
||||
| **DSGVO – Datenminimierung & Zweckbindung** | Datenmodellierung in SQL/Cosmos, RAG-Architektur mit selektivem Kontext, APIM-Policies | Nur notwendige Attribute im Prompt-Kontext; strenge Scoping-Policies; Pseudonymisierung vor KI-Aufruf. |
|
||||
| **DSGVO – Datenübermittlung & Residency** | Regionenauswahl EU (West/North Europe), Azure OpenAI EU Data-Zones | Sicherstellung, dass personenbezogene Daten die EU nicht verlassen; vertragliche & technische Kontrolle des Modellstandorts.([Microsoft Learn][2]) |
|
||||
| **DSGVO – Sicherheit der Verarbeitung (Art. 32)** | Entra ID, RBAC, PIM, Key Vault, Verschlüsselung at rest/in transit | Starke AuthN/AuthZ, geringste Rechte, Verschlüsselung (Storage, DB, TLS), Schutz der Schlüssel. |
|
||||
| **DORA – ICT Risk Management & Resilience** | Hub-Spoke-VNet, Azure Firewall, AKS mit Multi-AZ, Backup & DR-Strategie | Redundante Deployments, Netzsegmentierung, Backup/Restore, Test von Failover-Szenarien. |
|
||||
| **DORA – Monitoring & Incident Reporting** | Azure Monitor, Sentinel, Defender for Cloud, SOC-Runbooks | Durchgängige Überwachung, Alarmierung, automatisierte Playbooks, strukturierte Incident-Daten für Reporting. |
|
||||
| **NIS 2 – Security of Network & Information Systems** | VNet, NSGs, Firewall, WAF, Defender for Cloud | Schutz vor Netzangriffen, Härtung von Systemen, laufende Schwachstellen-Bewertung. |
|
||||
| **ISO 27001 – A.9 Zugriffskontrolle** | Entra ID, RBAC, PIM, Bastion, APIM-Auth | Rollenbasierte Zugriffssteuerung, Just-in-Time-Privilegien, kontrollierte Admin-Zugriffe ohne Public IPs. |
|
||||
| **ISO 27001 – A.12 Betriebs- & Änderungsmanagement** | Git-Repos, CI/CD, IaC (Terraform), Change-Approval Workflows | Versionierte Infrastruktur, klare Trennung von Umgebungen, dokumentierte Changes, Rückverfolgbarkeit. |
|
||||
| **ISO 27001 – A.16 Incident Management** | Sentinel, Splunk, Playbooks, Runbooks | Standardisierte Erkennung, Behandlung und Dokumentation von Sicherheitsvorfällen. |
|
||||
|
||||
---
|
||||
|
||||
## 5. Security & Data Protection – Datenflüsse & Kontrollen
|
||||
|
||||
### 5.1 Datenflüsse
|
||||
|
||||
1. **Eingabe-Prompts & Kontextdaten**
|
||||
|
||||
* User → Frontend (TLS) → Front Door WAF → Application Gateway WAF → APIM
|
||||
* APIM führt AuthN/AuthZ (Entra ID, OAuth2), Rate-Limiting und Input-Validierung durch.
|
||||
* Prompt Firewall (APIM Policy + Content Safety) prüft Prompts auf **sensible Inhalte, PII, Policy-Verstöße**.
|
||||
* Agent-Orchestrator auf AKS reichert Prompt mit **zugelassenem Kontext** aus Datenbanken / Vektorstore an.
|
||||
|
||||
2. **Model-Calls & Outputs**
|
||||
|
||||
* Orchestrator ruft Azure OpenAI (EU-Region / Data Zone) oder Self-hosted Model auf AKS per TLS auf.([Microsoft Learn][2])
|
||||
* Outputs werden erneut durch Content Safety / Post-Filter geleitet (Policy-check; „Red-Team“-Regeln).
|
||||
* Relevante Parameter (Modell, Version, Confidence Scores, Decision IDs) werden geloggt.
|
||||
|
||||
3. **Logs & Auditing**
|
||||
|
||||
* Alle Requests/Responses (ggf. pseudonymisiert) werden in Log Analytics und/oder Data Lake gespeichert.
|
||||
* Audit Logs für Admin-Aktivitäten (PIM, AKS-Cluster-Admin, Key Vault Access) werden zentral gesammelt.
|
||||
* Zugriff auf Logs nur via RBAC + PIM; Export zu Sentinel & Splunk für SOC-Auswertung.
|
||||
|
||||
### 5.2 Verschlüsselung (at rest / in transit)
|
||||
|
||||
* **At rest**
|
||||
|
||||
* Standardmäßig: Storage, Disk, SQL, Cosmos, Event Hubs, Service Bus → Transparent Data Encryption / SSE mit Microsoft-Managed Keys.
|
||||
* Für höhere Anforderungen: **Customer-Managed Keys (CMK)** in Key Vault / Managed HSM; z. B. für SQL, Storage, Event Hubs.
|
||||
* **In transit**
|
||||
|
||||
* Erzwingen von TLS 1.2+/1.3 auf allen Endpoints (APIM, AKS Ingress, OpenAI, DBs, Message-Broker).
|
||||
* Interne Kommunikation über Private Endpoints + VNet.
|
||||
|
||||
### 5.3 Zugriffssteuerung & PAM
|
||||
|
||||
* **Identity First**:
|
||||
|
||||
* Alle User/Apps über **Entra ID** und **Managed Identities** authentifiziert.
|
||||
* RBAC für Azure-Ressourcen, Kubernetes-RBAC für Clusterebene, App-Rollenkonzepte im Frontend/API.
|
||||
* **PAM mit PIM**:
|
||||
|
||||
* Admin-Rollen (Subscription Owner, AKS Admin, Key Vault Admin) nur als **Just-in-Time**-Rollen mit Approval und begrenzter Dauer.
|
||||
* **Zero Trust**:
|
||||
|
||||
* Conditional Access (MFA, Compliant Device), kein direkter Zugriff auf Produktionsressourcen ohne PIM und Bastion.
|
||||
|
||||
### 5.4 Protokollierung, Auditability, Nachvollziehbarkeit
|
||||
|
||||
* Protokollierung von:
|
||||
|
||||
* Benutzeraktionen (Frontends, APIs) mit Korrelations-IDs.
|
||||
* Modellaufrufen: Prompt-Metadaten (ggf. pseudonymisiert), Modell-ID, Version, Entscheidung, Confidence.
|
||||
* Datenzugriffe (SQL, Storage, Cosmos) über native Audit-Funktionen.
|
||||
* Admin-Aktivitäten (PIM, Key Vault, AKS-Clusterrollen).
|
||||
* Speicherung:
|
||||
|
||||
* Kurzfristig in **Log Analytics**, mittel-/langfristig in **Data Lake (immutable Storage)** zur Erfüllung gesetzlicher Aufbewahrungsfristen.
|
||||
|
||||
### 5.5 Data Residency & Souveränität
|
||||
|
||||
* Alle Schlüsselressourcen (AKS, SQL, Storage, OpenAI, Event Hubs, etc.) werden in EU-Regionen angelegt.
|
||||
* Für Azure OpenAI: **EU-residente Konfiguration (Data Zones)**, um sicherzustellen, dass Prompts/Responses in der EU verarbeitet werden.([Microsoft Learn][2])
|
||||
* Restriktive Firewall/NSG-Regeln, um ausgehenden Traffic zu begrenzen; optional Private DNS, keine Public Endpoints für interne Systeme.
|
||||
|
||||
### 5.6 Mitigation von Modell- & Prompt-Risiken
|
||||
|
||||
* **Prompt Injection & Jailbreaks**
|
||||
|
||||
* System-/Policy-Prompts zentral in Orchestrator verwaltet (nicht durch User überschreibbar).
|
||||
* Prompt-Filter in APIM + Content Safety; Blacklists/Regex und semantische Checks.
|
||||
* **Data Leakage**
|
||||
|
||||
* Pseudonymisierung / Maskierung sensibler Daten vor der Übergabe an LLM.
|
||||
* Kein Speichern von Roh-Prompts in Logs ohne Maskierung; strikte Rollenrechte für Log-Zugriff.
|
||||
* **Bias, Fairness, Explainability**
|
||||
|
||||
* Nutzung von Responsible-AI-Funktionen (z. B. Azure ML, externe Tooling) in Dev-/Testphase.([iks.fraunhofer.de][7])
|
||||
|
||||
---
|
||||
|
||||
## 6. Betriebs- & Lifecycle-Konzept
|
||||
|
||||
### 6.1 Versionierung, Test & Deployment von KI-Modellen / Agents
|
||||
|
||||
* **Repos & Branching**
|
||||
|
||||
* Code & Konfig (Infrastructure + App + Agents) in Git (Mono- oder Multi-Repo), Feature-Branches, PR-Gates.
|
||||
* **Pipelines (CI/CD)**
|
||||
|
||||
* Build: Linting, Tests, Security-Scans (SAST, Secrets-Scanning, Dependency-Scanning).
|
||||
* Container-Build in **Azure Container Registry** mit Signierung (Notary / cosign).
|
||||
* Deployment per Helm/Kustomize auf AKS (Blue-Green/Canary via Ingress Controller + APIM).
|
||||
* **MLOps**
|
||||
|
||||
* Modellversionierung, Evaluation gegen Testdatensätze, automatisierte Regressionstests.
|
||||
* Staging-Umgebung mit Shadow Traffic, Auswertung von KPIs, Bias-/Drift-Checks, bevor Go-Live.
|
||||
|
||||
### 6.2 Monitoring, Alerting, Incident Response
|
||||
|
||||
* **Monitoring**
|
||||
|
||||
* Health-Checks auf API/Frontend/Agents (SLIs: Latenz, Error-Rate, Throughput).
|
||||
* Business-KPIs (z. B. akzeptierte Anträge, manuelle Overrides, Review-Raten).
|
||||
* **Alerting**
|
||||
|
||||
* Schwellenwert-basierte Alerts aus Azure Monitor (z. B. Error Rate > x%, Response Time > y ms).
|
||||
* Security Alerts aus Defender / Sentinel (z. B. ungewöhnliche Zugriffe, Brute-Force, Anomalien).
|
||||
* **Incident Response**
|
||||
|
||||
* Playbooks in **Sentinel** (z. B. automatisches Sperren eines API-Keys, Quarantäne von Nodes).
|
||||
* Runbooks in **Automation** für Routineoperationen (Log-Rollover, Skalierung, Rotationen).
|
||||
|
||||
### 6.3 Schwachstellenmanagement (Vulnerability Management)
|
||||
|
||||
* **Container & Dependencies**
|
||||
|
||||
* Defender for Containers scannt Container-Images in ACR & Laufzeitumgebung.
|
||||
* CVE-Reports → Integration in CI-Pipeline als Quality Gate.
|
||||
* **OS & PaaS**
|
||||
|
||||
* Defender for Cloud überwacht VM-Guest-OS-Patching, Konfigurationsdrift, schwache Konfigurationen.
|
||||
* **Regelmäßige Reviews**
|
||||
|
||||
* Monatliche Security-Review-Boards, Abgleich mit DORA/NIS2/ISO27001-Kontrollkatalogen.
|
||||
|
||||
### 6.4 Terraform & Konfigurationsmanagement
|
||||
|
||||
* **State-Management**
|
||||
|
||||
* Remote Backend (Azure Storage) mit RBAC, Private Endpoint, Soft Delete, Versioning aktiviert.
|
||||
* **Policies & Guardrails**
|
||||
|
||||
* **Azure Policy** und **Blueprints** (oder Landing Zone Templates), um nur EU-Regionen, verschlüsselte Storage-Accounts, Private Endpoints etc. zuzulassen.
|
||||
* **Drift Detection**
|
||||
|
||||
* Regelmäßige `terraform plan`-Runs und Compliance-Checks; Abweichungen werden als Incident behandelt.
|
||||
|
||||
---
|
||||
|
||||
Wenn du möchtest, kann ich im nächsten Schritt:
|
||||
|
||||
* ein **konkretes Beispiel-Setup** (Liste von Terraform-Ressourcen und Namespaces) skizzieren oder
|
||||
* die **Compliance-Tabelle** weiter detaillieren (z. B. auf Ebene einzelner ISO-27001-Kontrollen).
|
||||
|
||||
[1]: https://artificialintelligenceact.eu/article/6/?utm_source=chatgpt.com "Article 6: Classification Rules for High-Risk AI Systems"
|
||||
[2]: https://learn.microsoft.com/en-us/azure/ai-foundry/responsible-ai/openai/data-privacy?view=foundry-classic&utm_source=chatgpt.com "Data, privacy, and security for Azure Direct Models in ..."
|
||||
[3]: https://digital-strategy.ec.europa.eu/en/policies/regulatory-framework-ai?utm_source=chatgpt.com "AI Act | Shaping Europe's digital future - European Union"
|
||||
[4]: https://learn.microsoft.com/de-de/azure/web-application-firewall/afds/afds-overview?utm_source=chatgpt.com "Was ist Azure Web Application Firewall für Azure Front Door?"
|
||||
[5]: https://learn.microsoft.com/en-us/azure/managed-grafana/overview?utm_source=chatgpt.com "What is Azure Managed Grafana?"
|
||||
[6]: https://21medien.de/en/library/azure-openai?utm_source=chatgpt.com "Azure OpenAI Service - 21medien AI Library"
|
||||
[7]: https://www.iks.fraunhofer.de/content/dam/iks/documents/whitepaper-eu-ai-act-fraunhofer-iks.pdf?utm_source=chatgpt.com "Whitepaper The European Artificial Intelligence Act"
|
||||
[8]: https://artificialintelligenceact.eu/article/26/?utm_source=chatgpt.com "Article 26: Obligations of Deployers of High-Risk AI Systems"
|
||||
[9]: https://artificialintelligenceact.eu/article/14/?utm_source=chatgpt.com "Article 14: Human Oversight | EU Artificial Intelligence Act"
|
||||
@@ -0,0 +1,287 @@
|
||||
|
||||
1. Analyze the EU AI Act, GDPR, DORA, NIS 2, and ISO 27001 compliance requirements for AI operations in the Azure cloud.
|
||||
2. Identify Azure components for Kubernetes cluster management, including AKS for container orchestration and Azure Container Registry for image storage.
|
||||
3. Select Azure components for agentic AI and LLM adapter, such as Azure Cognitive Services, Azure Machine Learning, and Azure Functions.
|
||||
4. Choose Azure components for WAF and prompt firewall, like Azure Web Application Firewall and Azure Front Door.
|
||||
5. Identify Azure components for AI model scanning and AI interface scanning, such as Azure Security Center and Azure Sentinel.
|
||||
6. Select Azure components for frontend containerization, load balancing with TLS termination, and LLM routing, like Azure App Service, Azure Load Balancer, and Azure API Management.
|
||||
7. Choose Azure components for IAM, RBAC, LDAP, and PAM, such as Azure Active Directory, Azure Role-Based Access Control, and Azure Privileged Identity Management.
|
||||
8. Identify Azure components for the database layer, including Azure SQL Database, Azure Cosmos DB, Azure Blob Storage, Azure Vector Database, Azure Redis Cache, and Azure Graph Database.
|
||||
9. Select Azure components for big data processing, such as Azure Synapse Analytics, Azure Data Lake Storage, and Azure Databricks.
|
||||
10. Choose Azure components for Apache Kafka routing, logging, monitoring, and real-time analytics, like Azure Event Hubs, Azure Monitor, Azure Log Analytics, and Azure Stream Analytics.
|
||||
11. Identify Azure components for Git repos, runners, pipelines, CI/CD, and IDE, such as Azure DevOps, Azure Pipelines, and Azure Repos.
|
||||
12. Select Azure components for metrics and vulnerability management, like Azure Grafana and Azure Security Center.
|
||||
13. Choose Azure components for queues and MQ, such as Azure Service Bus and Azure Event Grid.
|
||||
14. Identify Azure components for building, testing, and deploying AI agents, like Azure DevOps, Azure Pipelines, and Azure Machine Learning.
|
||||
15. Select Azure components for Terraform and Bastion host, such as Azure Terraform and Azure Bastion.
|
||||
16. Choose Azure components for Splunk integration, like Azure Monitor and Azure Log Analytics.
|
||||
17. Identify Azure components for public and private certificate management, such as Azure Key Vault and Azure Certificate Services.
|
||||
18. Design a comprehensive architecture diagram incorporating all the identified Azure components and their interactions.
|
||||
19. Create a detailed deployment plan, including resource provisioning, configuration, and integration steps for each Azure component.
|
||||
20. Develop a compliance checklist to ensure the proposed architecture meets the EU AI Act, GDPR, DORA, NIS 2, and ISO 27001 requirements.
|
||||
# Architekturvorschlag für den konformen KI-Betrieb auf Microsoft Azure unter Einhaltung von EU AI Act, DSGVO, DORA, NIS 2 und ISO 27001
|
||||
|
||||
> - Hochskalierbare, sichere und konforme KI-Architektur auf Azure mit klarer Trennung der Layer: Data, AI/ML, Application, Security, Operations, CI/CD.
|
||||
> - Integration spezifischer Azure-Dienste wie AKS, Azure Machine Learning, Azure API Management, Azure Sentinel und Azure Defender für Cloud.
|
||||
> - Umfassendes Compliance-Mapping der Azure-Komponenten zu EU AI Act (Hochrisiko-KI), DSGVO (Datenschutz, Datenminimierung), DORA (Resilienz im Finanzsektor), NIS 2 (Cybersicherheit) und ISO 27001 (ISMS).
|
||||
> - Implementierung von Zero Trust, Datenhoheit (EU-Residenz), Auditierbarkeit (Immutable Logs) und Zugriffskontrollen (RBAC, PAM).
|
||||
> - DevSecOps- und Security-by-Design-Ansatz mit automatisierten CI/CD-Pipelines, Vulnerability Scanning und Incident Response.
|
||||
|
||||
---
|
||||
|
||||
## 1. Übersicht der Architektur und Compliance-Mapping
|
||||
|
||||
### Architekturdiagramm
|
||||
|
||||
Die Architektur ist als mehrschichtiges System konzipiert, das die folgenden Layer umfasst:
|
||||
|
||||
- **Data Layer**: Datenhaltung und -verarbeitung mit Azure SQL DB, Azure Cosmos DB, Azure Blob Storage, Azure Vector Database, Azure Redis Cache und Azure Graph Database.
|
||||
- **AI/ML Layer**: KI-Modellentwicklung, Training und Inference mit Azure Machine Learning (AML), Azure Cognitive Services, Azure OpenAI Service und Azure Functions für Agentic AI.
|
||||
- **Application Layer**: Frontend und API-Gateway mit Azure App Service, Azure Load Balancer, Azure API Management und Azure Application Gateway.
|
||||
- **Security Layer**: Schutzmaßnahmen mit Azure Web Application Firewall (WAF), Azure Front Door, Azure Sentinel, Azure Defender for Cloud und Azure Key Vault für Zertifikatsmanagement.
|
||||
- **Operations Layer**: Überwachung, Logging und Incident Response mit Azure Monitor, Azure Log Analytics, Azure Event Hubs und Azure Stream Analytics.
|
||||
- **CI/CD Layer**: Automatisierte Build-, Test- und Deployment-Pipelines mit Azure DevOps, Azure Pipelines und Azure Repos.
|
||||
|
||||
### Compliance-Mapping-Tabelle
|
||||
|
||||
| **Regulatorische Anforderung** | **Azure-Komponente** | **Erfüllung der Anforderung** | **Hinweise** |
|
||||
|------------------------------------|--------------------------------------|----------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
|
||||
| **EU AI Act (Hochrisiko-KI)** | Azure Machine Learning (AML) | Risikoklassifizierung, Dokumentation, Transparenz, Bias-Detection, Explainability | AML Responsible AI Dashboard, Model Catalog, Prompt Flow für Governance und Auditierbarkeit |
|
||||
| **DSGVO (Datenschutz)** | Azure SQL DB, Azure Cosmos DB, Azure Blob Storage | Datenverschlüsselung, Zugriffskontrolle, Datenminimierung, Löschkonzept | Always Encrypted, Immutable Blob Storage, Azure Purview für Datenklassifizierung und -labeling |
|
||||
| **DORA (Resilienz Finanzsektor)** | Azure Event Hubs, Azure Stream Analytics, Azure Monitor | Kontinuierliches Monitoring, Resilienztests, Incident Response, Audit-Trails | Integration mit Azure Sentinel für SIEM und automatisierte Playbooks |
|
||||
| **NIS 2 (Cybersicherheit)** | Azure Sentinel, Azure Defender for Cloud, Azure WAF, Azure Bastion | Risikomanagement, Vorfallsmeldung, Zugriffskontrolle, Netzwerksegmentierung, PAM | Azure Bastion für sichere VM-Zugriffe, Azure WAF für Webanwendungen, Defender für Vulnerability Scanning |
|
||||
| **ISO 27001 (ISMS)** | Azure Policy, Azure RBAC, Azure Key Vault, Azure Monitor | Risikoanalyse, Sicherheitsziele, Dokumentation, interne und externe Audits | Azure Policy für Compliance-Enforcement, Key Vault für Zertifikatsmanagement, Monitor für Logging und Alerting |
|
||||
|
||||
---
|
||||
|
||||
## 2. Detaillierte Komponentenauswahl mit Azure-Diensten
|
||||
|
||||
### a) Kubernetes & Container-Orchestration
|
||||
|
||||
**Zweck**: Skalierbare, isolierte Ausführung von KI-Workloads (Training/Inference), Multi-Tenancy-Fähigkeit, Compliance mit EU AI Act (Transparenz, Dokumentation).
|
||||
|
||||
**Azure-Komponenten**:
|
||||
- **Azure Kubernetes Service (AKS)** mit:
|
||||
- Confidential Computing-Nodes (für sensible Datenverarbeitung, DSGVO-konform).
|
||||
- AKS Workload Identity (statt Service Principals für sichere Pod-Identitäten, IAM-Integration).
|
||||
- Azure Policy + Gatekeeper (Enforcement von Compliance-Richtlinien wie "keine privileged Container").
|
||||
- AKS Node Pools mit Spot Instances (Kosteneffizienz) + dedizierte Node Pools für Hochrisiko-KI (Isolation nach EU AI Act).
|
||||
- Azure Container Registry (ACR) mit Content Trust (Notarization) und Vulnerability Scanning (integriert in CI/CD).
|
||||
|
||||
**Begründung**: AKS bietet automatisiertes Patch-Management (NIS 2), Network Policies (Microsegmentierung) und Azure Monitor für AKS (Logging/Metriken).
|
||||
|
||||
**Compliance-Hinweis**: KI-Modell-Artefakte werden in Containern versioniert und unveränderlich gespeichert (ISO 27001:2022 A.12.3.1) durch Integration mit Azure DevOps und ACR.
|
||||
|
||||
---
|
||||
|
||||
### b) Agentic AI mit LLM-Adapter
|
||||
|
||||
**Zweck**: Orchestrierung autonomer KI-Agenten mit Prompt-Sicherheit, Halluzinationskontrolle und Audit-Trails.
|
||||
|
||||
**Azure-Komponenten**:
|
||||
- **Azure Machine Learning (AML)** für:
|
||||
- Prompt Flow (Versionierung und Governance von Prompts, EU AI Act-Konformität).
|
||||
- Model Catalog (Metadaten-Management für LLM-Adaptionen, inkl. Risikoklassifizierung).
|
||||
- AML Responsible AI Dashboard (Bias-Detection, Explainability für EU AI Act Art. 13).
|
||||
- **Azure OpenAI Service** (mit private Endpoints und Customer-Managed Keys (CMK) für Datenhoheit).
|
||||
- **Azure Functions/Durable Functions** (serverless Agent-Orchestration mit Retries & Dead-Letter Queues für Resilienz).
|
||||
|
||||
**Begründung**: AML bietet built-in Compliance-Templates (z. B. für DSGVO-Löschanfragen via Data Subject Requests).
|
||||
|
||||
**Offene Frage**: Agent-Aktionen werden über Azure Monitor und Azure Log Analytics protokolliert, um DORA-Anforderungen an "Complete Activity Logs" zu erfüllen.
|
||||
|
||||
---
|
||||
|
||||
### c) Security Layer (WAF, Prompt Firewall, AI-Scanning)
|
||||
|
||||
**Zweck**: Schutz vor Prompt Injections, Datenexfiltration und Model Poisoning (NIS 2, ISO 27001 A.14).
|
||||
|
||||
**Azure-Komponenten**:
|
||||
- **Azure Web Application Firewall (WAF)** auf Azure Front Door (OWASP Top 10 Abdeckung, Rate Limiting).
|
||||
- **Prompt Firewall**:
|
||||
- Azure API Management (APIM) mit Custom Policies (Regex/ML-basierte Prompt-Validation).
|
||||
- AML Content Safety (Toxizitätsfilterung + Custom Rules für domänenspezifische Compliance).
|
||||
- **AI Model Scanning**:
|
||||
- AML Model Scanning (Schadcode in Modellen, z. B. via Trivy-Integration in Pipelines).
|
||||
- Azure Defender for Containers (Runtime-Schutz für KI-Worker).
|
||||
- **AI Interface Scanning**:
|
||||
- Microsoft Defender for APIs (in APIM integriert, Detects Anomalies in LLM-Aufrufen).
|
||||
|
||||
**Begründung**: Kombiniert Pre-Deployment Scanning (CI/CD) mit Runtime Protection (Defender).
|
||||
|
||||
**Compliance-Hinweis**: Scanning-Ergebnisse werden für Audits (EU AI Act Art. 72) 10 Jahre in Azure Monitor Log Analytics gespeichert.
|
||||
|
||||
---
|
||||
|
||||
### d) Frontend & API-Gateway
|
||||
|
||||
**Zweck**: Sichere Exposition von KI-Services mit TLS 1.3, mTLS und Rate Limiting.
|
||||
|
||||
**Azure-Komponenten**:
|
||||
- **Frontend**: Azure Static Web Apps (Container-basiert, mit Managed Identity für Backend-Zugriff).
|
||||
- **Load Balancer**: Azure Load Balancer (Layer 4) + Application Gateway (Layer 7, TLS-Terminierung, WAF-Integration).
|
||||
- **API Gateway**: Azure API Management (APIM) mit:
|
||||
- OAuth2/JWT Validation (IAM-Integration).
|
||||
- Policy Expressions für Request/Response Transformation (z. B. PII-Redaktion).
|
||||
- Developer Portal (Dokumentation für EU AI Act-Transparenz).
|
||||
- **LLM Router**: Azure Traffic Manager (Global Routing) + APIM Routing Rules (A/B-Testing, Canary Deployments).
|
||||
|
||||
**Begründung**: APIM bietet Quota Management (DDoS-Schutz) und Usage Analytics (DORA-Reporting).
|
||||
|
||||
---
|
||||
|
||||
### e) IAM & PAM (RBAC, LDAP, Just-in-Time-Zugriff)
|
||||
|
||||
**Zweck**: Least Privilege, Separation of Duties (ISO 27001 A.9.1.2) und Notfallzugriff (DORA).
|
||||
|
||||
**Azure-Komponenten**:
|
||||
- **Azure Active Directory (AAD)** mit:
|
||||
- Privileged Identity Management (PIM) (Just-in-Time Admin-Zugriff).
|
||||
- Conditional Access Policies (MFA, Device Compliance).
|
||||
- Entra Permissions Management (Cloud Infrastructure Entitlement Management, CIEM).
|
||||
- **LDAP**: Azure AD Domain Services (für Legacy-Systeme, read-only Replicas).
|
||||
- **Bastion Host**: Azure Bastion (RDP/SSH ohne öffentliche IPs, Session Recording für Audits).
|
||||
|
||||
**Begründung**: AAD Access Reviews erfüllen DSGVO-Recertification (Art. 32).
|
||||
|
||||
**Offene Frage**: Break-Glass-Zugriff (DORA) wird über PIM mit zeitlich begrenzten Rollen und MFA umgesetzt, um Compliance nicht zu brechen.
|
||||
|
||||
---
|
||||
|
||||
### f) Datenlayer (SQL, NoSQL, Vektordatenbank, Cache, GraphDB, S3)
|
||||
|
||||
**Zweck**: Datenminimierung (DSGVO), Verschlüsselung (NIS 2), Performance für KI.
|
||||
|
||||
**Azure-Komponenten**:
|
||||
- **SQL**: Azure SQL DB mit Always Encrypted (Client-side) + Ledger (unveränderliche Tabellen für Audits).
|
||||
- **NoSQL**: Azure Cosmos DB (Global Distribution, Customer-Managed Keys).
|
||||
- **Vektordatenbank**: Azure AI Search (mit Vector Index) oder Weaviate/Pinecone auf AKS (falls Open-Source erforderlich).
|
||||
- **Object Store**: Azure Blob Storage mit Immutable Blob Storage (DSGVO-Löschsperren).
|
||||
- **Redis Cache**: Azure Cache for Redis (Enterprise Tier mit TLS und AAD-Auth).
|
||||
- **GraphDB**: Azure Cosmos DB Gremlin API oder Neptune auf AKS (für Knowledge Graphs).
|
||||
|
||||
**Begründung**: Azure Purview klassifiziert und labelt Daten automatisch (DSGVO Art. 30).
|
||||
|
||||
**Compliance-Hinweis**: Datenresidenz (EU-only) für Kafka-Topics und Cosmos DB wird durch Azure Policy und Azure Monitor erzwungen.
|
||||
|
||||
---
|
||||
|
||||
### g) Big Data & Echtzeit-Analytics
|
||||
|
||||
**Zweck**: Skalierbare Datenpipelines für Model Training/Monitoring (EU AI Act Art. 10).
|
||||
|
||||
**Azure-Komponenten**:
|
||||
- **Apache Kafka**: Azure Event Hubs (Kafka-kompatibel) + MirrorMaker für Multi-Region.
|
||||
- **Stream Processing**: Azure Stream Analytics (SQL-basierte Echtzeit-Aggregation).
|
||||
- **Batch Processing**: Azure Databricks (mit Delta Lake für ACID-Compliance).
|
||||
- **Data Lake**: Azure Data Lake Storage (ADLS) Gen2 mit Hierarchical Namespace.
|
||||
|
||||
**Begründung**: Event Hubs Geo-DR erfüllt NIS 2-Anforderungen an Ausfallsicherheit.
|
||||
|
||||
---
|
||||
|
||||
### h) Logging, Monitoring & Vulnerability Management
|
||||
|
||||
**Zweck**: Immutable Logs (DORA), Anomalieerkennung (NIS 2), Compliance-Reporting.
|
||||
|
||||
**Azure-Komponenten**:
|
||||
- **Logging**:
|
||||
- Azure Monitor (AKS, APIM, SQL Logs) + Log Analytics Workspace (7 Jahre Retention).
|
||||
- Azure Storage Lifecycle Management (Automatische Archivierung alter Logs).
|
||||
- **Monitoring**:
|
||||
- Grafana auf Azure Managed Grafana (mit Azure Monitor Datenquelle).
|
||||
- Azure Dashboards für SLO/SLI-Tracking (DORA-Metriken).
|
||||
- **Vulnerability Management**:
|
||||
- Microsoft Defender for Cloud (CVE-Scanning, Regulatory Compliance Dashboard).
|
||||
- Azure Security Benchmark (NIS 2-Alignment).
|
||||
- **Splunk**: Azure Native Splunk Integration (für SIEM, falls erforderlich).
|
||||
|
||||
**Begründung**: Defender for Cloud bietet automatisierte Response Playbooks (ISO 27001 A.16.1).
|
||||
|
||||
---
|
||||
|
||||
### i) CI/CD & DevSecOps (Git, Pipelines, IDE, Agent Builds)
|
||||
|
||||
**Zweck**: Compliance-as-Code (ISO 27001 A.14.2.5) und auditierbare Deployments.
|
||||
|
||||
**Azure-Komponenten**:
|
||||
- **Git Repos**: Azure Repos (mit Branch Policies für Code Reviews).
|
||||
- **Pipelines**: Azure Pipelines mit:
|
||||
- Static Application Security Testing (SAST) via Checkmarx oder SonarQube.
|
||||
- Dynamic Scanning in Load Tests (Azure Load Testing).
|
||||
- Terraform: Azure Bicep (native IaC) + Terraform in Azure DevOps (mit Plan Policy Validation).
|
||||
- **IDE**: VS Code + Azure DevOps Extension (mit Roslyn Analyzers für Secure Coding).
|
||||
- **Agent Builds**: Azure VM Scale Sets (ephemere Agenten für Sicherheit).
|
||||
|
||||
**Begründung**: Pipeline Compliance Reports (z. B. "Keine Hardcoded Secrets") für Audits.
|
||||
|
||||
---
|
||||
|
||||
### j) Queues & Message Broker
|
||||
|
||||
**Zweck**: Entkopplung von KI-Workloads (Resilienz nach DORA).
|
||||
|
||||
**Azure-Komponenten**:
|
||||
- **Azure Service Bus** (FIFO, Dead-Letter Queues) oder Azure Queue Storage (für einfache Szenarien).
|
||||
- **Event-Driven Architecture**: Azure Event Grid (für KI-Trigger, z. B. neue Daten in Blob Storage).
|
||||
|
||||
---
|
||||
|
||||
### k) Zertifikatsmanagement (öffentlich/privat)
|
||||
|
||||
**Zweck**: TLS Everywhere (NIS 2) und Code Signing (EU AI Act).
|
||||
|
||||
**Azure-Komponenten**:
|
||||
- **Azure Key Vault** (mit HSM-backed Keys für Code Signing).
|
||||
- **Azure App Service Certificates** (automatische Rotation).
|
||||
- **Private CA**: Azure Private Link + DigiCert/GlobalSign Integration.
|
||||
|
||||
---
|
||||
|
||||
## 3. Cross-Cutting Concerns & Risikomitigation
|
||||
|
||||
### Datenflüsse
|
||||
|
||||
- **Datenflussdiagramm (DFD)**: Markierung von DSGVO-relevanten Verarbeitungen (z. B. PII in LLM-Prompts).
|
||||
- **Incident Response**: Azure Sentinel Playbooks für KI-spezifische Vorfälle (z. B. Model Drift, Data Leakage).
|
||||
- **Cost Optimization**: Azure Cost Management + Billing und Reserved Instances (für AKS/Cosmos DB).
|
||||
- **Vendor Lock-in**: Bewertung von Open-Source-Alternativen (z. B. Kubernetes statt AKS, OpenSearch statt Azure AI Search).
|
||||
|
||||
---
|
||||
|
||||
## 4. Empfohlene Next Steps & Proof-of-Concept
|
||||
|
||||
- **Priorisierte Implementierungsschritte**:
|
||||
1. AKS mit Policy Guardrails,
|
||||
2. APIM + WAF,
|
||||
3. Azure Sentinel Integration,
|
||||
4. Azure DevOps CI/CD Pipeline mit Security Scanning.
|
||||
- **POC-Umfang**: Isoliertes AKS-Cluster mit AML und APIM für einen einzelnen KI-Agenten.
|
||||
- **Stakeholder-Kommunikation**: Compliance-Readiness-Dokument für interne Audits.
|
||||
|
||||
---
|
||||
|
||||
## Anhang
|
||||
|
||||
- **Architekturdiagramm**: Visio/Lucidchart mit Legende für Compliance-Symbole.
|
||||
- **Tabelle**: Mapping von Azure-Diensten zu Compliance-Anforderungen (DSGVO, NIS 2 etc.).
|
||||
- **Terraform/Bicep-Snippets**: Kritische Komponenten (z. B. AKS mit Policy, Key Vault Setup).
|
||||
- **Beispiel-Pipeline**: YAML für sicheres KI-Agent Deployment.
|
||||
|
||||
---
|
||||
|
||||
## Quellen
|
||||
|
||||
- EU AI Act, GDPR, DORA, NIS 2, ISO 27001 Guidelines.
|
||||
- Azure Kubernetes Service (AKS) Dokumentation.
|
||||
- Azure Machine Learning (AML) Dokumentation.
|
||||
- Azure API Management (APIM) Dokumentation.
|
||||
- Azure Sentinel und Defender for Cloud Dokumentation.
|
||||
- Azure DevOps und Azure Pipelines Dokumentation.
|
||||
- Azure Monitor und Log Analytics Dokumentation.
|
||||
- Azure Key Vault und Certificate Services Dokumentation.
|
||||
- Azure Event Grid und Service Bus Dokumentation.
|
||||
|
||||
---
|
||||
|
||||
Diese Architektur stellt einen umfassenden, regulatorisch konformen und technisch robusten Vorschlag für den KI-Betrieb auf Microsoft Azure dar, der die Anforderungen von EU AI Act, DSGVO, DORA, NIS 2 und ISO 27001 adressiert und gleichzeitig Skalierbarkeit, Observability, Security-by-Design und DevSecOps-Prinzipien integriert.
|
||||
@@ -0,0 +1,187 @@
|
||||
Hier ist eine leicht gestraffte und weiter präzisierte Version, die du so verwenden kannst:
|
||||
|
||||
---
|
||||
|
||||
Du bist **Azure Cloud Senior Architect** mit Schwerpunkt **Security, Compliance und KI-Plattformen** in regulierten Umgebungen (Finanz, Kritische Infrastrukturen, Behörden).
|
||||
|
||||
---
|
||||
|
||||
### 1. Auftrag
|
||||
|
||||
Erstelle einen **detaillierten Architekturvorschlag** für den **Betrieb von KI-Workloads auf Microsoft Azure**, der **konform** ist zu:
|
||||
|
||||
* **EU AI Act** (insb. Risikoklassen, Technical & Organizational Measures, Logging, Human Oversight)
|
||||
* **DSGVO (GDPR)**
|
||||
* **DORA**
|
||||
* **NIS 2**
|
||||
* **ISO 27001**
|
||||
|
||||
Die Architektur soll sowohl **sicherheits-** als auch **compliance-orientiert** sein und klar zeigen, wie regulatorische Anforderungen **technisch umgesetzt** werden.
|
||||
|
||||
Treffe, falls nötig, eigene **Annahmen** (z. B. Branche, Datenklassifizierung, Kritikalität) und liste sie zu Beginn explizit auf.
|
||||
|
||||
---
|
||||
|
||||
### 2. Rahmenbedingungen & Zielbild
|
||||
|
||||
Die Lösung basiert **vollständig auf Microsoft Azure** (Azure-native oder Azure-verwaltete Services) und stellt den Betrieb in **EU-Regionen** sicher (z. B. *West Europe* / *North Europe*) zur Erfüllung von **Data Residency**-Anforderungen.
|
||||
|
||||
Folgende technische Komponenten sind zu berücksichtigen und jeweils konkret mit Azure-Services zu hinterlegen:
|
||||
|
||||
#### 2.1 KI- und Compute-Layer
|
||||
|
||||
* Kubernetes-Cluster mit KI-Workern und Container-/Cluster-Management
|
||||
* Agentic AI mit LLM-Adapter (z. B. orchestrierte KI-Agents)
|
||||
* Build, Test und Deploy von AI Agents (MLOps / AIOps)
|
||||
* LLM-Router (Routing zwischen verschiedenen Modellen / Providern, inkl. Azure OpenAI und ggf. Self-Hosted Models)
|
||||
|
||||
#### 2.2 Security & Governance
|
||||
|
||||
* Web Application Firewall (WAF)
|
||||
* Prompt Firewall / Prompt Security
|
||||
* AI-Model Scanning (Model Security, Supply Chain Security)
|
||||
* AI-Interface Scanning (Security von APIs / Endpoints / UIs)
|
||||
* Vulnerability Management
|
||||
* IAM (RBAC, Anbindung an LDAP/Azure Entra ID)
|
||||
* PAM (Privileged Access Management)
|
||||
* Bastion Host
|
||||
* Öffentliche und private Zertifikate inkl. Lifecycle- und Secrets-Management
|
||||
|
||||
#### 2.3 Networking & Ingress
|
||||
|
||||
* Load Balancer mit TLS-Terminierung
|
||||
* API-Gateway
|
||||
* Netzwerksegmentierung, Private Endpoints, ggf. Hub-Spoke-Architektur und Zero-Trust-Prinzipien
|
||||
|
||||
#### 2.4 Daten- & Storage-Layer
|
||||
|
||||
* Datenbank-Layer mit:
|
||||
|
||||
* SQL-Datenbanken
|
||||
* NoSQL-Datenbanken
|
||||
* Object Stores (S3-ähnlich / Blob Storage)
|
||||
* Vektor-Datenbank
|
||||
* Redis Cache
|
||||
* GraphDB
|
||||
* Big Data / Data Lake / Data Warehouse
|
||||
* Real-Time Analytics
|
||||
* Apache Kafka Router (Event Streaming)
|
||||
* Queues / Message Queues (MQ)
|
||||
|
||||
#### 2.5 Frontend & Applikations-Layer
|
||||
|
||||
* Frontend als Container-Deployment
|
||||
* Git-Repositories, Runner, CI/CD-Pipelines und IDE-Integration
|
||||
|
||||
#### 2.6 Observability & Operations
|
||||
|
||||
* Logging (zentrale, revisionssichere Protokollierung)
|
||||
* Monitoring
|
||||
* Metriken (z. B. Visualisierung über Grafana)
|
||||
* SIEM/SOC-Integration (z. B. Splunk, Azure-native Alternativen)
|
||||
|
||||
#### 2.7 Infrastructure as Code
|
||||
|
||||
* Deployment mit Terraform (inkl. State-Handling, Workspaces, Pipelines)
|
||||
|
||||
---
|
||||
|
||||
### 3. Konkrete Anforderungen an deine Antwort
|
||||
|
||||
#### 3.1 Azure-Service-Mapping (sehr detailliert)
|
||||
|
||||
* Nenne **für jede** der oben genannten Komponenten die **konkreten Azure-Services**, z. B.:
|
||||
|
||||
* „Azure Kubernetes Service (AKS) für …“
|
||||
* „Azure API Management für …“
|
||||
* „Azure Front Door / Azure Application Gateway mit WAF für …“
|
||||
* „Azure Key Vault für Zertifikats- und Secrets-Management“
|
||||
* „Azure Monitor / Log Analytics / Application Insights für …“
|
||||
* usw.
|
||||
* Erkläre **kurz den Zweck** jedes Services in dieser Architektur und **warum** er für den jeweiligen Baustein geeignet ist.
|
||||
* Wenn es **mehrere Optionen** gibt (z. B. Front Door vs. Application Gateway), begründe die **präferierte Wahl**.
|
||||
|
||||
Nutze nach Möglichkeit eine **Tabelle „Azure-Service vs. Zweck“**.
|
||||
|
||||
---
|
||||
|
||||
#### 3.2 Compliance-Mapping
|
||||
|
||||
Erstelle eine **Tabelle**, die zeigt, welche Architekturbausteine (Services / Patterns) **welche Anforderungen** aus
|
||||
|
||||
* EU AI Act
|
||||
* DSGVO
|
||||
* DORA
|
||||
* NIS 2
|
||||
* ISO 27001
|
||||
|
||||
adressieren, z. B.:
|
||||
|
||||
* Logging & Audit
|
||||
* Data Residency & Data Protection
|
||||
* Verschlüsselung at rest / in transit
|
||||
* Zugriffskontrolle (RBAC, PAM, Least Privilege)
|
||||
* Risikomanagement & Schwachstellenmanagement
|
||||
* Monitoring, Incident Detection & Incident Response
|
||||
* Business Continuity / Resilienz / Verfügbarkeit
|
||||
|
||||
---
|
||||
|
||||
#### 3.3 Architekturübersicht
|
||||
|
||||
* Beschreibe zunächst eine **High-Level-Architektur** (Schichtenmodell), z. B.:
|
||||
**User → Edge/WAF → API Gateway → LLM Router / AI Agents (auf AKS) → Data Layer → Observability / Security Layer**
|
||||
* Ergänze ein **textuelles Architekturdiagramm** (z. B. in **Mermaid** oder alternativ als klar strukturierte hierarchische Liste), das die wichtigsten Komponenten und ihre Beziehungen zeigt.
|
||||
|
||||
---
|
||||
|
||||
#### 3.4 Security- & Data-Protection-Aspekte
|
||||
|
||||
Gehe explizit auf **Datenflüsse** ein (Eingabe-Prompts, Kontextdaten, Model-Outputs, Logs) und erkläre, wie:
|
||||
|
||||
* Daten **verschlüsselt** werden (at rest / in transit) inkl. Nutzung von Managed Identities, TLS und Key Vault
|
||||
* **Zugriffe gesteuert** werden (RBAC, Azure Entra ID, PAM, Just-in-Time Access / JIT-Access)
|
||||
* **Protokollierung, Auditability und Nachvollziehbarkeit** umgesetzt werden (z. B. Audit-Logs, Access-Logs, Model-Usage-Logs)
|
||||
* **Data Residency** (EU-Regionen, ggf. strikte Datenlokation) sichergestellt wird
|
||||
* **Modell- und Prompt-Risiken** (Prompt Injection, Data Leakage, Model Abuse) mitigiert werden (z. B. Prompt Firewall, Content Filter, Policy Enforcement)
|
||||
|
||||
---
|
||||
|
||||
#### 3.5 Betrieb & Lifecycle
|
||||
|
||||
Erläutere, wie:
|
||||
|
||||
* neue KI-Modelle / Agents **versioniert, getestet und ausgerollt** werden
|
||||
|
||||
* CI/CD-Pipelines (z. B. GitHub Actions / Azure DevOps)
|
||||
* Blue-Green- / Canary-Deployment auf AKS
|
||||
* Rollback-Strategien
|
||||
* **Monitoring, Alerting und Incident Response** organisiert sind
|
||||
|
||||
* Metriken, Logs, Traces
|
||||
* Alerts (z. B. via Azure Monitor)
|
||||
* Integration in SOC / SIEM (Splunk oder Microsoft Sentinel)
|
||||
* **Schwachstellenmanagement** (Vulnerability Management) in Container-Images, Bibliotheken und Modellen integriert wird
|
||||
|
||||
* Image-Scanning, Dependency-Scanning
|
||||
* regelmäßige Security-Updates / Patch-Mechanismen
|
||||
|
||||
---
|
||||
|
||||
### 4. Ausgabeformat
|
||||
|
||||
Strukturiere deine Antwort mit folgenden Hauptüberschriften:
|
||||
|
||||
1. Überblick & Annahmen
|
||||
2. Zielarchitektur (High Level)
|
||||
3. Azure-Service-Mapping pro Baustein
|
||||
4. Compliance-Mapping (Tabelle)
|
||||
5. Security & Data Protection
|
||||
6. Betriebs- & Lifecycle-Konzept
|
||||
|
||||
Verwende mindestens diese **Tabellen**:
|
||||
|
||||
* **Tabelle 1:** Azure-Komponenten vs. Zweck / Layer
|
||||
* **Tabelle 2:** Regulatorische Anforderung vs. Architekturbaustein (Service / Pattern)
|
||||
|
||||
Antworte auf **Deutsch**, **präzise und technisch**, mit **konkreten Azure-Service-Namen** und **technischen Details** statt allgemeiner Beschreibungen.
|
||||
@@ -0,0 +1,85 @@
|
||||
# Rolle
|
||||
Du bist ein Azure Cloud Senior Architekt mit Expertise in regulatorischer Compliance (EU AI Act, DSGVO, DORA, NIS 2) und Informationssicherheit (ISO 27001).
|
||||
|
||||
# Aufgabe
|
||||
Erstelle einen detaillierten Architekturvorschlag für eine KI-Betriebsplattform auf Microsoft Azure, die alle genannten regulatorischen Anforderungen erfüllt.
|
||||
|
||||
# Anforderungen an das Ergebnis
|
||||
|
||||
## 1. Architektur-Dokumentation
|
||||
Für jede der folgenden Komponenten benötige ich:
|
||||
- Die empfohlene Azure-Service(s) mit vollständigem Produktnamen
|
||||
- Begründung der Auswahl
|
||||
- Relevante Compliance-Aspekte (welche Regularien werden adressiert)
|
||||
- Konfigurationsempfehlungen für den regulatorischen Kontext
|
||||
|
||||
## 2. Zu berücksichtigende Komponenten
|
||||
|
||||
### Compute & Container
|
||||
- Kubernetes-Cluster für KI-Worker
|
||||
- Container- und Cluster-Management
|
||||
- Frontend-Container
|
||||
- Build-, Test- und Deploy-Umgebung für AI Agents
|
||||
|
||||
### KI-spezifische Komponenten
|
||||
- Agentic AI mit LLM-Adapter
|
||||
- LLM Router
|
||||
- Prompt Firewall
|
||||
- AI Model Scanning
|
||||
- AI Interface Scanning
|
||||
|
||||
### Netzwerk & Security
|
||||
- Web Application Firewall (WAF)
|
||||
- Load Balancer mit TLS-Terminierung
|
||||
- API Gateway
|
||||
- Bastion Host
|
||||
- Öffentliche und private Zertifikate inkl. Management
|
||||
|
||||
### Identity & Access
|
||||
- IAM (RBAC, LDAP-Integration)
|
||||
- Privileged Access Management (PAM)
|
||||
|
||||
### Daten-Layer
|
||||
- SQL-Datenbank
|
||||
- NoSQL-Datenbank
|
||||
- Object Storage (S3-kompatibel)
|
||||
- Vektordatenbank
|
||||
- Redis Cache
|
||||
- Graph-Datenbank
|
||||
- Big Data Platform
|
||||
|
||||
### Messaging & Integration
|
||||
- Apache Kafka / Event Streaming
|
||||
- Message Queues (MQ)
|
||||
|
||||
### DevOps & CI/CD
|
||||
- Git Repositories
|
||||
- Runner und Pipelines
|
||||
- IDE-Integration
|
||||
- Terraform (Infrastructure as Code)
|
||||
|
||||
### Observability & Security Operations
|
||||
- Logging (inkl. SIEM/Splunk-Integration)
|
||||
- Monitoring
|
||||
- Real-Time Analytics
|
||||
- Metriken-Dashboards (Grafana-kompatibel)
|
||||
- Vulnerability Management
|
||||
|
||||
## 3. Compliance-Mapping
|
||||
Erstelle eine Matrix, die zeigt, welche Architekturkomponente welche Anforderung der folgenden Regularien adressiert:
|
||||
- EU AI Act (insb. Hochrisiko-KI-Systeme)
|
||||
- DSGVO (Datenschutz, Datenverarbeitung, Betroffenenrechte)
|
||||
- DORA (Operative Resilienz, ICT-Risikomanagement)
|
||||
- NIS 2 (Cybersicherheit kritischer Infrastrukturen)
|
||||
- ISO 27001 (ISMS-Kontrollen)
|
||||
|
||||
## 4. Ausgabeformat
|
||||
- Strukturierte Darstellung nach logischen Architektur-Ebenen (z.B. Netzwerk, Compute, Data, Security, DevOps)
|
||||
- Tabellarische Übersicht: Komponente | Azure Service | Compliance-Relevanz
|
||||
- Architekturdiagramm-Beschreibung (textuell), die als Grundlage für ein Visio/Draw.io-Diagramm dienen kann
|
||||
|
||||
## 5. Zusätzliche Hinweise
|
||||
- Bevorzuge Azure-native Services gegenüber Third-Party-Lösungen, sofern funktional gleichwertig
|
||||
- Berücksichtige Azure Landing Zones und das Cloud Adoption Framework
|
||||
- Beachte Data Residency (EU-Region) für alle personenbezogenen Daten
|
||||
- Schlage eine sinnvolle Netzwerksegmentierung vor (Hub-Spoke, Private Endpoints)
|
||||
@@ -0,0 +1,102 @@
|
||||
flowchart LR
|
||||
subgraph Clients
|
||||
U[User / Apps]
|
||||
end
|
||||
|
||||
subgraph Edge
|
||||
AFD[Azure Front Door + WAF]
|
||||
AGW[Application Gateway WAF + TLS]
|
||||
AFW[Azure Firewall]
|
||||
end
|
||||
|
||||
subgraph API
|
||||
APIM[Azure API Management\n(API Gateway + Policies)]
|
||||
PF[Prompt Firewall\n(Content Safety + APIM Policies)]
|
||||
end
|
||||
|
||||
subgraph AI_Compute[AI & Compute Layer (AKS)]
|
||||
AKS[AKS Cluster\nGPU/CPU Node Pools]
|
||||
LLMR[LLM Router Service]
|
||||
AGENTS[Agentic AI Orchestrator\n(AI Agents)]
|
||||
FE[Frontend Container]
|
||||
end
|
||||
|
||||
subgraph AI_Services
|
||||
AOAI[Azure OpenAI / Azure AI Foundry\n(EU Data Residency)]
|
||||
MODELS[Self-hosted Models on AKS]
|
||||
end
|
||||
|
||||
subgraph Data[Data & Storage Layer]
|
||||
SQL[Azure SQL DB / MI]
|
||||
COSMOS[Azure Cosmos DB\n(NoSQL + Graph)]
|
||||
BLOB[Azure Data Lake / Blob Storage]
|
||||
VECTOR[Azure AI Search\n(Vektorindex)]
|
||||
REDIS[Azure Cache for Redis]
|
||||
KUSTO[Azure Data Explorer / Synapse]
|
||||
end
|
||||
|
||||
subgraph Events
|
||||
EH[Azure Event Hubs (Kafka Endpoint)]
|
||||
SB[Azure Service Bus (Queues/Topics)]
|
||||
SA[Azure Stream Analytics]
|
||||
end
|
||||
|
||||
subgraph Observability[Observability & Security]
|
||||
AM[Azure Monitor + Log Analytics]
|
||||
APPINS[Application Insights]
|
||||
GRAF[Azure Managed Grafana]
|
||||
SENT[Microsoft Sentinel]
|
||||
SPL[SPLUNK (VM/AKS)]
|
||||
DEF[Microsoft Defender for Cloud]
|
||||
end
|
||||
|
||||
subgraph Identity_Sec[Identity & Access]
|
||||
ENTRA[Azure Entra ID\n(RBAC, Conditional Access)]
|
||||
PIM[Entra Privileged Identity Mgmt]
|
||||
KV[Azure Key Vault + Managed HSM]
|
||||
BAST[Azure Bastion]
|
||||
end
|
||||
|
||||
subgraph DevOps[DevOps / MLOps / IaC]
|
||||
GIT[GitHub / Azure DevOps Repos]
|
||||
PIPE[CI/CD Pipelines\n(GitHub Actions / Azure Pipelines)]
|
||||
TF[Terraform\n(State in Storage)]
|
||||
end
|
||||
|
||||
U --> AFD --> AGW --> APIM --> PF --> LLMR --> AGENTS
|
||||
AGENTS --> AOAI
|
||||
AGENTS --> MODELS
|
||||
LLMR --> AOAI
|
||||
LLMR --> MODELS
|
||||
|
||||
AGENTS --> SQL
|
||||
AGENTS --> COSMOS
|
||||
AGENTS --> BLOB
|
||||
AGENTS --> VECTOR
|
||||
AGENTS --> REDIS
|
||||
|
||||
AGENTS --> EH
|
||||
EH --> SA --> KUSTO
|
||||
AGENTS --> SB
|
||||
|
||||
AKS --> AM
|
||||
AKS --> APPINS
|
||||
AM --> GRAF
|
||||
AM --> SENT
|
||||
AM --> SPL
|
||||
DEF --> SENT
|
||||
|
||||
ENTRA --> APIM
|
||||
ENTRA --> AKS
|
||||
ENTRA --> GIT
|
||||
PIM --> ENTRA
|
||||
KV --> AGW
|
||||
KV --> APIM
|
||||
KV --> AKS
|
||||
BAST --> AKS
|
||||
|
||||
GIT --> PIPE --> AKS
|
||||
PIPE --> TF
|
||||
TF --> AKS
|
||||
TF --> Data
|
||||
TF --> Networking((VNet/NSG/PE))
|
||||
@@ -0,0 +1,206 @@
|
||||
# Azure AI Architektur – EU‑konform, ISO 27001 & DORA bereit
|
||||
|
||||
## 1. Hoch‑Level Architekturdiagramm (Mermaid)
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
%% Externer Verkehr
|
||||
Internet([Internet]) --> WAF[WAF / Prompt‑Firewall]
|
||||
WAF --> LB[TLS‑Terminierende Lastenausgleichung]
|
||||
|
||||
%% API-Schicht
|
||||
LB --> APIGW[Azure API Management (LLM‑Router)]
|
||||
APIGW --> AKS[Kubernetes Cluster]
|
||||
|
||||
%% KI‑Kernservices
|
||||
AKS -->|LLM Adapter| AOAI[Azure OpenAI]
|
||||
AKS --> AIEngine[Agentic KI‑Engine]
|
||||
AIEngine -->|Prompt‑Routing| PromptFirewall
|
||||
AIEngine -->|Modellsscanning| SecScanner[KI‑Modell & Schnittstellen‑Scanner]
|
||||
|
||||
%% Datenschicht
|
||||
subgraph DB[Datenlager]
|
||||
SQL[Azure SQL Database]:::db
|
||||
NoSQL[Cosmos DB (NoSQL)]:::db
|
||||
Blob[Azure Blob Storage]:::db
|
||||
VectDB[Qdrant in AKS (Vektor‑DB)]:::db
|
||||
Redis[Azure Cache for Redis]:::db
|
||||
GraphDB[Azure Cosmos DB (Gremlin API)]:::db
|
||||
end
|
||||
|
||||
%% Big‑Data Pipeline
|
||||
subgraph BD[Big‑Data & Streaming]
|
||||
Synapse[Azure Synapse Analytics]:::pipeline
|
||||
Databricks[Azure Databricks]:::pipeline
|
||||
Kafka[Apache Kafka (AKS)]:::pipeline
|
||||
EH[Event Hubs]:::pipeline
|
||||
end
|
||||
|
||||
%% Beobachtbarkeit & Sicherheit
|
||||
subgraph OBS[Beobachtbarkeit]
|
||||
Monitor[Azure Monitor]:::obs
|
||||
AppInsights[Application Insights]:::obs
|
||||
Grafana[Grafana auf AKS]:::obs
|
||||
end
|
||||
|
||||
subgraph SEC[Sicherheit & Compliance]
|
||||
Defender[Defender for Cloud]:::sec
|
||||
SecCenter[Azure Security Center]:::sec
|
||||
Bastion[Bastion Host]:::sec
|
||||
KeyVault[Key Vault]:::sec
|
||||
end
|
||||
|
||||
%% Orchestrierung & CI/CD
|
||||
subgraph DEV[DevOps]
|
||||
DevOps[Azure DevOps Pipelines]:::dev
|
||||
Terraform[Terraform (IaC)]:::dev
|
||||
end
|
||||
|
||||
%% IAM & PAM
|
||||
subgraph AUTH[Identität]
|
||||
AD[AAD]:::auth
|
||||
PIM[Azure PIM]:::auth
|
||||
end
|
||||
|
||||
%% Queue & Messaging
|
||||
subgraph MQ[Messaging]
|
||||
SB[Service Bus]:::mq
|
||||
EH --> SB
|
||||
end
|
||||
|
||||
%% Verbindungen
|
||||
APIGW -->|Auth via AAD| AUTH
|
||||
AKS -->|Secrets from KV| KeyVault
|
||||
SecScanner -->|Logs to Monitor| Monitor
|
||||
AIEngine -->|Telemetry| AppInsights
|
||||
DevOps -->|Terraform| AKS
|
||||
DEV -->|Deploy to AKS| AKS
|
||||
DB -->|Access via Managed Identities| AKS
|
||||
BD -->|Ingress to AKS| AIEngine
|
||||
OBS -->|Logs & Metrics| Monitor
|
||||
SEC -->|Policy Enforcement| Defender
|
||||
```
|
||||
```css
|
||||
/* Mermaid diagram styles */
|
||||
%% {font-family: monospace;}
|
||||
```
|
||||
|
||||
::: note
|
||||
Das Diagramm ist bewusst vereinfacht – die reale Bereitstellung enthält zusätzliche Schichten wie interne VNET‑Peering, Azure Front Door für globale Routing und ggf. einen Azure AD Application Proxy.
|
||||
:::
|
||||
|
||||
## 2. Schichtbeschreibungen
|
||||
|
||||
| Schicht | Zweck | Kern‑Azure‑Dienste |
|
||||
|---------|-------|---------------------|
|
||||
| **Frontend** | TLS‑Terminierung, Ratenbegrenzung und WAF‑Schutz für gesamten eingehenden Verkehr. | Azure Front Door (oder Application Gateway mit WAF), TLS‑Terminierung via Azure Key Vault Zertifikate |
|
||||
| **API‑Schicht** | Öffentlicher API‑Gateway, Routing zu LLM‑Router und internen Diensten. | Azure API Management (APIM), OpenAPI Definitionen, JWT‑Validierung via AAD |
|
||||
| **KI‑Kern** | Ausführung von agentischen KI‑Workloads und LLM‑Inference. | Azure Kubernetes Service (AKS), Azure OpenAI, kundenspezifische Container‑Images für Agenten |
|
||||
| **Sicherheit & Scanning** | Kontinuierliches Scannen von Modellen und Schnittstellen, Schutz vor bösartigen Prompts. | Azure Defender for Container Apps (oder AKS), benutzerdefinierte Prometheus Exporter, Azure Sentinel Logs |
|
||||
| **Daten‑Schicht** | Persistente Speicherung für strukturierte und unstrukturierte Daten. | Azure SQL Database, Cosmos DB (SQL & Gremlin APIs), Blob Storage, Qdrant (Vektor‑DB) in AKS, Redis Cache |
|
||||
| **Big Data & Streaming** | ETL‑Pipelines und Echtzeit‑Analytics. | Azure Synapse Analytics, Azure Databricks, Apache Kafka auf AKS, Event Hubs |
|
||||
| **Beobachtbarkeit** | Metriken, Logs und Dashboards. | Azure Monitor, Application Insights, Grafana auf AKS |
|
||||
| **Compliance & Sicherheit** | Richtlinien‑Durchsetzung, Schwachstellenmanagement. | Azure Defender for Cloud, Azure Security Center, Bastion Host, Key Vault |
|
||||
| **Identität & Zugriff** | Rollenbasierter Zugriff und privilegiertes Identitäts‑Management. | Azure AD, Azure AD Connect (LDAP‑Sync), Azure PIM |
|
||||
| **DevOps** | CI/CD, IaC und automatisierte Tests. | Azure DevOps Pipelines, Terraform, GitHub Actions (falls verwendet) |
|
||||
| **Messaging** | Entkoppelter Austausch zwischen Diensten. | Azure Service Bus, Event Hubs |
|
||||
|
||||
## 3. Komponenten‑Mapping & Zweck (Anforderung‑nach‑Anforderung)
|
||||
|
||||
| Anforderung | Azure‑Dienst | Warum er die Anforderung erfüllt |
|
||||
|--------------|---------------|------------------------------------|
|
||||
| **AKS mit KI‑Workern** | AKS | Managed Kubernetes, Auto‑Scaling, Integration mit Azure AD für RBAC |
|
||||
| **Agentic KI + LLM Adapter** | Azure OpenAI + kundenspezifischer Container in AKS | Native LLM‑Unterstützung, keine externe PaaS |
|
||||
| **WAF & Prompt‑Firewall** | Azure Front Door (WAF) + benutzerdefinierte ASP.NET Core Middleware in AKS | Layer‑7‑Filterung, Prompt‑Validierung |
|
||||
| **KI‑Modell & Schnittstellen Scanning** | Azure Defender for Cloud (Container) + benutzerdefinierter Scanner‑Service | Kontinuierliche Schwachstellenbewertung |
|
||||
| **Frontend + Load Balancer** | Azure Application Gateway (WAF) oder Front Door + TLS‑Terminierung via Key Vault |
|
||||
| **LLM‑Router & API Gateway** | Azure APIM + benutzerdefinierte Routing‑Logik in AKS | Richtlinienbasierter Routing, Throttling |
|
||||
| **IAM (RBAC, LDAP via AAD Connect)** | Azure AD + Azure AD Connect | Einheitliche Identität, rollenbasierter Zugriff |
|
||||
| **PAM (Azure PIM)** | Azure Privileged Identity Management | Just‑in‑Time Zugriff, MFA |
|
||||
| **SQL / NoSQL / Blob / Vektor‑DB / Redis / Graph** | Azure SQL, Cosmos DB (Core & Gremlin), Blob Storage, Qdrant in AKS, Azure Cache for Redis | Native Speicher‑Dienste, S3‑Kompatibilität via Blob API |
|
||||
| **Big‑Data Pipeline** | Synapse, Databricks, Kafka (AKS), Event Hubs | Unified Analytics Plattform |
|
||||
| **Logging & Monitoring** | Azure Monitor, Application Insights, Grafana | Metriken, Logs, Dashboards |
|
||||
| **Vulnerability Management** | Defender for Cloud + Azure Security Center | Kontinuierliche Bewertung, Compliance‑Scans |
|
||||
| **Queue‑Services** | Service Bus / Event Hubs | Zuverlässige Messaging |
|
||||
| **Build/Test/Deploy KI‑Agenten** | Azure DevOps Pipelines, Terraform | CI/CD + IaC |
|
||||
| **Bastion Host** | Azure Bastion | Sicherer RDP/SSH Zugriff |
|
||||
| **Splunk SIEM** | Optional: Splunk Enterprise auf AKS oder Azure Sentinel (SIEM) |
|
||||
| **Public/private Zertifikate** | Key Vault + Azure Front Door | Managed Zertifikate |
|
||||
|
||||
## 4. Konfigurationshinweise
|
||||
|
||||
### 4.1 Azure Policy & Blueprints
|
||||
| Richtlinie | Ziel | Durchsetzung |
|
||||
|------------|------|--------------|
|
||||
| **Erlaubte Standorte** | Alle Ressourcen | Verhindert Deployments außerhalb von EU‑Regionen |
|
||||
| **Tagging‑Anforderung** | Alle Ressourcen | Erfordert `Owner`, `Environment`, `Compliance` Tags |
|
||||
| **Erlaubte SKUs** | AKS, Azure SQL, Cosmos DB | Beschränkt auf EU‑Datencenter SKUs |
|
||||
| **Datenresidenz** | Storage Accounts | Erzwingt `DataLocation=EU` |
|
||||
| **Key Vault‑Secret‑Schutz** | Key Vault | Erzwingt `SoftDelete`, `PurgeProtection` |
|
||||
|
||||
Verwende **Azure Blueprints**, um die Basis (VNET, NSG, Key Vault, AAD Connect) vor‑bereit zu stellen.
|
||||
|
||||
### 4.2 RBAC & Managed Identities
|
||||
- **AKS‑Cluster**: Aktiviere `Azure AD Integration` und weise die Rolle *Azure Kubernetes Service RBAC* AAD‑Gruppen zu.
|
||||
- **Service Principals**: Nutze *Azure AD App‑Registrierungen* für API Management und DevOps Pipelines. Erteile minimalen Rollenumfang (z.B. `Reader`, `Contributor` auf spezifische Resource Groups).
|
||||
- **Managed Identities**: Aktiviere sie für AKS‑Nodes, Databricks‑Workspaces und Synapse‑Pipelines, um Key Vault‑Secrets, Storage Accounts und Datenbanken zu nutzen.
|
||||
- **Privileged Identity Management**: Aktiviere JIT für *Owner* Rollen auf Produktions‑Resource Groups.
|
||||
|
||||
### 4.3 Key Vault Secrets & Zertifikate
|
||||
- Speichere alle Verbindungszeichenketten, API‑Keys (Azure OpenAI) und TLS‑Zertifikate im **Key Vault**.
|
||||
- Aktiviere *SoftDelete* + *PurgeProtection*.
|
||||
- Verwende **Key Vault‑References** in ARM/Terraform (z.B. `@Microsoft.KeyVault(????)`) zur Laufzeit‑Injektion.
|
||||
- Für TLS‑Terminierung importiere Zertifikate in Key Vault und referenziere sie im Front Door oder Application Gateway.
|
||||
|
||||
### 4.4 Netzwerke & Isolation
|
||||
- Stelle alle Ressourcen in ein **VNet** mit Subnetzen: `api`, `k8s`, `storage`, `data‑lake`, `monitor`.
|
||||
- Nutze **Network Security Groups** (NSGs) und **Azure Firewall**, um Egress/Ingress zu beschränken. Erlaube nur benötigte Ports (80, 443, 6443 für AKS etc.).
|
||||
- Aktiviere **Private Endpoints** für alle Storage Accounts und Datenbanken, um Traffic ins öffentliche Internet zu vermeiden.
|
||||
- Verwende **Service Tags** (z.B. `AzureCloud`, `AzureKeyVault`) in NSGs.
|
||||
|
||||
### 4.5 Logging & Retention
|
||||
- **Azure Monitor**: Aktiviere Diagnose‑Logs für alle Ressourcen. Speichere Logs in einem dedizierten Log Analytics Workspace mit Retention von 90 Tagen (oder länger, falls EU‑AI Act erfordert).
|
||||
- **Application Insights**: Aktiviere Telemetrie für API Gateway und AKS‑Workloads. Nutze benutzerdefinierte Dimensionen, um Anfragen nach `model_id` zu taggen.
|
||||
- **Azure Sentinel**: Ingestiere Logs von Key Vault, Defender, APIM und AKS. Erstelle Playbooks für automatisierte Incident‑Response.
|
||||
- **Splunk**: Falls nötig, betreibe Splunk Enterprise auf separatem AKS‑Cluster und ingestiere Daten über Splunk Connect for Microsoft Azure.
|
||||
|
||||
## 5. Compliance‑Check‑Plan
|
||||
|
||||
| Aspekt | Check | Tool / Artefakt |
|
||||
|--------|-------|-----------------|
|
||||
| **EU‑AI Act** | Modell‑Governance, Audit‑Trail, Bias‑Testing | Azure Machine Learning Model Registry, benutzerdefinierte Logging in APIM |
|
||||
| **DSGVO** | Datenminimierung, Consent‑Management | Azure Key Vault für Verschlüsselungsschlüssel, Azure AD Consent Framework |
|
||||
| **DORA** | Release‑Frequency, Deployment‑Automation | Azure DevOps Pipelines Metriken, Git Commit History |
|
||||
| **NIS 2** | Incident‑Response, Supply‑Chain‑Security | Azure Sentinel Playbooks, Defender for Cloud Alerts |
|
||||
| **ISO 27001** | Informationssicherheits‑Management | ISO‑27001 Audit‑Templates, Azure Policy Compliance Dashboard |
|
||||
|
||||
### 5.1 Periodische Audits
|
||||
- **Quartalsweise**: Führe Azure Policy Compliance Scans durch, überprüfe IAM‑Rollenvergabe.
|
||||
- **Halbjährlich**: Führe Penetration‑Tests am API Gateway und AKS Cluster durch, verifiziere Key Vault Secret Rotation.
|
||||
- **Jährlich**: Führe vollständigen ISO 27001 Audit durch, EU‑AI Act Modell‑Compliance Review.
|
||||
|
||||
### 5.2 Monitoring & Alerting
|
||||
- **Security Alerts**: Defender for Cloud + Azure Sentinel erzeugen Alarme bei anomalen Logins, Schwachstellen‑Ergebnissen.
|
||||
- **Compliance Alerts**: Policy‑Verletzungen lösen Alarme in Azure Monitor aus und senden Benachrichtigungen an das Compliance‑Team.
|
||||
- **Operational Alerts**: APIM Throttling, AKS Node‑Fehler, Datenbankverbindungsfehler.
|
||||
|
||||
## 6. Bereitstellungs‑Blueprint (IaC)
|
||||
- **Terraform‑Module** für VNet, Subnetze, NSGs, Key Vault, AKS, APIM, Cosmos DB.
|
||||
- **ARM‑Templates** für Azure AD Connect und Key Vault‑References.
|
||||
- **Azure DevOps Pipelines**: getrennte Pipelines für Infrastruktur (IaC) und Anwendung (Container‑Build, Push zu ACR, Deploy zu AKS).
|
||||
- **CI Secrets**: gespeichert im Key Vault und über Azure DevOps Service‑Connections abgerufen.
|
||||
|
||||
## 7. Nächste Schritte
|
||||
1. Erstelle das **Azure Blueprint** für Basis‑Compliance.
|
||||
2. Deploye **VNet** und **NSGs** mit den oben genannten Regeln.
|
||||
3. Erstelle **Key Vault**, importiere Zertifikate, erstelle Secrets für OpenAI‑Keys.
|
||||
4. Deploye **AKS** mit Azure AD Integration und Managed Identities.
|
||||
5. Richte **Azure APIM** ein, konfiguriere Policies (Rate‑Limit, JWT‑Validierung).
|
||||
6. Deploye **KI‑Engine** Container‑Images zu AKS.
|
||||
7. Konfiguriere **Azure Sentinel** mit Log‑Connectors.
|
||||
8. Führe Compliance‑Scan-Suite aus und behebe eventuelle Lücken.
|
||||
|
||||
---
|
||||
|
||||
> *Alle Dienste werden in EU‑Regionen (z. B. West Europe, North Europe) gehostet, um Daten‑Residency‑Anforderungen zu erfüllen.*
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# Verbesserter Prompt (Deutsch)
|
||||
|
||||
Du bist ein Azure‑Cloud‑Seniorarchitekt. Erstelle einen detaillierten Architekturvorschlag für einen KI‑Betrieb, der vollständig konform mit **EU AI Act, DSGVO, DORA, NIS 2 und ISO 27001** ist. Nutze ausschließlich Azure‑Native‑Services (keine Drittanbieter‑PaaS). Berücksichtige die folgenden Komponenten:
|
||||
|
||||
- Kubernetes‑Cluster (AKS) mit KI‑Workern und Cluster‑Management
|
||||
- Agentic AI inkl. LLM‑Adapter (Azure OpenAI)
|
||||
- Web Application Firewall (WAF) & Prompt‑Firewall
|
||||
- AI‑Model‑ und AI‑Interface‑Scanning (Sicherheit)
|
||||
- Frontend + Load Balancer mit TLS‑Termination
|
||||
- LLM‑Router & API Gateway (Azure API Management)
|
||||
- IAM (RBAC, LDAP via Azure AD Connect), PAM (Azure PIM)
|
||||
- Daten‑Layer: SQL, NoSQL, Blob‑Storage (S3‑Kompatibel), Vektor‑DB, Redis Cache, Graph‑DB
|
||||
- Big‑Data‑Pipeline (Synapse / Databricks), Apache Kafka Router, Event Hubs
|
||||
- Logging, Monitoring (Azure Monitor, Application Insights), Grafana Dashboards
|
||||
- Vulnerability Management (Defender for Cloud, Azure Security Center)
|
||||
- Queue‑Services (Service Bus / Event Hubs)
|
||||
- Build/Test/Deploy von AI‑Agents (Azure DevOps Pipelines, Terraform)
|
||||
- Bastion Host, Splunk SIEM, öffentliche/private Zertifikate (Key Vault)
|
||||
|
||||
## Aufgabe:
|
||||
1. Erstelle ein hoch‑level Architekturdiagramm (als Text‑Code) und beschreibe jede Schicht.
|
||||
2. Liste für jede Anforderung die passende Azure‑Komponente und deren Zweck auf.
|
||||
3. Gib konkrete Konfigurations‑Hinweise (z. B. Azure Policy, RBAC‑Rollen, Key‑Vault‑Secrets).
|
||||
4. Skizziere einen Compliance‑Check‑Plan (Regeln, Audits, Monitoring).
|
||||
5. Speichere das Ergebnis als Markdown‑Datei `azure_ai_architecture.md`.
|
||||
|
||||
## Ergebnisformat:
|
||||
- Markdown‑Datei mit Tabellen, Code‑Blöcken und erklärenden Absätzen.
|
||||
- Keine zusätzlichen Kommentare oder System‑Nachrichten.
|
||||
|
||||
## Beispiel-Ausgabe:
|
||||
(Siehe vorherige Antwort)
|
||||
|
||||
**Hinweis:** Verwende ausschließlich Azure‑Services; keine Drittanbieter‑PaaS.
|
||||
@@ -1,4 +1,5 @@
|
||||
(entwurf vor prompt engineering)
|
||||
verbessere diesen prompt:
|
||||
|
||||
du bist azure cloud senior architekt
|
||||
erstelle mir einen architekturvorschlag für den ki betrieb konform zum eu ai act, dsgvo, dora, nis 2 und iso27001 auf der basis von azure mit berücksichtigung der folgenden komponenten:
|
||||
- kubernetes cluster mit ki workern und container/ cluster management
|
||||
@@ -7,7 +8,8 @@ erstelle mir einen architekturvorschlag für den ki betrieb konform zum eu ai ac
|
||||
- prompt firewall
|
||||
- ai-model scanning
|
||||
- ai-interface scanning
|
||||
- frontend und load balancer mit tls terminierung
|
||||
- frontend in container
|
||||
- load balancer mit tls terminierung
|
||||
- llm router
|
||||
- api gateway
|
||||
- iam (rbac, ldap)
|
||||
@@ -17,12 +19,12 @@ erstelle mir einen architekturvorschlag für den ki betrieb konform zum eu ai ac
|
||||
- apache Kafka Router
|
||||
- Logging
|
||||
- Monitoring
|
||||
- Git repos, pipelines und ide
|
||||
- Git repos, runner, pipelines ci/cd und ide
|
||||
- real time analytics
|
||||
- metriken (grafana)
|
||||
- vulnerability management
|
||||
- queues mq
|
||||
- build, test und deploy von ai agents
|
||||
- build, test und deploy von ai agents
|
||||
- terraform
|
||||
- bastion host, splunk
|
||||
- öffentliche und private zertifikate mit management
|
||||
|
||||
@@ -0,0 +1,422 @@
|
||||
Alles klar, ich mache das jetzt konkret – so, als würdet ihr **morgen mit diesem Setup auf Azure starten**. Ich zeige dir:
|
||||
|
||||
1. Annahmen & Namenskonvention
|
||||
2. Netzwerk- & Security-Setup
|
||||
3. Compute- & KI-Setup (AKS, OpenAI, Agents)
|
||||
4. Data-Layer-Setup
|
||||
5. Observability / Security-Setup
|
||||
6. DevOps & Terraform-Setup (inkl. Beispiel-Snippets)
|
||||
|
||||
Ich halte es „konkret, aber noch lesbar“, nicht jede Kleinigkeit auf Ressourcenniveau – eher ein **Blueprint**, den man in Terraform übersetzen kann.
|
||||
|
||||
---
|
||||
|
||||
## 1. Annahmen & Namenskonvention
|
||||
|
||||
**Umgebung:**
|
||||
|
||||
* Subscription: `sub-ai-prod-001`
|
||||
* Region: `westeurope`
|
||||
* Tenant: euer Produktiv-Tenant
|
||||
|
||||
**Namenskonvention (Beispiele):**
|
||||
|
||||
* Resource Group: `rg-<scope>-<env>-<region>`
|
||||
|
||||
* z. B. `rg-network-prod-weu`, `rg-aks-prod-weu`
|
||||
* AKS: `aks-ai-prod-weu`
|
||||
* VNet: `vnet-hub-prod-weu`, `vnet-aks-prod-weu`, `vnet-data-prod-weu`
|
||||
* Key Vault: `kv-ai-prod-weu`
|
||||
* Log Analytics: `law-ai-prod-weu`
|
||||
|
||||
**Umgebungen:**
|
||||
|
||||
* `dev`, `test`, `prod` – identische Struktur, andere Größen und ggf. weniger Zonen.
|
||||
|
||||
---
|
||||
|
||||
## 2. Netzwerk- & Security-Setup
|
||||
|
||||
### 2.1 Resource Groups
|
||||
|
||||
* `rg-network-prod-weu` – VNet, Firewall, Bastion, DDoS, Peerings
|
||||
* `rg-aks-prod-weu` – AKS, ACR, Managed Identities
|
||||
* `rg-app-prod-weu` – APIM, Front Door, Application Gateway, Event Hubs, Service Bus
|
||||
* `rg-data-prod-weu` – SQL, Cosmos, Storage, Data Explorer/Synapse
|
||||
* `rg-secops-prod-weu` – Log Analytics, Sentinel, Key Vault, Managed Grafana
|
||||
* `rg-ops-prod-weu` – devOps Hilfsressourcen (Automation, Runbooks, ggf. Jump-VM, Splunk Forwarder etc.)
|
||||
|
||||
### 2.2 Netzwerk-Topologie (Beispiel)
|
||||
|
||||
**Hub-Spoke-Modell**
|
||||
|
||||
* Hub-VNet: `vnet-hub-prod-weu` – `10.0.0.0/16`
|
||||
|
||||
* Subnet `snet-azurefw` (Firewall): `10.0.0.0/24`
|
||||
* Subnet `snet-bastion`: `10.0.1.0/24`
|
||||
* Subnet `snet-shared-services`: `10.0.2.0/24`
|
||||
* Spoke-VNet AKS: `vnet-aks-prod-weu` – `10.1.0.0/16`
|
||||
|
||||
* Subnet `snet-aks-nodes`: `10.1.0.0/20`
|
||||
* Subnet `snet-aks-system`: `10.1.16.0/24`
|
||||
* Subnet `snet-aks-user`: `10.1.17.0/24`
|
||||
* Spoke-VNet Data: `vnet-data-prod-weu` – `10.2.0.0/16`
|
||||
|
||||
* Subnet `snet-data-sql`: `10.2.0.0/24`
|
||||
* Subnet `snet-data-analytics`: `10.2.1.0/24`
|
||||
|
||||
**Komponenten:**
|
||||
|
||||
* **Azure Firewall** in Hub, alle Spokes via VNet-Peering angebunden
|
||||
* **DDoS Protection Standard** auf Hub-VNet
|
||||
* **Azure Bastion** im Hub für SSH/RDP auf Admin-VMs oder Knoten (wenn nötig)
|
||||
* PaaS-Services (SQL, Storage, Event Hubs, Service Bus, OpenAI, Key Vault) via **Private Endpoints** in `vnet-data-prod-weu` und `vnet-aks-prod-weu`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Compute- & KI-Setup
|
||||
|
||||
### 3.1 AKS-Cluster
|
||||
|
||||
* Ressource: `aks-ai-prod-weu` in `rg-aks-prod-weu`
|
||||
* Konfiguration (Beispiel):
|
||||
|
||||
* Kubernetes-Version: `1.29.x` (aktuelle LTS zum Rolloutzeitpunkt)
|
||||
|
||||
* Nodepools:
|
||||
|
||||
* `systempool` (B4ms, 3 Nodes, autoscale 3–10)
|
||||
* `gpubatch` (Standard_NC4as_T4_v3, autoscale 0–10) – für Model Serving / featurized Inference
|
||||
* `appworkload` (D4as_v5, autoscale 3–20) – AI Agents, LLM-Router, APIs, Frontend
|
||||
|
||||
* Network: **Azure CNI**, IPAM über oben beschriebenes AKS-VNet
|
||||
|
||||
* **AKS-managed AAD Integration** (Azure Entra ID) für Kubernetes-RBAC
|
||||
|
||||
* **OIDC Issuer** für Workload-Identitäten (Federation mit Entra ID, kein Secret-basiertes Zugriffskonzept mehr)
|
||||
|
||||
Namespaces (Beispiele):
|
||||
|
||||
* `ns-frontend` – UI-Container
|
||||
* `ns-api` – API & LLM-Router
|
||||
* `ns-ai-agents` – Agent-Orchestration
|
||||
* `ns-ml-models` – self-hosted Modelle
|
||||
* `ns-observability` – Prometheus/Grafana-Agent, OpenTelemetry, etc.
|
||||
|
||||
### 3.2 Container Registry
|
||||
|
||||
* Ressource: `acr-ai-prod-weu` in `rg-aks-prod-weu`
|
||||
* Private Endpoint zu `vnet-aks-prod-weu`
|
||||
* Defender for Cloud / Defender for Containers Scan auf Images aktiv.
|
||||
|
||||
### 3.3 LLM-Router & Agentic AI
|
||||
|
||||
**LLM-Router (Beispiel):**
|
||||
|
||||
* Deployment `llm-router` in `ns-api` (z. B. Python/FastAPI)
|
||||
* Patterns:
|
||||
|
||||
* Endpunkte: `/v1/chat`, `/v1/completions`, `/v1/tools/<toolname>`
|
||||
* Routing-Tabelle in ConfigMap/Secret (z. B. `gpt4o` → Azure OpenAI, `internal-llm` → AKS-Model)
|
||||
|
||||
**Agentic AI Services:**
|
||||
|
||||
* Deployments `agent-orchestrator`, `retriever-service`, `task-planner` in `ns-ai-agents`
|
||||
* Kommunikation mit:
|
||||
|
||||
* Azure AI Search (`VECTOR`)
|
||||
* SQL/Cosmos für Domänendaten
|
||||
* Azure OpenAI / self-hosted Models über interne Services
|
||||
|
||||
### 3.4 Azure OpenAI & Self-hosted Models
|
||||
|
||||
* Ressource: `aoai-prod-weu` (Azure OpenAI / Azure AI Foundry Project)
|
||||
|
||||
* Region: `westeurope` oder andere EU-Region, Data Residency „EU only“
|
||||
* Bereitgestellte Modelle: `gpt-4.x`, `gpt-4o`, `embedding-3-large` etc.
|
||||
* Self-hosted Modelle (z. B. Llama, Mistral):
|
||||
|
||||
* Als `Deployment` in `ns-ml-models`
|
||||
* Exponiert via `ClusterIP` Services, nur intern erreichbar
|
||||
* GPU-Nodepool `gpubatch` zugeordnet.
|
||||
|
||||
---
|
||||
|
||||
## 4. Data-Layer-Setup
|
||||
|
||||
### 4.1 SQL, NoSQL, Graph
|
||||
|
||||
* **Azure SQL Managed Instance**: `sqlmi-ai-prod-weu` in `rg-data-prod-weu`
|
||||
|
||||
* Nutzung: Referenzdaten, Konfigurationen, Audit-Tabellen, fachliche Daten
|
||||
* Always Encrypted / TDE mit CMK aus Key Vault
|
||||
* **Azure Cosmos DB (Core + Gremlin)**:
|
||||
|
||||
* Account: `cosmos-ai-prod-weu`
|
||||
* DBs:
|
||||
|
||||
* `aiapp` (Core API) – Session-Daten, Konfigurationsobjekte, Agent-States
|
||||
* `graph` (Gremlin API) – Graph der Entitäten / Policies / Relationen
|
||||
|
||||
### 4.2 Object Storage / Data Lake / Vektorindex
|
||||
|
||||
* **Storage Account**: `staiaprodweu` (Data Lake Gen2 aktiviert)
|
||||
|
||||
* Container:
|
||||
|
||||
* `logs-ai` – Langfrist-Logs (immutable)
|
||||
* `model-artifacts` – Model-Checkpoints, Eval-Reports
|
||||
* `training-data` – z. B. anonymisierte Historik
|
||||
* **Azure AI Search**: `aisearch-ai-prod-weu`
|
||||
|
||||
* Index `idx-documents` (klassisch) und `idx-documents-vector` (Vektor)
|
||||
* Nutzt Blob Storage als Quelle (Indexers)
|
||||
* Dient RAG-Orchestrator als Retrieval-Ziel.
|
||||
|
||||
### 4.3 Messaging & Streaming
|
||||
|
||||
* **Event Hubs Namespace**: `evh-ai-prod-weu`
|
||||
|
||||
* Event Hub `eh-ai-telemetry` (Kafka-kompatibler Endpoint) – Streams von Request-/Systemevents
|
||||
* **Service Bus Namespace**: `sb-ai-prod-weu`
|
||||
|
||||
* Queues: `q-agent-tasks`, `q-agent-results`
|
||||
* Topics: `t-notifications`, `t-compliance-events`
|
||||
|
||||
### 4.4 Analytics
|
||||
|
||||
* **Azure Data Explorer / Synapse Data Explorer**: `adx-ai-prod-weu`
|
||||
|
||||
* Tabellen: `ai_requests`, `ai_decisions`, `security_events`
|
||||
* Optional: **Synapse Workspace** für Reporting und DWH-Workloads.
|
||||
|
||||
---
|
||||
|
||||
## 5. Observability & Security-Setup
|
||||
|
||||
### 5.1 Logging & Monitoring
|
||||
|
||||
* **Log Analytics Workspace**: `law-ai-prod-weu` in `rg-secops-prod-weu`
|
||||
|
||||
* Datenquellen:
|
||||
|
||||
* AKS (Container Logs/Insights)
|
||||
* APIM, Application Gateway, Front Door
|
||||
* Event Hubs, Service Bus, SQL, Storage
|
||||
* Azure OpenAI (über Diagnostic Settings, soweit verfügbar)
|
||||
|
||||
* **Azure Monitor + Application Insights**:
|
||||
|
||||
* Application Insights Ressource: `appi-ai-prod-weu`
|
||||
* Instrumentation in Frontend, API, Agents, Router
|
||||
* Distributed Tracing mit Correlation IDs
|
||||
|
||||
* **Azure Managed Grafana**:
|
||||
|
||||
* Workspace: `graf-ai-prod-weu`
|
||||
* Data Source: Log Analytics, Azure Monitor
|
||||
* Dashboards:
|
||||
|
||||
* „Platform Health“ (CPU, Memory, Node Status, Error Rates)
|
||||
* „LLM Performance“ (Latenzen, Tokenverbrauch, Rate-Limits)
|
||||
* „Compliance & Oversight“ (Anzahl manueller Overrides, abgebrochene Anfragen, Policy-Verstöße)
|
||||
|
||||
### 5.2 SIEM / SOC
|
||||
|
||||
* **Microsoft Sentinel**: `sentinel-ai-prod-weu` (auf `law-ai-prod-weu`)
|
||||
|
||||
* Daten-Connectors: Azure Activity, Sign-In Logs, Defender, AKS, APIM, WAF-Logs, Custom Logs von Agents
|
||||
* Regeln:
|
||||
|
||||
* Anomalie bei LLM-Nutzung (z. B. plötzlicher Spike an bestimmten Endpunkten)
|
||||
* Ungewöhnliche Admin-Logins (PIM, Key Vault, AKS)
|
||||
* **Splunk**:
|
||||
|
||||
* Entweder via Add-on für Azure Monitor oder Event Hub Export
|
||||
* Forwarder in `rg-ops-prod-weu` (VM in `snet-shared-services`)
|
||||
|
||||
### 5.3 Identity, Key Management & Bastion
|
||||
|
||||
* **Azure Entra ID**:
|
||||
|
||||
* Security-Gruppen: `sg-aks-admins`, `sg-ai-dev`, `sg-ai-ops`, `sg-soc`
|
||||
* App Registrations für Frontend, API, Agents (client credentials / auth code with PKCE)
|
||||
|
||||
* **PIM**:
|
||||
|
||||
* Rollen wie `Owner`, `Contributor`, `User Access Administrator`, `AKS Cluster Admin`, `Key Vault Admin` nur via PIM.
|
||||
|
||||
* **Key Vault**: `kv-ai-prod-weu`
|
||||
|
||||
* Secrets:
|
||||
|
||||
* DB Connection Strings (wenn nicht komplett MI-basiert)
|
||||
* API Keys (z. B. Drittsysteme), Webhook-Secrets
|
||||
* Keys:
|
||||
|
||||
* CMK für SQL, Storage, eventuell OpenAI (falls unterstützt)
|
||||
* Certificates:
|
||||
|
||||
* TLS-Zertifikate für App Gateway, APIM (öffentlich + privat)
|
||||
|
||||
* **Azure Bastion**:
|
||||
|
||||
* Zugriff auf Jump VM (z. B. für Splunk-Forwarder, Spezial-Tools) ohne Public IP.
|
||||
|
||||
---
|
||||
|
||||
## 6. DevOps & Terraform-Setup
|
||||
|
||||
### 6.1 Git & Pipelines
|
||||
|
||||
* **GitHub Enterprise** Org: `mycompany-ai` (oder Azure DevOps Project `ai-platform`)
|
||||
* Repositories:
|
||||
|
||||
* `infra-landingzone` – VNet, RGs, Policies, Key Vault, Log Analytics, Firewall
|
||||
* `infra-platform` – AKS, ACR, APIM, OpenAI, Data Layer
|
||||
* `app-llm-router`, `app-ai-agents`, `app-frontend` – Applikationscode
|
||||
* Pipelines:
|
||||
|
||||
* `infra-landingzone-ci` / `cd` → Terraform Plan/Apply gegen `dev`/`test`/`prod`
|
||||
* `app-*-ci` → Build, Tests, Container-Build, Push nach ACR
|
||||
* `app-*-cd` → Deploy nach AKS (Helm)
|
||||
|
||||
### 6.2 Terraform – Beispiel-Snippets (stark vereinfacht)
|
||||
|
||||
**Backend & Provider (infra-landingzone)**
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
required_version = ">= 1.8.0"
|
||||
|
||||
backend "azurerm" {
|
||||
resource_group_name = "rg-tfstate-prod-weu"
|
||||
storage_account_name = "sttfstateprodweu"
|
||||
container_name = "tfstate"
|
||||
key = "landingzone-prod.tfstate"
|
||||
}
|
||||
|
||||
required_providers {
|
||||
azurerm = {
|
||||
source = "hashicorp/azurerm"
|
||||
version = "~> 4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "azurerm" {
|
||||
features {}
|
||||
}
|
||||
```
|
||||
|
||||
**Beispiel: VNet + Subnet für AKS**
|
||||
|
||||
```hcl
|
||||
resource "azurerm_virtual_network" "vnet_aks" {
|
||||
name = "vnet-aks-prod-weu"
|
||||
location = "westeurope"
|
||||
resource_group_name = "rg-network-prod-weu"
|
||||
address_space = ["10.1.0.0/16"]
|
||||
}
|
||||
|
||||
resource "azurerm_subnet" "snet_aks_nodes" {
|
||||
name = "snet-aks-nodes"
|
||||
resource_group_name = azurerm_virtual_network.vnet_aks.resource_group_name
|
||||
virtual_network_name = azurerm_virtual_network.vnet_aks.name
|
||||
address_prefixes = ["10.1.0.0/20"]
|
||||
}
|
||||
```
|
||||
|
||||
**Beispiel: AKS-Cluster (gekürzt)**
|
||||
|
||||
```hcl
|
||||
resource "azurerm_kubernetes_cluster" "aks_ai" {
|
||||
name = "aks-ai-prod-weu"
|
||||
location = "westeurope"
|
||||
resource_group_name = "rg-aks-prod-weu"
|
||||
dns_prefix = "aks-ai-prod"
|
||||
|
||||
sku_tier = "Standard"
|
||||
|
||||
default_node_pool {
|
||||
name = "systempool"
|
||||
vm_size = "Standard_D4as_v5"
|
||||
node_count = 3
|
||||
enable_auto_scaling = true
|
||||
min_count = 3
|
||||
max_count = 10
|
||||
vnet_subnet_id = azurerm_subnet.snet_aks_nodes.id
|
||||
}
|
||||
|
||||
identity {
|
||||
type = "SystemAssigned"
|
||||
}
|
||||
|
||||
azure_active_directory_role_based_access_control {
|
||||
managed = true
|
||||
admin_group_object_ids = ["<GUID-AKS-Admins-Group>"]
|
||||
}
|
||||
|
||||
oidc_issuer_enabled = true
|
||||
workload_identity_enabled = true
|
||||
|
||||
network_profile {
|
||||
network_plugin = "azure"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Beispiel: Log Analytics + Diagnostics**
|
||||
|
||||
```hcl
|
||||
resource "azurerm_log_analytics_workspace" "law_ai" {
|
||||
name = "law-ai-prod-weu"
|
||||
location = "westeurope"
|
||||
resource_group_name = "rg-secops-prod-weu"
|
||||
sku = "PerGB2018"
|
||||
}
|
||||
|
||||
resource "azurerm_monitor_diagnostic_setting" "aks_diag" {
|
||||
name = "diag-aks-ai-prod"
|
||||
target_resource_id = azurerm_kubernetes_cluster.aks_ai.id
|
||||
log_analytics_workspace_id = azurerm_log_analytics_workspace.law_ai.id
|
||||
|
||||
enabled_log {
|
||||
category = "kube-apiserver"
|
||||
}
|
||||
|
||||
enabled_log {
|
||||
category = "kube-audit"
|
||||
}
|
||||
|
||||
metric {
|
||||
category = "AllMetrics"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Beispiel: Azure OpenAI (Symbolisch)**
|
||||
(Hinweis: die Terraform-Resource ist je nach Provider-Version ggf. unterschiedlich; pseudomäßig:)
|
||||
|
||||
```hcl
|
||||
resource "azurerm_cognitive_account" "aoai" {
|
||||
name = "aoai-prod-weu"
|
||||
location = "westeurope"
|
||||
resource_group_name = "rg-app-prod-weu"
|
||||
kind = "OpenAI"
|
||||
sku_name = "S0"
|
||||
|
||||
custom_subdomain_name = "aoai-prod"
|
||||
|
||||
identity {
|
||||
type = "SystemAssigned"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Wenn du magst, kann ich als nächsten Schritt:
|
||||
|
||||
* das Beispiel auf **„nur KI-Kernplattform“** zurechtschneiden (Minimal-Setup),
|
||||
* oder dir eine **konkrete AKS-Namespace- und Helm-Chart-Struktur** für LLM-Router & Agents vorschlagen.
|
||||
@@ -0,0 +1,474 @@
|
||||
# ✅ Deployment Checklist
|
||||
|
||||
Diese Checkliste führt dich Schritt-für-Schritt durch das erste Deployment.
|
||||
|
||||
## 📋 Pre-Deployment
|
||||
|
||||
### 1. Lokale Umgebung
|
||||
|
||||
- [ ] Terraform >= 1.9.0 installiert
|
||||
- [ ] Azure CLI >= 2.0 installiert
|
||||
- [ ] kubectl >= 1.28 installiert
|
||||
- [ ] Docker >= 20.10 installiert
|
||||
- [ ] PHP >= 8.2 installiert (für Laravel-Befehle)
|
||||
|
||||
**Prüfen:**
|
||||
```bash
|
||||
terraform version
|
||||
az version
|
||||
kubectl version
|
||||
docker version
|
||||
php -v
|
||||
```
|
||||
|
||||
### 2. Azure Zugriff
|
||||
|
||||
- [ ] Azure Account vorhanden
|
||||
- [ ] Subscription ID bekannt: `77677a80-2dea-493d-9867-f1c961b80fb3`
|
||||
- [ ] Zugriff auf Resource Group: `trusted_ai_demo_rg`
|
||||
- [ ] Zugriff auf AKS Cluster: `trai_k8s_cluster`
|
||||
|
||||
**Prüfen:**
|
||||
```bash
|
||||
az login
|
||||
az account set --subscription "77677a80-2dea-493d-9867-f1c961b80fb3"
|
||||
az account show
|
||||
az aks show --resource-group trusted_ai_demo_rg --name trai_k8s_cluster
|
||||
```
|
||||
|
||||
## 🔧 Setup
|
||||
|
||||
### 3. Azure Container Registry
|
||||
|
||||
- [ ] ACR erstellt oder vorhanden
|
||||
- [ ] ACR Name: `mylaravelregistry` (oder eigener Name)
|
||||
- [ ] AKS hat Pull-Berechtigung für ACR
|
||||
|
||||
**Erstellen:**
|
||||
```bash
|
||||
# ACR erstellen (falls noch nicht vorhanden)
|
||||
az acr create \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name mylaravelregistry \
|
||||
--sku Basic \
|
||||
--location germanywestcentral
|
||||
|
||||
# AKS Zugriff geben
|
||||
az aks update \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name trai_k8s_cluster \
|
||||
--attach-acr mylaravelregistry
|
||||
|
||||
# Login
|
||||
az acr login --name mylaravelregistry
|
||||
```
|
||||
|
||||
### 4. Docker Image
|
||||
|
||||
- [ ] Docker Image gebaut
|
||||
- [ ] Docker Image zu ACR gepusht
|
||||
- [ ] Image Tag notiert
|
||||
|
||||
**Bauen & Pushen:**
|
||||
```bash
|
||||
cd /Users/sebastianfrohlich/Herd/frontend
|
||||
|
||||
# Bauen
|
||||
docker build -t mylaravelregistry.azurecr.io/laravel-app:v1.0.0 .
|
||||
|
||||
# Pushen
|
||||
docker push mylaravelregistry.azurecr.io/laravel-app:v1.0.0
|
||||
|
||||
# Image Tag notieren:
|
||||
IMAGE_TAG="mylaravelregistry.azurecr.io/laravel-app:v1.0.0"
|
||||
```
|
||||
|
||||
### 5. Laravel Konfiguration
|
||||
|
||||
- [ ] APP_KEY generiert
|
||||
- [ ] APP_KEY notiert
|
||||
|
||||
**Generieren:**
|
||||
```bash
|
||||
cd /Users/sebastianfrohlich/Herd/frontend
|
||||
php artisan key:generate --show
|
||||
|
||||
# Output: base64:xyz...
|
||||
# Notieren für terraform.tfvars
|
||||
```
|
||||
|
||||
### 6. Backup-Dateien
|
||||
|
||||
- [ ] Database Backup vorhanden
|
||||
- [ ] Backup-Pfad notiert
|
||||
|
||||
**Prüfen:**
|
||||
```bash
|
||||
ls -lh /Users/sebastianfrohlich/Herd/frontend/backups/
|
||||
|
||||
# Wähle ein Backup, z.B.:
|
||||
BACKUP_FILE="../backups/backup_backend_20251203_101741.dump"
|
||||
```
|
||||
|
||||
## ⚙️ Terraform Konfiguration
|
||||
|
||||
### 7. terraform.tfvars erstellen
|
||||
|
||||
- [ ] `terraform.tfvars` aus Example kopiert
|
||||
- [ ] Alle erforderlichen Werte eingetragen
|
||||
|
||||
**Erstellen:**
|
||||
```bash
|
||||
cd /Users/sebastianfrohlich/Herd/frontend/terraform
|
||||
cp terraform.tfvars.example terraform.tfvars
|
||||
nano terraform.tfvars
|
||||
```
|
||||
|
||||
**Erforderliche Werte:**
|
||||
|
||||
```hcl
|
||||
# ✅ Docker Image
|
||||
docker_image = "mylaravelregistry.azurecr.io/laravel-app:v1.0.0"
|
||||
|
||||
# ✅ Laravel APP_KEY
|
||||
app_key = "base64:YOUR_GENERATED_KEY_HERE"
|
||||
|
||||
# ✅ PostgreSQL Admin Password (sicher wählen!)
|
||||
postgresql_admin_password = "YourVerySecurePassword123!"
|
||||
|
||||
# ✅ Alert Email
|
||||
alert_email_address = "your-email@example.com"
|
||||
|
||||
# ✅ Database Restore (beim ersten Deployment)
|
||||
db_restore_enabled = true
|
||||
db_backup_file_path = "../backups/backup_backend_20251203_101741.dump"
|
||||
```
|
||||
|
||||
**Optionale Werte:**
|
||||
|
||||
```hcl
|
||||
# Für Custom Domain
|
||||
ingress_host = "app.yourdomain.com"
|
||||
|
||||
# Für SSL/TLS (benötigt Domain)
|
||||
ssl_enabled = true
|
||||
ssl_issuer_email = "admin@yourdomain.com"
|
||||
|
||||
# Ressourcen anpassen
|
||||
app_replicas = 2
|
||||
postgresql_sku_name = "B_Standard_B1ms"
|
||||
```
|
||||
|
||||
### 8. Konfiguration validieren
|
||||
|
||||
- [ ] terraform.tfvars Syntax korrekt
|
||||
- [ ] Alle Secrets/Passwörter sicher
|
||||
- [ ] Backup-Pfad korrekt
|
||||
|
||||
**Prüfen:**
|
||||
```bash
|
||||
cd terraform
|
||||
|
||||
# Terraform init
|
||||
terraform init
|
||||
|
||||
# Validate
|
||||
terraform validate
|
||||
|
||||
# Sollte ausgeben: Success! The configuration is valid.
|
||||
```
|
||||
|
||||
## 🚀 Deployment
|
||||
|
||||
### 9. Deployment ausführen
|
||||
|
||||
**Option A: Automatisches Script (Empfohlen)**
|
||||
|
||||
- [ ] Deploy-Script ausgeführt
|
||||
|
||||
```bash
|
||||
cd /Users/sebastianfrohlich/Herd/frontend/terraform
|
||||
./scripts/deploy.sh
|
||||
```
|
||||
|
||||
Das Script führt automatisch aus:
|
||||
1. ✅ Prerequisite Check
|
||||
2. ✅ Azure Login Status
|
||||
3. ✅ terraform.tfvars Check
|
||||
4. ✅ Docker Image Check
|
||||
5. ✅ APP_KEY Check
|
||||
6. ✅ terraform init
|
||||
7. ✅ terraform validate
|
||||
8. ✅ terraform plan
|
||||
9. ✅ terraform apply (nach Bestätigung)
|
||||
10. ✅ kubectl konfigurieren
|
||||
11. ✅ Pod Status prüfen
|
||||
|
||||
**Option B: Manuell**
|
||||
|
||||
- [ ] Terraform Plan erstellt
|
||||
- [ ] Plan überprüft
|
||||
- [ ] Terraform Apply ausgeführt
|
||||
|
||||
```bash
|
||||
cd terraform
|
||||
|
||||
# Plan
|
||||
terraform plan -out=tfplan
|
||||
|
||||
# Plan überprüfen
|
||||
# Sollte Resources anzeigen: +XX to add, ~0 to change, -0 to destroy
|
||||
|
||||
# Apply
|
||||
terraform apply tfplan
|
||||
|
||||
# Outputs anzeigen
|
||||
terraform output
|
||||
terraform output deployment_instructions
|
||||
```
|
||||
|
||||
### 10. kubectl konfigurieren
|
||||
|
||||
- [ ] kubectl Credentials abgerufen
|
||||
- [ ] Cluster-Zugriff getestet
|
||||
|
||||
```bash
|
||||
az aks get-credentials \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name trai_k8s_cluster \
|
||||
--overwrite-existing
|
||||
|
||||
# Testen
|
||||
kubectl get nodes
|
||||
kubectl get namespaces
|
||||
```
|
||||
|
||||
## 🔄 Database Restore
|
||||
|
||||
### 11. Datenbank wiederherstellen
|
||||
|
||||
**Option A: Automatisches Script (Empfohlen)**
|
||||
|
||||
- [ ] Restore-Script ausgeführt
|
||||
- [ ] Job Status geprüft
|
||||
|
||||
```bash
|
||||
cd /Users/sebastianfrohlich/Herd/frontend/terraform
|
||||
./scripts/restore-db.sh
|
||||
|
||||
# Script fragt nach:
|
||||
# - Welches Backup-File?
|
||||
# - Bestätigung: yes
|
||||
# Zeigt dann Logs und Status
|
||||
```
|
||||
|
||||
**Option B: Via Terraform**
|
||||
|
||||
- [ ] `db_restore_enabled = true` in terraform.tfvars
|
||||
- [ ] `terraform apply` ausgeführt
|
||||
|
||||
```bash
|
||||
# In terraform.tfvars:
|
||||
db_restore_enabled = true
|
||||
db_backup_file_path = "../backups/backup_backend_20251203_101741.dump"
|
||||
|
||||
# Apply
|
||||
terraform apply
|
||||
|
||||
# Job Status prüfen
|
||||
kubectl get jobs -n laravel-app
|
||||
kubectl logs -n laravel-app -l job-type=database-restore -f
|
||||
```
|
||||
|
||||
## ✅ Post-Deployment Checks
|
||||
|
||||
### 12. Infrastruktur Status
|
||||
|
||||
- [ ] Pods sind running
|
||||
- [ ] Service ist erreichbar
|
||||
- [ ] Ingress hat External IP
|
||||
- [ ] Database ist connected
|
||||
|
||||
**Prüfen:**
|
||||
```bash
|
||||
# Pods
|
||||
kubectl get pods -n laravel-app
|
||||
# Sollte: 2/2 Running anzeigen
|
||||
|
||||
# Service
|
||||
kubectl get svc -n laravel-app
|
||||
|
||||
# Ingress
|
||||
kubectl get svc ingress-nginx-controller -n ingress-nginx
|
||||
# Sollte: EXTERNAL-IP anzeigen (dauert 5-10 Min)
|
||||
|
||||
# Database
|
||||
kubectl get secret laravel-app-db-credentials -n laravel-app
|
||||
```
|
||||
|
||||
### 13. Application Health
|
||||
|
||||
- [ ] Application ist erreichbar
|
||||
- [ ] HTTP Status 200 oder 302
|
||||
- [ ] Keine Fehler in Logs
|
||||
|
||||
**Prüfen:**
|
||||
```bash
|
||||
# URL abrufen
|
||||
terraform output app_url
|
||||
|
||||
# Beispiel Output: http://20.79.123.456
|
||||
|
||||
# Im Browser öffnen oder:
|
||||
curl -I http://20.79.123.456
|
||||
|
||||
# Sollte: HTTP/1.1 200 OK oder 302 Found
|
||||
|
||||
# Logs prüfen
|
||||
kubectl logs -n laravel-app -l app=laravel-app -f
|
||||
|
||||
# Sollte: Keine Errors zeigen
|
||||
```
|
||||
|
||||
### 14. Database Connectivity
|
||||
|
||||
- [ ] Database Restore Job completed
|
||||
- [ ] Laravel kann auf DB zugreifen
|
||||
|
||||
**Prüfen:**
|
||||
```bash
|
||||
# Restore Job Status
|
||||
kubectl get jobs -n laravel-app
|
||||
# Sollte: db-restore-* mit COMPLETIONS 1/1
|
||||
|
||||
# Job Logs
|
||||
kubectl logs -n laravel-app -l job-type=database-restore
|
||||
|
||||
# Sollte: "Database restore completed successfully!" enthalten
|
||||
|
||||
# Laravel Migrations Status (in Pod)
|
||||
POD_NAME=$(kubectl get pods -n laravel-app -l app=laravel-app -o jsonpath='{.items[0].metadata.name}')
|
||||
kubectl exec -it -n laravel-app $POD_NAME -- php artisan migrate:status
|
||||
|
||||
# Sollte: Migration table anzeigen
|
||||
```
|
||||
|
||||
## 🎯 Finalisierung
|
||||
|
||||
### 15. Monitoring Setup
|
||||
|
||||
- [ ] Azure Monitor funktioniert
|
||||
- [ ] Alert Rules aktiv
|
||||
- [ ] Email Alerts konfiguriert
|
||||
|
||||
**Prüfen:**
|
||||
```bash
|
||||
# Azure Portal öffnen
|
||||
open https://portal.azure.com
|
||||
|
||||
# Gehe zu:
|
||||
# 1. Resource Group: trusted_ai_demo_rg
|
||||
# 2. AKS Cluster: trai_k8s_cluster
|
||||
# 3. Monitoring > Insights
|
||||
|
||||
# Alert Rules prüfen:
|
||||
az monitor metrics alert list \
|
||||
--resource-group trusted_ai_demo_rg
|
||||
```
|
||||
|
||||
### 16. Dokumentation
|
||||
|
||||
- [ ] LoadBalancer IP notiert
|
||||
- [ ] Database Connection String notiert
|
||||
- [ ] Terraform Outputs gespeichert
|
||||
|
||||
**Notieren:**
|
||||
```bash
|
||||
# Alle Outputs anzeigen
|
||||
terraform output
|
||||
|
||||
# Spezifische Outputs
|
||||
terraform output app_url
|
||||
terraform output postgresql_server_fqdn
|
||||
terraform output postgresql_connection_string
|
||||
|
||||
# In Passwort-Manager oder sicheren Ort speichern!
|
||||
```
|
||||
|
||||
### 17. Cleanup & Security
|
||||
|
||||
- [ ] Sensitive Dateien nicht committed (terraform.tfvars)
|
||||
- [ ] Passwörter sicher gespeichert
|
||||
- [ ] .gitignore überprüft
|
||||
|
||||
**Prüfen:**
|
||||
```bash
|
||||
# Git Status
|
||||
git status
|
||||
|
||||
# Sollte NICHT enthalten:
|
||||
# - terraform.tfvars
|
||||
# - *.tfstate
|
||||
# - kubeconfig
|
||||
|
||||
# Falls vorhanden:
|
||||
git rm --cached terraform/terraform.tfvars
|
||||
git rm --cached terraform/*.tfstate
|
||||
```
|
||||
|
||||
## 🚦 Success Criteria
|
||||
|
||||
Deployment ist erfolgreich wenn:
|
||||
|
||||
- ✅ `kubectl get pods -n laravel-app` zeigt 2/2 Running
|
||||
- ✅ `terraform output app_url` zeigt eine URL
|
||||
- ✅ URL im Browser ist erreichbar
|
||||
- ✅ Database Restore Job ist completed
|
||||
- ✅ Keine Fehler in Pod Logs
|
||||
- ✅ Ingress hat External IP
|
||||
|
||||
## 🎉 Fertig!
|
||||
|
||||
Deine Laravel-Anwendung läuft jetzt auf Azure Kubernetes Service!
|
||||
|
||||
### Nächste Schritte:
|
||||
|
||||
1. **Testen**: Funktionalität der Anwendung testen
|
||||
2. **Monitoring**: Azure Monitor regelmäßig prüfen
|
||||
3. **Backups**: PostgreSQL Backup-Strategy überprüfen
|
||||
4. **Domain**: Custom Domain konfigurieren (optional)
|
||||
5. **SSL**: Let's Encrypt aktivieren (optional)
|
||||
6. **CI/CD**: GitHub Actions einrichten (optional)
|
||||
|
||||
### Wichtige Commands:
|
||||
|
||||
```bash
|
||||
# Status prüfen
|
||||
kubectl get all -n laravel-app
|
||||
|
||||
# Logs anzeigen
|
||||
kubectl logs -n laravel-app -l app=laravel-app -f
|
||||
|
||||
# Shell in Pod
|
||||
kubectl exec -it -n laravel-app <pod-name> -- /bin/sh
|
||||
|
||||
# Port-forward für lokalen Zugriff
|
||||
kubectl port-forward -n laravel-app svc/laravel-app 8080:80
|
||||
|
||||
# Terraform Outputs
|
||||
terraform output deployment_instructions
|
||||
```
|
||||
|
||||
## 📞 Support
|
||||
|
||||
Bei Problemen:
|
||||
|
||||
1. **Troubleshooting Guide**: [README.md#troubleshooting](README.md#troubleshooting)
|
||||
2. **Kubernetes Events**: `kubectl get events -n laravel-app --sort-by='.lastTimestamp'`
|
||||
3. **Pod Logs**: `kubectl logs -n laravel-app <pod-name>`
|
||||
4. **Terraform State**: `terraform show`
|
||||
|
||||
---
|
||||
|
||||
**Deployment Time**: ~30 Minuten
|
||||
**Status**: ✅ Ready for Production
|
||||
@@ -0,0 +1,410 @@
|
||||
# GitHub Actions CI/CD Setup
|
||||
|
||||
Diese Anleitung zeigt, wie du die automatische Deployment-Pipeline mit GitHub Actions einrichtest.
|
||||
|
||||
## 📋 Übersicht
|
||||
|
||||
Die Pipeline führt automatisch folgende Schritte aus:
|
||||
|
||||
1. **Build & Push**: Docker Image bauen und zu Azure Container Registry pushen
|
||||
2. **Terraform Deploy**: Infrastruktur mit Terraform deployen
|
||||
3. **Smoke Tests**: Basis-Tests nach Deployment ausführen
|
||||
4. **Notifications**: Status-Benachrichtigungen
|
||||
|
||||
## 🔐 Erforderliche GitHub Secrets
|
||||
|
||||
Gehe zu deinem GitHub Repository → Settings → Secrets and variables → Actions → New repository secret
|
||||
|
||||
### Azure Credentials
|
||||
|
||||
```bash
|
||||
# Azure Service Principal erstellen
|
||||
az ad sp create-for-rbac \
|
||||
--name "github-actions-laravel-app" \
|
||||
--role contributor \
|
||||
--scopes /subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/trusted_ai_demo_rg \
|
||||
--sdk-auth
|
||||
|
||||
# Output sieht so aus:
|
||||
{
|
||||
"clientId": "xxx",
|
||||
"clientSecret": "xxx",
|
||||
"subscriptionId": "77677a80-2dea-493d-9867-f1c961b80fb3",
|
||||
"tenantId": "xxx",
|
||||
"activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
|
||||
"resourceManagerEndpointUrl": "https://management.azure.com/",
|
||||
"activeDirectoryGraphResourceId": "https://graph.windows.net/",
|
||||
"sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
|
||||
"galleryEndpointUrl": "https://gallery.azure.com/",
|
||||
"managementEndpointUrl": "https://management.core.windows.net/"
|
||||
}
|
||||
```
|
||||
|
||||
**Secret Name**: `AZURE_CREDENTIALS`
|
||||
**Secret Value**: Der gesamte JSON Output von oben
|
||||
|
||||
### Azure Container Registry
|
||||
|
||||
```bash
|
||||
# ACR Credentials abrufen
|
||||
az acr credential show --name mylaravelregistry
|
||||
|
||||
# Output:
|
||||
{
|
||||
"passwords": [
|
||||
{
|
||||
"name": "password",
|
||||
"value": "xxx"
|
||||
},
|
||||
{
|
||||
"name": "password2",
|
||||
"value": "yyy"
|
||||
}
|
||||
],
|
||||
"username": "mylaravelregistry"
|
||||
}
|
||||
```
|
||||
|
||||
**Secret Name**: `ACR_USERNAME`
|
||||
**Secret Value**: `mylaravelregistry`
|
||||
|
||||
**Secret Name**: `ACR_PASSWORD`
|
||||
**Secret Value**: Der Wert von `password` (oder `password2`)
|
||||
|
||||
### Azure Subscription
|
||||
|
||||
**Secret Name**: `AZURE_SUBSCRIPTION_ID`
|
||||
**Secret Value**: `77677a80-2dea-493d-9867-f1c961b80fb3`
|
||||
|
||||
### Laravel Application
|
||||
|
||||
**Secret Name**: `LARAVEL_APP_KEY`
|
||||
**Secret Value**: Generiere mit `php artisan key:generate --show`
|
||||
**Beispiel**: `base64:abcdefgh12345...`
|
||||
|
||||
### PostgreSQL Database
|
||||
|
||||
**Secret Name**: `POSTGRESQL_ADMIN_USERNAME`
|
||||
**Secret Value**: `pgadmin`
|
||||
|
||||
**Secret Name**: `POSTGRESQL_ADMIN_PASSWORD`
|
||||
**Secret Value**: Dein sicheres PostgreSQL Passwort
|
||||
|
||||
### Ingress & SSL
|
||||
|
||||
**Secret Name**: `INGRESS_HOST`
|
||||
**Secret Value**: Deine Domain (z.B. `app.yourdomain.com`) oder leer lassen für IP-Zugriff
|
||||
|
||||
**Secret Name**: `SSL_ISSUER_EMAIL`
|
||||
**Secret Value**: Email für Let's Encrypt (z.B. `admin@yourdomain.com`)
|
||||
|
||||
### Monitoring
|
||||
|
||||
**Secret Name**: `ALERT_EMAIL`
|
||||
**Secret Value**: Email für Azure Alerts
|
||||
|
||||
## 📝 Secrets Checkliste
|
||||
|
||||
- [ ] `AZURE_CREDENTIALS` (JSON von Service Principal)
|
||||
- [ ] `ACR_USERNAME` (mylaravelregistry)
|
||||
- [ ] `ACR_PASSWORD` (ACR password)
|
||||
- [ ] `AZURE_SUBSCRIPTION_ID` (77677a80-2dea-493d-9867-f1c961b80fb3)
|
||||
- [ ] `LARAVEL_APP_KEY` (base64:...)
|
||||
- [ ] `POSTGRESQL_ADMIN_USERNAME` (pgadmin)
|
||||
- [ ] `POSTGRESQL_ADMIN_PASSWORD` (sicheres Passwort)
|
||||
- [ ] `INGRESS_HOST` (optional: deine Domain)
|
||||
- [ ] `SSL_ISSUER_EMAIL` (optional: für SSL)
|
||||
- [ ] `ALERT_EMAIL` (deine Email)
|
||||
|
||||
## 🚀 Workflow Trigger
|
||||
|
||||
### Automatisch bei Push
|
||||
|
||||
Die Pipeline wird automatisch ausgeführt bei Push auf:
|
||||
- `main` Branch (Staging Deployment)
|
||||
- `production` Branch (Production Deployment)
|
||||
|
||||
```bash
|
||||
# Code ändern und committen
|
||||
git add .
|
||||
git commit -m "Update feature"
|
||||
|
||||
# Push zu main für Staging
|
||||
git push origin main
|
||||
|
||||
# Push zu production für Production
|
||||
git push origin production
|
||||
```
|
||||
|
||||
### Manuell über GitHub UI
|
||||
|
||||
1. Gehe zu deinem Repository auf GitHub
|
||||
2. Klicke auf "Actions" Tab
|
||||
3. Wähle "Deploy to Azure AKS" Workflow
|
||||
4. Klicke auf "Run workflow"
|
||||
5. Wähle Environment (staging/production)
|
||||
6. Klicke auf "Run workflow"
|
||||
|
||||
### Manuell über GitHub CLI
|
||||
|
||||
```bash
|
||||
# Installiere gh CLI (falls noch nicht vorhanden)
|
||||
brew install gh # macOS
|
||||
# oder: https://cli.github.com/
|
||||
|
||||
# Login
|
||||
gh auth login
|
||||
|
||||
# Workflow manuell triggern
|
||||
gh workflow run deploy-azure.yml \
|
||||
--ref main \
|
||||
--field environment=staging
|
||||
|
||||
# Workflow Status prüfen
|
||||
gh run list --workflow=deploy-azure.yml
|
||||
|
||||
# Logs anzeigen
|
||||
gh run view --log
|
||||
```
|
||||
|
||||
## 🔧 Workflow Konfiguration anpassen
|
||||
|
||||
Die Workflow-Datei liegt in [.github/workflows/deploy-azure.yml](../.github/workflows/deploy-azure.yml).
|
||||
|
||||
### Environment-spezifische Settings
|
||||
|
||||
Im Workflow werden verschiedene Settings basierend auf dem Environment gesetzt:
|
||||
|
||||
**Staging:**
|
||||
- `app_debug = true`
|
||||
- `app_replicas = 2`
|
||||
- `postgresql_sku_name = "B_Standard_B1ms"` (Basic)
|
||||
- `postgresql_storage_mb = 32768` (32 GB)
|
||||
- `ssl_enabled = false`
|
||||
|
||||
**Production:**
|
||||
- `app_debug = false`
|
||||
- `app_replicas = 3`
|
||||
- `postgresql_sku_name = "GP_Standard_D2s_v3"` (General Purpose)
|
||||
- `postgresql_storage_mb = 131072` (128 GB)
|
||||
- `ssl_enabled = true`
|
||||
|
||||
### Weitere Trigger hinzufügen
|
||||
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- production
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
schedule:
|
||||
- cron: '0 2 * * 0' # Jeden Sonntag um 2 Uhr
|
||||
workflow_dispatch:
|
||||
# ... existing inputs
|
||||
```
|
||||
|
||||
## 📊 Workflow Monitoring
|
||||
|
||||
### In GitHub UI
|
||||
|
||||
1. Gehe zu "Actions" Tab in deinem Repository
|
||||
2. Siehst alle Workflow-Runs
|
||||
3. Klicke auf einen Run für Details
|
||||
4. Siehst Logs für jeden Job/Step
|
||||
|
||||
### Via GitHub CLI
|
||||
|
||||
```bash
|
||||
# Aktuelle Runs anzeigen
|
||||
gh run list --workflow=deploy-azure.yml
|
||||
|
||||
# Spezifischen Run anzeigen
|
||||
gh run view <run-id>
|
||||
|
||||
# Logs anzeigen
|
||||
gh run view <run-id> --log
|
||||
|
||||
# Run erneut starten
|
||||
gh run rerun <run-id>
|
||||
|
||||
# Run abbrechen
|
||||
gh run cancel <run-id>
|
||||
```
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Pipeline schlägt bei "Build and Push" fehl
|
||||
|
||||
**Problem**: ACR Authentication fehlgeschlagen
|
||||
|
||||
**Lösung**:
|
||||
```bash
|
||||
# Prüfe ACR Credentials
|
||||
az acr credential show --name mylaravelregistry
|
||||
|
||||
# Update GitHub Secrets mit neuen Credentials
|
||||
```
|
||||
|
||||
### Pipeline schlägt bei "Terraform Deploy" fehl
|
||||
|
||||
**Problem**: Azure Credentials ungültig
|
||||
|
||||
**Lösung**:
|
||||
```bash
|
||||
# Service Principal neu erstellen
|
||||
az ad sp create-for-rbac \
|
||||
--name "github-actions-laravel-app" \
|
||||
--role contributor \
|
||||
--scopes /subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/trusted_ai_demo_rg \
|
||||
--sdk-auth
|
||||
|
||||
# AZURE_CREDENTIALS Secret updaten
|
||||
```
|
||||
|
||||
**Problem**: Terraform State Lock
|
||||
|
||||
**Lösung**:
|
||||
```bash
|
||||
# State Lock manuell entfernen
|
||||
cd terraform
|
||||
terraform force-unlock <lock-id>
|
||||
```
|
||||
|
||||
### Pipeline schlägt bei "Smoke Tests" fehl
|
||||
|
||||
**Problem**: Pods nicht ready
|
||||
|
||||
**Lösung**:
|
||||
```bash
|
||||
# kubectl credentials abrufen
|
||||
az aks get-credentials \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name trai_k8s_cluster
|
||||
|
||||
# Pod Status prüfen
|
||||
kubectl get pods -n laravel-app
|
||||
kubectl describe pod <pod-name> -n laravel-app
|
||||
kubectl logs <pod-name> -n laravel-app
|
||||
```
|
||||
|
||||
## 🔒 Sicherheit
|
||||
|
||||
### Service Principal Permissions
|
||||
|
||||
Der Service Principal benötigt folgende Berechtigungen:
|
||||
- Contributor auf Resource Group
|
||||
- AcrPush auf Container Registry (optional, wenn über ACR credentials)
|
||||
|
||||
```bash
|
||||
# Berechtigungen prüfen
|
||||
az role assignment list \
|
||||
--assignee <service-principal-client-id> \
|
||||
--resource-group trusted_ai_demo_rg
|
||||
```
|
||||
|
||||
### Secrets Rotation
|
||||
|
||||
Rotiere Secrets regelmäßig:
|
||||
|
||||
```bash
|
||||
# Neues ACR Password generieren
|
||||
az acr credential renew \
|
||||
--name mylaravelregistry \
|
||||
--password-name password
|
||||
|
||||
# Service Principal Secret erneuern
|
||||
az ad sp credential reset \
|
||||
--id <service-principal-object-id>
|
||||
```
|
||||
|
||||
## 🚦 Branch Protection Rules
|
||||
|
||||
Empfohlene Branch Protection Rules für `main` und `production`:
|
||||
|
||||
1. Gehe zu Repository → Settings → Branches
|
||||
2. Füge Branch Protection Rule hinzu für `main` und `production`
|
||||
3. Aktiviere:
|
||||
- ✅ Require status checks to pass before merging
|
||||
- ✅ Require branches to be up to date before merging
|
||||
- ✅ Require deployments to succeed before merging
|
||||
- ✅ Require conversation resolution before merging
|
||||
- ✅ Include administrators
|
||||
|
||||
## 📈 Deployment Environments
|
||||
|
||||
GitHub Environments für bessere Kontrolle:
|
||||
|
||||
1. Gehe zu Repository → Settings → Environments
|
||||
2. Erstelle zwei Environments: `staging` und `production`
|
||||
3. Für `production`:
|
||||
- ✅ Required reviewers: Füge Reviewer hinzu
|
||||
- ✅ Wait timer: 5 minutes
|
||||
- ✅ Deployment branches: Only `production` branch
|
||||
|
||||
## 🔄 CI/CD Best Practices
|
||||
|
||||
### 1. Feature Branch Workflow
|
||||
|
||||
```bash
|
||||
# Feature branch erstellen
|
||||
git checkout -b feature/new-feature
|
||||
|
||||
# Änderungen committen
|
||||
git add .
|
||||
git commit -m "Add new feature"
|
||||
|
||||
# Push und Pull Request erstellen
|
||||
git push origin feature/new-feature
|
||||
|
||||
# Nach Review: Merge in main (automatisches Staging Deployment)
|
||||
# Dann: Merge in production (automatisches Production Deployment)
|
||||
```
|
||||
|
||||
### 2. Semantic Versioning
|
||||
|
||||
Verwende Git Tags für Releases:
|
||||
|
||||
```bash
|
||||
# Tag erstellen
|
||||
git tag -a v1.0.0 -m "Release version 1.0.0"
|
||||
git push origin v1.0.0
|
||||
|
||||
# Workflow wird Docker Image mit diesem Tag bauen
|
||||
```
|
||||
|
||||
### 3. Rollback Strategy
|
||||
|
||||
```bash
|
||||
# Bei Problemen: Zu vorheriger Version zurück
|
||||
gh workflow run deploy-azure.yml \
|
||||
--ref <previous-commit-sha> \
|
||||
--field environment=production
|
||||
|
||||
# Oder: Kubernetes Rollback
|
||||
kubectl rollout undo deployment/laravel-app -n laravel-app
|
||||
```
|
||||
|
||||
## 📚 Weitere Ressourcen
|
||||
|
||||
- [GitHub Actions Documentation](https://docs.github.com/en/actions)
|
||||
- [Azure DevOps Documentation](https://docs.microsoft.com/en-us/azure/devops/)
|
||||
- [Terraform Cloud](https://www.terraform.io/cloud) - Alternative für Terraform State Management
|
||||
|
||||
## 🎯 Nächste Schritte
|
||||
|
||||
Nach Setup der CI/CD Pipeline:
|
||||
|
||||
1. [ ] Teste Pipeline mit Dummy-Commit
|
||||
2. [ ] Erstelle Feature Branch und PR
|
||||
3. [ ] Richte Branch Protection Rules ein
|
||||
4. [ ] Konfiguriere GitHub Environments
|
||||
5. [ ] Dokumentiere Team-Workflow
|
||||
6. [ ] Teste Rollback-Prozess
|
||||
7. [ ] Richte Slack/Teams Notifications ein (optional)
|
||||
|
||||
---
|
||||
|
||||
**Support**: Bei Problemen prüfe GitHub Actions Logs und Terraform State
|
||||
@@ -0,0 +1,398 @@
|
||||
# 🚀 Quickstart Guide - Laravel auf Azure AKS
|
||||
|
||||
Diese Anleitung führt dich in ~30 Minuten durch das komplette Deployment deiner Laravel-Anwendung auf Azure Kubernetes Service.
|
||||
|
||||
## ✅ Voraussetzungen Check
|
||||
|
||||
```bash
|
||||
# Prüfe ob alle Tools installiert sind
|
||||
terraform version # >= 1.9.0
|
||||
az version # >= 2.0
|
||||
kubectl version # >= 1.28
|
||||
docker version # >= 20.10
|
||||
php -v # >= 8.2
|
||||
```
|
||||
|
||||
## 📝 Schritt-für-Schritt Anleitung
|
||||
|
||||
### 1️⃣ Azure Login (2 Min)
|
||||
|
||||
```bash
|
||||
# Login
|
||||
az login
|
||||
|
||||
# Subscription setzen
|
||||
az account set --subscription "77677a80-2dea-493d-9867-f1c961b80fb3"
|
||||
|
||||
# Verify
|
||||
az account show
|
||||
```
|
||||
|
||||
### 2️⃣ Container Registry Setup (5 Min)
|
||||
|
||||
```bash
|
||||
# ACR erstellen (wenn nicht vorhanden)
|
||||
az acr create \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name mylaravelregistry \
|
||||
--sku Basic \
|
||||
--location germanywestcentral
|
||||
|
||||
# AKS Zugriff auf ACR geben
|
||||
az aks update \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name trai_k8s_cluster \
|
||||
--attach-acr mylaravelregistry
|
||||
|
||||
# Login
|
||||
az acr login --name mylaravelregistry
|
||||
```
|
||||
|
||||
### 3️⃣ Docker Image Build & Push (5 Min)
|
||||
|
||||
```bash
|
||||
# Zum Projekt-Root wechseln
|
||||
cd /Users/sebastianfrohlich/Herd/frontend
|
||||
|
||||
# Image bauen
|
||||
docker build -t mylaravelregistry.azurecr.io/laravel-app:v1.0.0 .
|
||||
|
||||
# Image pushen
|
||||
docker push mylaravelregistry.azurecr.io/laravel-app:v1.0.0
|
||||
```
|
||||
|
||||
### 4️⃣ Laravel APP_KEY generieren (1 Min)
|
||||
|
||||
```bash
|
||||
# Im Projekt-Root
|
||||
php artisan key:generate --show
|
||||
|
||||
# Beispiel Output: base64:abcdefgh12345...
|
||||
# Kopiere diesen Wert für den nächsten Schritt
|
||||
```
|
||||
|
||||
### 5️⃣ Terraform Konfiguration (5 Min)
|
||||
|
||||
```bash
|
||||
# Zu Terraform wechseln
|
||||
cd terraform
|
||||
|
||||
# Konfiguration kopieren
|
||||
cp terraform.tfvars.example terraform.tfvars
|
||||
|
||||
# Bearbeiten
|
||||
nano terraform.tfvars
|
||||
```
|
||||
|
||||
**Wichtige Werte in `terraform.tfvars` ändern:**
|
||||
|
||||
```hcl
|
||||
# Docker Image (von Schritt 3)
|
||||
docker_image = "mylaravelregistry.azurecr.io/laravel-app:v1.0.0"
|
||||
|
||||
# Laravel Key (von Schritt 4)
|
||||
app_key = "base64:abcdefgh12345..."
|
||||
|
||||
# PostgreSQL Password (sicheres Passwort wählen!)
|
||||
postgresql_admin_password = "YourSecurePassword123!"
|
||||
|
||||
# Email für Alerts
|
||||
alert_email_address = "your-email@example.com"
|
||||
```
|
||||
|
||||
### 6️⃣ Deployment ausführen (10 Min)
|
||||
|
||||
```bash
|
||||
# Automatisches Deployment mit Script
|
||||
./scripts/deploy.sh
|
||||
```
|
||||
|
||||
Das Script führt automatisch aus:
|
||||
- ✅ Prerequisite-Check
|
||||
- ✅ Terraform init
|
||||
- ✅ Terraform validate
|
||||
- ✅ Terraform plan
|
||||
- ✅ Terraform apply (nach Bestätigung)
|
||||
- ✅ kubectl konfigurieren
|
||||
- ✅ Pod-Status prüfen
|
||||
|
||||
**Oder manuell:**
|
||||
|
||||
```bash
|
||||
# Init
|
||||
terraform init
|
||||
|
||||
# Plan
|
||||
terraform plan -out=tfplan
|
||||
|
||||
# Apply
|
||||
terraform apply tfplan
|
||||
|
||||
# kubectl konfigurieren
|
||||
az aks get-credentials \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name trai_k8s_cluster \
|
||||
--overwrite-existing
|
||||
```
|
||||
|
||||
### 7️⃣ Datenbank wiederherstellen (5 Min)
|
||||
|
||||
```bash
|
||||
# Automatisches Restore-Script
|
||||
./scripts/restore-db.sh
|
||||
|
||||
# Script fragt:
|
||||
# - Welches Backup-File? (z.B. backup_backend_20251203_101741.dump)
|
||||
# - Bestätigung: yes
|
||||
# - Zeigt Fortschritt und Logs
|
||||
```
|
||||
|
||||
**Oder in terraform.tfvars setzen und nochmal apply:**
|
||||
|
||||
```hcl
|
||||
db_restore_enabled = true
|
||||
db_backup_file_path = "../backups/backup_backend_20251203_101741.dump"
|
||||
```
|
||||
|
||||
```bash
|
||||
terraform apply
|
||||
```
|
||||
|
||||
### 8️⃣ Anwendung testen (2 Min)
|
||||
|
||||
```bash
|
||||
# LoadBalancer IP abrufen
|
||||
kubectl get svc ingress-nginx-controller -n ingress-nginx
|
||||
|
||||
# Oder über Terraform Output
|
||||
terraform output app_url
|
||||
|
||||
# Example Output:
|
||||
# app_url = "http://20.79.123.456"
|
||||
```
|
||||
|
||||
🎉 **Öffne die URL im Browser!**
|
||||
|
||||
### 9️⃣ Status prüfen
|
||||
|
||||
```bash
|
||||
# Pods prüfen
|
||||
kubectl get pods -n laravel-app
|
||||
|
||||
# Services prüfen
|
||||
kubectl get svc -n laravel-app
|
||||
|
||||
# Ingress prüfen
|
||||
kubectl get ingress -n laravel-app
|
||||
|
||||
# Logs anzeigen
|
||||
kubectl logs -n laravel-app -l app=laravel-app -f
|
||||
|
||||
# Database restore job prüfen (wenn aktiviert)
|
||||
kubectl get jobs -n laravel-app
|
||||
kubectl logs -n laravel-app -l job-type=database-restore
|
||||
```
|
||||
|
||||
## 🔧 Häufige Probleme
|
||||
|
||||
### Problem: Pods starten nicht (ImagePullBackOff)
|
||||
|
||||
```bash
|
||||
# Prüfe ob ACR-Integration funktioniert
|
||||
az aks check-acr \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name trai_k8s_cluster \
|
||||
--acr mylaravelregistry.azurecr.io
|
||||
|
||||
# Fix: ACR Integration neu setzen
|
||||
az aks update \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name trai_k8s_cluster \
|
||||
--attach-acr mylaravelregistry
|
||||
```
|
||||
|
||||
### Problem: LoadBalancer IP bleibt "Pending"
|
||||
|
||||
```bash
|
||||
# Prüfe Service Events
|
||||
kubectl describe svc ingress-nginx-controller -n ingress-nginx
|
||||
|
||||
# Warte 5-10 Minuten - Azure braucht Zeit für LoadBalancer Setup
|
||||
kubectl get svc -n ingress-nginx -w
|
||||
```
|
||||
|
||||
### Problem: Database Connection Failed
|
||||
|
||||
```bash
|
||||
# Prüfe PostgreSQL Firewall Rules
|
||||
az postgres flexible-server firewall-rule list \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name <server-name>
|
||||
|
||||
# Prüfe Secrets
|
||||
kubectl get secret laravel-app-db-credentials -n laravel-app -o yaml | grep DB_
|
||||
```
|
||||
|
||||
### Problem: 502 Bad Gateway
|
||||
|
||||
```bash
|
||||
# Prüfe ob Pods ready sind
|
||||
kubectl get pods -n laravel-app
|
||||
|
||||
# Prüfe Pod Logs
|
||||
kubectl logs -n laravel-app <pod-name>
|
||||
|
||||
# Teste direkt zum Service
|
||||
kubectl port-forward -n laravel-app svc/laravel-app 8080:80
|
||||
# Dann: http://localhost:8080
|
||||
```
|
||||
|
||||
## 📊 Nützliche Befehle
|
||||
|
||||
### Status-Übersicht
|
||||
|
||||
```bash
|
||||
# Alles auf einen Blick
|
||||
kubectl get all -n laravel-app
|
||||
kubectl get all -n ingress-nginx
|
||||
|
||||
# Terraform Outputs
|
||||
terraform output
|
||||
```
|
||||
|
||||
### Logs anzeigen
|
||||
|
||||
```bash
|
||||
# Application Logs (alle Pods)
|
||||
kubectl logs -n laravel-app -l app=laravel-app -f
|
||||
|
||||
# Bestimmter Pod
|
||||
kubectl logs -n laravel-app <pod-name> -f
|
||||
|
||||
# Ingress Controller Logs
|
||||
kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller -f
|
||||
```
|
||||
|
||||
### Scale Up/Down
|
||||
|
||||
```bash
|
||||
# Mehr Replicas
|
||||
kubectl scale deployment/laravel-app --replicas=3 -n laravel-app
|
||||
|
||||
# Prüfen
|
||||
kubectl get pods -n laravel-app
|
||||
```
|
||||
|
||||
### Shell in Pod
|
||||
|
||||
```bash
|
||||
# Shell öffnen
|
||||
kubectl exec -it -n laravel-app <pod-name> -- /bin/sh
|
||||
|
||||
# Laravel Artisan in Pod ausführen
|
||||
kubectl exec -it -n laravel-app <pod-name> -- php artisan migrate:status
|
||||
kubectl exec -it -n laravel-app <pod-name> -- php artisan route:list
|
||||
```
|
||||
|
||||
### Port-Forward für lokalen Zugriff
|
||||
|
||||
```bash
|
||||
# App direkt aufrufen (ohne LoadBalancer)
|
||||
kubectl port-forward -n laravel-app svc/laravel-app 8080:80
|
||||
|
||||
# Dann: http://localhost:8080
|
||||
```
|
||||
|
||||
## 🔄 Updates deployen
|
||||
|
||||
### Neue Version deployen
|
||||
|
||||
```bash
|
||||
# 1. Code ändern
|
||||
# 2. Neues Image bauen
|
||||
docker build -t mylaravelregistry.azurecr.io/laravel-app:v1.0.1 .
|
||||
docker push mylaravelregistry.azurecr.io/laravel-app:v1.0.1
|
||||
|
||||
# 3. Deployment updaten (schnell)
|
||||
kubectl set image deployment/laravel-app \
|
||||
laravel-app=mylaravelregistry.azurecr.io/laravel-app:v1.0.1 \
|
||||
-n laravel-app
|
||||
|
||||
# Rollout Status prüfen
|
||||
kubectl rollout status deployment/laravel-app -n laravel-app
|
||||
```
|
||||
|
||||
### Rollback
|
||||
|
||||
```bash
|
||||
# Zur vorherigen Version zurück
|
||||
kubectl rollout undo deployment/laravel-app -n laravel-app
|
||||
```
|
||||
|
||||
## 🧹 Cleanup
|
||||
|
||||
### Nur App löschen (DB behalten)
|
||||
|
||||
```bash
|
||||
terraform destroy -target=kubernetes_deployment.app
|
||||
terraform destroy -target=kubernetes_service.app
|
||||
```
|
||||
|
||||
### Alles löschen
|
||||
|
||||
```bash
|
||||
terraform destroy
|
||||
```
|
||||
|
||||
## 📚 Weitere Informationen
|
||||
|
||||
- Ausführliche Dokumentation: [README.md](README.md)
|
||||
- Terraform Outputs: `terraform output`
|
||||
- Azure Portal: https://portal.azure.com
|
||||
|
||||
## 🆘 Hilfe benötigt?
|
||||
|
||||
```bash
|
||||
# Terraform Outputs anzeigen
|
||||
terraform output deployment_instructions
|
||||
|
||||
# Kubernetes Events prüfen
|
||||
kubectl get events -n laravel-app --sort-by='.lastTimestamp'
|
||||
|
||||
# Resource Status
|
||||
kubectl describe deployment laravel-app -n laravel-app
|
||||
kubectl describe svc laravel-app -n laravel-app
|
||||
```
|
||||
|
||||
## ✨ Next Steps
|
||||
|
||||
Nach erfolgreichem Deployment:
|
||||
|
||||
1. **Domain konfigurieren** (optional):
|
||||
```hcl
|
||||
# In terraform.tfvars
|
||||
ingress_host = "app.yourdomain.com"
|
||||
ssl_enabled = true
|
||||
ssl_issuer_email = "admin@yourdomain.com"
|
||||
```
|
||||
|
||||
```bash
|
||||
terraform apply
|
||||
```
|
||||
|
||||
2. **Monitoring einrichten**: Prüfe Azure Monitor in Azure Portal
|
||||
|
||||
3. **CI/CD Pipeline**: Erstelle GitHub Actions oder Azure DevOps Pipeline
|
||||
|
||||
4. **Backups testen**: Teste Restore-Prozess
|
||||
|
||||
5. **Security Hardening**:
|
||||
- VNet Integration für PostgreSQL
|
||||
- Private Endpoints
|
||||
- RBAC konfigurieren
|
||||
|
||||
---
|
||||
|
||||
**Deployment Time:** ~30 Minuten
|
||||
**Kosten:** ~50-100€/Monat (abhängig von Ressourcen)
|
||||
**Scaling:** Horizontal (mehr Pods) & Vertikal (größere VMs)
|
||||
@@ -0,0 +1,841 @@
|
||||
# Laravel Application Deployment auf Azure Kubernetes Service (AKS)
|
||||
|
||||
Diese Terraform-Konfiguration stellt deine Laravel-Anwendung auf Azure Kubernetes Service (AKS) bereit und erstellt eine Azure PostgreSQL Datenbank.
|
||||
|
||||
## 📋 Inhaltsverzeichnis
|
||||
|
||||
- [Voraussetzungen](#voraussetzungen)
|
||||
- [Architektur-Übersicht](#architektur-übersicht)
|
||||
- [Schnellstart](#schnellstart)
|
||||
- [Detaillierte Anleitung](#detaillierte-anleitung)
|
||||
- [Konfiguration](#konfiguration)
|
||||
- [Deployment](#deployment)
|
||||
- [Datenbank-Wiederherstellung](#datenbank-wiederherstellung)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Wartung](#wartung)
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
Folgende Tools müssen installiert sein:
|
||||
|
||||
- [Terraform](https://www.terraform.io/downloads.html) >= 1.9.0
|
||||
- [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) >= 2.0
|
||||
- [kubectl](https://kubernetes.io/docs/tasks/tools/) >= 1.28
|
||||
- [Docker](https://docs.docker.com/get-docker/) >= 20.10
|
||||
- [PHP](https://www.php.net/downloads) >= 8.2 (für lokale Laravel-Befehle)
|
||||
|
||||
## Architektur-Übersicht
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Azure Cloud │
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────────────────┐ │
|
||||
│ │ Resource Group: trusted_ai_demo_rg │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────────────────────────────────────────────┐ │ │
|
||||
│ │ │ AKS Cluster: trai_k8s_cluster │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ ┌────────────────────────────────────────────┐ │ │ │
|
||||
│ │ │ │ Namespace: laravel-app │ │ │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ │ │ • Deployment (2+ Pods) │ │ │ │
|
||||
│ │ │ │ • Service (ClusterIP) │ │ │ │
|
||||
│ │ │ │ • ConfigMaps & Secrets │ │ │ │
|
||||
│ │ │ │ • Horizontal Pod Autoscaler │ │ │ │
|
||||
│ │ │ └────────────────────────────────────────────┘ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ ┌────────────────────────────────────────────┐ │ │ │
|
||||
│ │ │ │ Namespace: ingress-nginx │ │ │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ │ │ • Nginx Ingress Controller │ │ │ │
|
||||
│ │ │ │ • LoadBalancer Service (Public IP) │ │ │ │
|
||||
│ │ │ └────────────────────────────────────────────┘ │ │ │
|
||||
│ │ └──────────────────────────────────────────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────────────────────────────────────────────┐ │ │
|
||||
│ │ │ Azure PostgreSQL Flexible Server │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ • PostgreSQL 16 │ │ │
|
||||
│ │ │ • 32 GB Storage │ │ │
|
||||
│ │ │ • Automated Backups (7 days) │ │ │
|
||||
│ │ └──────────────────────────────────────────────────┘ │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Komponenten:
|
||||
|
||||
1. **AKS Cluster**: Bestehender Kubernetes-Cluster
|
||||
2. **Laravel Application**: Containerisierte Laravel-App mit Nginx + PHP-FPM
|
||||
3. **PostgreSQL Database**: Azure Database for PostgreSQL Flexible Server
|
||||
4. **Nginx Ingress Controller**: Load Balancer für externen Zugriff
|
||||
5. **Secrets Management**: Kubernetes Secrets für sensible Daten
|
||||
|
||||
## Schnellstart
|
||||
|
||||
### 1. Azure CLI Login
|
||||
|
||||
```bash
|
||||
az login
|
||||
az account set --subscription "77677a80-2dea-493d-9867-f1c961b80fb3"
|
||||
```
|
||||
|
||||
### 2. Docker Image erstellen und pushen
|
||||
|
||||
Erstelle zuerst eine Azure Container Registry (falls noch nicht vorhanden):
|
||||
|
||||
```bash
|
||||
# Container Registry erstellen
|
||||
az acr create \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name mylaravelregistry \
|
||||
--sku Basic \
|
||||
--location germanywestcentral
|
||||
|
||||
# In Registry einloggen
|
||||
az acr login --name mylaravelregistry
|
||||
```
|
||||
|
||||
Dann baue und pushe das Docker Image:
|
||||
|
||||
```bash
|
||||
# Zurück zum Projekt-Root
|
||||
cd /Users/sebastianfrohlich/Herd/frontend
|
||||
|
||||
# Docker Image bauen
|
||||
docker build -t mylaravelregistry.azurecr.io/laravel-app:v1.0.0 .
|
||||
|
||||
# Image pushen
|
||||
docker push mylaravelregistry.azurecr.io/laravel-app:v1.0.0
|
||||
```
|
||||
|
||||
### 3. Terraform konfigurieren
|
||||
|
||||
```bash
|
||||
cd terraform
|
||||
|
||||
# Kopiere die Beispiel-Konfiguration
|
||||
cp terraform.tfvars.example terraform.tfvars
|
||||
|
||||
# Bearbeite terraform.tfvars
|
||||
nano terraform.tfvars
|
||||
```
|
||||
|
||||
Wichtige Werte in `terraform.tfvars`:
|
||||
|
||||
```hcl
|
||||
docker_image = "mylaravelregistry.azurecr.io/laravel-app:v1.0.0"
|
||||
app_key = "base64:YOUR_GENERATED_KEY" # Generiere mit: php artisan key:generate --show
|
||||
postgresql_admin_password = "YourSecurePassword123!"
|
||||
alert_email_address = "your-email@example.com"
|
||||
```
|
||||
|
||||
### 4. Deployment ausführen
|
||||
|
||||
```bash
|
||||
# Automatisches Deployment mit Script
|
||||
./scripts/deploy.sh
|
||||
```
|
||||
|
||||
Oder manuell:
|
||||
|
||||
```bash
|
||||
# Terraform initialisieren
|
||||
terraform init
|
||||
|
||||
# Plan überprüfen
|
||||
terraform plan
|
||||
|
||||
# Deployment ausführen
|
||||
terraform apply
|
||||
```
|
||||
|
||||
### 5. Datenbank wiederherstellen
|
||||
|
||||
```bash
|
||||
# Interaktives Restore-Script
|
||||
./scripts/restore-db.sh
|
||||
```
|
||||
|
||||
Oder manuell:
|
||||
|
||||
```bash
|
||||
# Setze db_restore_enabled = true in terraform.tfvars
|
||||
terraform apply
|
||||
```
|
||||
|
||||
## Detaillierte Anleitung
|
||||
|
||||
### Schritt 1: Azure Container Registry (ACR) Setup
|
||||
|
||||
Die Laravel-Anwendung muss als Docker Image bereitgestellt werden:
|
||||
|
||||
```bash
|
||||
# ACR erstellen (wenn nicht vorhanden)
|
||||
az acr create \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name mylaravelregistry \
|
||||
--sku Basic
|
||||
|
||||
# Admin-Zugriff aktivieren (optional, für einfachere Handhabung)
|
||||
az acr update -n mylaravelregistry --admin-enabled true
|
||||
|
||||
# Login credentials abrufen
|
||||
az acr credential show --name mylaravelregistry
|
||||
|
||||
# Docker login
|
||||
az acr login --name mylaravelregistry
|
||||
```
|
||||
|
||||
### Schritt 2: Docker Image Build & Push
|
||||
|
||||
```bash
|
||||
cd /Users/sebastianfrohlich/Herd/frontend
|
||||
|
||||
# Image mit Tag bauen
|
||||
docker build -t mylaravelregistry.azurecr.io/laravel-app:latest .
|
||||
docker build -t mylaravelregistry.azurecr.io/laravel-app:v1.0.0 .
|
||||
|
||||
# Image pushen
|
||||
docker push mylaravelregistry.azurecr.io/laravel-app:latest
|
||||
docker push mylaravelregistry.azurecr.io/laravel-app:v1.0.0
|
||||
```
|
||||
|
||||
### Schritt 3: AKS Pull-Berechtigung für ACR
|
||||
|
||||
```bash
|
||||
# AKS Pull-Berechtigung für ACR erteilen
|
||||
az aks update \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name trai_k8s_cluster \
|
||||
--attach-acr mylaravelregistry
|
||||
```
|
||||
|
||||
### Schritt 4: Laravel APP_KEY generieren
|
||||
|
||||
```bash
|
||||
cd /Users/sebastianfrohlich/Herd/frontend
|
||||
php artisan key:generate --show
|
||||
```
|
||||
|
||||
Kopiere den Output (z.B. `base64:xyz...`) in deine `terraform.tfvars`.
|
||||
|
||||
### Schritt 5: Terraform Konfiguration
|
||||
|
||||
Erstelle `terraform/terraform.tfvars`:
|
||||
|
||||
```hcl
|
||||
# Azure Configuration
|
||||
subscription_id = "77677a80-2dea-493d-9867-f1c961b80fb3"
|
||||
resource_group_name = "trusted_ai_demo_rg"
|
||||
location = "germanywestcentral"
|
||||
aks_cluster_name = "trai_k8s_cluster"
|
||||
|
||||
# Application Configuration
|
||||
app_name = "laravel-app"
|
||||
app_namespace = "laravel-app"
|
||||
app_env = "production"
|
||||
app_debug = false
|
||||
app_replicas = 2
|
||||
|
||||
# Docker Image
|
||||
docker_image = "mylaravelregistry.azurecr.io/laravel-app:v1.0.0"
|
||||
|
||||
# Laravel APP_KEY (generiert mit: php artisan key:generate --show)
|
||||
app_key = "base64:YOUR_ACTUAL_KEY_HERE"
|
||||
|
||||
# PostgreSQL Configuration
|
||||
postgresql_admin_username = "pgadmin"
|
||||
postgresql_admin_password = "YourVerySecurePassword123!"
|
||||
postgresql_sku_name = "B_Standard_B1ms"
|
||||
postgresql_storage_mb = 32768
|
||||
postgresql_version = "16"
|
||||
postgresql_backup_retention_days = 7
|
||||
|
||||
# Ingress Configuration
|
||||
ingress_enabled = true
|
||||
ingress_host = "" # Leer lassen für IP-basierten Zugriff
|
||||
|
||||
# SSL/TLS Configuration (optional, später aktivieren)
|
||||
ssl_enabled = false
|
||||
ssl_issuer_email = ""
|
||||
|
||||
# Database Restore Configuration
|
||||
db_restore_enabled = true
|
||||
db_backup_file_path = "../backups/backup_backend_20251203_101741.dump"
|
||||
|
||||
# Alert Configuration
|
||||
alert_email_address = "your-email@example.com"
|
||||
```
|
||||
|
||||
### Schritt 6: Terraform Deployment
|
||||
|
||||
```bash
|
||||
cd /Users/sebastianfrohlich/Herd/frontend/terraform
|
||||
|
||||
# Terraform initialisieren
|
||||
terraform init
|
||||
|
||||
# Validierung
|
||||
terraform validate
|
||||
|
||||
# Plan erstellen und überprüfen
|
||||
terraform plan -out=tfplan
|
||||
|
||||
# Deployment ausführen
|
||||
terraform apply tfplan
|
||||
|
||||
# Outputs anzeigen
|
||||
terraform output
|
||||
terraform output deployment_instructions
|
||||
```
|
||||
|
||||
### Schritt 7: kubectl konfigurieren
|
||||
|
||||
```bash
|
||||
# kubectl Credentials abrufen
|
||||
az aks get-credentials \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name trai_k8s_cluster \
|
||||
--overwrite-existing
|
||||
|
||||
# Cluster-Zugriff testen
|
||||
kubectl get nodes
|
||||
|
||||
# Pods prüfen
|
||||
kubectl get pods -n laravel-app
|
||||
|
||||
# Services prüfen
|
||||
kubectl get svc -n laravel-app
|
||||
kubectl get svc -n ingress-nginx
|
||||
```
|
||||
|
||||
### Schritt 8: Anwendung testen
|
||||
|
||||
```bash
|
||||
# LoadBalancer IP abrufen
|
||||
kubectl get svc ingress-nginx-controller -n ingress-nginx
|
||||
|
||||
# External IP sollte angezeigt werden, z.B.:
|
||||
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
|
||||
# ingress-nginx-controller LoadBalancer 10.0.45.123 20.79.123.456 80:31234/TCP,443:31567/TCP
|
||||
|
||||
# Anwendung im Browser öffnen:
|
||||
# http://<EXTERNAL-IP>
|
||||
```
|
||||
|
||||
### HTTP Basic Authentication
|
||||
|
||||
Die Anwendung ist mit HTTP Basic Auth geschützt:
|
||||
|
||||
- **Benutzer**: `afc-user`
|
||||
- **Passwort**: `vDZFrZ+*~4gW=3^?-,:8{ga=7M.o5k,G`
|
||||
|
||||
Die Credentials sind in [`deploy/nginx/.htpasswd`](../deploy/nginx/.htpasswd) konfiguriert und werden während des Docker-Builds in das Image kopiert.
|
||||
|
||||
### Aktueller Deployment-Status
|
||||
|
||||
**Aktuell deployed**: Version `1.0.7`
|
||||
|
||||
**Aktuelle Konfiguration**:
|
||||
|
||||
- URL: `http://72.144.113.194/`
|
||||
- Docker Image: `laravelappreg.azurecr.io/laravel-app:1.0.7`
|
||||
- Replicas: 2
|
||||
- Datenbank: `laravel_app` (PostgreSQL 16) mit 3 Schemas
|
||||
- HTTP Basic Auth: Aktiv
|
||||
|
||||
**Letzte Änderungen (v1.0.7)**:
|
||||
|
||||
- HTTP Basic Authentication hinzugefügt
|
||||
- Nginx Redirect für Livewire JavaScript (`.min.js` → `.js`)
|
||||
- Alle 3 Datenbank-Schemas nach `laravel_app` migriert
|
||||
- Datenbankverbindungen in `config/database.php` aktualisiert
|
||||
- Session-Driver auf `database` umgestellt (Multi-Pod Support)
|
||||
|
||||
## Konfiguration
|
||||
|
||||
### Umgebungsvariablen
|
||||
|
||||
Die Laravel-Umgebungsvariablen werden über Kubernetes ConfigMaps und Secrets verwaltet:
|
||||
|
||||
**ConfigMap** (`kubernetes_config_map.app_config`):
|
||||
- APP_NAME, APP_ENV, APP_DEBUG, APP_URL
|
||||
- Session, Cache, Queue, Mail Konfiguration
|
||||
|
||||
**Secrets** (`kubernetes_secret.app_secrets`):
|
||||
- APP_KEY
|
||||
|
||||
**Database Secrets** (`kubernetes_secret.db_credentials`):
|
||||
- DB_CONNECTION, DB_HOST, DB_PORT, DB_DATABASE
|
||||
- DB_USERNAME, DB_PASSWORD
|
||||
|
||||
### Resource Limits
|
||||
|
||||
Standard-Einstellungen in `variables.tf`:
|
||||
|
||||
```hcl
|
||||
app_resources_requests_cpu = "100m"
|
||||
app_resources_requests_memory = "256Mi"
|
||||
app_resources_limits_cpu = "500m"
|
||||
app_resources_limits_memory = "512Mi"
|
||||
```
|
||||
|
||||
Diese können in `terraform.tfvars` überschrieben werden.
|
||||
|
||||
### Horizontal Pod Autoscaling
|
||||
|
||||
Die Anwendung skaliert automatisch basierend auf CPU und Memory:
|
||||
|
||||
- Min Replicas: `app_replicas` (default: 2)
|
||||
- Max Replicas: `app_replicas * 3` (default: 6)
|
||||
- CPU Threshold: 80%
|
||||
- Memory Threshold: 80%
|
||||
|
||||
## Datenbank-Konfiguration
|
||||
|
||||
### Datenbank-Struktur
|
||||
|
||||
Die Anwendung verwendet **eine PostgreSQL-Datenbank** (`laravel_app`) mit **drei Schemas**:
|
||||
|
||||
#### 1. **public** Schema (12 Tabellen)
|
||||
Standard Laravel-Tabellen und Application-Daten:
|
||||
- backend_data_pool, cache, cache_locks
|
||||
- companies, failed_jobs, job_batches, jobs
|
||||
- migrations, password_reset_tokens, sessions
|
||||
- transactions, users
|
||||
|
||||
**Connection**: `DB::connection('pgsql_second')` oder `DB::connection('pgsql')`
|
||||
|
||||
#### 2. **backend** Schema (11 Tabellen)
|
||||
Backend-spezifische Daten:
|
||||
- entity_corporate_context, evidence_registry
|
||||
- fallback_events, internal_context_blobs
|
||||
- prompt_results, prompt_runs, prompt_templates, prompt_token_metrics
|
||||
- sources, transaction_outputs, transactions
|
||||
|
||||
**Connection**: `DB::connection('backend')`
|
||||
|
||||
#### 3. **devbackend** Schema (13 Tabellen)
|
||||
Development/Testing Backend-Daten:
|
||||
- document_facts, entity_corporate_context
|
||||
- evidence_registry, fallback_events
|
||||
- internal_context_blobs, pdf_documents
|
||||
- prompt_results, prompt_runs, prompt_templates, prompt_token_metrics
|
||||
- sources, transaction_outputs, transactions
|
||||
|
||||
**Connection**: `DB::connection('devbackend')`
|
||||
|
||||
### Verwendung in Laravel
|
||||
|
||||
```php
|
||||
// Public Schema (Standard)
|
||||
$users = DB::connection('pgsql')->table('users')->get();
|
||||
$users = DB::connection('pgsql_second')->table('users')->get();
|
||||
|
||||
// Backend Schema
|
||||
$transactions = DB::connection('backend')->table('transactions')->get();
|
||||
|
||||
// DevBackend Schema
|
||||
$documents = DB::connection('devbackend')->table('pdf_documents')->get();
|
||||
```
|
||||
|
||||
Alle Connections sind in [config/database.php](../config/database.php) konfiguriert und zeigen auf dieselbe Datenbank `laravel_app` mit unterschiedlichen `search_path` Einstellungen.
|
||||
|
||||
## Datenbank-Wiederherstellung
|
||||
|
||||
### Automatische Wiederherstellung während Deployment
|
||||
|
||||
Die Datenbank-Wiederherstellung importiert alle drei Schemas in die `laravel_app` Datenbank:
|
||||
|
||||
```bash
|
||||
# Backups befinden sich in:
|
||||
/Users/sebastianfrohlich/Herd/frontend/backups/
|
||||
- backup_public_20251203_101723.dump # Public Schema
|
||||
- backup_backend_20251203_101741.dump # Backend Schema
|
||||
- backup_devbackend_20251203_103857.dump # DevBackend Schema
|
||||
```
|
||||
|
||||
Setze in `terraform.tfvars`:
|
||||
|
||||
```hcl
|
||||
db_restore_enabled = true
|
||||
db_backup_file_path = "../backups/backup_backend_20251203_101741.dump"
|
||||
```
|
||||
|
||||
Das Backup wird als Kubernetes Job ausgeführt.
|
||||
|
||||
### Manuelle Wiederherstellung
|
||||
|
||||
```bash
|
||||
# Interaktives Script
|
||||
./scripts/restore-db.sh
|
||||
|
||||
# Script fragt nach:
|
||||
# - Welche Backup-Datei verwendet werden soll
|
||||
# - Bestätigung zum Überschreiben der Datenbank
|
||||
# - Zeigt Fortschritt und Logs an
|
||||
```
|
||||
|
||||
### Manuelles DB-Restore (kubectl)
|
||||
|
||||
```bash
|
||||
# ConfigMap mit Backup erstellen
|
||||
kubectl create configmap db-restore-backup \
|
||||
--from-file=backup.dump=/Users/sebastianfrohlich/Herd/frontend/backups/backup_backend_20251203_101741.dump \
|
||||
-n laravel-app
|
||||
|
||||
# Job aus db-restore.tf verwenden oder manuell erstellen
|
||||
kubectl apply -f - <<EOF
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: db-restore-manual
|
||||
namespace: laravel-app
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: db-restore
|
||||
image: postgres:16-alpine
|
||||
command: ["/bin/sh", "-c"]
|
||||
args:
|
||||
- |
|
||||
set -e
|
||||
export PGPASSWORD="\$DB_PASSWORD"
|
||||
pg_restore -h \$DB_HOST -p \$DB_PORT -U \$DB_USERNAME -d \$DB_DATABASE \
|
||||
--verbose --clean --if-exists --no-owner --no-privileges /backup/backup.dump
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: laravel-app-db-credentials
|
||||
volumeMounts:
|
||||
- name: backup-volume
|
||||
mountPath: /backup
|
||||
volumes:
|
||||
- name: backup-volume
|
||||
configMap:
|
||||
name: db-restore-backup
|
||||
restartPolicy: OnFailure
|
||||
EOF
|
||||
|
||||
# Job-Status prüfen
|
||||
kubectl get jobs -n laravel-app
|
||||
|
||||
# Logs anzeigen
|
||||
kubectl logs -n laravel-app -l job-name=db-restore-manual -f
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
### Neues Image deployen
|
||||
|
||||
```bash
|
||||
# 1. Neues Image bauen
|
||||
docker build -t mylaravelregistry.azurecr.io/laravel-app:v1.0.1 .
|
||||
docker push mylaravelregistry.azurecr.io/laravel-app:v1.0.1
|
||||
|
||||
# 2. terraform.tfvars aktualisieren
|
||||
# docker_image = "mylaravelregistry.azurecr.io/laravel-app:v1.0.1"
|
||||
|
||||
# 3. Terraform apply
|
||||
terraform apply
|
||||
|
||||
# Oder direkt kubectl verwenden für schnelleres Update:
|
||||
kubectl set image deployment/laravel-app \
|
||||
laravel-app=mylaravelregistry.azurecr.io/laravel-app:v1.0.1 \
|
||||
-n laravel-app
|
||||
|
||||
# Rollout Status prüfen
|
||||
kubectl rollout status deployment/laravel-app -n laravel-app
|
||||
```
|
||||
|
||||
### Rollback
|
||||
|
||||
```bash
|
||||
# Rollout Historie anzeigen
|
||||
kubectl rollout history deployment/laravel-app -n laravel-app
|
||||
|
||||
# Zu vorheriger Version zurückkehren
|
||||
kubectl rollout undo deployment/laravel-app -n laravel-app
|
||||
|
||||
# Zu spezifischer Revision zurückkehren
|
||||
kubectl rollout undo deployment/laravel-app --to-revision=2 -n laravel-app
|
||||
```
|
||||
|
||||
### Replicas skalieren
|
||||
|
||||
```bash
|
||||
# Über kubectl
|
||||
kubectl scale deployment/laravel-app --replicas=3 -n laravel-app
|
||||
|
||||
# Über Terraform
|
||||
# Ändere app_replicas in terraform.tfvars und führe aus:
|
||||
terraform apply
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Pods starten nicht
|
||||
|
||||
```bash
|
||||
# Pod-Status prüfen
|
||||
kubectl get pods -n laravel-app
|
||||
|
||||
# Detaillierte Pod-Informationen
|
||||
kubectl describe pod <pod-name> -n laravel-app
|
||||
|
||||
# Pod-Logs anzeigen
|
||||
kubectl logs <pod-name> -n laravel-app
|
||||
|
||||
# Vorherige Pod-Logs (bei CrashLoopBackOff)
|
||||
kubectl logs <pod-name> -n laravel-app --previous
|
||||
```
|
||||
|
||||
### Datenbank-Verbindungsprobleme
|
||||
|
||||
```bash
|
||||
# Secrets prüfen
|
||||
kubectl get secret laravel-app-db-credentials -n laravel-app -o yaml
|
||||
|
||||
# PostgreSQL Server Firewall prüfen
|
||||
az postgres flexible-server firewall-rule list \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name <postgresql-server-name>
|
||||
|
||||
# Verbindung vom Pod aus testen
|
||||
kubectl run -it --rm debug --image=postgres:16-alpine -n laravel-app -- \
|
||||
psql "postgresql://user:pass@host:5432/database"
|
||||
```
|
||||
|
||||
### Ingress funktioniert nicht
|
||||
|
||||
```bash
|
||||
# Ingress Status prüfen
|
||||
kubectl get ingress -n laravel-app
|
||||
|
||||
# Ingress Controller Logs
|
||||
kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller
|
||||
|
||||
# LoadBalancer Service prüfen
|
||||
kubectl get svc -n ingress-nginx
|
||||
|
||||
# Ingress Events prüfen
|
||||
kubectl describe ingress laravel-app-ingress -n laravel-app
|
||||
```
|
||||
|
||||
### SSL/TLS Zertifikat-Probleme
|
||||
|
||||
```bash
|
||||
# cert-manager Pods prüfen
|
||||
kubectl get pods -n cert-manager
|
||||
|
||||
# Certificate Status prüfen
|
||||
kubectl get certificate -n laravel-app
|
||||
|
||||
# Certificate Details
|
||||
kubectl describe certificate laravel-app-tls -n laravel-app
|
||||
|
||||
# cert-manager Logs
|
||||
kubectl logs -n cert-manager -l app=cert-manager
|
||||
```
|
||||
|
||||
### Performance-Probleme
|
||||
|
||||
```bash
|
||||
# Resource Usage prüfen
|
||||
kubectl top pods -n laravel-app
|
||||
kubectl top nodes
|
||||
|
||||
# HPA Status prüfen
|
||||
kubectl get hpa -n laravel-app
|
||||
|
||||
# Events prüfen
|
||||
kubectl get events -n laravel-app --sort-by='.lastTimestamp'
|
||||
```
|
||||
|
||||
## Wartung
|
||||
|
||||
### Backups
|
||||
|
||||
PostgreSQL Flexible Server erstellt automatische Backups:
|
||||
|
||||
```bash
|
||||
# Backup-Konfiguration prüfen
|
||||
az postgres flexible-server show \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name <postgresql-server-name> \
|
||||
--query "{backupRetentionDays:backup.backupRetentionDays,geoRedundantBackup:backup.geoRedundantBackup}"
|
||||
|
||||
# Manuelles Backup erstellen
|
||||
az postgres flexible-server backup create \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--name <postgresql-server-name> \
|
||||
--backup-name manual-backup-$(date +%Y%m%d)
|
||||
```
|
||||
|
||||
### Updates
|
||||
|
||||
#### Terraform Updates
|
||||
|
||||
```bash
|
||||
# Terraform Zustand aktualisieren
|
||||
terraform refresh
|
||||
|
||||
# Änderungen planen
|
||||
terraform plan
|
||||
|
||||
# Änderungen anwenden
|
||||
terraform apply
|
||||
```
|
||||
|
||||
#### Laravel Updates
|
||||
|
||||
```bash
|
||||
# Composer Dependencies aktualisieren
|
||||
composer update
|
||||
|
||||
# NPM Dependencies aktualisieren
|
||||
npm update
|
||||
|
||||
# Neues Image bauen und deployen
|
||||
docker build -t mylaravelregistry.azurecr.io/laravel-app:v1.0.2 .
|
||||
docker push mylaravelregistry.azurecr.io/laravel-app:v1.0.2
|
||||
|
||||
# Deployment aktualisieren
|
||||
kubectl set image deployment/laravel-app \
|
||||
laravel-app=mylaravelregistry.azurecr.io/laravel-app:v1.0.2 \
|
||||
-n laravel-app
|
||||
```
|
||||
|
||||
### Monitoring
|
||||
|
||||
```bash
|
||||
# Log Analytics Workspace
|
||||
az monitor log-analytics workspace show \
|
||||
--resource-group trusted_ai_demo_rg \
|
||||
--workspace-name <workspace-name>
|
||||
|
||||
# Azure Monitor für Container
|
||||
# Im Azure Portal: AKS Cluster > Monitoring > Insights
|
||||
|
||||
# Prometheus Metrics (wenn aktiviert)
|
||||
kubectl port-forward -n ingress-nginx \
|
||||
svc/ingress-nginx-controller-metrics 10254:10254
|
||||
# Dann: http://localhost:10254/metrics
|
||||
```
|
||||
|
||||
### Cleanup
|
||||
|
||||
```bash
|
||||
# Einzelne Resources löschen
|
||||
terraform destroy -target=kubernetes_deployment.app
|
||||
terraform destroy -target=azurerm_postgresql_flexible_server.main
|
||||
|
||||
# Alles löschen
|
||||
terraform destroy
|
||||
|
||||
# Namespace löschen (löscht alle Resources im Namespace)
|
||||
kubectl delete namespace laravel-app
|
||||
```
|
||||
|
||||
## Kosten-Optimierung
|
||||
|
||||
### Development/Staging
|
||||
|
||||
Für Dev/Staging Umgebungen kannst du Kosten sparen:
|
||||
|
||||
```hcl
|
||||
# terraform.tfvars für Staging
|
||||
postgresql_sku_name = "B_Standard_B1ms" # Burstable tier
|
||||
postgresql_storage_mb = 32768 # 32 GB
|
||||
app_replicas = 1 # Weniger Replicas
|
||||
aks_node_pool_min_count = 1 # Weniger Nodes
|
||||
```
|
||||
|
||||
### Production
|
||||
|
||||
Für Production empfohlen:
|
||||
|
||||
```hcl
|
||||
postgresql_sku_name = "GP_Standard_D2s_v3" # General Purpose
|
||||
postgresql_storage_mb = 131072 # 128 GB
|
||||
postgresql_backup_retention_days = 35 # Längere Retention
|
||||
app_replicas = 3 # Mehr Replicas
|
||||
```
|
||||
|
||||
## Sicherheit
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Secrets Management**:
|
||||
- Verwende Azure Key Vault für Production
|
||||
- Rotiere Passwörter regelmäßig
|
||||
- Verwende starke, generierte Passwörter
|
||||
|
||||
2. **Network Security**:
|
||||
- Aktiviere VNet Integration für PostgreSQL
|
||||
- Verwende Private Endpoints
|
||||
- Beschränke Firewall-Regeln
|
||||
|
||||
3. **SSL/TLS**:
|
||||
- Aktiviere SSL für Production:
|
||||
```hcl
|
||||
ssl_enabled = true
|
||||
ssl_issuer_email = "admin@yourdomain.com"
|
||||
ingress_host = "app.yourdomain.com"
|
||||
```
|
||||
|
||||
4. **RBAC**:
|
||||
- Verwende Kubernetes RBAC
|
||||
- Minimale Berechtigungen für Service Accounts
|
||||
|
||||
## Support und Kontakt
|
||||
|
||||
Bei Fragen oder Problemen:
|
||||
|
||||
1. Prüfe die [Troubleshooting](#troubleshooting) Sektion
|
||||
2. Prüfe Kubernetes Events: `kubectl get events -n laravel-app`
|
||||
3. Prüfe Logs: `kubectl logs -n laravel-app -l app=laravel-app`
|
||||
|
||||
## Anhang
|
||||
|
||||
### Nützliche Befehle
|
||||
|
||||
```bash
|
||||
# Alle Resources in einem Namespace anzeigen
|
||||
kubectl get all -n laravel-app
|
||||
|
||||
# Port-forward für lokalen Zugriff
|
||||
kubectl port-forward -n laravel-app svc/laravel-app 8080:80
|
||||
|
||||
# Shell in einem Pod öffnen
|
||||
kubectl exec -it -n laravel-app <pod-name> -- /bin/sh
|
||||
|
||||
# ConfigMap/Secret bearbeiten
|
||||
kubectl edit configmap laravel-app-config -n laravel-app
|
||||
|
||||
# Resource Usage live monitoren
|
||||
watch kubectl top pods -n laravel-app
|
||||
|
||||
# Cluster Info
|
||||
kubectl cluster-info
|
||||
kubectl get nodes -o wide
|
||||
```
|
||||
|
||||
### Terraform State Management
|
||||
|
||||
Für Team-Arbeit solltest du Remote State verwenden:
|
||||
|
||||
```hcl
|
||||
# backend.hcl
|
||||
resource_group_name = "trusted_ai_demo_rg"
|
||||
storage_account_name = "tfstate<random>"
|
||||
container_name = "tfstate"
|
||||
key = "laravel-app.terraform.tfstate"
|
||||
```
|
||||
|
||||
```bash
|
||||
# Backend initialisieren
|
||||
terraform init -backend-config=backend.hcl
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
# Note: Database backup files are too large for ConfigMaps (>1MB limit)
|
||||
# Use manual restore via kubectl after deployment instead:
|
||||
#
|
||||
# kubectl cp ../backups/backup_backend_20251203_101741.dump \
|
||||
# laravel-app/postgresql-0:/tmp/backup.dump
|
||||
#
|
||||
# kubectl exec -it -n laravel-app postgresql-0 -- \
|
||||
# pg_restore -U pgadmin -d laravel_app --clean --if-exists \
|
||||
# /tmp/backup.dump
|
||||
#
|
||||
# Or use the restore script: ./scripts/restore-db.sh
|
||||
|
||||
# Disabled: Automatic database restore via Terraform
|
||||
# The ConfigMap approach doesn't work for files >1MB
|
||||
# Use manual restore after deployment (see comments above)
|
||||
@@ -0,0 +1,230 @@
|
||||
# Install nginx-ingress-controller via Helm
|
||||
resource "helm_release" "nginx_ingress" {
|
||||
count = var.ingress_enabled ? 1 : 0
|
||||
|
||||
name = "ingress-nginx"
|
||||
repository = "https://kubernetes.github.io/ingress-nginx"
|
||||
chart = "ingress-nginx"
|
||||
version = "4.11.3"
|
||||
namespace = "ingress-nginx"
|
||||
create_namespace = true
|
||||
|
||||
set {
|
||||
name = "controller.service.type"
|
||||
value = "LoadBalancer"
|
||||
}
|
||||
|
||||
set {
|
||||
name = "controller.service.annotations.service\\.beta\\.kubernetes\\.io/azure-load-balancer-health-probe-request-path"
|
||||
value = "/healthz"
|
||||
}
|
||||
|
||||
set {
|
||||
name = "controller.metrics.enabled"
|
||||
value = "true"
|
||||
}
|
||||
|
||||
set {
|
||||
name = "controller.podAnnotations.prometheus\\.io/scrape"
|
||||
value = "true"
|
||||
}
|
||||
|
||||
set {
|
||||
name = "controller.podAnnotations.prometheus\\.io/port"
|
||||
value = "10254"
|
||||
}
|
||||
|
||||
# Resource limits
|
||||
set {
|
||||
name = "controller.resources.requests.cpu"
|
||||
value = "100m"
|
||||
}
|
||||
|
||||
set {
|
||||
name = "controller.resources.requests.memory"
|
||||
value = "128Mi"
|
||||
}
|
||||
|
||||
set {
|
||||
name = "controller.resources.limits.cpu"
|
||||
value = "500m"
|
||||
}
|
||||
|
||||
set {
|
||||
name = "controller.resources.limits.memory"
|
||||
value = "512Mi"
|
||||
}
|
||||
|
||||
# Enable rate limiting
|
||||
set {
|
||||
name = "controller.config.limit-req-status-code"
|
||||
value = "429"
|
||||
}
|
||||
|
||||
timeout = 600
|
||||
}
|
||||
|
||||
# Wait for nginx-ingress to be ready and get LoadBalancer IP
|
||||
data "kubernetes_service" "ingress_nginx" {
|
||||
count = var.ingress_enabled ? 1 : 0
|
||||
|
||||
metadata {
|
||||
name = "ingress-nginx-controller"
|
||||
namespace = "ingress-nginx"
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
helm_release.nginx_ingress
|
||||
]
|
||||
}
|
||||
|
||||
# Install cert-manager for SSL/TLS (optional)
|
||||
resource "helm_release" "cert_manager" {
|
||||
count = var.ssl_enabled ? 1 : 0
|
||||
|
||||
name = "cert-manager"
|
||||
repository = "https://charts.jetstack.io"
|
||||
chart = "cert-manager"
|
||||
version = "v1.16.2"
|
||||
namespace = "cert-manager"
|
||||
create_namespace = true
|
||||
|
||||
set {
|
||||
name = "crds.enabled"
|
||||
value = "true"
|
||||
}
|
||||
|
||||
set {
|
||||
name = "global.leaderElection.namespace"
|
||||
value = "cert-manager"
|
||||
}
|
||||
|
||||
timeout = 600
|
||||
}
|
||||
|
||||
# ClusterIssuer for Let's Encrypt (production)
|
||||
resource "kubernetes_manifest" "letsencrypt_prod" {
|
||||
count = var.ssl_enabled && var.ssl_issuer_email != "" ? 1 : 0
|
||||
|
||||
manifest = {
|
||||
apiVersion = "cert-manager.io/v1"
|
||||
kind = "ClusterIssuer"
|
||||
metadata = {
|
||||
name = "letsencrypt-prod"
|
||||
}
|
||||
spec = {
|
||||
acme = {
|
||||
server = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
email = var.ssl_issuer_email
|
||||
privateKeySecretRef = {
|
||||
name = "letsencrypt-prod"
|
||||
}
|
||||
solvers = [
|
||||
{
|
||||
http01 = {
|
||||
ingress = {
|
||||
class = "nginx"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
helm_release.cert_manager
|
||||
]
|
||||
}
|
||||
|
||||
# ClusterIssuer for Let's Encrypt (staging) - for testing
|
||||
resource "kubernetes_manifest" "letsencrypt_staging" {
|
||||
count = var.ssl_enabled && var.ssl_issuer_email != "" ? 1 : 0
|
||||
|
||||
manifest = {
|
||||
apiVersion = "cert-manager.io/v1"
|
||||
kind = "ClusterIssuer"
|
||||
metadata = {
|
||||
name = "letsencrypt-staging"
|
||||
}
|
||||
spec = {
|
||||
acme = {
|
||||
server = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
email = var.ssl_issuer_email
|
||||
privateKeySecretRef = {
|
||||
name = "letsencrypt-staging"
|
||||
}
|
||||
solvers = [
|
||||
{
|
||||
http01 = {
|
||||
ingress = {
|
||||
class = "nginx"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
helm_release.cert_manager
|
||||
]
|
||||
}
|
||||
|
||||
# Kubernetes Ingress for Laravel Application
|
||||
resource "kubernetes_ingress_v1" "app" {
|
||||
count = var.ingress_enabled ? 1 : 0
|
||||
|
||||
metadata {
|
||||
name = "${var.app_name}-ingress"
|
||||
namespace = kubernetes_namespace.app.metadata[0].name
|
||||
labels = local.common_labels
|
||||
|
||||
annotations = merge(
|
||||
{
|
||||
"kubernetes.io/ingress.class" = "nginx"
|
||||
"nginx.ingress.kubernetes.io/rewrite-target" = "/"
|
||||
"nginx.ingress.kubernetes.io/ssl-redirect" = var.ssl_enabled ? "true" : "false"
|
||||
},
|
||||
var.ssl_enabled && var.ingress_host != "" ? {
|
||||
"cert-manager.io/cluster-issuer" = "letsencrypt-prod"
|
||||
} : {}
|
||||
)
|
||||
}
|
||||
|
||||
spec {
|
||||
dynamic "tls" {
|
||||
for_each = var.ssl_enabled && var.ingress_host != "" ? [1] : []
|
||||
content {
|
||||
hosts = [var.ingress_host]
|
||||
secret_name = "${var.app_name}-tls"
|
||||
}
|
||||
}
|
||||
|
||||
rule {
|
||||
host = var.ingress_host != "" ? var.ingress_host : null
|
||||
|
||||
http {
|
||||
path {
|
||||
path = "/"
|
||||
path_type = "Prefix"
|
||||
|
||||
backend {
|
||||
service {
|
||||
name = kubernetes_service.app.metadata[0].name
|
||||
port {
|
||||
number = 80
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
helm_release.nginx_ingress,
|
||||
kubernetes_service.app
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
# Kubernetes Namespace
|
||||
resource "kubernetes_namespace" "app" {
|
||||
metadata {
|
||||
name = var.app_namespace
|
||||
labels = merge(
|
||||
local.common_labels,
|
||||
{
|
||||
name = var.app_namespace
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
# Kubernetes Secret for Database Connection
|
||||
resource "kubernetes_secret" "db_credentials" {
|
||||
metadata {
|
||||
name = "${var.app_name}-db-credentials"
|
||||
namespace = kubernetes_namespace.app.metadata[0].name
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
data = {
|
||||
# Primary connection uses DB_HOST2 variables (for pgsql_second connection)
|
||||
DB_CONNECTION2 = "pgsql"
|
||||
DB_HOST2 = "postgresql.${kubernetes_namespace.app.metadata[0].name}.svc.cluster.local"
|
||||
DB_PORT2 = "5432"
|
||||
DB_DATABASE2 = local.postgresql_db_name
|
||||
DB_USERNAME2 = var.postgresql_admin_username
|
||||
DB_PASSWORD2 = local.postgresql_admin_pass
|
||||
|
||||
# Also set standard DB_ variables for pgsql connection (used by sessions)
|
||||
DB_CONNECTION = "pgsql"
|
||||
DB_HOST = "postgresql.${kubernetes_namespace.app.metadata[0].name}.svc.cluster.local"
|
||||
DB_PORT = "5432"
|
||||
DB_DATABASE = local.postgresql_db_name
|
||||
DB_USERNAME = var.postgresql_admin_username
|
||||
DB_PASSWORD = local.postgresql_admin_pass
|
||||
}
|
||||
|
||||
type = "Opaque"
|
||||
|
||||
depends_on = [
|
||||
kubernetes_service.postgresql
|
||||
]
|
||||
}
|
||||
|
||||
# Kubernetes Secret for Laravel Application
|
||||
resource "kubernetes_secret" "app_secrets" {
|
||||
metadata {
|
||||
name = "${var.app_name}-secrets"
|
||||
namespace = kubernetes_namespace.app.metadata[0].name
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
data = {
|
||||
APP_KEY = var.app_key
|
||||
}
|
||||
|
||||
type = "Opaque"
|
||||
}
|
||||
|
||||
# Kubernetes ConfigMap for Laravel Application
|
||||
resource "kubernetes_config_map" "app_config" {
|
||||
metadata {
|
||||
name = "${var.app_name}-config"
|
||||
namespace = kubernetes_namespace.app.metadata[0].name
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
data = {
|
||||
APP_NAME = var.app_name
|
||||
APP_ENV = var.app_env
|
||||
APP_DEBUG = tostring(var.app_debug)
|
||||
APP_URL = local.app_url
|
||||
APP_LOCALE = "en"
|
||||
APP_FALLBACK_LOCALE = "en"
|
||||
APP_FAKER_LOCALE = "en_US"
|
||||
LOG_CHANNEL = "stack"
|
||||
LOG_STACK = "single"
|
||||
LOG_LEVEL = "info"
|
||||
SESSION_DRIVER = "database"
|
||||
SESSION_CONNECTION = "pgsql"
|
||||
SESSION_LIFETIME = "120"
|
||||
SESSION_ENCRYPT = "false"
|
||||
CACHE_STORE = "database"
|
||||
QUEUE_CONNECTION = "database"
|
||||
BROADCAST_CONNECTION = "log"
|
||||
FILESYSTEM_DISK = "local"
|
||||
MAIL_MAILER = "log"
|
||||
VITE_APP_NAME = var.app_name
|
||||
}
|
||||
}
|
||||
|
||||
# Kubernetes Deployment for Laravel Application
|
||||
resource "kubernetes_deployment" "app" {
|
||||
metadata {
|
||||
name = var.app_name
|
||||
namespace = kubernetes_namespace.app.metadata[0].name
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
spec {
|
||||
replicas = var.app_replicas
|
||||
|
||||
selector {
|
||||
match_labels = {
|
||||
app = var.app_name
|
||||
}
|
||||
}
|
||||
|
||||
template {
|
||||
metadata {
|
||||
labels = merge(
|
||||
local.common_labels,
|
||||
{
|
||||
version = "latest"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
spec {
|
||||
image_pull_secrets {
|
||||
name = "acr-secret"
|
||||
}
|
||||
|
||||
container {
|
||||
name = var.app_name
|
||||
image = var.docker_image
|
||||
|
||||
port {
|
||||
name = "http"
|
||||
container_port = 80
|
||||
protocol = "TCP"
|
||||
}
|
||||
|
||||
env_from {
|
||||
config_map_ref {
|
||||
name = kubernetes_config_map.app_config.metadata[0].name
|
||||
}
|
||||
}
|
||||
|
||||
env_from {
|
||||
secret_ref {
|
||||
name = kubernetes_secret.app_secrets.metadata[0].name
|
||||
}
|
||||
}
|
||||
|
||||
env_from {
|
||||
secret_ref {
|
||||
name = kubernetes_secret.db_credentials.metadata[0].name
|
||||
}
|
||||
}
|
||||
|
||||
resources {
|
||||
requests = {
|
||||
cpu = var.app_resources_requests_cpu
|
||||
memory = var.app_resources_requests_memory
|
||||
}
|
||||
limits = {
|
||||
cpu = var.app_resources_limits_cpu
|
||||
memory = var.app_resources_limits_memory
|
||||
}
|
||||
}
|
||||
|
||||
liveness_probe {
|
||||
exec {
|
||||
command = ["sh", "-c", "ps aux | grep -v grep | grep -q nginx && ps aux | grep -v grep | grep -q php-fpm"]
|
||||
}
|
||||
initial_delay_seconds = 30
|
||||
period_seconds = 10
|
||||
timeout_seconds = 5
|
||||
failure_threshold = 3
|
||||
}
|
||||
|
||||
readiness_probe {
|
||||
exec {
|
||||
command = ["sh", "-c", "ps aux | grep -v grep | grep -q nginx && ps aux | grep -v grep | grep -q php-fpm"]
|
||||
}
|
||||
initial_delay_seconds = 10
|
||||
period_seconds = 5
|
||||
timeout_seconds = 3
|
||||
failure_threshold = 3
|
||||
}
|
||||
}
|
||||
|
||||
restart_policy = "Always"
|
||||
}
|
||||
}
|
||||
|
||||
strategy {
|
||||
type = "RollingUpdate"
|
||||
rolling_update {
|
||||
max_surge = "1"
|
||||
max_unavailable = "0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
kubernetes_secret.db_credentials,
|
||||
kubernetes_secret.app_secrets,
|
||||
kubernetes_config_map.app_config
|
||||
]
|
||||
}
|
||||
|
||||
# Kubernetes Service for Laravel Application
|
||||
resource "kubernetes_service" "app" {
|
||||
metadata {
|
||||
name = var.app_name
|
||||
namespace = kubernetes_namespace.app.metadata[0].name
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
spec {
|
||||
selector = {
|
||||
app = var.app_name
|
||||
}
|
||||
|
||||
port {
|
||||
name = "http"
|
||||
port = 80
|
||||
target_port = 80
|
||||
protocol = "TCP"
|
||||
}
|
||||
|
||||
type = "ClusterIP"
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
kubernetes_deployment.app
|
||||
]
|
||||
}
|
||||
|
||||
# Horizontal Pod Autoscaler
|
||||
resource "kubernetes_horizontal_pod_autoscaler_v2" "app" {
|
||||
metadata {
|
||||
name = "${var.app_name}-hpa"
|
||||
namespace = kubernetes_namespace.app.metadata[0].name
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
spec {
|
||||
scale_target_ref {
|
||||
api_version = "apps/v1"
|
||||
kind = "Deployment"
|
||||
name = kubernetes_deployment.app.metadata[0].name
|
||||
}
|
||||
|
||||
min_replicas = var.app_replicas
|
||||
max_replicas = var.app_replicas * 3
|
||||
|
||||
metric {
|
||||
type = "Resource"
|
||||
resource {
|
||||
name = "cpu"
|
||||
target {
|
||||
type = "Utilization"
|
||||
average_utilization = 80
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
metric {
|
||||
type = "Resource"
|
||||
resource {
|
||||
name = "memory"
|
||||
target {
|
||||
type = "Utilization"
|
||||
average_utilization = 80
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# Data sources for existing resources
|
||||
data "azurerm_resource_group" "main" {
|
||||
name = var.resource_group_name
|
||||
}
|
||||
|
||||
data "azurerm_kubernetes_cluster" "main" {
|
||||
name = var.aks_cluster_name
|
||||
resource_group_name = var.resource_group_name
|
||||
}
|
||||
|
||||
data "azurerm_subscription" "current" {}
|
||||
|
||||
# Generate random suffix for unique resource names
|
||||
resource "random_string" "suffix" {
|
||||
length = 8
|
||||
special = false
|
||||
upper = false
|
||||
}
|
||||
|
||||
# Generate random password for PostgreSQL if not provided
|
||||
resource "random_password" "postgresql_admin_password" {
|
||||
count = var.postgresql_admin_password == null ? 1 : 0
|
||||
length = 32
|
||||
special = true
|
||||
}
|
||||
|
||||
# Local variables
|
||||
locals {
|
||||
app_name_safe = replace(var.app_name, "_", "-")
|
||||
postgresql_server_name = "${local.app_name_safe}-psql-${random_string.suffix.result}"
|
||||
postgresql_db_name = replace(var.app_name, "-", "_")
|
||||
postgresql_admin_pass = var.postgresql_admin_password != null ? var.postgresql_admin_password : random_password.postgresql_admin_password[0].result
|
||||
app_url = var.app_url != "" ? var.app_url : (var.ingress_host != "" ? "https://${var.ingress_host}" : (var.ingress_enabled ? "http://${try(data.kubernetes_service.ingress_nginx[0].status[0].load_balancer[0].ingress[0].ip, "pending")}" : "ingress-not-enabled"))
|
||||
|
||||
common_labels = {
|
||||
app = var.app_name
|
||||
environment = var.app_env
|
||||
managed-by = "terraform"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
output "resource_group_name" {
|
||||
description = "Name of the resource group"
|
||||
value = data.azurerm_resource_group.main.name
|
||||
}
|
||||
|
||||
output "aks_cluster_name" {
|
||||
description = "Name of the AKS cluster"
|
||||
value = data.azurerm_kubernetes_cluster.main.name
|
||||
}
|
||||
|
||||
output "postgresql_service_name" {
|
||||
description = "Name of the PostgreSQL service (in-cluster)"
|
||||
value = kubernetes_service.postgresql.metadata[0].name
|
||||
}
|
||||
|
||||
output "postgresql_service_fqdn" {
|
||||
description = "Internal FQDN of the PostgreSQL service (in-cluster)"
|
||||
value = "postgresql.${kubernetes_namespace.app.metadata[0].name}.svc.cluster.local"
|
||||
}
|
||||
|
||||
output "postgresql_database_name" {
|
||||
description = "Name of the PostgreSQL database"
|
||||
value = local.postgresql_db_name
|
||||
}
|
||||
|
||||
output "postgresql_admin_username" {
|
||||
description = "Administrator username for PostgreSQL"
|
||||
value = var.postgresql_admin_username
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "postgresql_connection_string" {
|
||||
description = "PostgreSQL connection string (from within cluster)"
|
||||
value = "postgresql://${var.postgresql_admin_username}:${nonsensitive(local.postgresql_admin_pass)}@postgresql.${kubernetes_namespace.app.metadata[0].name}.svc.cluster.local:5432/${local.postgresql_db_name}"
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "app_namespace" {
|
||||
description = "Kubernetes namespace for the application"
|
||||
value = kubernetes_namespace.app.metadata[0].name
|
||||
}
|
||||
|
||||
output "app_service_name" {
|
||||
description = "Kubernetes service name for the application"
|
||||
value = kubernetes_service.app.metadata[0].name
|
||||
}
|
||||
|
||||
output "ingress_enabled" {
|
||||
description = "Whether ingress is enabled"
|
||||
value = var.ingress_enabled
|
||||
}
|
||||
|
||||
output "ingress_ip" {
|
||||
description = "IP address of the ingress controller load balancer"
|
||||
value = var.ingress_enabled ? try(data.kubernetes_service.ingress_nginx[0].status[0].load_balancer[0].ingress[0].ip, "pending") : "not enabled"
|
||||
}
|
||||
|
||||
output "app_url" {
|
||||
description = "URL of the Laravel application"
|
||||
value = var.ingress_enabled ? (var.ingress_host != "" ? "https://${var.ingress_host}" : "http://${try(data.kubernetes_service.ingress_nginx[0].status[0].load_balancer[0].ingress[0].ip, "pending")}") : "ingress not enabled"
|
||||
}
|
||||
|
||||
output "ssl_enabled" {
|
||||
description = "Whether SSL/TLS is enabled"
|
||||
value = var.ssl_enabled
|
||||
}
|
||||
|
||||
output "db_restore_info" {
|
||||
description = "Database restore information"
|
||||
value = "Database restore disabled in Terraform. Use manual restore via kubectl (see db-restore.tf for instructions)"
|
||||
}
|
||||
|
||||
output "deployment_instructions" {
|
||||
description = "Next steps after deployment"
|
||||
value = <<-EOT
|
||||
========================================
|
||||
Deployment Complete!
|
||||
========================================
|
||||
|
||||
1. Get kubectl credentials:
|
||||
az aks get-credentials --resource-group ${data.azurerm_resource_group.main.name} --name ${data.azurerm_kubernetes_cluster.main.name}
|
||||
|
||||
2. Check application status:
|
||||
kubectl get pods -n ${kubernetes_namespace.app.metadata[0].name}
|
||||
kubectl get svc -n ${kubernetes_namespace.app.metadata[0].name}
|
||||
kubectl get ingress -n ${kubernetes_namespace.app.metadata[0].name}
|
||||
|
||||
3. Check database restore job (if enabled):
|
||||
kubectl get jobs -n ${kubernetes_namespace.app.metadata[0].name}
|
||||
kubectl logs -n ${kubernetes_namespace.app.metadata[0].name} -l job-type=database-restore
|
||||
|
||||
4. Access the application:
|
||||
${var.ingress_enabled ? (var.ingress_host != "" ? "https://${var.ingress_host}" : "http://${try(data.kubernetes_service.ingress_nginx[0].status[0].load_balancer[0].ingress[0].ip, "pending")}") : "ingress not enabled - use port-forward"}
|
||||
|
||||
5. Port-forward (if ingress not ready):
|
||||
kubectl port-forward -n ${kubernetes_namespace.app.metadata[0].name} svc/${kubernetes_service.app.metadata[0].name} 8080:80
|
||||
|
||||
6. View logs:
|
||||
kubectl logs -n ${kubernetes_namespace.app.metadata[0].name} -l app=${var.app_name} -f
|
||||
|
||||
7. Database connection details (in-cluster):
|
||||
Host: postgresql.${kubernetes_namespace.app.metadata[0].name}.svc.cluster.local
|
||||
Database: ${local.postgresql_db_name}
|
||||
Username: ${var.postgresql_admin_username}
|
||||
|
||||
8. Restore database manually:
|
||||
kubectl cp ../backups/backup_backend_20251203_101741.dump ${kubernetes_namespace.app.metadata[0].name}/postgresql-0:/tmp/backup.dump
|
||||
kubectl exec -it -n ${kubernetes_namespace.app.metadata[0].name} postgresql-0 -- pg_restore -U ${var.postgresql_admin_username} -d ${local.postgresql_db_name} --clean --if-exists /tmp/backup.dump
|
||||
|
||||
========================================
|
||||
EOT
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
# PostgreSQL in Kubernetes Cluster
|
||||
# Using StatefulSet with PersistentVolume for data persistence
|
||||
|
||||
# Kubernetes Secret for PostgreSQL
|
||||
resource "kubernetes_secret" "postgresql" {
|
||||
metadata {
|
||||
name = "postgresql-credentials"
|
||||
namespace = kubernetes_namespace.app.metadata[0].name
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
data = {
|
||||
POSTGRES_USER = var.postgresql_admin_username
|
||||
POSTGRES_PASSWORD = var.postgresql_admin_password
|
||||
POSTGRES_DB = local.postgresql_db_name
|
||||
}
|
||||
|
||||
type = "Opaque"
|
||||
}
|
||||
|
||||
# PersistentVolumeClaim for PostgreSQL data
|
||||
resource "kubernetes_persistent_volume_claim" "postgresql" {
|
||||
metadata {
|
||||
name = "postgresql-pvc"
|
||||
namespace = kubernetes_namespace.app.metadata[0].name
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
spec {
|
||||
access_modes = ["ReadWriteOnce"]
|
||||
|
||||
resources {
|
||||
requests = {
|
||||
storage = "10Gi"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait_until_bound = false
|
||||
}
|
||||
|
||||
# PostgreSQL StatefulSet
|
||||
resource "kubernetes_stateful_set" "postgresql" {
|
||||
metadata {
|
||||
name = "postgresql"
|
||||
namespace = kubernetes_namespace.app.metadata[0].name
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
spec {
|
||||
service_name = "postgresql"
|
||||
replicas = 1
|
||||
|
||||
selector {
|
||||
match_labels = {
|
||||
app = "postgresql"
|
||||
}
|
||||
}
|
||||
|
||||
template {
|
||||
metadata {
|
||||
labels = merge(
|
||||
local.common_labels,
|
||||
{
|
||||
app = "postgresql"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
spec {
|
||||
container {
|
||||
name = "postgresql"
|
||||
image = "postgres:16-alpine"
|
||||
|
||||
port {
|
||||
container_port = 5432
|
||||
name = "postgresql"
|
||||
}
|
||||
|
||||
env_from {
|
||||
secret_ref {
|
||||
name = kubernetes_secret.postgresql.metadata[0].name
|
||||
}
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
name = "postgresql-storage"
|
||||
mount_path = "/var/lib/postgresql/data"
|
||||
sub_path = "postgres"
|
||||
}
|
||||
|
||||
resources {
|
||||
requests = {
|
||||
cpu = "250m"
|
||||
memory = "512Mi"
|
||||
}
|
||||
limits = {
|
||||
cpu = "1000m"
|
||||
memory = "1Gi"
|
||||
}
|
||||
}
|
||||
|
||||
liveness_probe {
|
||||
exec {
|
||||
command = ["pg_isready", "-U", var.postgresql_admin_username]
|
||||
}
|
||||
initial_delay_seconds = 30
|
||||
period_seconds = 10
|
||||
timeout_seconds = 5
|
||||
failure_threshold = 3
|
||||
}
|
||||
|
||||
readiness_probe {
|
||||
exec {
|
||||
command = ["pg_isready", "-U", var.postgresql_admin_username]
|
||||
}
|
||||
initial_delay_seconds = 5
|
||||
period_seconds = 5
|
||||
timeout_seconds = 3
|
||||
failure_threshold = 3
|
||||
}
|
||||
}
|
||||
|
||||
volume {
|
||||
name = "postgresql-storage"
|
||||
persistent_volume_claim {
|
||||
claim_name = kubernetes_persistent_volume_claim.postgresql.metadata[0].name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
kubernetes_persistent_volume_claim.postgresql,
|
||||
kubernetes_secret.postgresql
|
||||
]
|
||||
}
|
||||
|
||||
# PostgreSQL Service
|
||||
resource "kubernetes_service" "postgresql" {
|
||||
metadata {
|
||||
name = "postgresql"
|
||||
namespace = kubernetes_namespace.app.metadata[0].name
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
spec {
|
||||
selector = {
|
||||
app = "postgresql"
|
||||
}
|
||||
|
||||
port {
|
||||
name = "postgresql"
|
||||
port = 5432
|
||||
target_port = 5432
|
||||
protocol = "TCP"
|
||||
}
|
||||
|
||||
type = "ClusterIP"
|
||||
cluster_ip = "None" # Headless service for StatefulSet
|
||||
session_affinity = "None"
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
kubernetes_stateful_set.postgresql
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
# Azure Database for PostgreSQL Flexible Server
|
||||
resource "azurerm_postgresql_flexible_server" "main" {
|
||||
name = local.postgresql_server_name
|
||||
resource_group_name = data.azurerm_resource_group.main.name
|
||||
location = var.location
|
||||
|
||||
administrator_login = var.postgresql_admin_username
|
||||
administrator_password = local.postgresql_admin_pass
|
||||
|
||||
sku_name = var.postgresql_sku_name
|
||||
version = var.postgresql_version
|
||||
storage_mb = var.postgresql_storage_mb
|
||||
|
||||
backup_retention_days = var.postgresql_backup_retention_days
|
||||
geo_redundant_backup_enabled = false
|
||||
|
||||
# Public access for initial setup (can be restricted later)
|
||||
# For production, consider using private endpoints or VNet integration
|
||||
public_network_access_enabled = true
|
||||
|
||||
zone = "1"
|
||||
|
||||
tags = merge(
|
||||
local.common_labels,
|
||||
{
|
||||
purpose = "database"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
# PostgreSQL Firewall Rule - Allow Azure Services
|
||||
resource "azurerm_postgresql_flexible_server_firewall_rule" "allow_azure_services" {
|
||||
name = "AllowAzureServices"
|
||||
server_id = azurerm_postgresql_flexible_server.main.id
|
||||
start_ip_address = "0.0.0.0"
|
||||
end_ip_address = "0.0.0.0"
|
||||
}
|
||||
|
||||
# PostgreSQL Firewall Rule - Allow AKS Outbound IPs
|
||||
# Note: In production, consider using VNet integration or private endpoints
|
||||
resource "azurerm_postgresql_flexible_server_firewall_rule" "allow_all_temporary" {
|
||||
name = "AllowAllTemporary"
|
||||
server_id = azurerm_postgresql_flexible_server.main.id
|
||||
start_ip_address = "0.0.0.0"
|
||||
end_ip_address = "255.255.255.255"
|
||||
|
||||
# This is a temporary rule for initial setup
|
||||
# Replace with specific IP ranges or VNet integration in production
|
||||
}
|
||||
|
||||
# PostgreSQL Database
|
||||
resource "azurerm_postgresql_flexible_server_database" "main" {
|
||||
name = local.postgresql_db_name
|
||||
server_id = azurerm_postgresql_flexible_server.main.id
|
||||
collation = "en_US.utf8"
|
||||
charset = "utf8"
|
||||
}
|
||||
|
||||
# PostgreSQL Configuration - Optimize for Laravel
|
||||
resource "azurerm_postgresql_flexible_server_configuration" "max_connections" {
|
||||
name = "max_connections"
|
||||
server_id = azurerm_postgresql_flexible_server.main.id
|
||||
value = "200"
|
||||
}
|
||||
|
||||
resource "azurerm_postgresql_flexible_server_configuration" "timezone" {
|
||||
name = "timezone"
|
||||
server_id = azurerm_postgresql_flexible_server.main.id
|
||||
value = "UTC"
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
provider "azurerm" {
|
||||
features {
|
||||
resource_group {
|
||||
prevent_deletion_if_contains_resources = false
|
||||
}
|
||||
}
|
||||
subscription_id = var.subscription_id
|
||||
environment = "public"
|
||||
use_cli = true
|
||||
resource_provider_registrations = "none"
|
||||
}
|
||||
|
||||
provider "kubernetes" {
|
||||
host = data.azurerm_kubernetes_cluster.main.kube_config[0].host
|
||||
client_certificate = base64decode(data.azurerm_kubernetes_cluster.main.kube_config[0].client_certificate)
|
||||
client_key = base64decode(data.azurerm_kubernetes_cluster.main.kube_config[0].client_key)
|
||||
cluster_ca_certificate = base64decode(data.azurerm_kubernetes_cluster.main.kube_config[0].cluster_ca_certificate)
|
||||
}
|
||||
|
||||
provider "helm" {
|
||||
kubernetes {
|
||||
host = data.azurerm_kubernetes_cluster.main.kube_config[0].host
|
||||
client_certificate = base64decode(data.azurerm_kubernetes_cluster.main.kube_config[0].client_certificate)
|
||||
client_key = base64decode(data.azurerm_kubernetes_cluster.main.kube_config[0].client_key)
|
||||
cluster_ca_certificate = base64decode(data.azurerm_kubernetes_cluster.main.kube_config[0].cluster_ca_certificate)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
terraform {
|
||||
required_version = ">= 1.9.0"
|
||||
|
||||
required_providers {
|
||||
azurerm = {
|
||||
source = "hashicorp/azurerm"
|
||||
version = "~> 4.0"
|
||||
}
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
version = "~> 2.33"
|
||||
}
|
||||
helm = {
|
||||
source = "hashicorp/helm"
|
||||
version = "~> 2.16"
|
||||
}
|
||||
random = {
|
||||
source = "hashicorp/random"
|
||||
version = "~> 3.6"
|
||||
}
|
||||
}
|
||||
|
||||
# Use local backend for initial deployment
|
||||
# For production, consider using remote backend (Azure Storage or Terraform Cloud)
|
||||
# backend "azurerm" {
|
||||
# # Backend configuration will be provided via backend-config file or CLI
|
||||
# # Example: terraform init -backend-config=backend.hcl
|
||||
# }
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
# Azure Configuration
|
||||
subscription_id = "77677a80-2dea-493d-9867-f1c961b80fb3"
|
||||
resource_group_name = "trusted_ai_demo_rg"
|
||||
location = "germanywestcentral"
|
||||
aks_cluster_name = "trai_k8s_cluster"
|
||||
|
||||
# Application Configuration
|
||||
app_name = "laravel-app"
|
||||
app_namespace = "laravel-app"
|
||||
app_env = "production"
|
||||
app_debug = false
|
||||
app_replicas = 2
|
||||
|
||||
# Docker Image
|
||||
docker_image = "laravelappreg.azurecr.io/laravel-app:v1.0.1"
|
||||
|
||||
# Laravel Application Key
|
||||
app_key = "base64:hcb6PXhQOot3SKMl7USAimUWY3c3OGVaRFtaJkDRiTc="
|
||||
|
||||
# PostgreSQL Configuration
|
||||
postgresql_admin_username = "pgadmin"
|
||||
postgresql_admin_password = "SecureLaravelPwd2024!"
|
||||
postgresql_sku_name = "B_Standard_B1ms"
|
||||
postgresql_storage_mb = 32768
|
||||
postgresql_version = "16"
|
||||
postgresql_backup_retention_days = 7
|
||||
|
||||
# Ingress Configuration
|
||||
ingress_enabled = true
|
||||
ingress_host = "" # Leave empty for IP-based access, or set to your domain
|
||||
|
||||
# SSL/TLS Configuration
|
||||
ssl_enabled = false
|
||||
ssl_issuer_email = "" # Required if ssl_enabled = true
|
||||
|
||||
# Database Restore Configuration
|
||||
db_restore_enabled = true
|
||||
db_backup_file_path = "../backups/backup_backend_20251203_101741.dump"
|
||||
|
||||
# Resource Limits
|
||||
app_resources_requests_cpu = "100m"
|
||||
app_resources_requests_memory = "256Mi"
|
||||
app_resources_limits_cpu = "500m"
|
||||
app_resources_limits_memory = "512Mi"
|
||||
|
||||
# Alert Configuration
|
||||
alert_email_address = "your-email@example.com"
|
||||
@@ -0,0 +1,47 @@
|
||||
# Azure Configuration
|
||||
subscription_id = "77677a80-2dea-493d-9867-f1c961b80fb3"
|
||||
resource_group_name = "trusted_ai_demo_rg"
|
||||
location = "germanywestcentral"
|
||||
aks_cluster_name = "trai_k8s_cluster"
|
||||
|
||||
# Application Configuration
|
||||
app_name = "laravel-app"
|
||||
app_namespace = "laravel-app"
|
||||
app_env = "production"
|
||||
app_debug = false
|
||||
app_replicas = 2
|
||||
|
||||
# Docker Image (Update this with your actual image)
|
||||
docker_image = "myregistry.azurecr.io/laravel-app:latest"
|
||||
|
||||
# Laravel Application Key (Generate with: php artisan key:generate --show)
|
||||
app_key = "base64:YOUR_APP_KEY_HERE"
|
||||
|
||||
# PostgreSQL Configuration
|
||||
postgresql_admin_username = "pgadmin"
|
||||
postgresql_admin_password = "ChangeThisPassword123!"
|
||||
postgresql_sku_name = "B_Standard_B1ms"
|
||||
postgresql_storage_mb = 32768
|
||||
postgresql_version = "16"
|
||||
postgresql_backup_retention_days = 7
|
||||
|
||||
# Ingress Configuration
|
||||
ingress_enabled = true
|
||||
ingress_host = "" # Leave empty for IP-based access, or set to your domain
|
||||
|
||||
# SSL/TLS Configuration
|
||||
ssl_enabled = false
|
||||
ssl_issuer_email = "" # Required if ssl_enabled = true
|
||||
|
||||
# Database Restore Configuration
|
||||
db_restore_enabled = false
|
||||
db_backup_file_path = "../backups/backup_backend_20251203_101741.dump"
|
||||
|
||||
# Resource Limits
|
||||
app_resources_requests_cpu = "100m"
|
||||
app_resources_requests_memory = "256Mi"
|
||||
app_resources_limits_cpu = "500m"
|
||||
app_resources_limits_memory = "512Mi"
|
||||
|
||||
# Alert Configuration
|
||||
alert_email_address = "your-email@example.com"
|
||||
@@ -0,0 +1,180 @@
|
||||
variable "subscription_id" {
|
||||
description = "Azure subscription ID"
|
||||
type = string
|
||||
default = "77677a80-2dea-493d-9867-f1c961b80fb3"
|
||||
}
|
||||
|
||||
variable "resource_group_name" {
|
||||
description = "Name of the existing resource group"
|
||||
type = string
|
||||
default = "trusted_ai_demo_rg"
|
||||
}
|
||||
|
||||
variable "location" {
|
||||
description = "Azure region for resources"
|
||||
type = string
|
||||
default = "germanywestcentral"
|
||||
}
|
||||
|
||||
variable "aks_cluster_name" {
|
||||
description = "Name of the existing AKS cluster"
|
||||
type = string
|
||||
default = "trai_k8s_cluster"
|
||||
}
|
||||
|
||||
variable "app_name" {
|
||||
description = "Name of the application"
|
||||
type = string
|
||||
default = "laravel-app"
|
||||
}
|
||||
|
||||
variable "app_namespace" {
|
||||
description = "Kubernetes namespace for the application"
|
||||
type = string
|
||||
default = "laravel-app"
|
||||
}
|
||||
|
||||
variable "docker_image" {
|
||||
description = "Docker image for the Laravel application"
|
||||
type = string
|
||||
# Format: <registry>/<image>:<tag>
|
||||
# Example: "myregistry.azurecr.io/laravel-app:latest"
|
||||
}
|
||||
|
||||
variable "app_replicas" {
|
||||
description = "Number of application replicas"
|
||||
type = number
|
||||
default = 2
|
||||
}
|
||||
|
||||
variable "app_resources_requests_cpu" {
|
||||
description = "CPU resource requests for application pods"
|
||||
type = string
|
||||
default = "100m"
|
||||
}
|
||||
|
||||
variable "app_resources_requests_memory" {
|
||||
description = "Memory resource requests for application pods"
|
||||
type = string
|
||||
default = "256Mi"
|
||||
}
|
||||
|
||||
variable "app_resources_limits_cpu" {
|
||||
description = "CPU resource limits for application pods"
|
||||
type = string
|
||||
default = "500m"
|
||||
}
|
||||
|
||||
variable "app_resources_limits_memory" {
|
||||
description = "Memory resource limits for application pods"
|
||||
type = string
|
||||
default = "512Mi"
|
||||
}
|
||||
|
||||
# PostgreSQL Variables
|
||||
variable "postgresql_admin_username" {
|
||||
description = "Administrator username for PostgreSQL"
|
||||
type = string
|
||||
default = "pgadmin"
|
||||
}
|
||||
|
||||
variable "postgresql_admin_password" {
|
||||
description = "Administrator password for PostgreSQL"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "postgresql_sku_name" {
|
||||
description = "SKU name for PostgreSQL (e.g., B_Standard_B1ms, GP_Standard_D2s_v3)"
|
||||
type = string
|
||||
default = "B_Standard_B1ms"
|
||||
}
|
||||
|
||||
variable "postgresql_storage_mb" {
|
||||
description = "Storage size in MB for PostgreSQL"
|
||||
type = number
|
||||
default = 32768 # 32 GB
|
||||
}
|
||||
|
||||
variable "postgresql_version" {
|
||||
description = "PostgreSQL version"
|
||||
type = string
|
||||
default = "16"
|
||||
}
|
||||
|
||||
variable "postgresql_backup_retention_days" {
|
||||
description = "Backup retention days for PostgreSQL"
|
||||
type = number
|
||||
default = 7
|
||||
}
|
||||
|
||||
# Laravel Application Variables
|
||||
variable "app_key" {
|
||||
description = "Laravel application key (base64 encoded)"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "app_env" {
|
||||
description = "Laravel application environment"
|
||||
type = string
|
||||
default = "production"
|
||||
}
|
||||
|
||||
variable "app_debug" {
|
||||
description = "Enable Laravel debug mode"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "app_url" {
|
||||
description = "Laravel application URL"
|
||||
type = string
|
||||
# Will be set based on ingress if not provided
|
||||
default = ""
|
||||
}
|
||||
|
||||
# Ingress Variables
|
||||
variable "ingress_enabled" {
|
||||
description = "Enable ingress controller"
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "ingress_host" {
|
||||
description = "Hostname for the ingress (leave empty for IP-based access)"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "ssl_enabled" {
|
||||
description = "Enable SSL/TLS (requires cert-manager)"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "ssl_issuer_email" {
|
||||
description = "Email for Let's Encrypt certificate issuer"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
# Database Restore Variables
|
||||
variable "db_restore_enabled" {
|
||||
description = "Enable database restore job on deployment"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "db_backup_file_path" {
|
||||
description = "Path to the database backup file (local path that will be uploaded)"
|
||||
type = string
|
||||
default = "../backups/backup_backend_20251203_101741.dump"
|
||||
}
|
||||
|
||||
# Alert Variables
|
||||
variable "alert_email_address" {
|
||||
description = "Email address for alert notifications"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
# Local .terraform directories
|
||||
**/.terraform/*
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
|
||||
# Crash log files
|
||||
crash.log
|
||||
crash.*.log
|
||||
|
||||
# Exclude all .tfvars files, which are likely to contain sensitive data
|
||||
*.tfvars
|
||||
*.tfvars.json
|
||||
|
||||
# Ignore override files as they are usually used to override resources locally
|
||||
override.tf
|
||||
override.tf.json
|
||||
*_override.tf
|
||||
*_override.tf.json
|
||||
|
||||
# Include override files you do wish to add to version control using negation pattern
|
||||
# !example_override.tf
|
||||
|
||||
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
|
||||
*tfplan*
|
||||
|
||||
# Ignore CLI configuration files
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
|
||||
# Ignore aztfexport generated files (optional, keep if you want version history)
|
||||
aztfexportResourceMapping.json
|
||||
aztfexportSkippedResources.txt
|
||||
|
||||
# Ignore Mac OS files
|
||||
.DS_Store
|
||||
|
||||
# Ignore editor/IDE files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Ignore kubeconfig
|
||||
kubeconfig
|
||||
*.kubeconfig
|
||||
@@ -0,0 +1,22 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/hashicorp/azurerm" {
|
||||
version = "4.33.0"
|
||||
constraints = "4.33.0"
|
||||
hashes = [
|
||||
"h1:3N8/4SkUbJcy+s4W74sx0KCM75T8U0ytUfSv/rCj3ok=",
|
||||
"zh:1f61ce7671de78f09a8e7532bfe1366eff6e6af47050de0a06217162638b11a0",
|
||||
"zh:20f103ad60399090c219685ef71d29713f46aba32499c02cc640508f8821b067",
|
||||
"zh:22ce8ad46b32be74d7bd13c982f30e5ffc0749a42191de468309d4ca1ee427f2",
|
||||
"zh:23ec8730b2f22701dbef4fd72459b95800bd4f87dfd517898064e291b807af20",
|
||||
"zh:273202db879542def36a057072ef1b87aa0f1ccced81029c8ede55508d16080e",
|
||||
"zh:2c5bc87083e7ddf55e49e3159a4836353a24a01288f78a846399453d33375938",
|
||||
"zh:5dcae547287b377bc4c8e472e313d2178821264ac00cbf4fc6469dffb27a79cd",
|
||||
"zh:6601669c92bea9b7c6fc7e1e20a957389e5d7c02d06cd2dcda44c6ef62f3d7df",
|
||||
"zh:8b91e6153a586be514680c90b0d1a4aee7556a39376100e73dde8e9452537671",
|
||||
"zh:9b6742b8b4a7bc4efa62794bd3425e39166dd3ac53fbdd8efb3214d79f36ffab",
|
||||
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
|
||||
"zh:f664e54e87c9466d54adc1f0dff41d8cca8d623aca317ff38aab29522b0d2508",
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
# AKS Cluster Terraform Configuration
|
||||
|
||||
Dieses Projekt verwaltet einen Azure Kubernetes Service (AKS) Cluster mit vollständigem Monitoring und Alerting über Terraform.
|
||||
|
||||
## Projektübersicht
|
||||
|
||||
Der AKS Cluster wurde ursprünglich in Azure erstellt und anschließend mit `aztfexport` in Terraform importiert. Die Konfiguration wurde optimiert und um zusätzliche Monitoring-Komponenten erweitert.
|
||||
|
||||
## Infrastruktur-Komponenten
|
||||
|
||||
### Netzwerk
|
||||
- **Resource Group**: `trusted_ai_demo_rg` (Germany West Central)
|
||||
- **Virtual Network**: `trusted_ai_demo_vn` (10.0.0.0/16)
|
||||
- **Subnet**: `default` (10.0.0.0/24)
|
||||
|
||||
### AKS Cluster
|
||||
- **Name**: `trai_k8s_cluster`
|
||||
- **Kubernetes Version**: 1.33
|
||||
- **VM Size**: `Standard_D2as_v6` (2 vCPUs, 8 GB RAM)
|
||||
- **Node Count**: 2-3 (Auto-Scaling)
|
||||
- **Availability Zones**: 1, 2
|
||||
- **Features**:
|
||||
- OIDC Issuer enabled
|
||||
- Workload Identity enabled
|
||||
- Image Cleaner enabled (wöchentlich)
|
||||
- Automatische Updates (Sonntags)
|
||||
- Azure CNI Overlay Networking
|
||||
|
||||
### Monitoring & Logging
|
||||
- **Log Analytics Workspace**: `aksloganalyticstraik8scluster`
|
||||
- SKU: PerGB2018
|
||||
- Retention: 30 Tage
|
||||
- **Data Collection Rule**: Container Insights mit:
|
||||
- Container Logs V2
|
||||
- Kubernetes Events
|
||||
- Pod Inventory
|
||||
- **OMS Agent**: Automatisch auf allen Nodes deployed
|
||||
|
||||
### Alerting
|
||||
- **Action Group**: Email-Benachrichtigungen
|
||||
- **Metric Alerts**:
|
||||
- CPU Usage > 95%
|
||||
- Memory Working Set > 100%
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
```
|
||||
.
|
||||
├── README.md # Diese Datei
|
||||
├── main.tf # Haupt-Ressourcendefinitionen
|
||||
├── variables.tf # Variablendefinitionen
|
||||
├── terraform.tfvars # Aktuelle Variablenwerte
|
||||
├── outputs.tf # Output-Definitionen
|
||||
├── provider.tf # Azure Provider-Konfiguration
|
||||
├── terraform.tf # Terraform-Einstellungen
|
||||
├── terraform.tfstate # State-Datei (lokal)
|
||||
├── aztfexportResourceMapping.json # Import-Mapping
|
||||
├── aztfexportSkippedResources.txt # Übersprungene Ressourcen
|
||||
└── cluster-status-commands.md # Nützliche Azure CLI Befehle
|
||||
|
||||
```
|
||||
|
||||
## Verwendung
|
||||
|
||||
### Voraussetzungen
|
||||
- Terraform >= 1.0
|
||||
- Azure CLI
|
||||
- kubectl
|
||||
- Authentifizierung bei Azure (`az login`)
|
||||
|
||||
### Initialisierung
|
||||
```bash
|
||||
terraform init
|
||||
```
|
||||
|
||||
### Änderungen anzeigen
|
||||
```bash
|
||||
terraform plan
|
||||
```
|
||||
|
||||
### Infrastruktur erstellen/aktualisieren
|
||||
```bash
|
||||
terraform apply
|
||||
```
|
||||
|
||||
### Infrastruktur löschen
|
||||
```bash
|
||||
terraform destroy
|
||||
```
|
||||
|
||||
### Cluster-Zugriff einrichten
|
||||
```bash
|
||||
az aks get-credentials --resource-group trusted_ai_demo_rg --name trai_k8s_cluster --overwrite-existing
|
||||
kubectl get nodes
|
||||
```
|
||||
|
||||
## Konfiguration
|
||||
|
||||
### Wichtige Variablen (terraform.tfvars)
|
||||
|
||||
```hcl
|
||||
location = "germanywestcentral"
|
||||
resource_group_name = "trusted_ai_demo_rg"
|
||||
aks_cluster_name = "trai_k8s_cluster"
|
||||
aks_node_vm_size = "Standard_D2as_v6"
|
||||
aks_node_zones = ["1", "2"]
|
||||
alert_email_address = "your-email@example.com"
|
||||
```
|
||||
|
||||
### Anpassbare Parameter
|
||||
|
||||
| Variable | Beschreibung | Default |
|
||||
|----------|--------------|---------|
|
||||
| `aks_node_pool_min_count` | Minimale Anzahl Nodes | 2 |
|
||||
| `aks_node_pool_max_count` | Maximale Anzahl Nodes | 3 |
|
||||
| `aks_node_vm_size` | VM-Größe für Nodes | Standard_D2as_v6 |
|
||||
| `aks_node_zones` | Availability Zones | ["1", "2"] |
|
||||
|
||||
## Outputs
|
||||
|
||||
Nach dem Apply werden folgende Informationen ausgegeben:
|
||||
|
||||
```bash
|
||||
terraform output
|
||||
```
|
||||
|
||||
- `aks_cluster_fqdn` - Cluster FQDN
|
||||
- `aks_cluster_id` - Cluster Resource ID
|
||||
- `aks_oidc_issuer_url` - OIDC Issuer URL
|
||||
- `log_analytics_workspace_id` - Log Analytics Workspace ID
|
||||
- Weitere...
|
||||
|
||||
## Kostenübersicht
|
||||
|
||||
**Monatliche Kosten (geschätzt)**:
|
||||
- 2x Standard_D2as_v6 Nodes: ~€70/Monat
|
||||
- Log Analytics Workspace: ~€2-5/Monat (bei geringer Nutzung)
|
||||
- **Gesamt**: ~€75-80/Monat
|
||||
|
||||
**Kostenoptimierung**:
|
||||
- Ursprünglich Standard_D4d_v4: ~€140/Monat
|
||||
- **Ersparnis**: ~€60-65/Monat (ca. 50%)
|
||||
|
||||
## Import-Historie
|
||||
|
||||
Das Projekt wurde mit `aztfexport` erstellt:
|
||||
|
||||
```bash
|
||||
aztfexport resource-group trusted_ai_demo_rg --non-interactive --plain-ui --continue
|
||||
```
|
||||
|
||||
**Importierte Ressourcen**: 9
|
||||
- Resource Group
|
||||
- Virtual Network + Subnet
|
||||
- AKS Cluster
|
||||
- Monitor Action Group
|
||||
- 2x Metric Alerts
|
||||
- Data Collection Rule (nachträglich hinzugefügt)
|
||||
- Log Analytics Workspace (nachträglich hinzugefügt)
|
||||
|
||||
**Übersprungene Ressourcen**: 2
|
||||
- Maintenance Configurations (automatisch verwaltet)
|
||||
|
||||
## Optimierungen
|
||||
|
||||
1. **VM-Größe**: Wechsel von Standard_D4d_v4 zu Standard_D2as_v6
|
||||
- 50% Kostenreduktion
|
||||
- Moderne AMD EPYC v6 Prozessoren
|
||||
- Ausreichend für die meisten Workloads
|
||||
|
||||
2. **Availability Zones**: Reduziert auf Zonen 1 und 2
|
||||
- Zone 3 war für das Abonnement nicht verfügbar
|
||||
|
||||
3. **Monitoring**: Komplett neu aufgesetzt
|
||||
- Eigener Log Analytics Workspace (statt shared)
|
||||
- Container Insights mit modernem V2 Format
|
||||
- Strukturierte Alerting-Regeln
|
||||
|
||||
4. **Variabilisierung**: Alle Werte in variables.tf ausgelagert
|
||||
- Einfache Anpassung
|
||||
- Wiederverwendbar
|
||||
|
||||
## Nützliche Befehle
|
||||
|
||||
Siehe [cluster-status-commands.md](./cluster-status-commands.md) für eine vollständige Liste nützlicher Azure CLI und kubectl Befehle.
|
||||
|
||||
## Sicherheitshinweise
|
||||
|
||||
- `terraform.tfvars` enthält sensitive Werte (Email-Adresse) und sollte nicht in Version Control eingecheckt werden
|
||||
- `.gitignore` ist konfiguriert, um State-Dateien und sensitive Daten auszuschließen
|
||||
- Verwenden Sie Azure Key Vault oder ähnliche Lösungen für Production-Secrets
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Quota-Fehler bei Node Pool Rotation
|
||||
Wenn Sie VM-Größen ändern und einen Quota-Fehler erhalten:
|
||||
```bash
|
||||
terraform destroy
|
||||
terraform apply
|
||||
```
|
||||
|
||||
### Log Analytics Workspace existiert nicht
|
||||
Falls der Workspace gelöscht wurde, erstellt Terraform automatisch einen neuen.
|
||||
|
||||
## Weiterführende Schritte
|
||||
|
||||
- [ ] State zu Azure Blob Storage migrieren (Remote State)
|
||||
- [ ] Azure Key Vault Integration für Secrets
|
||||
- [ ] CI/CD Pipeline einrichten
|
||||
- [ ] Weitere Monitoring Dashboards erstellen
|
||||
- [ ] Backup-Strategie implementieren
|
||||
|
||||
## Kontakt
|
||||
|
||||
Für Fragen oder Probleme, siehe die GitHub Issues.
|
||||
|
||||
## Lizenz
|
||||
|
||||
Projekt für Demo-Zwecke.
|
||||
@@ -0,0 +1,36 @@
|
||||
# AKS Cluster Status Befehle
|
||||
|
||||
Hier sind die Bash-Befehle, mit denen ich den Cluster-Status geprüft habe:
|
||||
|
||||
```bash
|
||||
# 1. Cluster-Status prüfen
|
||||
az aks show --resource-group trusted_ai_demo_rg --name trai_k8s_cluster --query "{name:name, powerState:powerState.code, provisioningState:provisioningState, kubernetesVersion:kubernetesVersion, nodeResourceGroup:nodeResourceGroup}" -o table
|
||||
|
||||
# 2. Node Pool Status prüfen
|
||||
az aks nodepool list --resource-group trusted_ai_demo_rg --cluster-name trai_k8s_cluster --query "[].{name:name, count:count, vmSize:vmSize, provisioningState:provisioningState, powerState:powerState.code}" -o table
|
||||
|
||||
# 3. Kubeconfig herunterladen (für kubectl Zugriff)
|
||||
az aks get-credentials --resource-group trusted_ai_demo_rg --name trai_k8s_cluster --overwrite-existing
|
||||
|
||||
# 4. Kubernetes Nodes anzeigen
|
||||
kubectl get nodes
|
||||
|
||||
# 5. Alle Pods in allen Namespaces anzeigen
|
||||
kubectl get pods -A
|
||||
```
|
||||
|
||||
## Zusätzliche nützliche Befehle
|
||||
|
||||
```bash
|
||||
# Detaillierte Cluster-Informationen
|
||||
az aks show --resource-group trusted_ai_demo_rg --name trai_k8s_cluster -o json
|
||||
|
||||
# Cluster-Ressourcen anzeigen
|
||||
kubectl top nodes
|
||||
|
||||
# Spezifische Namespace-Pods anzeigen
|
||||
kubectl get pods -n kube-system
|
||||
|
||||
# Cluster-Events anzeigen
|
||||
kubectl get events -A --sort-by='.lastTimestamp'
|
||||
```
|
||||
@@ -0,0 +1,195 @@
|
||||
resource "azurerm_resource_group" "main" {
|
||||
location = var.location
|
||||
name = var.resource_group_name
|
||||
}
|
||||
|
||||
resource "azurerm_virtual_network" "main" {
|
||||
address_space = var.vnet_address_space
|
||||
location = var.location
|
||||
name = var.vnet_name
|
||||
resource_group_name = var.resource_group_name
|
||||
depends_on = [
|
||||
azurerm_resource_group.main
|
||||
]
|
||||
}
|
||||
|
||||
resource "azurerm_subnet" "default" {
|
||||
address_prefixes = var.subnet_address_prefixes
|
||||
name = var.subnet_name
|
||||
resource_group_name = var.resource_group_name
|
||||
virtual_network_name = var.vnet_name
|
||||
depends_on = [
|
||||
azurerm_virtual_network.main
|
||||
]
|
||||
}
|
||||
|
||||
resource "azurerm_log_analytics_workspace" "main" {
|
||||
name = "aksloganalytics${replace(var.aks_cluster_name, "_", "")}"
|
||||
location = var.location
|
||||
resource_group_name = var.resource_group_name
|
||||
sku = "PerGB2018"
|
||||
retention_in_days = 30
|
||||
depends_on = [
|
||||
azurerm_resource_group.main
|
||||
]
|
||||
}
|
||||
|
||||
resource "azurerm_kubernetes_cluster" "main" {
|
||||
automatic_upgrade_channel = "patch"
|
||||
dns_prefix = var.aks_dns_prefix
|
||||
image_cleaner_enabled = true
|
||||
image_cleaner_interval_hours = 168
|
||||
location = var.location
|
||||
name = var.aks_cluster_name
|
||||
oidc_issuer_enabled = true
|
||||
resource_group_name = var.resource_group_name
|
||||
workload_identity_enabled = true
|
||||
|
||||
default_node_pool {
|
||||
auto_scaling_enabled = true
|
||||
max_count = var.aks_node_pool_max_count
|
||||
min_count = var.aks_node_pool_min_count
|
||||
name = "agentpool"
|
||||
vm_size = var.aks_node_vm_size
|
||||
zones = var.aks_node_zones
|
||||
temporary_name_for_rotation = "temppool"
|
||||
upgrade_settings {
|
||||
max_surge = "10%"
|
||||
}
|
||||
}
|
||||
|
||||
identity {
|
||||
type = "SystemAssigned"
|
||||
}
|
||||
|
||||
maintenance_window_auto_upgrade {
|
||||
day_of_week = "Sunday"
|
||||
duration = 8
|
||||
frequency = "Weekly"
|
||||
interval = 1
|
||||
start_time = "00:00"
|
||||
utc_offset = "+00:00"
|
||||
}
|
||||
|
||||
maintenance_window_node_os {
|
||||
day_of_week = "Sunday"
|
||||
duration = 8
|
||||
frequency = "Weekly"
|
||||
interval = 1
|
||||
start_time = "00:00"
|
||||
utc_offset = "+00:00"
|
||||
}
|
||||
|
||||
oms_agent {
|
||||
log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id
|
||||
msi_auth_for_monitoring_enabled = true
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
azurerm_resource_group.main,
|
||||
azurerm_log_analytics_workspace.main
|
||||
]
|
||||
}
|
||||
|
||||
resource "azurerm_monitor_data_collection_rule" "aks_monitoring" {
|
||||
kind = "Linux"
|
||||
location = var.location
|
||||
name = "MSCI-${var.location}-${var.aks_cluster_name}"
|
||||
resource_group_name = var.resource_group_name
|
||||
|
||||
data_flow {
|
||||
destinations = ["ciworkspace"]
|
||||
streams = ["Microsoft-ContainerLog", "Microsoft-ContainerLogV2", "Microsoft-KubeEvents", "Microsoft-KubePodInventory"]
|
||||
}
|
||||
|
||||
data_sources {
|
||||
extension {
|
||||
extension_json = jsonencode({
|
||||
dataCollectionSettings = {
|
||||
enableContainerLogV2 = true
|
||||
interval = "1m"
|
||||
namespaceFilteringMode = "Off"
|
||||
}
|
||||
})
|
||||
extension_name = "ContainerInsights"
|
||||
name = "ContainerInsightsExtension"
|
||||
streams = ["Microsoft-ContainerLog", "Microsoft-ContainerLogV2", "Microsoft-KubeEvents", "Microsoft-KubePodInventory"]
|
||||
}
|
||||
}
|
||||
|
||||
destinations {
|
||||
log_analytics {
|
||||
name = "ciworkspace"
|
||||
workspace_resource_id = azurerm_log_analytics_workspace.main.id
|
||||
}
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
azurerm_resource_group.main,
|
||||
azurerm_log_analytics_workspace.main
|
||||
]
|
||||
}
|
||||
|
||||
resource "azurerm_monitor_action_group" "alerts" {
|
||||
name = "RecommendedAlertRules-AG-c92012"
|
||||
resource_group_name = var.resource_group_name
|
||||
short_name = "alertc92012"
|
||||
|
||||
email_receiver {
|
||||
email_address = var.alert_email_address
|
||||
name = "Email_-EmailAction-"
|
||||
use_common_alert_schema = true
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
azurerm_resource_group.main
|
||||
]
|
||||
}
|
||||
|
||||
resource "azurerm_monitor_metric_alert" "cpu_alert" {
|
||||
auto_mitigate = false
|
||||
frequency = "PT5M"
|
||||
name = "CPU Usage Percentage - ${var.aks_cluster_name}"
|
||||
resource_group_name = var.resource_group_name
|
||||
scopes = [azurerm_kubernetes_cluster.main.id]
|
||||
|
||||
action {
|
||||
action_group_id = azurerm_monitor_action_group.alerts.id
|
||||
}
|
||||
|
||||
criteria {
|
||||
aggregation = "Average"
|
||||
metric_name = "node_cpu_usage_percentage"
|
||||
metric_namespace = "Microsoft.ContainerService/managedClusters"
|
||||
operator = "GreaterThan"
|
||||
threshold = 95
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
azurerm_resource_group.main
|
||||
]
|
||||
}
|
||||
|
||||
resource "azurerm_monitor_metric_alert" "memory_alert" {
|
||||
auto_mitigate = false
|
||||
frequency = "PT5M"
|
||||
name = "Memory Working Set Percentage - ${var.aks_cluster_name}"
|
||||
resource_group_name = var.resource_group_name
|
||||
scopes = [azurerm_kubernetes_cluster.main.id]
|
||||
|
||||
action {
|
||||
action_group_id = azurerm_monitor_action_group.alerts.id
|
||||
}
|
||||
|
||||
criteria {
|
||||
aggregation = "Average"
|
||||
metric_name = "node_memory_working_set_percentage"
|
||||
metric_namespace = "Microsoft.ContainerService/managedClusters"
|
||||
operator = "GreaterThan"
|
||||
threshold = 100
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
azurerm_resource_group.main
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
output "resource_group_name" {
|
||||
description = "Name of the resource group"
|
||||
value = azurerm_resource_group.main.name
|
||||
}
|
||||
|
||||
output "aks_cluster_name" {
|
||||
description = "Name of the AKS cluster"
|
||||
value = azurerm_kubernetes_cluster.main.name
|
||||
}
|
||||
|
||||
output "aks_cluster_id" {
|
||||
description = "ID of the AKS cluster"
|
||||
value = azurerm_kubernetes_cluster.main.id
|
||||
}
|
||||
|
||||
output "aks_cluster_fqdn" {
|
||||
description = "FQDN of the AKS cluster"
|
||||
value = azurerm_kubernetes_cluster.main.fqdn
|
||||
}
|
||||
|
||||
output "aks_kube_config" {
|
||||
description = "Kubeconfig for the AKS cluster"
|
||||
value = azurerm_kubernetes_cluster.main.kube_config_raw
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "aks_oidc_issuer_url" {
|
||||
description = "OIDC issuer URL of the AKS cluster"
|
||||
value = azurerm_kubernetes_cluster.main.oidc_issuer_url
|
||||
}
|
||||
|
||||
output "vnet_id" {
|
||||
description = "ID of the virtual network"
|
||||
value = azurerm_virtual_network.main.id
|
||||
}
|
||||
|
||||
output "subnet_id" {
|
||||
description = "ID of the subnet"
|
||||
value = azurerm_subnet.default.id
|
||||
}
|
||||
|
||||
output "log_analytics_workspace_id" {
|
||||
description = "ID of the Log Analytics workspace"
|
||||
value = azurerm_log_analytics_workspace.main.id
|
||||
}
|
||||
|
||||
output "log_analytics_workspace_name" {
|
||||
description = "Name of the Log Analytics workspace"
|
||||
value = azurerm_log_analytics_workspace.main.name
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
provider "azurerm" {
|
||||
features {
|
||||
}
|
||||
subscription_id = var.subscription_id
|
||||
environment = "public"
|
||||
use_msi = false
|
||||
use_cli = true
|
||||
use_oidc = false
|
||||
resource_provider_registrations = "none"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
terraform {
|
||||
backend "local" {}
|
||||
|
||||
required_providers {
|
||||
azurerm = {
|
||||
source = "hashicorp/azurerm"
|
||||
version = "4.33.0"
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
# Azure Region
|
||||
location = "germanywestcentral"
|
||||
|
||||
# Resource Group
|
||||
resource_group_name = "trusted_ai_demo_rg"
|
||||
|
||||
# Virtual Network
|
||||
vnet_name = "trusted_ai_demo_vn"
|
||||
subnet_name = "default"
|
||||
|
||||
# AKS Cluster Configuration
|
||||
aks_cluster_name = "trai_k8s_cluster"
|
||||
aks_dns_prefix = "traik8scluster"
|
||||
|
||||
# AKS Node Pool Configuration
|
||||
aks_node_vm_size = "Standard_D2as_v6"
|
||||
aks_node_zones = ["1", "2"]
|
||||
|
||||
# Auto-Scaling Configuration
|
||||
aks_node_pool_min_count = 2
|
||||
aks_node_pool_max_count = 3
|
||||
|
||||
# Alert Configuration
|
||||
# WICHTIG: Ersetzen Sie diese Email-Adresse mit Ihrer eigenen
|
||||
alert_email_address = "your-email@example.com"
|
||||
|
||||
# Azure Subscription ID (optional, falls abweichend vom Default)
|
||||
# subscription_id = "your-subscription-id"
|
||||
@@ -0,0 +1,83 @@
|
||||
variable "location" {
|
||||
description = "Azure region for resources"
|
||||
type = string
|
||||
default = "germanywestcentral"
|
||||
}
|
||||
|
||||
variable "resource_group_name" {
|
||||
description = "Name of the resource group"
|
||||
type = string
|
||||
default = "trusted_ai_demo_rg"
|
||||
}
|
||||
|
||||
variable "vnet_name" {
|
||||
description = "Name of the virtual network"
|
||||
type = string
|
||||
default = "trusted_ai_demo_vn"
|
||||
}
|
||||
|
||||
variable "vnet_address_space" {
|
||||
description = "Address space for the virtual network"
|
||||
type = list(string)
|
||||
default = ["10.0.0.0/16"]
|
||||
}
|
||||
|
||||
variable "subnet_name" {
|
||||
description = "Name of the subnet"
|
||||
type = string
|
||||
default = "default"
|
||||
}
|
||||
|
||||
variable "subnet_address_prefixes" {
|
||||
description = "Address prefixes for the subnet"
|
||||
type = list(string)
|
||||
default = ["10.0.0.0/24"]
|
||||
}
|
||||
|
||||
variable "aks_cluster_name" {
|
||||
description = "Name of the AKS cluster"
|
||||
type = string
|
||||
default = "trai_k8s_cluster"
|
||||
}
|
||||
|
||||
variable "aks_dns_prefix" {
|
||||
description = "DNS prefix for the AKS cluster"
|
||||
type = string
|
||||
default = "traik8scluster"
|
||||
}
|
||||
|
||||
variable "aks_node_pool_min_count" {
|
||||
description = "Minimum number of nodes in the AKS node pool"
|
||||
type = number
|
||||
default = 2
|
||||
}
|
||||
|
||||
variable "aks_node_pool_max_count" {
|
||||
description = "Maximum number of nodes in the AKS node pool"
|
||||
type = number
|
||||
default = 3
|
||||
}
|
||||
|
||||
variable "aks_node_vm_size" {
|
||||
description = "VM size for AKS node pool"
|
||||
type = string
|
||||
default = "Standard_D2as_v6"
|
||||
}
|
||||
|
||||
variable "aks_node_zones" {
|
||||
description = "Availability zones for AKS nodes"
|
||||
type = list(string)
|
||||
default = ["1", "2"]
|
||||
}
|
||||
|
||||
variable "alert_email_address" {
|
||||
description = "Email address for alert notifications"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "subscription_id" {
|
||||
description = "Azure subscription ID"
|
||||
type = string
|
||||
default = "77677a80-2dea-493d-9867-f1c961b80fb3"
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
# Trusted AI Demo - OpenTofu Infrastructure
|
||||
|
||||
This project contains OpenTofu/Terraform configuration for the Trusted AI Demo infrastructure on Azure.
|
||||
|
||||
## Infrastructure Components
|
||||
|
||||
This configuration creates:
|
||||
|
||||
- **Resource Group**: Container for all Azure resources
|
||||
- **Virtual Network**: 10.0.0.0/16 address space
|
||||
- **Subnet**: 10.0.1.0/24 for VM placement
|
||||
- **Network Security Group**: With RDP (port 3389) rule for remote access
|
||||
- **Public IP**: Static public IP for VM access
|
||||
- **Network Interface**: Connects VM to the virtual network
|
||||
- **Windows Server 2022 VM**: Standard_B2s (2 vCPUs, 4 GB RAM)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [OpenTofu](https://opentofu.org/) >= 1.0 or [Terraform](https://www.terraform.io/) >= 1.0
|
||||
- Azure CLI configured with appropriate credentials
|
||||
- Azure subscription with necessary permissions
|
||||
|
||||
## Getting Started
|
||||
|
||||
### 1. Authenticate with Azure
|
||||
|
||||
```bash
|
||||
az login
|
||||
az account set --subscription "<your-subscription-id>"
|
||||
```
|
||||
|
||||
### 2. Initialize OpenTofu
|
||||
|
||||
```bash
|
||||
tofu init
|
||||
```
|
||||
|
||||
Or if using Terraform:
|
||||
|
||||
```bash
|
||||
terraform init
|
||||
```
|
||||
|
||||
### 3. Review the Plan
|
||||
|
||||
```bash
|
||||
tofu plan
|
||||
```
|
||||
|
||||
### 4. Apply the Configuration
|
||||
|
||||
```bash
|
||||
tofu apply
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
- `main.tf` - Main infrastructure configuration
|
||||
- `variables.tf` - Input variable definitions
|
||||
- `outputs.tf` - Output value definitions
|
||||
- `terraform.tfvars.example` - Example variable values (copy to `terraform.tfvars`)
|
||||
|
||||
## Configuration
|
||||
|
||||
### Required Configuration
|
||||
|
||||
1. Copy the example variables file:
|
||||
```bash
|
||||
cp terraform.tfvars.example terraform.tfvars
|
||||
```
|
||||
|
||||
2. Edit `terraform.tfvars` and set your admin password:
|
||||
```hcl
|
||||
admin_password = "YourSecurePassword123!"
|
||||
```
|
||||
|
||||
**Important**: The password must be at least 12 characters long and contain uppercase, lowercase, and numbers.
|
||||
|
||||
3. **Security Recommendation**: Change `allowed_rdp_source` to your public IP address instead of `"*"`:
|
||||
```hcl
|
||||
allowed_rdp_source = "YOUR_PUBLIC_IP/32"
|
||||
```
|
||||
|
||||
You can find your public IP with:
|
||||
```bash
|
||||
curl ifconfig.me
|
||||
```
|
||||
|
||||
### Optional Customization
|
||||
|
||||
You can also customize:
|
||||
- `resource_group_name`: Name of the resource group
|
||||
- `location`: Azure region (default: westeurope)
|
||||
- `vm_name`: Name of the virtual machine
|
||||
- `vm_size`: VM size (default: Standard_B2s)
|
||||
- `vnet_address_space`: Virtual network address space
|
||||
- `subnet_address_prefix`: Subnet address prefix
|
||||
|
||||
## Connecting to the VM
|
||||
|
||||
After the infrastructure is created, you can connect to the Windows VM via RDP:
|
||||
|
||||
1. Get the public IP address:
|
||||
```bash
|
||||
tofu output vm_public_ip
|
||||
```
|
||||
|
||||
2. Connect using Remote Desktop:
|
||||
- **Windows**: Use the connection string from output:
|
||||
```bash
|
||||
tofu output rdp_connection_string
|
||||
```
|
||||
- **macOS**: Use Microsoft Remote Desktop app
|
||||
- **Linux**: Use Remmina or similar RDP client
|
||||
|
||||
3. Login credentials:
|
||||
- Username: The value you set for `admin_username` (default: azureadmin)
|
||||
- Password: The password you set in `terraform.tfvars`
|
||||
|
||||
## Cleanup
|
||||
|
||||
To destroy all resources:
|
||||
|
||||
```bash
|
||||
tofu destroy
|
||||
```
|
||||
|
||||
**Warning**: This will permanently delete all resources created by this configuration.
|
||||
@@ -0,0 +1,123 @@
|
||||
terraform {
|
||||
required_version = ">= 1.0"
|
||||
|
||||
required_providers {
|
||||
azurerm = {
|
||||
source = "hashicorp/azurerm"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "azurerm" {
|
||||
features {}
|
||||
}
|
||||
|
||||
# Resource Group
|
||||
resource "azurerm_resource_group" "main" {
|
||||
name = var.resource_group_name
|
||||
location = var.location
|
||||
|
||||
tags = var.tags
|
||||
}
|
||||
|
||||
# Virtual Network
|
||||
resource "azurerm_virtual_network" "main" {
|
||||
name = "${var.resource_group_name}-vnet"
|
||||
address_space = [var.vnet_address_space]
|
||||
location = azurerm_resource_group.main.location
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
|
||||
tags = var.tags
|
||||
}
|
||||
|
||||
# Subnet
|
||||
resource "azurerm_subnet" "main" {
|
||||
name = "default"
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
virtual_network_name = azurerm_virtual_network.main.name
|
||||
address_prefixes = [var.subnet_address_prefix]
|
||||
}
|
||||
|
||||
# Network Security Group
|
||||
resource "azurerm_network_security_group" "main" {
|
||||
name = "${var.vm_name}-nsg"
|
||||
location = azurerm_resource_group.main.location
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
|
||||
security_rule {
|
||||
name = "SSH"
|
||||
priority = 1001
|
||||
direction = "Inbound"
|
||||
access = "Allow"
|
||||
protocol = "Tcp"
|
||||
source_port_range = "*"
|
||||
destination_port_range = "22"
|
||||
source_address_prefix = var.allowed_ssh_source
|
||||
destination_address_prefix = "*"
|
||||
}
|
||||
|
||||
tags = var.tags
|
||||
}
|
||||
|
||||
# Public IP
|
||||
resource "azurerm_public_ip" "main" {
|
||||
name = "${var.vm_name}-pip"
|
||||
location = azurerm_resource_group.main.location
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
allocation_method = "Static"
|
||||
sku = "Standard"
|
||||
|
||||
tags = var.tags
|
||||
}
|
||||
|
||||
# Network Interface
|
||||
resource "azurerm_network_interface" "main" {
|
||||
name = "${var.vm_name}-nic"
|
||||
location = azurerm_resource_group.main.location
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
|
||||
ip_configuration {
|
||||
name = "internal"
|
||||
subnet_id = azurerm_subnet.main.id
|
||||
private_ip_address_allocation = "Dynamic"
|
||||
public_ip_address_id = azurerm_public_ip.main.id
|
||||
}
|
||||
|
||||
tags = var.tags
|
||||
}
|
||||
|
||||
# Associate NSG with Network Interface
|
||||
resource "azurerm_network_interface_security_group_association" "main" {
|
||||
network_interface_id = azurerm_network_interface.main.id
|
||||
network_security_group_id = azurerm_network_security_group.main.id
|
||||
}
|
||||
|
||||
# Linux Virtual Machine
|
||||
resource "azurerm_linux_virtual_machine" "main" {
|
||||
name = var.vm_name
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
location = azurerm_resource_group.main.location
|
||||
size = var.vm_size
|
||||
admin_username = var.admin_username
|
||||
admin_password = var.admin_password
|
||||
disable_password_authentication = false
|
||||
|
||||
network_interface_ids = [
|
||||
azurerm_network_interface.main.id,
|
||||
]
|
||||
|
||||
os_disk {
|
||||
caching = "ReadWrite"
|
||||
storage_account_type = "Standard_LRS"
|
||||
}
|
||||
|
||||
source_image_reference {
|
||||
publisher = "Canonical"
|
||||
offer = "0001-com-ubuntu-server-jammy"
|
||||
sku = "22_04-lts-gen2"
|
||||
version = "latest"
|
||||
}
|
||||
|
||||
tags = var.tags
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
output "resource_group_name" {
|
||||
description = "Name of the created resource group"
|
||||
value = azurerm_resource_group.main.name
|
||||
}
|
||||
|
||||
output "resource_group_id" {
|
||||
description = "ID of the created resource group"
|
||||
value = azurerm_resource_group.main.id
|
||||
}
|
||||
|
||||
output "location" {
|
||||
description = "Location of the resource group"
|
||||
value = azurerm_resource_group.main.location
|
||||
}
|
||||
|
||||
# Network Outputs
|
||||
output "vnet_name" {
|
||||
description = "Name of the virtual network"
|
||||
value = azurerm_virtual_network.main.name
|
||||
}
|
||||
|
||||
output "vnet_id" {
|
||||
description = "ID of the virtual network"
|
||||
value = azurerm_virtual_network.main.id
|
||||
}
|
||||
|
||||
output "subnet_id" {
|
||||
description = "ID of the subnet"
|
||||
value = azurerm_subnet.main.id
|
||||
}
|
||||
|
||||
# VM Outputs
|
||||
output "vm_name" {
|
||||
description = "Name of the virtual machine"
|
||||
value = azurerm_linux_virtual_machine.main.name
|
||||
}
|
||||
|
||||
output "vm_id" {
|
||||
description = "ID of the virtual machine"
|
||||
value = azurerm_linux_virtual_machine.main.id
|
||||
}
|
||||
|
||||
output "vm_private_ip" {
|
||||
description = "Private IP address of the VM"
|
||||
value = azurerm_network_interface.main.private_ip_address
|
||||
}
|
||||
|
||||
output "vm_public_ip" {
|
||||
description = "Public IP address of the VM"
|
||||
value = azurerm_public_ip.main.ip_address
|
||||
}
|
||||
|
||||
output "ssh_connection_string" {
|
||||
description = "SSH connection string for the VM"
|
||||
value = "ssh ${azurerm_linux_virtual_machine.main.admin_username}@${azurerm_public_ip.main.ip_address}"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"version":4,"terraform_version":"1.11.1","serial":11,"lineage":"cad35d7f-13a9-933b-a7ef-af99f16a5d08","outputs":{},"resources":[],"check_results":null}
|
||||
@@ -0,0 +1 @@
|
||||
{"version":4,"terraform_version":"1.11.1","serial":7,"lineage":"cad35d7f-13a9-933b-a7ef-af99f16a5d08","outputs":{"location":{"value":"austriaeast","type":"string"},"resource_group_id":{"value":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo","type":"string"},"resource_group_name":{"value":"rg-trusted-ai-demo","type":"string"},"ssh_connection_string":{"value":"ssh azureadmin@68.210.97.125","type":"string"},"subnet_id":{"value":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/virtualNetworks/rg-trusted-ai-demo-vnet/subnets/default","type":"string"},"vm_id":{"value":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Compute/virtualMachines/vm-trusted-ai","type":"string"},"vm_name":{"value":"vm-trusted-ai","type":"string"},"vm_private_ip":{"value":"10.0.1.4","type":"string"},"vm_public_ip":{"value":"68.210.97.125","type":"string"},"vnet_id":{"value":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/virtualNetworks/rg-trusted-ai-demo-vnet","type":"string"},"vnet_name":{"value":"rg-trusted-ai-demo-vnet","type":"string"}},"resources":[{"mode":"managed","type":"azurerm_linux_virtual_machine","name":"main","provider":"provider[\"registry.opentofu.org/hashicorp/azurerm\"]","instances":[{"schema_version":0,"attributes":{"additional_capabilities":[],"admin_password":"Hab2009Keins!","admin_ssh_key":[],"admin_username":"azureadmin","allow_extension_operations":true,"availability_set_id":"","boot_diagnostics":[],"bypass_platform_safety_checks_on_user_schedule_enabled":false,"capacity_reservation_group_id":"","computer_name":"vm-trusted-ai","custom_data":null,"dedicated_host_group_id":"","dedicated_host_id":"","disable_password_authentication":false,"disk_controller_type":"SCSI","edge_zone":"","encryption_at_host_enabled":false,"eviction_policy":"","extensions_time_budget":"PT1H30M","gallery_application":[],"id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Compute/virtualMachines/vm-trusted-ai","identity":[],"license_type":"","location":"austriaeast","max_bid_price":-1,"name":"vm-trusted-ai","network_interface_ids":["/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/networkInterfaces/vm-trusted-ai-nic"],"os_disk":[{"caching":"ReadWrite","diff_disk_settings":[],"disk_encryption_set_id":"","disk_size_gb":30,"name":"vm-trusted-ai_OsDisk_1_26ed4d6fa684410abb7d858ef85be362","secure_vm_disk_encryption_set_id":"","security_encryption_type":"","storage_account_type":"Standard_LRS","write_accelerator_enabled":false}],"os_image_notification":[],"patch_assessment_mode":"ImageDefault","patch_mode":"ImageDefault","plan":[],"platform_fault_domain":-1,"priority":"Regular","private_ip_address":"10.0.1.4","private_ip_addresses":["10.0.1.4"],"provision_vm_agent":true,"proximity_placement_group_id":"","public_ip_address":"68.210.97.125","public_ip_addresses":["68.210.97.125"],"reboot_setting":"","resource_group_name":"rg-trusted-ai-demo","secret":[],"secure_boot_enabled":false,"size":"Standard_B2ats_v2","source_image_id":"","source_image_reference":[{"offer":"0001-com-ubuntu-server-jammy","publisher":"Canonical","sku":"22_04-lts-gen2","version":"latest"}],"tags":{"Environment":"dev","ManagedBy":"OpenTofu","Project":"trusted-ai-demo"},"termination_notification":null,"timeouts":null,"user_data":"","virtual_machine_id":"c0bc058a-065f-44e0-9bf7-e9343e91e666","virtual_machine_scale_set_id":"","vm_agent_platform_updates_enabled":false,"vtpm_enabled":false,"zone":""},"sensitive_attributes":[[{"type":"get_attr","value":"admin_password"}],[{"type":"get_attr","value":"custom_data"}]],"private":"eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoyNzAwMDAwMDAwMDAwLCJkZWxldGUiOjI3MDAwMDAwMDAwMDAsInJlYWQiOjMwMDAwMDAwMDAwMCwidXBkYXRlIjoyNzAwMDAwMDAwMDAwfX0=","dependencies":["azurerm_network_interface.main","azurerm_public_ip.main","azurerm_resource_group.main","azurerm_subnet.main","azurerm_virtual_network.main"]}]},{"mode":"managed","type":"azurerm_network_interface","name":"main","provider":"provider[\"registry.opentofu.org/hashicorp/azurerm\"]","instances":[{"schema_version":0,"attributes":{"accelerated_networking_enabled":false,"applied_dns_servers":[],"auxiliary_mode":"","auxiliary_sku":"","dns_servers":[],"edge_zone":"","enable_accelerated_networking":false,"enable_ip_forwarding":false,"id":"/subscriptions/77677a80-2dea-493d-9867-f1c961b80fb3/resourceGroups/rg-trusted-ai-demo/providers/Microsoft.Network/networkInterfaces/vm-trusted-ai-nic","internal_dns_name_label":"","internal_domain_name_suffix":"cu22vk1awgdetoispofbty41oa.viex.internal.cloudapp.net","ip_configuration":[{"gateway_load_balancer_frontend_ip_configuration_id":"","name":"internal","primary":true,"private_ip_address":"10.0.1Line truncated
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copy this file to terraform.tfvars and customize as needed
|
||||
|
||||
# Resource Group Configuration
|
||||
resource_group_name = "rg-trusted-ai-demo"
|
||||
location = "austriaeast"
|
||||
|
||||
# Network Configuration
|
||||
vnet_address_space = "10.0.0.0/16"
|
||||
subnet_address_prefix = "10.0.1.0/24"
|
||||
allowed_ssh_source = "*" # Change to your public IP for better security, e.g., "1.2.3.4/32"
|
||||
|
||||
# Virtual Machine Configuration
|
||||
vm_name = "vm-trusted-ai"
|
||||
vm_size = "Standard_B2ats_v2"
|
||||
admin_username = "azureadmin"
|
||||
admin_password = "Hab2009Keins!"
|
||||
|
||||
# Tags
|
||||
tags = {
|
||||
Environment = "dev"
|
||||
Project = "trusted-ai-demo"
|
||||
ManagedBy = "OpenTofu"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copy this file to terraform.tfvars and customize as needed
|
||||
|
||||
# Resource Group Configuration
|
||||
resource_group_name = "rg-trusted-ai-demo"
|
||||
location = "westus"
|
||||
|
||||
# Network Configuration
|
||||
vnet_address_space = "10.0.0.0/16"
|
||||
subnet_address_prefix = "10.0.1.0/24"
|
||||
allowed_rdp_source = "*" # Change to your public IP for better security, e.g., "1.2.3.4/32"
|
||||
|
||||
# Virtual Machine Configuration
|
||||
vm_name = "vm-trusted-ai"
|
||||
vm_size = "Standard_B2s"
|
||||
admin_username = "azureadmin"
|
||||
admin_password = "YourSecurePassword123!" # IMPORTANT: Change this! Min 12 characters, must include upper, lower, number
|
||||
|
||||
# Tags
|
||||
tags = {
|
||||
Environment = "dev"
|
||||
Project = "trusted-ai-demo"
|
||||
ManagedBy = "OpenTofu"
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
variable "resource_group_name" {
|
||||
description = "Name of the resource group"
|
||||
type = string
|
||||
default = "rg-trusted-ai-demo"
|
||||
}
|
||||
|
||||
variable "location" {
|
||||
description = "Azure region for resources"
|
||||
type = string
|
||||
default = "austriaeast"
|
||||
}
|
||||
|
||||
variable "tags" {
|
||||
description = "Tags to apply to all resources"
|
||||
type = map(string)
|
||||
default = {
|
||||
Environment = "dev"
|
||||
Project = "trusted-ai-demo"
|
||||
ManagedBy = "OpenTofu"
|
||||
}
|
||||
}
|
||||
|
||||
# Network Configuration
|
||||
variable "vnet_address_space" {
|
||||
description = "Address space for the virtual network"
|
||||
type = string
|
||||
default = "10.0.0.0/16"
|
||||
}
|
||||
|
||||
variable "subnet_address_prefix" {
|
||||
description = "Address prefix for the subnet"
|
||||
type = string
|
||||
default = "10.0.1.0/24"
|
||||
}
|
||||
|
||||
variable "allowed_ssh_source" {
|
||||
description = "Source IP address or range allowed to SSH to the VM (use your public IP or '*' for any - not recommended)"
|
||||
type = string
|
||||
default = "*"
|
||||
}
|
||||
|
||||
# Virtual Machine Configuration
|
||||
variable "vm_name" {
|
||||
description = "Name of the virtual machine"
|
||||
type = string
|
||||
default = "vm-trusted-ai"
|
||||
}
|
||||
|
||||
variable "vm_size" {
|
||||
description = "Size of the virtual machine"
|
||||
type = string
|
||||
default = "Standard_B2s"
|
||||
}
|
||||
|
||||
variable "admin_username" {
|
||||
description = "Admin username for the virtual machine"
|
||||
type = string
|
||||
default = "azureadmin"
|
||||
}
|
||||
|
||||
variable "admin_password" {
|
||||
description = "Admin password for the virtual machine (use strong password, min 12 characters)"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<mxfile host="Electron" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/28.0.6 Chrome/138.0.7204.100 Electron/37.2.3 Safari/537.36" version="28.0.6">
|
||||
<diagram name="Seite-1" id="13kzIe54gCjrgZH2jZoa">
|
||||
<mxGraphModel dx="3433" dy="2120" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1654" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" value="Beschriftung Blatt" style="" parent="0" />
|
||||
<mxCell id="mBbMmTsdGeR7BbYYs1AZ-6" value=""Landkarte" Sopra Steria Trusted AI Platform auf Azure zur Übersicht und Abstimmung" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;fontFamily=Tahoma;" vertex="1" parent="1">
|
||||
<mxGeometry x="140" y="120" width="790" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="mBbMmTsdGeR7BbYYs1AZ-7" value="" style="shape=image;verticalLabelPosition=bottom;labelBackgroundColor=default;verticalAlign=top;aspect=fixed;imageAspect=0;editableCssRules=.*;image=data:image/svg+xml,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjIuMDA2Mjg5OTU4OTUzODU3NCAyLjAxODg0MDA3NDUzOTE4NDYgNTI1Ljk4NzczMTkzMzU5MzggNjYuOTYyMzY0MTk2Nzc3MzQiIGhlaWdodD0iNjYuOTYyMzY0MTk2Nzc3MzQiIHdpZHRoPSI1MjUuOTg3NzMxOTMzNTkzOCI+JiN4YTs8cGF0aCBmaWxsPSIjRjY3MjAwIiBkPSJNMjM4LjM0OSAxMS43NzYxQzIzMy4xNzYgMTEuNjkwNCAyMjkuMTI2IDEzLjEwMDQgMjI4LjAwOSAxNi43NjU0QzIyNS42OTkgMjQuMzM4NSAyNDAuNjQ1IDMyLjQ2NTMgMjQ3LjY5IDM2LjMzODlDMjQ5LjIwNCAzNy4xNjY4IDI0OS44NTIgMzYuNjUwMiAyNDguODk0IDM1LjU0MThDMjQ0LjQ0MiAzMC40MTE0IDIzOC4xMSAyMC4zMDgzIDI0Ny4wNDYgMTQuMzc5QzI0Ny40ODEgMTQuMDkyIDI0OC4wMTEgMTMuNzY1NyAyNDguNDg2IDEzLjQ4ODRMMjQ4LjYyOSAxMy40MDk4TDI0OC40ODYgMTMuMzg0NkMyNDQuOTYzIDEyLjQxNzUgMjQxLjQ1MiAxMS44MjcgMjM4LjM0OSAxMS43NzU2VjExLjc3NjFaTTI4Ni40MTMgMzQuMjEzMkMyODUuNjM4IDM0LjA2MTEgMjg1LjQ0OCAzNC41NTc5IDI4Ni4xNzIgMzUuMzg5MkMyOTAuNjE3IDQwLjUxODkgMjk2Ljk2IDUwLjYxOTUgMjg4LjAxNSA1Ni41NTJWNTYuNTQ3QzI4Ny41ODggNTYuODM0IDI4Ny4wNjggNTcuMTYxOSAyODYuNTg1IDU3LjQ0MjVMMjg2LjQzOCA1Ny41MjYxTDI4Ni41ODUgNTcuNTQ2M0MyOTUuOTczIDYwLjEyNTIgMzA1LjI2NSA2MC4wMjgzIDMwNy4wNTIgNTQuMTYxQzMwOS4zNjMgNDYuNTg3OSAyOTQuNDI0IDM4LjQ2MjggMjg3LjM3MSAzNC41OTI0QzI4Ni45OTUgMzQuMzg1NCAyODYuNjcyIDM0LjI2NDMgMjg2LjQxMyAzNC4yMTM2TDI4Ni40MTMgMzQuMjEzMloiLz4mI3hhOzxwYXRoIGZpbGw9IiMyMzFGMjAiIGQ9Ik00NzQuNzg5IDIuMDE4ODRDNDcxLjk0NyAyLjAxODg0IDQ2OS42NDQgNC4zMjU1IDQ2OS42NDQgNy4xNjU2MkM0NjkuNjQ0IDEwLjAwNjUgNDcxLjk0NyAxMi4zMTI0IDQ3NC43ODkgMTIuMzEyNEM0NzcuNjIzIDEyLjMxMjQgNDc5LjkxOSAxMC4wMDY1IDQ3OS45MTkgNy4xNjU2MkM0NzkuOTE5IDQuMzI1NSA0NzcuNjIzIDIuMDE4ODQgNDc0Ljc4OSAyLjAxODg0Wk0zNjguOTY1IDYuODY1NDdWMTYuNzA2M0gzNjEuMDU4VjIzLjMzOTFIMzY4Ljk2NVY0NC4yMTY1QzM2OC45NjUgNDguOTk3IDM2OS45MDUgNTcuNzEzMyAzODEuOTgyIDU3LjcxMzNDMzg1LjAwOSA1Ny43MTMzIDM4Ni45NDkgNTcuNTA0NCAzODkuNjEzIDU3LjE0MjVWNTAuMjI5M0MzODkuNjEzIDUwLjIyOTMgMzg2Ljg2MiA1MC45MzI5IDM4NC40MTQgNTAuOTMyOUMzNzguMDMgNTAuOTMyOSAzNzYuNjM1IDQ4LjMxMSAzNzYuNjM1IDQzLjMxNjFWMjMuMzM5MUgzODkuMjZWMTYuNzA2M0gzNzYuNjM1VjYuODY1NDdIMzY4Ljk2NVpNNDEzLjMzNCAxNS44NzQ4QzQwMC42NDUgMTUuODc0OCAzOTEuMTQ3IDI0LjEzNzcgMzkxLjE0NyAzNy4wMDgxVjM3LjMzMjhDMzkxLjE0NyA0OS44ODU2IDQwMC42OTYgNTcuOTg4OCA0MTQuNTUzIDU3Ljk4ODhDNDIxLjIxMSA1Ny45ODg4IDQyNy40MzQgNTYuMzIyMSA0MzMuMTQyIDUwLjgzOTRMNDI3LjUxMSA0NS44ODk1QzQyNy41MTEgNDUuODg5NSA0MjIuNjgzIDUwLjkzNzggNDE0LjU0MyA1MC45Mzc4QzQwNi45ODMgNTAuOTM3OCAzOTkuNjU1IDQ2LjE0MzEgMzk5LjMwOSAzOS43ODgxTDQzNC40MyAzOS42NDA1QzQzNC42MDYgMzguMDQ5OSA0MzQuNjA2IDM2LjI5OTUgNDM0LjYwNiAzNi4yOTk1QzQzNC42MDYgMjUuMDk4NyA0MjUuOTQxIDE1Ljg3NDggNDEzLjMzMyAxNS44NzQ4TDQxMy4zMzQgMTUuODc0OFpNNTA1Ljc3MiAxNi4xMjU3QzQ5Ny4xMDEgMTYuMTI1NyA0ODQuNjAzIDIxLjM3ODcgNDg0LjYwMyAzNi44NTA2QzQ4NC42MDMgNTIuMTc2NiA0OTUuODQ1IDU3LjkwNTEgNTA1LjY4NCA1Ny45MDUxQzUxNS40OSA1Ny45MDUxIDUyMC40MDYgNTIuNDQ4NCA1MjAuNDA2IDUyLjQ0ODRMNTIwLjgwNSA1Ny44OTUzSDUyNy45OTRWMTYuNjcxOUg1MjAuODE5TDUyMC40MDYgMjEuNzg0MkM1MjAuNDA2IDIxLjc4NDIgNTE1LjYzNSAxNi4xMjU3IDUwNS43NzIgMTYuMTI1N1pNMzM3LjMxMyAxNi4xNTUyQzMyOS4wMjggMTYuMTU1MiAzMjAuMTMzIDE5LjI5MzQgMzIwLjEzMyAyNy42OTg1QzMyMC4xMzMgMzQuOTc1OCAzMjYuNzQzIDM3LjIzOTUgMzM3LjY1MiAzOS4yOTZDMzQ0LjkzOSA0MC42NjU3IDM1MS42NjIgNDEuMzYyNCAzNTEuNjYyIDQ1LjI5ODlDMzUxLjY2MiA0Ny43ODAzIDM0OC4zNTMgNTAuOTYyMyAzNDAuMDg1IDUwLjk2MjNDMzM0LjIxNSA1MC45NjIzIDMyOC4yODkgNDkuNzI4IDMyNi40NzggNDUuNTRMMzE5LjQxMSA0OC41MTJDMzIyLjMxIDU0Ljk3NjYgMzI5Ljk5NyA1OC4zMjMzIDMzOC45MSA1OC4zMjMzQzM1Ni43MTEgNTguMzIzMyAzNjAuMTQ5IDQ5Ljg1MDEgMzYwLjE0OSA0NS4wMzgxQzM2MC4xNDkgMzcuMDI0MSAzNTIuMTg0IDM0LjcyMzQgMzQ0LjIzNyAzMy4zMDI5QzMzNi4zMzEgMzEuODg4OSAzMjguNTU2IDMwLjkwMiAzMjguNTU2IDI3LjIxMTRDMzI4LjU1NiAyNC44MDgyIDMzMC45NDggMjIuNjg0NiAzMzcuODY0IDIyLjY4NDZDMzQzLjAwOCAyMi42ODQ2IDM0OC45NDEgMjMuODgxNyAzNTAuNTUyIDI3LjUzMTJMMzU3LjgzOSAyNC40NjA5QzM1NS4wNTMgMTguODUyNCAzNDYuNzk3IDE2LjE1NTIgMzM3LjMxMyAxNi4xNTUyWk00NjIuMzE3IDE2LjYyNzVDNDU2LjAyMyAxNi41Njk2IDQ1MS4zOTMgMTguNDc3NCA0NDcuNjczIDIyLjM1OThMNDQ3LjI2MSAxNi42ODY2SDQzOS44MTZWNTcuODcwNkg0NDcuNjczVjM2LjUyNThDNDQ3LjY3MyAzMC41NzQ3IDQ1Mi40NjIgMjMuMDI5IDQ2NS4xMTggMjMuNzM3NlYxNi43NzAyQzQ2NC4xNDkgMTYuNjgxNiA0NjMuMjE2IDE2LjYzNiA0NjIuMzE3IDE2LjYyNzZWMTYuNjI3NVpNNDcwLjg1MyAxNi42NjIxVjU3LjgxMTdINDc4LjcxMVYxNi42NjIxSDQ3MC44NTNaTTUwNi4xMDIgMjMuMDA0NUM1MTMuNzE4IDIzLjAwNDUgNTIwLjQwNiAyNy4zMjk0IDUyMC40MDYgMzYuNzc2OEM1MjAuNDA2IDQ2LjIxNzggNTE0LjUyMSA1MC45Mzc4IDUwNi4xOCA1MC45Mzc4QzUwMS44MDggNTAuOTM3OCA0OTIuNTkzIDQ4LjE0NjUgNDkyLjU5MyAzNi45MzkyQzQ5Mi41OTMgMjUuNTUyMSA1MDEuNzg2IDIzLjAwNDUgNTA2LjEwMiAyMy4wMDQ1Wk00MTMuNDM3IDIzLjIyMDlDNDIwLjM0NSAyMy4yMjA5IDQyNS45NzMgMjcuODg0NCA0MjUuOTczIDMzLjQ4SDM5OS40MzdDMzk5LjkyOCAyNy43OTI1IDQwNi4wMzggMjMuMjIwOSA0MTMuNDM3IDIzLjIyMDlaIi8+JiN4YTs8cGF0aCBmaWxsPSIjMjMxRjIwIiBkPSJNNjguODMyOCAxNi4xMjA4QzU4LjMyNTkgMTYuMTIwOCA0NS44ODQxIDIxLjM4MDQgNDUuODg0MSAzNi44NTU1QzQ1Ljg4NDEgNTIuMTc4MyA1Ny4wNzggNTguMTMxNSA2OC44MzI4IDU4LjEzMTVDODAuNTk1NiA1OC4xMzE1IDkxLjc4NjQgNTIuMTc4MyA5MS43ODY0IDM2Ljg1NTVDOTEuNzg2NCAyMS4zODA0IDc5LjM0NzcgMTYuMTIwOCA2OC44MzI4IDE2LjEyMDhaTTELine truncated
|
||||
<mxGeometry x="160" y="160" width="167.01" height="21.27" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="mBbMmTsdGeR7BbYYs1AZ-8" value="" style="shape=image;verticalLabelPosition=bottom;labelBackgroundColor=default;verticalAlign=top;aspect=fixed;imageAspect=0;editableCssRules=.*;image=data:image/svg+xml,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAuMTk3NTg5ODc0MjY3NTc4MTIgOS41MzY3NDMxNjQwNjI1ZS03IDQyNC44MDE2MDUyMjQ2MDk0IDU5LjUyNzU2NTAwMjQ0MTQwNiIgaWQ9ImEiIHdpZHRoPSI0MjQuODAxNjA1MjI0NjA5NCIgaGVpZ2h0PSI1OS41Mjc1NjUwMDI0NDE0MDYiPjxwYXRoIHN0cm9rZS13aWR0aD0iMCIgZmlsbD0iI2VmN2QwMCIgZD0ibTYwLjI5Mzk4NDYwNzg4NjE1OSw1MC43NjkyOTMwMTMxMzA2NjRjOC45MjIwODUyNDQwODU3OTMtNS45MDg5NDExMjUxMDQzMiwyLjU5NzUxMDExNTM2ODA5NS0xNS45NjgwMTUxMzE5NDI1OTMtMS44NDE4MzIwMDA0NDAyNjMtMjEuMDgwNTAyMjg5NzcwMjY2LS45NjIzMDUyMDc3MDQwNDgtMS4xMDQ3MDE3NDYxMTQxODgtLjMwNzA5NDY3MTA5NTAwMS0xLjYyMDQ4NDM4MzM4MTE1NCwxLjE5ODY2Nzc1OTU1NTk3NC0uNzk0NTE1NzY0OTAxOTE3LDcuMDMyODUzMTU1NDA5NjEyLDMuODU3MTIwODQ1NTM0MTU1LDIxLjkzMjIwNzI5MTU4MjkyNywxMS45NTEyMjU2NjYxNjkxOTQsMTkuNjI5NzQ1NTUxNTgzMjU1LDE5LjQ5NjE4MTMyNzc3Njg2Ny0xLjc3OTEyMjU2NjkxMTYxLDUuODQzODAyODA1MDcxMzkxLTExLjA1MzM0NzI4NjQ2NjMwNSw1Ljk0MDI5NTg0MTg2MTM3MS0yMC40MjAwMTY4OTg2MzkwMDUsMy4zNjg0OTc1NzU1ODU5MzRsLS4xNDU1MTIzODI3MDQ1MS0uMDE2ODc5NTM0NTI1Mjg3LjE0NTUxMjM4MjcwNDUxLS4wODQ0MjIyMDY4NjA4MzZjLjQ3NzU1ODM0MjUwMzQ3MS0uMjc0OTU0ODU5NzIyMTI5LDEuMDAwMTEyNDIwOTUyMDY0LS42MDIxNDMwNDY0OTcwNzYsMS40MzM0MzU1ODc5NDAwMzktLjg4ODM1OTEwNzMwNDQ5NWgwWiIvPjxwYXRoIHN0cm9rZS13aWR0aD0iMCIgZmlsbD0iI2NmMDIyYiIgZD0ibTU4LjcyMzg5MzQ4NjAyNzkxOSw1MS43MzgwNTA3MTczMjIwMzlzLTExLjc5Mjk1NTQ5NTcxNzI1MSw3LjQxMjI3NDY2OTI0ODI5MS0zMi42MDUyNzQzNjY5NjA2NTcsNy40MTIyNzQ2NjkyNDgyOTFjLTguMDYzNDg2MTMwMDgyMzEsMC0xNC44NTE5Mjk1MTM3MDIwODktMS4yMzQ4MDY2NDY3MDIwNTQtMTcuNzIyNzk5NzY1MzI4MDkxLTQuMDU5MDYxOTA0NzEyNTA3LTMuMDIzNTk1NjkxMjUxMTAzLTIuOTczNzY2NzE2NDk3MjY2LTQuNjYzNjMzODM3NzMwMjMxLTcuOTgzODk3MTYyMDQxMTAzLTQuNjYzNjMzODM3NzMwMjMxLTEyLjU5MDQ2NDQzMzg5NTI0OSwwLTMuOTQ3MzMzMTI1MjgxMTQ4LDEuNjQwMDM4MTQ2NDc5MTI4LTcuMjY1OTc3MTkxOTE3MDQ1LDUuMDU2NzY5OTczMzY4MzE4LTcuMjY1OTc3MTkxOTE3MDQ1LDguNzc2NTcyODYxMzg0OTIxLDAsMzYuMTEwNDI3NDc2NDc1NzA2LDEzLjkwMzI4OTg1OTM4ODY3OCw0OS45MzQ5Mzc5OTY2NTA2NiwxNi41MDMyMjg4NjEyNzY1MTFoMFoiLz48cGF0aCBzdHJva2Utd2lkdGg9IjAiIGZpbGw9IiNlZjdkMDAiIGQ9Im0xOS40MjI2MjA2MjgwNTM2NjQsOC43NTgxNDMzNzA5NTAxNjVjLTguOTIyMDg1MjQ0MDkzOTc4LDUuOTA4NTk3NjQ2MjA5MjczLTIuNTk2NzAwNDg2NTQ4Nzk4LDE1Ljk2ODAxNTEzMTkzMjU4OSwxLjg0MTgzMjAwMDQzMjA3OCwyMS4wODA2MDA0MjY2MTE0NTQuOTYyMzA1MjA3Njk5NTAxLDEuMTA0NjAzNjA5MjY2NjMzLjMwNzkwNDI5OTk0MzQwMiwxLjYyMDQxMDc4MDc0NjE3LTEuMTk3ODU4MTMwNzE2NjY4Ljc5NDUxNTc2NDg4NTU0NkMxMy4wMzM3NDEzNDIzNDk0NTgsMjYuNzc2MDQwNTgwMDkwMDEyLTEuODY1NjEyNzkzODA1NjY3LDE4LjY4MTY5MDQxNzM4NDc0My40MzY4NDg5NDYxODU4MTksMTEuMTM2OTgwMDk3ODUwMDI4YzEuNzc5MTIyNTY2OTA2MTUzLTUuODQzNDgzODYwMzgwNzI5LDExLjA1MzMyMjc1MjI2NjQ2Ny01Ljk0MDA1MDQ5OTc5MDIzMiwyMC40MTkyMDcyNjk3OTk2OTktMy4zNjg4MTY1MjAyODQ3ODFsLjE0NjI5NzQ3NzMzMzk3NC4wMTcxOTg0NzkyMjUwNDQtLjE0NjI5NzQ3NzMzMzk3NC4wODQ4MzkyODgzNzcyMjVjLS40NzY3NDg3MTM2NzUwNzkuMjc0NzU4NTg2MDcyNDk0LTEuMDAwMTEyNDIwOTU1NzAyLjYwMTQwNzAyMDI4MTg0LTEuNDMzNDM1NTg3OTMxODU0Ljg4Nzk0MjAyNTc4MjY1aDBaIi8+PHBhdGggc3Ryb2tlLXdpZHRoPSIwIiBmaWxsPSIjY2YwMjJiIiBkPSJtMjAuOTkyNzExNzQ5OTAxOSw3Ljc4OTMxMjA2NDE0MzgxNFMzMi43ODU2NjcyNDU2MzE4ODUuMzc3MjMzNjY4NTUwNjE2LDUzLjU5ODc5NTc0NTcwMjc3My4zNzcyMzM2Njg1NTA2MTZjOC4wNjM0ODYxMzAwNzc3NjMsMCwxNC44NTE5Mjk1MTM3MDM5MDgsMS4yMzQ5NTM4NTE5NDY1NTcsMTcuNzIyNzc1MjMxMTMxODkxLDQuMDU4OTM5MjMzNjc3ODQ3LDMuMDIzNjIwMjI1NDQ0NTc0LDIuOTczOTEzOTIxNzI4MTI2LDQuNjYzNjU4MzcxOTI3MzQsNy45ODM2NTE4MTk5NjI2ODgsNC42NjM2NTgzNzE5MjczNCwxMi41OTA2MTE2MzkxMzc5MzMsMCwzLjk0NzQwNjcyNzg5NzAzMi0xLjY0MDAzODE0NjQ4Mjc2Niw3LjI2NTk3NzE5MTkxNTIyNi01LjA1Njc2OTk3MzM3MTA0Nyw3LjI2NTk3NzE5MTkxNTIyNi04Ljc3NjU3Mjg2MTM4NDkyMSwwLTM2LjExMTIzNzEwNTMxNDEwMy0xMy45MDMyMTYyNTY3NzAwNjUtNDkuOTM1NzQ3NjI1NDg5MDU3LTE2LjUwMzQ0OTY2OTEzNzgwOGgwWiIvPjxwYXRoIHN0cm9rZS13aWR0aD0iMCIgZmlsbD0iIzFkMWQxYiIgZD0ibTE3NS40NjczMDkzNDQyMzI0OSwyOS45MjE2NzM5OTIwMzg0NWMtNy4wNTgyOTE5MTU2MDg2MDYsMC0xMC41NTUzNjkxOTg3Nzc5NTMtNC40ODUzMjg2Mjk3MzAyNDctMTAuNTU1MzY5MTk4Nzc3OTUzLTQuNDg1MzI4NjI5NzMwMjQ3djEyLjQxODA0MTk4NjIwMjI5N2gtNS40Mzk1MDUwODA0NTA1NDRWLjM4OTQyNzM4MTExMmg1LjE0NTkxNzc1MzE3MDU3OWwuMjkzNTg3MzI3Mjc5OTY1LDMuNjY0NTUwNDU3ODc3MzRTMTY4LjMzNTYyMDU5NjgxNTcxNi4wMDExMTExMzc5MDg5OTMsMTc1LjQwNjYyODg2ODQ3ODk0Ny4wMDExMTExMzc5MDg5OTNjNi4yMjE0NDYzMzY4NDM2NTUsMCwxNS4xNzkzOTYwNTg4MjExMTIsMy4xNzE4NzU0MzY4Njg1MDQsMTUuMTc5Mzk2MDU4ODIxMTEyLDE0Ljg0NTY0OTAzMjg5MDgzMywwLDExLjY3MzIwOTIwODQ5OTQyOS04LjA1OTg0MTY0Mjg0MjkxMiwxNS4wNzQ5MTM4MjEyMzg2MjQtMTUuMTE4NzE1NTgzMDY3NTY5LDE1LjA3NDkxMzgyMTIzODYyNGgwWm0tLjI5NDc0MjU1NzkzNzc1Mi0yNC45OTE3NjY3MzkzMTI4OGMtNS40NjMyMDA1MzcyNzE0MDUsMC0xMC4yNjA2MjY2NDA4NDAyMDEsMy4wOTczNzYyODU3MDM4MzEtMTAuMjYwNjI2NjQwODQwMjAxLDkuODU5ODg1MDUzODkwMTM5LDAsNi43NjIyNjE4NDg2NTU3MjYsNC4yMTk0OTMyOTQ0OTc2MjQsMTAuMTQzNzM2Njk2NjI0NzI4LDEwLjIwMjI0NzgwNzg4MDY2NywxMC4xNDM3MzY2OTY2MjQ3MjgsMy4xMzM1NTg4MTYxNTU1MjgsMCw5Ljc0Nzk5NTIzMDI1NTU4My0xLjI3MTQ0MTU5NzY3NDMxMSw5Ljc0Nzk5NTIzMDI1NTU4My0xMC4wMjgwNTQ4OTQ0MTkyNTYsMC04Ljc1NzA1NDIyNDQ3NjYzMy02LjU5NDIxNTQ2Nzg3OTYyMS05Ljk3NTU2Njg1NjA5NTYxMS05LjY4OTYxNjM5NzI5NjALine truncated
|
||||
<mxGeometry x="360" y="158.14" width="177.08" height="25" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="mBbMmTsdGeR7BbYYs1AZ-9" value="" style="shape=image;verticalLabelPosition=bottom;labelBackgroundColor=default;verticalAlign=top;aspect=fixed;imageAspect=0;image=data:image/png,iVBORw0KGgoAAAANSUhEUgAAAZQAAAAyCAYAAAB79y3nAAAABGdBTUEAALGPC/xhBQAACklpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAAEiJnVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/stRzjPAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAJcEhZcwAADsQAAA7EAZUrDhsAABSwSURBVHic7Z152F7TuYfvJ74kYkramiJFcuKYQg2tFqVmQcycoqoXSqihphKOGNI6pDUdreJU65QaT44WNdZMTDUlwTHVTA4xx0ySX/94duLLl/3ttfd+9/B+yb6va/3xvnvttZ49rr3WM5kkysTMlgE2A+YH7pE0odQOS8bMFgI2AL4LrBCVxYAFo/Iu8AnwIvAs8ChwFzBJ0vQaRG5IiZl1AN8G1sGv678CCwNfAaYDU4G38ev6JDAev67lPkQNDT0EK/NZMLNRwElAR6e/LwP2lvRpaR0XjJktAOwI7AFszOzHk5YpwDjgYkn3FyheEDPrA2wBDAX6VNl3BqYC4yU9VmWnZjYfMBz4ETACWChjE28BVwIXSbq3YPFmw8y2BrbBB7giWAQYjH/sfQC8ABT1XL4KXCHpgYLam4WZDQQ2AZYBlgQWB3oV3M004GXgkqrvyR6NpFIKsDOgbspZZfVb8DEMAI7DXxrdHUuech+wA9GAXvIxrARMLFj+MsuFQO8KzktfYD/guQJlfwj/8Cj0uuIvy8va4NrkKScUdA76AAcAD+CzxarknwYcWPe7qKeUsh7W+YGXAhdpWN0HnyB/r+hl83bJN+tdwBolHkc/fGmm7pdK1jK25Ou7Ucnn5SFgrQLlPawNrkkrZZMWj38Xih34s5bplPiczk2lrAf2yBQX6Zq6D74b2ZcF7qnwZv0COAHoKOFY9myDl0me8gHQt4TzsQBwQYXX9TigVwFyP9sG16SVclHO4+4NnNsG8gs4s+53U08oRa87YmYDgKNTVN3GzDYquv9WMLMRwCPAuhV22wGcCNxqZosW3PbyBbdXFQsBg4ps0MyGAH8H9iqy3QQ6gJ8DN5pZ/xbbGliAPHWSWX4z6wfcBOxfvDi5WKpuAXoChQ8owLHAV1PWHWtmVoIMmTGzHwNXk172ovkecJ+Z/UuBbb5eYFtV8gXwZlGNmdkwfHlxWFFtZmAzYLyZtTJAPlOUMDXxdJbK0TvhAnxpsl3o6degGgpeUhgEfEy2qeROdU/TgIOAGRnlLqs8QUFKafyr6qM2OKas5fICr+1y+MBa9zE9Ayye8xj2aAP585ZPgZUzHu+oNpC7c/kAWKbu91RLine truncated
|
||||
<mxGeometry x="560" y="160.64" width="161.6" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="mBbMmTsdGeR7BbYYs1AZ-1" value="Data Center" style="" parent="0" />
|
||||
<mxCell id="mBbMmTsdGeR7BbYYs1AZ-10" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;dashed=1;arcSize=2;" vertex="1" parent="mBbMmTsdGeR7BbYYs1AZ-1">
|
||||
<mxGeometry x="580" y="240" width="170" height="320" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="mBbMmTsdGeR7BbYYs1AZ-11" value="" style="shape=image;verticalLabelPosition=bottom;labelBackgroundColor=default;verticalAlign=top;aspect=fixed;imageAspect=0;editableCssRules=.*;image=data:image/svg+xml,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjQuMDAzNTE2MTk3MjA0NTkgNi41NDMwMDAyMjEyNTI0NDEgODcuOTkzMjU1NjE1MjM0MzggODIuOTEzMDAyMDE0MTYwMTYiIGhlaWdodD0iMTI5LjU1MTU2NTY0NzEyNTI0IiB3aWR0aD0iMTM3LjQ4OTQ2MTg5ODgwMzciPiYjeGE7ICAgIDxkZWZzPiYjeGE7ICAgICAgICA8bGluZWFyR3JhZGllbnQgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMTA3NSAxNTgpIiB5Mj0iNjUuNDI2IiB5MT0iMTQ1LjMxMiIgeDI9Ii0xMDU5LjIxMyIgeDE9Ii0xMDMyLjE3MiIgaWQ9ImUzOTljMTlmLWI2OGYtNDI5ZC1iMTc2LTE4YzIxMTdmZjczYyI+JiN4YTsgICAgICAgICAgICA8c3RvcCBzdG9wLWNvbG9yPSIjMTE0YThiIiBvZmZzZXQ9IjAiLz4mI3hhOyAgICAgICAgICAgIDxzdG9wIHN0b3AtY29sb3I9IiMwNjY5YmMiIG9mZnNldD0iMSIvPiYjeGE7ICAgICAgICA8L2xpbmVhckdyYWRpZW50PiYjeGE7ICAgICAgICA8bGluZWFyR3JhZGllbnQgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMTA3NSAxNTgpIiB5Mj0iMTA1Ljk2OCIgeTE9IjEwOC4wODMiIHgyPSItMTAyOS45OCIgeDE9Ii0xMDIzLjcyNSIgaWQ9ImFjMmE2ZmMyLWNhNDgtNDMyNy05YTNjLWQ0ZGNjMzI1NmUxNSI+JiN4YTsgICAgICAgICAgICA8c3RvcCBzdG9wLW9wYWNpdHk9Ii4zIiBvZmZzZXQ9IjAiLz4mI3hhOyAgICAgICAgICAgIDxzdG9wIHN0b3Atb3BhY2l0eT0iLjIiIG9mZnNldD0iLjA3MSIvPiYjeGE7ICAgICAgICAgICAgPHN0b3Agc3RvcC1vcGFjaXR5PSIuMSIgb2Zmc2V0PSIuMzIxIi8+JiN4YTsgICAgICAgICAgICA8c3RvcCBzdG9wLW9wYWNpdHk9Ii4wNSIgb2Zmc2V0PSIuNjIzIi8+JiN4YTsgICAgICAgICAgICA8c3RvcCBzdG9wLW9wYWNpdHk9IjAiIG9mZnNldD0iMSIvPiYjeGE7ICAgICAgICA8L2xpbmVhckdyYWRpZW50PiYjeGE7ICAgICAgICA8bGluZWFyR3JhZGllbnQgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMTA3NSAxNTgpIiB5Mj0iNjguNTYxIiB5MT0iMTQ3LjY0MiIgeDI9Ii05OTcuNDgyIiB4MT0iLTEwMjcuMTY1IiBpZD0iYTdmZWU5NzAtYTc4NC00YmIxLWFmOGQtNjNkMThlNWY3ZGI5Ij4mI3hhOyAgICAgICAgICAgIDxzdG9wIHN0b3AtY29sb3I9IiMzY2NiZjQiIG9mZnNldD0iMCIvPiYjeGE7ICAgICAgICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzI4OTJkZiIgb2Zmc2V0PSIxIi8+JiN4YTsgICAgICAgIDwvbGluZWFyR3JhZGllbnQ+JiN4YTsgICAgPC9kZWZzPiYjeGE7ICAgIDxwYXRoIGQ9Ik0zMy4zMzggNi41NDRoMjYuMDM4bC0yNy4wMyA4MC4wODdhNC4xNTIgNC4xNTIgMCAwIDEtMy45MzMgMi44MjRIOC4xNDlhNC4xNDUgNC4xNDUgMCAwIDEtMy45MjgtNS40N0wyOS40MDQgOS4zNjhhNC4xNTIgNC4xNTIgMCAwIDEgMy45MzQtMi44MjV6IiBmaWxsPSJ1cmwoI2UzOTljMTlmLWI2OGYtNDI5ZC1iMTc2LTE4YzIxMTdmZjczYykiLz4mI3hhOyAgICA8cGF0aCBkPSJNNzEuMTc1IDYwLjI2MWgtNDEuMjlhMS45MTEgMS45MTEgMCAwIDAtMS4zMDUgMy4zMDlsMjYuNTMyIDI0Ljc2NGE0LjE3MSA0LjE3MSAwIDAgMCAyLjg0NiAxLjEyMWgyMy4zOHoiIGZpbGw9IiMwMDc4ZDQiLz4mI3hhOyAgICA8cGF0aCBkPSJNMzMuMzM4IDYuNTQ0YTQuMTE4IDQuMTE4IDAgMCAwLTMuOTQzIDIuODc5TDQuMjUyIDgzLjkxN2E0LjE0IDQuMTQgMCAwIDAgMy45MDggNS41MzhoMjAuNzg3YTQuNDQzIDQuNDQzIDAgMCAwIDMuNDEtMi45bDUuMDE0LTE0Ljc3NyAxNy45MSAxNi43MDVhNC4yMzcgNC4yMzcgMCAwIDAgMi42NjYuOTcySDgxLjI0TDcxLjAyNCA2MC4yNjFsLTI5Ljc4MS4wMDdMNTkuNDcgNi41NDR6IiBmaWxsPSJ1cmwoI2FjMmE2ZmMyLWNhNDgtNDMyNy05YTNjLWQ0ZGNjMzI1NmUxNSkiLz4mI3hhOyAgICA8cGF0aCBkPSJNNjYuNTk1IDkuMzY0YTQuMTQ1IDQuMTQ1IDAgMCAwLTMuOTI4LTIuODJIMzMuNjQ4YTQuMTQ2IDQuMTQ2IDAgMCAxIDMuOTI4IDIuODJsMjUuMTg0IDc0LjYyYTQuMTQ2IDQuMTQ2IDAgMCAxLTMuOTI4IDUuNDcyaDI5LjAyYTQuMTQ2IDQuMTQ2IDAgMCAwIDMuOTI3LTUuNDcyeiIgZmlsbD0idXJsKCNhN2ZlZTk3MC1hNzg0LTRiYjEtYWY4ZC02M2QxOGU1ZjdkYjkpIi8+JiN4YTs8L3N2Zz4=;" vertex="1" parent="mBbMmTsdGeR7BbYYs1AZ-1">
|
||||
<mxGeometry x="590" y="580" width="42.15" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="mBbMmTsdGeR7BbYYs1AZ-12" value="Microsoft Azure" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontFamily=Tahoma;fontSize=16;" vertex="1" parent="mBbMmTsdGeR7BbYYs1AZ-1">
|
||||
<mxGeometry x="640" y="585" width="130" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="mBbMmTsdGeR7BbYYs1AZ-2" value="Layer" parent="0" />
|
||||
<mxCell id="mBbMmTsdGeR7BbYYs1AZ-3" value="Komponenten" parent="0" />
|
||||
<mxCell id="mBbMmTsdGeR7BbYYs1AZ-4" value="Gruppen" parent="0" />
|
||||
<mxCell id="mBbMmTsdGeR7BbYYs1AZ-5" value="Zuständigkeiten" parent="0" />
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1,11 @@
|
||||
Durchsuchen von Azure-Architekturen
|
||||
https://learn.microsoft.com/de-de/azure/architecture/browse/
|
||||
|
||||
Baselinearchitektur für einen AKS-Cluster (Azure Kubernetes Service)
|
||||
https://learn.microsoft.com/de-de/azure/architecture/reference-architectures/containers/aks/baseline-aks
|
||||
|
||||
Microsoft AI Tour
|
||||
https://aitour.microsoft.com/flow/microsoft/aitour/landing/page/home?wt.mc_ID=AITourFY26-Tier1_GMEE_corp_bn_oo_bn_MSLearn
|
||||
|
||||
Erstellen ausgezeichneter Lösungen mit dem Microsoft Azure Well-Architected Framework
|
||||
https://learn.microsoft.com/de-de/training/paths/azure-well-architected-framework/?source=recommendations
|
||||
|
After Width: | Height: | Size: 7.9 KiB |
@@ -0,0 +1,26 @@
|
||||
<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_98_3132)">
|
||||
<g clip-path="url(#clip1_98_3132)">
|
||||
<rect x="12.05" y="0.05" width="71.9" height="95.9" rx="3.95" stroke="white" stroke-width="0.1"/>
|
||||
<rect x="0.05" y="83.95" width="71.9" height="95.9" rx="3.95" transform="rotate(-90 0.05 83.95)" stroke="white" stroke-width="0.1"/>
|
||||
<rect x="6.05" y="89.95" width="83.9" height="83.9" rx="3.95" transform="rotate(-90 6.05 89.95)" stroke="white" stroke-width="0.1"/>
|
||||
<circle cx="48" cy="48" r="23.95" stroke="white" stroke-width="0.1"/>
|
||||
<circle cx="48" cy="48" r="47.95" stroke="white" stroke-width="0.1"/>
|
||||
<path d="M48 0V96" stroke="white" stroke-width="0.1"/>
|
||||
<path d="M96 47.9999L0 47.9999" stroke="white" stroke-width="0.1"/>
|
||||
<path d="M0 6.10352e-05L96 96.0001" stroke="white" stroke-width="0.1"/>
|
||||
<path d="M0 96.0001L96 5.62289e-05" stroke="white" stroke-width="0.1"/>
|
||||
<path d="M25.6416 90.4728C27.0797 90.4644 28.4659 90.1346 29.4945 89.5368L59.8103 71.5187C63.2057 69.563 65.9594 65.2917 65.9594 61.976V33.5368C65.9594 26.8761 64.5079 20.922 62.0167 16.4448C73.5834 19.878 81.7427 28.1929 81.7427 37.908V84.1749C81.7427 87.6859 78.0767 90.5367 73.5439 90.5367H26.8083C26.4122 90.5367 26.0227 90.5149 25.6416 90.4728Z" stroke="black" stroke-width="4"/>
|
||||
<path d="M18.6128 64.2749C19.261 64.091 19.9526 63.7767 20.6707 63.3246L37.1189 53.216C41.7761 50.3538 44.6384 45.1107 44.6384 39.4421V22.9964C44.6384 17.5879 43.7219 13.256 42.1194 10.3192C42.1754 10.2866 42.2315 10.2542 42.2876 10.2218C55.3597 2.69704 65.9603 15.5367 65.9603 33.5368V61.976C65.9603 65.2917 63.2066 69.563 59.8111 71.5187L29.4954 89.5368C26.0538 91.5368 18.6104 90.5368 18.6104 84.5368L18.6128 64.2749Z" stroke="black" stroke-width="4"/>
|
||||
<path d="M33.2387 6.50703C23.3322 10.1545 14.0049 26.5433 14.0049 44.4012L14 60.4046C14 64.3366 16.9871 65.6438 20.6705 63.3246L37.1187 53.216C41.7759 50.3538 44.6382 45.1107 44.6382 39.4421V22.9964C44.6382 10.7625 39.9484 4.03662 33.2387 6.50703Z" stroke="black" stroke-width="4"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_98_3132">
|
||||
<rect width="96" height="96" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_98_3132">
|
||||
<rect width="96" height="96" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="uuid-224e3580-d59f-48a8-9bb3-c822840f911d" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<path d="m17.558,9.638L10.436,1.632c-.358-.402-.881-.632-1.435-.632h0c-.554,0-1.078.231-1.435.632L.442,9.638c-.346.389-.5.9-.423,1.403.077.503.379.952.827,1.231l7.123,4.436c.313.195.672.293,1.031.293s.718-.098,1.031-.293l7.123-4.436c.448-.279.75-.728.827-1.231.077-.503-.077-1.014-.423-1.403Zm-8.557,2.564l-3.47-2.161,3.47-3.9,3.47,3.9-3.47,2.161Zm-4.821-1.975l3.995,2.489-1.137.708-.016.01c-.394.25-.854.382-1.33.382-.528,0-1.059-.214-1.38-.413l-2.969-1.849c-.235-.147-.387-.373-.428-.637-.04-.264.037-.521.219-.725L8.257,2.186c.185-.208.456-.327.743-.328.287,0,.558.119.743.328l1.576,1.771c-.009,0-.018,0-.028,0-.856,0-1.668.313-2.278.866l-4.833,5.404Zm12.905.69c-.041.264-.192.49-.428.637l-7.123,4.436c-.325.202-.744.202-1.068,0l-2.192-1.365c.451-.076.88-.238,1.264-.484l6.259-3.898-4.197-4.759c.448-.425,1.053-.667,1.692-.667.335,0,.661.07.97.201l4.605,5.175c.181.204.259.461.219.725Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<g id="uuid-81cf057c-ff75-4803-9a10-fe44c632e0c8" />
|
||||
<g id="uuid-b8784509-f923-44ee-a3b9-d40f96b60fd9">
|
||||
<g>
|
||||
<path d="M18,9c0-1.574-.406-3.052-1.117-4.34,.387-.44,.629-1.011,.629-1.643,0-1.381-1.119-2.5-2.5-2.5-.628,0-1.196,.24-1.635,.623-1.296-.724-2.788-1.14-4.378-1.14s-3.082,.416-4.378,1.14c-.439-.383-1.007-.623-1.635-.623C1.606,.517,.487,1.636,.487,3.017c0,.632,.242,1.203,.63,1.643-.71,1.287-1.116,2.766-1.116,4.34,0,1.594,.418,3.089,1.146,4.388-.405,.444-.659,1.03-.659,1.679,0,1.381,1.119,2.5,2.5,2.5,.656,0,1.247-.258,1.693-.671,1.282,.703,2.754,1.104,4.32,1.104s3.037-.401,4.32-1.104c.446,.413,1.038,.671,1.693,.671,1.381,0,2.5-1.119,2.5-2.5,0-.649-.253-1.234-.659-1.678,.727-1.299,1.146-2.794,1.146-4.388ZM13.617,2.478c.125-.324,.36-.589,.658-.759,.219-.125,.468-.202,.738-.202,.827,0,1.5,.673,1.5,1.5,0,.272-.078,.523-.205,.743-.172,.297-.438,.53-.762,.654-.166,.064-.344,.102-.533,.102-.827,0-1.5-.673-1.5-1.5,0-.19,.039-.371,.104-.539ZM2.987,1.517c.269,0,.519,.077,.738,.202,.298,.17,.533,.436,.658,.759,.065,.168,.104,.348,.104,.539,0,.827-.673,1.5-1.5,1.5-.188,0-.366-.039-.532-.102-.324-.124-.591-.357-.762-.654-.127-.22-.205-.472-.205-.744,0-.827,.673-1.5,1.5-1.5Zm1.417,14.02c-.11,.33-.327,.608-.615,.792-.233,.148-.507,.237-.802,.237-.827,0-1.5-.673-1.5-1.5,0-.291,.087-.561,.231-.792,.181-.29,.458-.509,.787-.622,.152-.052,.312-.086,.482-.086,.827,0,1.5,.673,1.5,1.5,0,.165-.033,.322-.083,.471Zm10.609,1.029c-.296,0-.57-.089-.802-.237-.288-.184-.505-.462-.615-.792-.05-.149-.083-.306-.083-.471,0-.827,.673-1.5,1.5-1.5,.17,0,.33,.034,.482,.087,.328,.113,.605,.332,.787,.622,.144,.23,.231,.5,.231,.792,0,.827-.673,1.5-1.5,1.5Zm0-4c-1.381,0-2.5,1.119-2.5,2.5,0,.359,.079,.698,.215,1.007-1.114,.59-2.382,.927-3.728,.927s-2.613-.338-3.728-.927c.136-.308,.215-.648,.215-1.006,0-1.381-1.119-2.5-2.5-2.5-.368,0-.714,.084-1.028,.226-.611-1.129-.959-2.421-.959-3.793,0-1.346,.337-2.613,.927-3.727,.323,.152,.68,.244,1.06,.244,1.381,0,2.5-1.119,2.5-2.5,0-.386-.095-.746-.25-1.073,1.123-.601,2.403-.945,3.764-.945s2.641,.343,3.763,.944c-.156,.326-.25,.687-.25,1.073,0,1.381,1.119,2.5,2.5,2.5,.381,0,.737-.092,1.06-.244,.589,1.114,.927,2.381,.927,3.727,0,1.372-.348,2.663-.959,3.793-.314-.143-.661-.227-1.029-.227Z" fill="#212121" />
|
||||
<g>
|
||||
<path d="M11.874,10.037H6.125c-.621,0-1.124,.503-1.124,1.124v.289c0,.446,.159,.878,.449,1.217,.783,.917,1.976,1.369,3.548,1.369s2.765-.452,3.55-1.369c.291-.34,.45-.772,.45-1.219v-.288c0-.621-.503-1.124-1.124-1.124Z" fill="#212121" />
|
||||
<path d="M8.998,9.04c1.38,0,2.499-1.119,2.499-2.499s-1.119-2.499-2.499-2.499-2.499,1.119-2.499,2.499,1.119,2.499,2.499,2.499Z" fill="#212121" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="uuid-af471f8d-f5bf-4123-94c3-4139b0ba56d8" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<path d="m17.679,10.013c-.013-.015-3.337-3.818-3.334-3.806-.54-.751-1.484-1.224-2.511-1.224-.028,0-.055.003-.082.004-.856.022-1.553.463-2.085,1.031l3.701,4.182h0s-6.159,3.855-6.159,3.855c0,0-1.168.856-2.495.541.109.068,3.474,2.169,3.474,2.169.499.313,1.146.313,1.645,0,0,0,7.339-4.596,7.489-4.687.826-.501.838-1.512.357-2.066Z"/>
|
||||
<path d="m10.113,1.467c-.525-.577-1.598-.683-2.248.043-.189.211-7.325,8.262-7.526,8.489-.568.638-.403,1.615.353,2.081,0,0,1.751,1.097,1.865,1.169.696.436,2.571,1.036,4.454-.096l1.179-.738-3.536-2.213s4.461-5.039,4.467-5.047c1.294-1.416,2.987-1.032,3.613-.735.002.001-2.607-2.937-2.621-2.951Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 825 B |
@@ -0,0 +1,7 @@
|
||||
<svg id="uuid-13cc8fd5-c6d0-4916-a4e6-c8d153fb7667" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<g>
|
||||
<path d="m10.775,15.071c-.81.308-1.687.479-2.603.479-2.353,0-4.45-1.115-5.795-2.845l.026-.099c.192-.733.396-1.507.672-2.288.28.103.58.161.895.161.472,0,.914-.128,1.297-.348.616.526,1.27,1.005,2.088,1.482-.113.255-.176.536-.176.832,0,1.139.925,2.065,2.062,2.065.436,0,.84-.137,1.174-.369-.068-.271-.108-.55-.108-.836v-.233c-.215.366-.612.612-1.066.612-.682,0-1.237-.556-1.237-1.239s.555-1.239,1.237-1.239c.454,0,.85.247,1.066.612v-1.138c-.311-.189-.675-.3-1.066-.3-.541,0-1.033.212-1.401.554-.739-.432-1.373-.884-1.917-1.34.408-.462.659-1.067.659-1.731,0-.709-.284-1.352-.744-1.824.613-.55,1.317-1.036,2.11-1.463.968,1.058,2.027,2.031,3.164,2.905-.14.278-.22.591-.22.924,0,.548.215,1.045.564,1.415.461-.059.736-.17.959-.302-.413-.201-.698-.624-.698-1.113,0-.683.555-1.239,1.237-1.239s1.237.556,1.237,1.239c0,.149-.028.292-.076.424.071-.004.144-.006.222-.006.243,0,.447.019.624.051.035-.151.056-.308.056-.47,0-1.139-.925-2.065-2.062-2.065-.508,0-.972.185-1.332.491-1.039-.797-2.01-1.68-2.904-2.637,1.281-.586,2.774-1.036,4.493-1.357,1.42,1.343,2.309,3.244,2.309,5.351,0,.285-.018.565-.049.842.201.09.359.2.515.309.082.057.164.114.252.167.07-.429.107-.869.107-1.318C16.345,3.673,12.679,0,8.172,0S0,3.673,0,8.188s3.666,8.188,8.172,8.188c1.077,0,2.106-.212,3.049-.593-.165-.226-.316-.464-.446-.712ZM8.172.826c1.565,0,3.016.494,4.209,1.333-1.677.358-3.076.831-4.246,1.387-.097-.112-.195-.223-.29-.336-.566-.672-1.077-1.391-1.528-2.146.593-.155,1.214-.238,1.855-.238Zm-2.657.5c.496.852,1.065,1.663,1.699,2.416.054.065.111.128.166.192-.881.487-1.616,1.026-2.233,1.598-.354-.18-.753-.284-1.177-.284-.366,0-.714.076-1.03.213-.338-.965-.389-1.71-.395-1.999.789-.942,1.809-1.682,2.971-2.136Zm-2.941,5.422c.179-.224.411-.404.677-.522.22-.097.462-.152.718-.152.193,0,.379.032.553.089.277.091.525.247.724.451.315.323.51.763.51,1.249,0,.452-.17.864-.447,1.179-.188.214-.427.383-.697.488-.2.078-.417.122-.644.122-.21,0-.41-.038-.597-.105-.271-.097-.512-.256-.705-.462-.3-.32-.486-.749-.486-1.222,0-.422.147-.809.392-1.116Zm-.808,5.038c-.598-1.065-.941-2.292-.941-3.599,0-1.347.364-2.609.997-3.696.073.406.194.894.397,1.436-.528.479-.862,1.168-.862,1.936,0,.827.386,1.563.986,2.043-.232.642-.414,1.275-.577,1.88Z"/>
|
||||
<path d="m14.337,10.018c.536,0,.68.101.967.301.361.252.835.583,1.871.698v2.288c0,1.673-2.027,3.209-2.84,3.758-.377-.252-.981-.693-1.54-1.262-.592-.603-1.297-1.525-1.297-2.495v-2.288c1.035-.115,1.51-.446,1.871-.698.287-.2.431-.301.968-.301m0-.826c-1.696,0-1.175.978-3.438,1.037-.124.002-.223.102-.225.226v2.85c0,2.368,2.91,4.278,3.543,4.662.037.022.079.034.121.034s.084-.011.121-.034c.633-.387,3.543-2.294,3.543-4.662v-2.85c-.002-.124-.101-.224-.225-.226-2.264-.06-1.742-1.037-3.438-1.037h0Z"/>
|
||||
</g>
|
||||
<path d="m16.727,13.453c-.016-.526-.41-.963-.932-1.032-.032-.74-.653-1.318-1.394-1.295-.59-.011-1.122.352-1.327.905-.629.076-1.109.601-1.127,1.235.028.716.63,1.274,1.346,1.249.04,0,.081-.002.118-.005h2.18c.019,0,.039-.003.057-.009.582-.004,1.057-.466,1.078-1.048Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg id="uuid-4ac2bde4-e4f4-4501-bf11-e537e78bc3b0" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<path d="m10.775,15.071c-.81.308-1.687.479-2.603.479-2.353,0-4.45-1.115-5.795-2.845l.026-.099c.192-.733.396-1.507.672-2.288.28.103.58.161.895.161.472,0,.914-.128,1.297-.348.616.526,1.27,1.005,2.088,1.482-.113.255-.176.536-.176.832,0,1.139.925,2.065,2.062,2.065.436,0,.84-.137,1.174-.369-.068-.271-.108-.55-.108-.836v-.233c-.215.366-.612.612-1.066.612-.682,0-1.237-.556-1.237-1.239s.555-1.239,1.237-1.239c.454,0,.85.247,1.066.612v-1.138c-.311-.189-.675-.3-1.066-.3-.541,0-1.033.212-1.401.554-.739-.432-1.373-.884-1.917-1.34.408-.462.659-1.067.659-1.731,0-.709-.284-1.352-.744-1.824.613-.55,1.317-1.036,2.11-1.463.968,1.058,2.027,2.031,3.164,2.905-.14.278-.22.591-.22.924,0,.548.215,1.045.564,1.415.461-.059.736-.17.959-.302-.413-.201-.698-.624-.698-1.113,0-.683.555-1.239,1.237-1.239s1.237.556,1.237,1.239c0,.149-.028.292-.076.424.071-.004.144-.006.222-.006.243,0,.447.019.624.051.035-.151.056-.308.056-.47,0-1.139-.925-2.065-2.062-2.065-.508,0-.972.185-1.332.491-1.039-.797-2.01-1.68-2.904-2.637,1.281-.586,2.774-1.036,4.493-1.357,1.42,1.343,2.309,3.244,2.309,5.351,0,.285-.018.565-.049.842.201.09.359.2.515.309.082.057.164.114.252.167.07-.429.107-.869.107-1.318C16.345,3.673,12.679,0,8.172,0S0,3.673,0,8.188s3.666,8.188,8.172,8.188c1.077,0,2.106-.212,3.049-.593-.165-.226-.316-.464-.446-.712ZM8.172.826c1.565,0,3.016.494,4.209,1.333-1.677.358-3.076.831-4.246,1.387-.097-.112-.195-.223-.29-.336-.566-.672-1.077-1.391-1.528-2.146.593-.155,1.214-.238,1.855-.238Zm-2.657.5c.496.852,1.065,1.663,1.699,2.416.054.065.111.128.166.192-.881.487-1.616,1.026-2.233,1.598-.354-.18-.753-.284-1.177-.284-.366,0-.714.076-1.03.213-.338-.965-.389-1.71-.395-1.999.789-.942,1.809-1.682,2.971-2.136Zm-2.941,5.422c.179-.224.411-.404.677-.522.22-.097.462-.152.718-.152.193,0,.379.032.553.089.277.091.525.247.724.451.315.323.51.763.51,1.249,0,.452-.17.864-.447,1.179-.188.214-.427.383-.697.488-.2.078-.417.122-.644.122-.21,0-.41-.038-.597-.105-.271-.097-.512-.256-.705-.462-.3-.32-.486-.749-.486-1.222,0-.422.147-.809.392-1.116Zm-.808,5.038c-.598-1.065-.941-2.292-.941-3.599,0-1.347.364-2.609.997-3.696.073.406.194.894.397,1.436-.528.479-.862,1.168-.862,1.936,0,.827.386,1.563.986,2.043-.232.642-.414,1.275-.577,1.88Z"/>
|
||||
<path d="m14.337,10.018c.536,0,.68.101.967.301.361.252.835.583,1.871.698v2.288c0,1.673-2.027,3.209-2.84,3.758-.377-.252-.981-.693-1.54-1.262-.592-.603-1.297-1.525-1.297-2.495v-2.288c1.035-.115,1.51-.446,1.871-.698.287-.2.431-.301.968-.301m0-.826c-1.696,0-1.175.978-3.438,1.037-.124.002-.223.102-.225.226v2.85c0,2.368,2.91,4.278,3.543,4.662.037.022.079.034.121.034s.084-.011.121-.034c.633-.387,3.543-2.294,3.543-4.662v-2.85c-.002-.124-.101-.224-.225-.226-2.264-.06-1.742-1.037-3.438-1.037h0Z"/>
|
||||
<path d="m15.789,12.926l.002-.002c-.027-.024-.063-.038-.099-.038h-.221v-.684c.002-.322-.116-.633-.331-.872-.206-.24-.508-.376-.824-.371-.315-.004-.617.132-.823.371-.217.238-.335.55-.331.872v.684h-.179c-.01,0-.018.004-.027.006-.008.002-.016.002-.024.005-.059.022-.102.076-.103.143v1.766c0,.016.005.03.01.044,0,0,0,0,0,0,.002.007.004.014.007.02.009.024.024.046.044.062l.007-.005c.025.018.054.03.087.031h2.702c.035,0,.066-.014.092-.034l.012.008c.034-.029.055-.071.055-.116v-1.776c0-.046-.022-.086-.055-.115Zm-2.09-.735c.001-.178.07-.349.192-.478.199-.232.547-.258.779-.06.021.018.041.038.06.06.027.03.051.062.072.096.083.11.13.244.134.382v.695s-1.237,0-1.237,0v-.695Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,13 @@
|
||||
<svg id="uuid-588fe919-40d5-4cd4-ba0b-9913154d9b1c" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<rect x="7.862" y="5.437" width="7.182" height="1.396" rx=".698" ry=".698"/>
|
||||
<path d="m12.98,8.144h-4.395c-.335,0-.644.225-.71.554-.09.449.251.842.683.842h3.022c.268-.557.67-1.076,1.399-1.396Z"/>
|
||||
<path d="m17.436,11.063c0-1.461-1.18-2.645-2.636-2.645s-2.636,1.184-2.636,2.645c0,.892.441,1.679,1.115,2.158l-.169,3.387c-.008.161.116.298.276.306.083.004.164-.027.222-.086l1.146-.726,1.137.736c.113.115.297.117.412.004.059-.058.091-.138.087-.22l-.131-3.357c.709-.474,1.176-1.283,1.176-2.202Z"/>
|
||||
<path d="m12.701,13.482c-.193-.168-.358-.362-.504-.568H1.905c-.506,0-.917-.412-.917-.917V2.98c0-.506.412-.917.917-.917h14.19c.506,0,.917.412.917.917v5.691c.48.427.831.99.987,1.634V2.98c0-1.05-.855-1.905-1.905-1.905H1.905C.855,1.075,0,1.93,0,2.98v9.017C0,13.047.855,13.902,1.905,13.902h10.776l.021-.419Z"/>
|
||||
<g>
|
||||
<path d="m4.637,8.479c-1.187,0-2.154-.966-2.154-2.153s.966-2.153,2.154-2.153,2.153.966,2.153,2.153-.966,2.153-2.153,2.153Zm0-3.319c-.643,0-1.166.523-1.166,1.166s.523,1.166,1.166,1.166,1.166-.523,1.166-1.166-.523-1.166-1.166-1.166Z"/>
|
||||
<path d="m7.457,10.311h-.987c0-1.01-.822-1.832-1.832-1.832s-1.832.822-1.832,1.832h-.987c0-1.554,1.265-2.819,2.82-2.819s2.819,1.265,2.819,2.819Z"/>
|
||||
</g>
|
||||
<g id="uuid-51c02749-836c-4702-ab14-9b01ec3151e7">
|
||||
<path d="m14.338,12.249c-.079,0-.154-.031-.209-.087l-.859-.859c-.111-.12-.103-.307.017-.418.113-.104.288-.104.401,0l.65.652,1.592-1.593c.12-.111.307-.103.418.017.104.113.104.288,0,.401l-1.801,1.801c-.056.055-.131.085-.209.085Z" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,27 @@
|
||||
<svg id="f861199a-9921-482a-87ea-8504385ff0cd" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<defs>
|
||||
<linearGradient id="f13c7428-fd1f-409a-aa83-518164fbbc0c" x1="9" y1="7.11" x2="9" y2="14.2" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#a67af4" />
|
||||
<stop offset="1" stop-color="#552f99" />
|
||||
</linearGradient>
|
||||
<linearGradient id="a6b01360-3b65-4cb5-8c1b-eed2f64af70c" x1="8.8" y1="3.31" x2="9.32" y2="9.75" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#a67af4" />
|
||||
<stop offset="1" stop-color="#552f99" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<title>Icon-identity-235</title>
|
||||
<g>
|
||||
<path d="M2.15,11.75a7.34,7.34,0,0,1,1-7.2,1.94,1.94,0,0,1-.7-.83,8.48,8.48,0,0,0-1.17,8.6A3.1,3.1,0,0,1,2.15,11.75ZM5.94,1.14a3.14,3.14,0,0,1,.62.9A7.5,7.5,0,0,1,9,1.61,7.33,7.33,0,0,1,13.33,3a2.15,2.15,0,0,1,.55-.69h0L14,2.22A8.44,8.44,0,0,0,5.94,1.14Zm9.91,5.12a7.32,7.32,0,0,1-1,7.19,2,2,0,0,1,.43.4,1.94,1.94,0,0,1,.27.43,8.48,8.48,0,0,0,1.17-8.6A3.13,3.13,0,0,1,15.85,6.26ZM11.44,16A7.25,7.25,0,0,1,9,16.39H9A7.32,7.32,0,0,1,4.67,15a2.05,2.05,0,0,1-.55.69h0L4,15.78a8.48,8.48,0,0,0,8.09,1.09A3.13,3.13,0,0,1,11.44,16Z" fill="#0078d4" />
|
||||
<path d="M5.18,1.87a2.09,2.09,0,1,1-3,0,2.09,2.09,0,0,1,3,0Z" fill="#32bedd" />
|
||||
<path d="M4.42,4.07a1,1,0,0,0,0-1.45A1,1,0,0,0,3,2.62,1,1,0,0,0,3,4.07H3a1,1,0,0,0,1.44,0" fill="#fff" />
|
||||
<path d="M13.18,1.87a2.09,2.09,0,1,1,0,3,2.09,2.09,0,0,1,0-2.95Z" fill="#32bedd" />
|
||||
<path d="M15.38,2.62a1,1,0,1,0,0,1.45h0a1,1,0,0,0,0-1.45" fill="#fff" />
|
||||
<path d="M12.82,16.13a2.09,2.09,0,1,1,3,0,2.09,2.09,0,0,1-3,0Z" fill="#32bedd" />
|
||||
<path d="M13.58,13.93a1,1,0,0,0,0,1.45,1,1,0,0,0,1.44,0,1,1,0,0,0,0-1.45h0a1,1,0,0,0-1.44,0" fill="#fff" />
|
||||
<path d="M4.82,16.13a2.09,2.09,0,1,1,0-2.95,2.09,2.09,0,0,1,0,2.95Z" fill="#32bedd" />
|
||||
<path d="M2.62,15.38a1,1,0,1,0,0-1.45h0a1,1,0,0,0,0,1.45" fill="#fff" />
|
||||
<path d="M12.51,12.92a.76.76,0,0,0,.76-.76v-.09C13,9.69,11.61,7.74,9,7.74S5,9.39,4.74,12.08a.76.76,0,0,0,.68.84h7.09Z" fill="url(#f13c7428-fd1f-409a-aa83-518164fbbc0c)" />
|
||||
<path d="M9,8.31a2.41,2.41,0,0,1-1.3-.38L9,11.28,10.27,8A2.37,2.37,0,0,1,9,8.31Z" fill="#fff" opacity="0.8" />
|
||||
<circle cx="9.01" cy="5.92" r="2.39" fill="url(#a6b01360-3b65-4cb5-8c1b-eed2f64af70c)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="uuid-f8d4d392-7c12-4bd9-baff-66fbf7814b91" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<path d="m3.802,14.032c.388.242,1.033.511,1.715.511.621,0,1.198-.18,1.676-.487,0,0,.001,0,.002-.001l1.805-1.128v4.073c-.286,0-.574-.078-.824-.234l-4.374-2.734Z" fill="#225086"/>
|
||||
<path d="m7.853,1.507L.353,9.967c-.579.654-.428,1.642.323,2.111,0,0,2.776,1.735,3.126,1.954.388.242,1.033.511,1.715.511.621,0,1.198-.18,1.676-.487,0,0,.001,0,.002-.001l1.805-1.128-4.364-2.728,4.365-4.924V1s0,0,0,0c-.424,0-.847.169-1.147.507Z" fill="#6df"/>
|
||||
<polygon points="4.636 10.199 4.688 10.231 9 12.927 9.001 12.927 9.001 12.927 9.001 5.276 9 5.275 4.636 10.199" fill="#cbf8ff"/>
|
||||
<path d="m17.324,12.078c.751-.469.902-1.457.323-2.111l-4.921-5.551c-.397-.185-.842-.291-1.313-.291-.925,0-1.752.399-2.302,1.026l-.109.123h0s4.364,4.924,4.364,4.924h0s0,0,0,0l-4.365,2.728v4.073c.287,0,.573-.078.823-.234l7.5-4.688Z" fill="#074793"/>
|
||||
<path d="m9.001,1v4.275s.109-.123.109-.123c.55-.627,1.377-1.026,2.302-1.026.472,0,.916.107,1.313.291l-2.579-2.909c-.299-.338-.723-.507-1.146-.507Z" fill="#0294e4"/>
|
||||
<polygon points="13.365 10.199 13.365 10.199 13.365 10.199 9.001 5.276 9.001 12.926 13.365 10.199" fill="#96bcc2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,30 @@
|
||||
<svg id="uuid-f4ec8483-67f3-444b-ae88-1121f3f27867" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<defs>
|
||||
<radialGradient id="uuid-a9da7b2d-3102-4725-93ea-0cda2352c3c6" cx="7.958" cy="7.925" r="7.985" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".18" stop-color="#5ea0ef"/>
|
||||
<stop offset="1" stop-color="#0078d4"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="uuid-2be490ce-8f11-4771-88f0-040758b1514f" x1="300.661" y1="1169.257" x2="300.632" y2="1165.546" gradientTransform="translate(-297 -1159)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#ccc"/>
|
||||
<stop offset="1" stop-color="#fcfcfc"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="uuid-4dd8ce3d-a528-40d6-81cd-9a779af9902b" x1="14.251" y1="18" x2="14.251" y2="8.984" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#003067"/>
|
||||
<stop offset="1" stop-color="#005ba1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g>
|
||||
<path d="m10.126,13.194v-2.922c.004-.333.268-.597.596-.602,1.118-.03,1.469-.275,1.841-.534.372-.259.756-.527,1.688-.527.827,0,1.223.211,1.561.44.266-1.984-.208-4.068-1.484-5.801C11.714-.302,6.721-1.059,3.176,1.558l-.086.057C-.392,4.318-1.026,9.336,1.673,12.822c2.207,2.85,5.958,3.792,9.157,2.564-.416-.659-.704-1.398-.704-2.192Z" fill="url(#uuid-a9da7b2d-3102-4725-93ea-0cda2352c3c6)"/>
|
||||
<path d="m5.809,6.264c2.256-2.148,5.246-3.351,8.359-3.363-.41-.507-.88-.961-1.401-1.352-1.514.094-3.002.443-4.4,1.033-1.438.619-2.739,1.52-3.825,2.649.523.199.967.561,1.267,1.033Z" fill="#fff" isolation="isolate" opacity=".6"/>
|
||||
<path d="m1.859,9.478c-.325.799-.587,1.621-.785,2.461.174.301.369.59.584.864.162.212.334.416.516.611.227-1.118.566-2.211,1.013-3.26-.499-.09-.961-.325-1.329-.676Z" fill="#fff" isolation="isolate" opacity=".6"/>
|
||||
<path d="m2.778,5.212c-.331-.89-.549-1.818-.647-2.762-.394.414-.741.871-1.032,1.363.106.761.285,1.509.535,2.235.294-.382.691-.672,1.144-.836Z" fill="#f2f2f2" isolation="isolate" opacity=".55"/>
|
||||
<circle cx="3.641" cy="7.626" r="2.561" fill="url(#uuid-2be490ce-8f11-4771-88f0-040758b1514f)"/>
|
||||
<path d="m7.379,12.315c0-.45.183-.881.507-1.193-.865-.474-1.664-1.062-2.375-1.748-.372.403-.863.676-1.401.78.424.434.879.836,1.361,1.203.601.452,1.245.844,1.923,1.169-.009-.07-.015-.14-.015-.21Z" fill="#f2f2f2" isolation="isolate" opacity=".55"/>
|
||||
<path d="m11.09,7.861c.084-.366.285-.694.573-.935-1.546-1.126-2.951-2.433-4.185-3.894-.719-.845-1.348-1.764-1.876-2.741-.35.105-.692.237-1.023.394.565,1.064,1.241,2.065,2.017,2.987,1.319,1.578,2.828,2.985,4.494,4.189Z" fill="#f2f2f2" isolation="isolate" opacity=".7"/>
|
||||
<circle cx="3.641" cy="7.626" r="2.561" fill="#fff"/>
|
||||
<path d="m10.126,13.194v-2.136c-.293-.255-.671-.415-1.09-.415-.922,0-1.669.748-1.669,1.672s.747,1.672,1.669,1.672c.431,0,.82-.168,1.116-.437-.013-.118-.026-.235-.026-.355Z" fill="#fff"/>
|
||||
<path d="m11.72,9.55c.393-.102.615-.255.843-.414.372-.259.756-.527,1.688-.527.106,0,.204.004.297.011.036-.141.061-.287.061-.439,0-.981-.794-1.776-1.773-1.776s-1.773.795-1.773,1.776c0,.555.26,1.044.658,1.37Z" fill="#fff"/>
|
||||
</g>
|
||||
<path d="m17.77,10.046c-2.317-.061-1.783-1.062-3.519-1.062s-1.203,1.001-3.519,1.062c-.127.002-.229.104-.23.231v2.917c0,2.424,2.978,4.378,3.626,4.772.076.046.171.046.247,0,.647-.396,3.626-2.348,3.626-4.772v-2.917c-.002-.127-.104-.229-.23-.231Z" fill="url(#uuid-4dd8ce3d-a528-40d6-81cd-9a779af9902b)"/>
|
||||
<path d="m16.812,13.666c-.017-.564-.44-1.032-.999-1.106-.034-.793-.7-1.412-1.494-1.387-.632-.011-1.202.377-1.422.97-.674.082-1.188.644-1.208,1.323.03.767.675,1.366,1.443,1.338.043,0,.086-.002.127-.005h2.336c.021,0,.042-.004.062-.009.624-.004,1.133-.499,1.156-1.123Z" fill="#fff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,30 @@
|
||||
<svg id="uuid-11b6e5fc-40a5-405d-9c32-7035b72b9b0a" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<defs>
|
||||
<radialGradient id="uuid-cacf3cc5-0a3d-4115-943b-5bd551efadcd" cx="7.957" cy="7.925" r="7.985" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".18" stop-color="#5ea0ef"/>
|
||||
<stop offset="1" stop-color="#0078d4"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="uuid-2a25906c-e495-4ebf-b557-92e03673ecb2" x1="300.661" y1="1169.257" x2="300.632" y2="1165.546" gradientTransform="translate(-297 -1159)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#ccc"/>
|
||||
<stop offset="1" stop-color="#fcfcfc"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="uuid-73ce8cf9-4957-4dc1-9af7-dfbfa3d5e4cb" x1="14.251" y1="19.336" x2="14.251" y2="10.32" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#5e9624"/>
|
||||
<stop offset=".999" stop-color="#76bc2d"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g>
|
||||
<path d="m10.126,13.194v-2.922c.004-.333.268-.597.596-.602,1.118-.03,1.469-.275,1.841-.534.372-.259.756-.527,1.688-.527.827,0,1.223.211,1.561.44.266-1.984-.208-4.068-1.484-5.801C11.714-.302,6.721-1.059,3.176,1.558l-.086.057C-.392,4.318-1.026,9.336,1.673,12.822c2.207,2.85,5.958,3.792,9.157,2.564-.416-.659-.704-1.398-.704-2.192Z" fill="url(#uuid-cacf3cc5-0a3d-4115-943b-5bd551efadcd)"/>
|
||||
<path d="m5.809,6.264c2.256-2.148,5.246-3.351,8.359-3.363-.41-.507-.88-.961-1.401-1.352-1.514.094-3.002.443-4.4,1.033-1.438.619-2.739,1.52-3.825,2.649.523.199.967.561,1.267,1.033Z" fill="#fff" isolation="isolate" opacity=".6"/>
|
||||
<path d="m1.859,9.478c-.325.799-.587,1.621-.785,2.461.174.301.369.59.584.864.162.212.334.416.516.611.227-1.118.566-2.211,1.013-3.26-.499-.09-.961-.325-1.329-.676Z" fill="#fff" isolation="isolate" opacity=".6"/>
|
||||
<path d="m2.778,5.212c-.331-.89-.549-1.818-.647-2.762-.394.414-.741.871-1.032,1.363.106.761.285,1.509.535,2.235.294-.382.691-.672,1.144-.836Z" fill="#f2f2f2" isolation="isolate" opacity=".55"/>
|
||||
<circle cx="3.641" cy="7.626" r="2.561" fill="url(#uuid-2a25906c-e495-4ebf-b557-92e03673ecb2)"/>
|
||||
<path d="m7.379,12.315c0-.45.183-.881.507-1.193-.865-.474-1.664-1.062-2.375-1.748-.372.403-.863.676-1.401.78.424.434.879.836,1.361,1.203.601.452,1.245.844,1.923,1.169-.009-.07-.015-.14-.015-.21Z" fill="#f2f2f2" isolation="isolate" opacity=".55"/>
|
||||
<path d="m11.09,7.861c.084-.366.285-.694.573-.935-1.546-1.126-2.951-2.433-4.185-3.894-.719-.845-1.348-1.764-1.876-2.741-.35.105-.692.237-1.023.394.565,1.064,1.241,2.065,2.017,2.987,1.319,1.578,2.828,2.985,4.494,4.189Z" fill="#f2f2f2" isolation="isolate" opacity=".7"/>
|
||||
<circle cx="3.641" cy="7.626" r="2.561" fill="#fcfcfc"/>
|
||||
<path d="m10.126,13.194v-2.136c-.293-.255-.671-.415-1.09-.415-.922,0-1.669.748-1.669,1.672s.747,1.672,1.669,1.672c.431,0,.82-.168,1.116-.437-.013-.118-.026-.235-.026-.355Z" fill="#fcfcfc"/>
|
||||
<path d="m11.72,9.55c.393-.102.615-.255.843-.414.372-.259.756-.527,1.688-.527.106,0,.204.004.297.011.036-.141.061-.287.061-.439,0-.981-.794-1.776-1.773-1.776s-1.773.795-1.773,1.776c0,.555.26,1.044.658,1.37Z" fill="#fcfcfc"/>
|
||||
</g>
|
||||
<path d="m17.77,10.046c-2.317-.061-1.783-1.062-3.519-1.062s-1.203,1.001-3.519,1.062c-.127.002-.229.104-.23.231v2.917c0,2.424,2.978,4.378,3.626,4.772.076.046.171.046.247,0,.647-.396,3.626-2.348,3.626-4.772v-2.917c-.002-.127-.104-.229-.23-.231Z" fill="url(#uuid-73ce8cf9-4957-4dc1-9af7-dfbfa3d5e4cb)"/>
|
||||
<path d="m15.976,12.783l.003-.002c-.033-.029-.074-.045-.118-.045h-.262v-.812c.002-.382-.138-.751-.393-1.036-.244-.285-.603-.447-.978-.44-.375-.005-.732.156-.977.44-.257.283-.398.653-.394,1.036v.812h-.212c-.012,0-.022.005-.033.007-.01.002-.019.002-.028.006-.07.026-.121.091-.122.17v2.097c0,.019.006.036.011.052,0,0,0,0,0,0,.003.008.005.017.008.024.011.028.028.054.052.074l.008-.006c.03.021.064.036.103.037h3.208c.042,0,.078-.017.109-.041l.014.01c.041-.034.065-.085.066-.138v-2.109c0-.055-.027-.103-.065-.136Zm-2.482-.873c.002-.211.083-.414.229-.568.236-.275.65-.307.925-.071.025.022.049.046.071.071.032.035.06.074.085.114.099.131.154.289.159.454v.825s-1.469,0-1.469,0v-.825Z" fill="#fff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,59 @@
|
||||
<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_98_3087)">
|
||||
<g clip-path="url(#clip1_98_3087)">
|
||||
<path d="M50.4537 12.4478C31.927 12.4478 16.8994 30.8113 16.8994 44.4229V86.3461C16.8994 90.0906 20.8042 93.119 25.6134 93.119H75.2856C80.1032 93.119 83.9996 90.084 83.9996 86.3461V37.0891C83.9996 23.4776 68.9804 12.4478 50.4537 12.4478Z" fill="url(#paint0_linear_98_3087)"/>
|
||||
<path d="M50.4537 12.4478C31.927 12.4478 16.8994 30.8113 16.8994 44.4229V86.3461C16.8994 90.0906 20.8042 93.119 25.6134 93.119H75.2856C80.1032 93.119 83.9996 90.084 83.9996 86.3461V37.0891C83.9996 23.4776 68.9804 12.4478 50.4537 12.4478Z" fill="url(#paint1_linear_98_3087)" fill-opacity="0.6"/>
|
||||
<path d="M50.4537 12.4478C31.927 12.4478 16.8994 30.8113 16.8994 44.4229V86.3461C16.8994 90.0906 20.8042 93.119 25.6134 93.119H75.2856C80.1032 93.119 83.9996 90.084 83.9996 86.3461V37.0891C83.9996 23.4776 68.9804 12.4478 50.4537 12.4478Z" fill="url(#paint2_radial_98_3087)" fill-opacity="0.5"/>
|
||||
<path d="M42.0644 7.61354C28.1709 15.6294 16.9043 30.2449 16.9043 47.3401L16.8994 86.7313C16.8994 93.119 24.8106 94.1837 28.4684 92.0544L60.689 72.8718C64.2978 70.7898 67.2245 66.2424 67.2245 62.7124V32.4353C67.2245 13.2719 55.9579 -0.397553 42.0644 7.61354Z" fill="url(#paint3_radial_98_3087)"/>
|
||||
<path d="M42.0644 7.61354C28.1709 15.6294 16.9043 30.2449 16.9043 47.3401L16.8994 86.7313C16.8994 93.119 24.8106 94.1837 28.4684 92.0544L60.689 72.8718C64.2978 70.7898 67.2245 66.2424 67.2245 62.7124V32.4353C67.2245 13.2719 55.9579 -0.397553 42.0644 7.61354Z" fill="url(#paint4_radial_98_3087)" fill-opacity="0.25"/>
|
||||
<path d="M42.0644 7.61354C28.1709 15.6294 16.9043 30.2449 16.9043 47.3401L16.8994 86.7313C16.8994 93.119 24.8106 94.1837 28.4684 92.0544L60.689 72.8718C64.2978 70.7898 67.2245 66.2424 67.2245 62.7124V32.4353C67.2245 13.2719 55.9579 -0.397553 42.0644 7.61354Z" fill="url(#paint5_radial_98_3087)" fill-opacity="0.25"/>
|
||||
<path d="M42.0644 7.61354C28.1709 15.6294 16.9043 30.2449 16.9043 47.3401L16.8994 86.7313C16.8994 93.119 24.8106 94.1837 28.4684 92.0544L60.689 72.8718C64.2978 70.7898 67.2245 66.2424 67.2245 62.7124V32.4353C67.2245 13.2719 55.9579 -0.397553 42.0644 7.61354Z" fill="url(#paint6_radial_98_3087)" fill-opacity="0.25"/>
|
||||
<path d="M42.0644 7.61354C28.1709 15.6294 16.9043 30.2449 16.9043 47.3401L16.8994 86.7313C16.8994 93.119 24.8106 94.1837 28.4684 92.0544L60.689 72.8718C64.2978 70.7898 67.2245 66.2424 67.2245 62.7124V32.4353C67.2245 13.2719 55.9579 -0.397553 42.0644 7.61354Z" fill="url(#paint7_linear_98_3087)" fill-opacity="0.6"/>
|
||||
<path d="M32.4476 3.65882C21.9186 7.542 12.0053 24.99 12.0053 44.002L12 61.0396C12 65.2258 15.1748 66.6174 19.0896 64.1484L36.5713 53.3864C41.5211 50.3393 44.5633 44.7574 44.5633 38.7224V21.2139C44.5633 8.18931 39.5788 1.02875 32.4476 3.65882Z" fill="url(#paint8_linear_98_3087)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_98_3087" x1="55.5758" y1="66.7305" x2="81.9176" y2="88.7778" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#224F87"/>
|
||||
<stop offset="1" stop-color="#0055C6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_98_3087" x1="82.9438" y1="52.7834" x2="71.5177" y2="54.2164" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0059C5"/>
|
||||
<stop offset="1" stop-color="#0059C5" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint2_radial_98_3087" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(36.4448 50.628) rotate(51.4758) scale(40.3141 36.3696)">
|
||||
<stop stop-color="#041642"/>
|
||||
<stop offset="1" stop-color="#041642" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint3_radial_98_3087" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(50.6602 5.27905) rotate(101.468) scale(52.7905 58.7614)">
|
||||
<stop stop-color="#66DFFF"/>
|
||||
<stop offset="1" stop-color="#0296E6"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint4_radial_98_3087" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(28.4736 31.3316) scale(25.9066 40.722)">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint5_radial_98_3087" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(38.7033 30.6662) rotate(-3.01258) scale(13.5717 37.6157)">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint6_radial_98_3087" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(20.1037 61.8066) rotate(63.4736) scale(13.3863 29.831)">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint7_linear_98_3087" x1="43.2204" y1="83.0991" x2="27.1104" y2="61.6996" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0086E2"/>
|
||||
<stop offset="1" stop-color="#0086E2" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_98_3087" x1="-7.66242" y1="25.7423" x2="10.5321" y2="56.9523" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#CDFAFF"/>
|
||||
<stop offset="1" stop-color="#45DDFB"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_98_3087">
|
||||
<rect width="96" height="96" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_98_3087">
|
||||
<rect width="96" height="96" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.1 KiB |
@@ -0,0 +1,30 @@
|
||||
<svg id="uuid-0bbb5b12-3243-469f-a125-16e9fd0c10bb" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<defs>
|
||||
<linearGradient id="uuid-0af6b584-dd4c-4308-9282-d2a7c9e792b7" x1="8.985" y1="13.416" x2="8.985" y2=".837" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".089" stop-color="#225086"/>
|
||||
<stop offset=".458" stop-color="#0055c5"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="uuid-7632880e-fcbf-4182-861a-db42fad415fd" x1="15.2" y1="17.152" x2="15.2" y2="8.114" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#0294e4"/>
|
||||
<stop offset=".192" stop-color="#23aced"/>
|
||||
<stop offset=".398" stop-color="#40c1f4"/>
|
||||
<stop offset=".603" stop-color="#55d0fa"/>
|
||||
<stop offset=".805" stop-color="#61d9fd"/>
|
||||
<stop offset="1" stop-color="#6df"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g>
|
||||
<path d="m11.95,10.924c0-1.797,1.458-3.26,3.25-3.26,1.17,0,2.197.623,2.77,1.556V1.437c0-.331-.268-.6-.6-.6H.6c-.331,0-.6.268-.6.6v11.379c0,.331.268.6.6.6h12.509c-.73-.617-1.159-1.529-1.159-2.492Z" fill="url(#uuid-0af6b584-dd4c-4308-9282-d2a7c9e792b7)"/>
|
||||
<g>
|
||||
<path d="m6.616,10.125c.259.009.477-.193.486-.452,0-.005,0-.009,0-.014.002-.019.002-.038,0-.058-.193-1.532-1.063-2.771-2.723-2.771s-2.568,1.054-2.748,2.759c-.026.269.17.508.438.535h4.546Z" fill="#6df"/>
|
||||
<path d="m4.398,7.194c-.288-.003-.569-.088-.811-.244l.811,2.145.82-2.13c-.247.151-.531.23-.82.229Z" fill="#f2f2f2"/>
|
||||
<circle cx="4.388" cy="5.661" r="1.533" fill="#6df"/>
|
||||
</g>
|
||||
<rect x="7.828" y="4.128" width="7.631" height="1.483" rx=".741" ry=".741" fill="#6df"/>
|
||||
<path d="m13.267,7.823h-4.697c-.409,0-.741.332-.741.741s.332.741.741.741h3.211c.284-.592.712-1.144,1.486-1.483Z" fill="#6df"/>
|
||||
</g>
|
||||
<path d="m18,10.924c0-1.552-1.254-2.81-2.8-2.81s-2.8,1.258-2.8,2.81c0,.948.469,1.784,1.185,2.293l-.18,3.598c-.009.171.123.316.293.325.088.004.174-.029.236-.092l1.217-.771,1.208.782c.12.122.316.124.438.005.063-.061.096-.146.093-.234l-.139-3.566c.753-.504,1.25-1.363,1.25-2.339Z" fill="url(#uuid-7632880e-fcbf-4182-861a-db42fad415fd)"/>
|
||||
<g id="uuid-a7e8402a-60cf-4e4a-910f-8d3e78ed18c9">
|
||||
<path d="m14.652,12.251c-.089,0-.174-.035-.237-.098l-.971-.971c-.125-.136-.116-.347.019-.472.128-.118.325-.118.453,0l.735.737,1.8-1.801c.136-.125.347-.116.472.019.118.128.118.325,0,.453l-2.036,2.036c-.063.062-.148.097-.236.096Z" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1 @@
|
||||
<svg id="a76a0103-ce03-4d58-859d-4c27e02925d2" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18"><defs><linearGradient id="efeb8e96-2af0-4681-9a6a-45f9b0262f19" x1="-6518.78" y1="1118.86" x2="-6518.78" y2="1090.06" gradientTransform="matrix(0.5, 0, 0, -0.5, 3267.42, 559.99)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#5ea0ef" /><stop offset="0.18" stop-color="#589eed" /><stop offset="0.41" stop-color="#4897e9" /><stop offset="0.66" stop-color="#2e8ce1" /><stop offset="0.94" stop-color="#0a7cd7" /><stop offset="1" stop-color="#0078d4" /></linearGradient></defs><title>Icon-identity-232</title><path d="M5.67,10.61H10v4.32H5.67Zm-5-5.76H5V.53H1.23a.6.6,0,0,0-.6.6Zm.6,10.08H5V10.61H.63v3.72A.6.6,0,0,0,1.23,14.93Zm-.6-5H5V5.57H.63Zm10.08,5h3.72a.6.6,0,0,0,.6-.6V10.61H10.71Zm-5-5H10V5.57H5.67Zm5,0H15V5.57H10.71Zm0-9.36V4.85H15V1.13a.6.6,0,0,0-.6-.6Zm-5,4.32H10V.53H5.67Z" fill="url(#efeb8e96-2af0-4681-9a6a-45f9b0262f19)" /><polygon points="17.37 10.7 17.37 15.21 13.5 17.47 13.5 12.96 17.37 10.7" fill="#32bedd" /><polygon points="17.37 10.7 13.5 12.97 9.63 10.7 13.5 8.44 17.37 10.7" fill="#9cebff" /><polygon points="13.5 12.97 13.5 17.47 9.63 15.21 9.63 10.7 13.5 12.97" fill="#50e6ff" /><polygon points="9.63 15.21 13.5 12.96 13.5 17.47 9.63 15.21" fill="#9cebff" /><polygon points="17.37 15.21 13.5 12.96 13.5 17.47 17.37 15.21" fill="#50e6ff" /></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,8 @@
|
||||
Herunterladen von Azure-Symbolen zur Verwendung in Architekturdiagrammen und Dokumentationen
|
||||
https://learn.microsoft.com/de-de/azure/architecture/icons/
|
||||
https://arch-center.azureedge.net/icons/Azure_Public_Service_Icons_V23.zip
|
||||
|
||||
Microsoft Entra-Architektursymbole
|
||||
Erlaubt für Architekturdiagramme, Schulungsmaterialien oder in Dokumentationen
|
||||
https://learn.microsoft.com/de-de/entra/architecture/architecture-icons
|
||||
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 425.196850393701425 59.527559055117308"><path d="m60.293984607886159,50.769293013130664c8.922085244085793-5.90894112510432,2.597510115368095-15.968015131942593-1.841832000440263-21.080502289770266-.962305207704048-1.104701746114188-.307094671095001-1.620484383381154,1.198667759555974-.794515764901917,7.032853155409612,3.857120845534155,21.932207291582927,11.951225666169194,19.629745551583255,19.496181327776867-1.77912256691161,5.843802805071391-11.053347286466305,5.940295841861371-20.420016898639005,3.368497575585934l-.14551238270451-.016879534525287.14551238270451-.084422206860836c.477558342503471-.274954859722129,1.000112420952064-.602143046497076,1.433435587940039-.888359107304495h0Z" fill="#ef7d00" stroke-width="0"/><path d="m58.723893486027919,51.738050717322039s-11.792955495717251,7.412274669248291-32.605274366960657,7.412274669248291c-8.06348613008231,0-14.851929513702089-1.234806646702054-17.722799765328091-4.059061904712507-3.023595691251103-2.973766716497266-4.663633837730231-7.983897162041103-4.663633837730231-12.590464433895249,0-3.947333125281148,1.640038146479128-7.265977191917045,5.056769973368318-7.265977191917045,8.776572861384921,0,36.110427476475706,13.903289859388678,49.93493799665066,16.503228861276511h0Z" fill="#cf022b" stroke-width="0"/><path d="m19.422620628053664,8.758143370950165c-8.922085244093978,5.908597646209273-2.596700486548798,15.968015131932589,1.841832000432078,21.080600426611454.962305207699501,1.104603609266633.307904299943402,1.62041078074617-1.197858130716668.794515764885546C13.033741342349458,26.776040580090012-1.865612793805667,18.681690417384743.436848946185819,11.136980097850028c1.779122566906153-5.843483860380729,11.053322752266467-5.940050499790232,20.419207269799699-3.368816520284781l.146297477333974.017198479225044-.146297477333974.084839288377225c-.476748713675079.274758586072494-1.000112420955702.60140702028184-1.433435587931854.88794202578265h0Z" fill="#ef7d00" stroke-width="0"/><path d="m20.9927117499019,7.789312064143814S32.785667245631885.377233668550616,53.598795745702773.377233668550616c8.063486130077763,0,14.851929513703908,1.234953851946557,17.722775231131891,4.058939233677847,3.023620225444574,2.973913921728126,4.66365837192734,7.983651819962688,4.66365837192734,12.590611639137933,0,3.947406727897032-1.640038146482766,7.265977191915226-5.056769973371047,7.265977191915226-8.776572861384921,0-36.111237105314103-13.903216256770065-49.935747625489057-16.503449669137808h0Z" fill="#cf022b" stroke-width="0"/><path d="m175.46730934423249,29.92167399203845c-7.058291915608606,0-10.555369198777953-4.485328629730247-10.555369198777953-4.485328629730247v12.418041986202297h-5.439505080450544V.389427381112h5.145917753170579l.293587327279965,3.66455045787734S168.335620596815716.001111137908993,175.406628868478947.001111137908993c6.221446336843655,0,15.179396058821112,3.171875436868504,15.179396058821112,14.845649032890833,0,11.673209208499429-8.059841642842912,15.074913821238624-15.118715583067569,15.074913821238624h0Zm-.294742557937752-24.99176673931288c-5.463200537271405,0-10.260626640840201,3.097376285703831-10.260626640840201,9.859885053890139,0,6.762261848655726,4.219493294497624,10.143736696624728,10.202247807880667,10.143736696624728,3.133558816155528,0,9.747995230255583-1.271441597674311,9.747995230255583-10.028054894419256,0-8.757054224476633-6.594215467879621-9.975566856095611-9.689616397296049-9.975566856095611h0Zm36.606844034394271,9.916852918074255c0-11.082313123853964,8.957958540529035-14.846760170799826,15.182870569425177-14.846760170799826,7.067542579625297,0,10.492951467726016,4.05397783898934,10.492951467726016,4.05397783898934l.29129450300934-3.66455045787734h5.147037709646611v29.525879614341648h-5.134921015314831l-.30341119734112-3.900446799464589s-3.524247250592452,3.906813796049391-10.556533248012784,3.906813796049391c-7.058283097057938,0-15.119288789138409-4.099851961190325-15.119288789138409-15.074913821238624h0Zm5.725570180450632.058713938021356c0,8.026701513805165,6.612134771299679,10.028054894419256,9.748577254866177,10.028054894419256,5.982172488775177,0,10.201674601834384-3.381474847969002,10.201674601834384-10.143736696624728,0-6.763090792807816-4.797434922136745-9.859885053890139-10.259471410165133-9.859885053890139-3.097147003254577,0-9.690780446535427,1.820714103560931-9.690780446535427,9.975566856095611h0Zm-5.314096412284016-9.451215586149374V.463926532279402c-5.562594468691714-.508513145477991-9.468261852613068.824534876218422-12.511086558157331,4.00316532607485l-.29127686591346-4.0647188387602h-5.344714434622801v29.493303872820434h5.635991300536261v-15.281409101045938c0-4.263418516103229,3.436970013483915-9.668540051277887,12.511086558157331-9.16000926869674h0Zm-102.303119767699172,6.849953557847584c-5.671530076464478-1.013057941288025-11.246003138526248-1.720799877382888-11.246003138526248-4.364955356365499,0-1.722528314125157,1.709Line truncated
|
||||
|
After Width: | Height: | Size: 14 KiB |