Kubernetes YAML a Helm pro oba AKS scénáře
Navazující interní materiál k AKS průvodci pro dva scénáře:
- scénář 1: Hermes Agent v Dockeru na AKS
- scénář 2: vlastní .NET 10 aplikace v Dockeru na AKS
Cíl: mít připravené starter Kubernetes YAML a Helm skeleton, který jde rychle upravit pro reálný deployment.
1. Doporučení před použitím
Než to nasadíš do produkce, počítej s tím, že tohle je výchozí kostra, ne finální hardened deployment.
Doporučené doplnění pro produkci: - secrets přes Azure Key Vault + CSI driver - image pull přes AKS ↔ ACR integraci nebo managed identity - ingress controller standardizovat podle zvolené platformy - TLS řešit přes cert-manager nebo firemní cert workflow - resource limity a replikační počty doladit podle reality - pro .NET app doplnit DB migrace, observability a alerting
ČÁST A — Scénář 1: Hermes Agent na AKS
2. Plain Kubernetes YAML
apiVersion: v1
kind: Namespace
metadata:
name: hermes
---
apiVersion: v1
kind: Secret
metadata:
name: hermes-secrets
namespace: hermes
type: Opaque
stringData:
OPENAI_API_KEY: "replace-me"
HERMES_SECRET: "replace-me"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hermes-agent
namespace: hermes
spec:
replicas: 1
selector:
matchLabels:
app: hermes-agent
template:
metadata:
labels:
app: hermes-agent
spec:
containers:
- name: hermes-agent
image: acrhermesprod.azurecr.io/hermes-agent:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
env:
- name: OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: hermes-secrets
key: OPENAI_API_KEY
- name: HERMES_SECRET
valueFrom:
secretKeyRef:
name: hermes-secrets
key: HERMES_SECRET
- name: ASPNETCORE_URLS
value: http://+:8080
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 20
periodSeconds: 20
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
---
apiVersion: v1
kind: Service
metadata:
name: hermes-agent
namespace: hermes
spec:
selector:
app: hermes-agent
ports:
- port: 80
targetPort: 8080
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hermes-agent
namespace: hermes
annotations:
kubernetes.io/ingress.class: nginx
spec:
tls:
- hosts:
- hermes.example.com
secretName: hermes-tls
rules:
- host: hermes.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hermes-agent
port:
number: 80
3. Poznámky ke scénáři Hermes
Doporučení: - 1 replika je rozumný start, pokud nejde o vysoce paralelní HTTP službu - ingress používej jen pokud Hermes opravdu vystavuje endpoint - pokud je to spíš worker/automation kontejner, ingress nemusí být potřeba vůbec - secrets nedrž dlouhodobě natvrdo v manifestu
ČÁST B — Scénář 2: .NET 10 aplikace na AKS
4. Plain Kubernetes YAML
apiVersion: v1
kind: Namespace
metadata:
name: dotnet-app
---
apiVersion: v1
kind: Secret
metadata:
name: dotnet-app-secrets
namespace: dotnet-app
type: Opaque
stringData:
ConnectionStrings__Default: "Server=tcp:sqlserver.database.windows.net,1433;Initial Catalog=appdb;User ID=appuser;Password=replace-me;Encrypt=True;"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: dotnet-app
namespace: dotnet-app
spec:
replicas: 2
selector:
matchLabels:
app: dotnet-app
template:
metadata:
labels:
app: dotnet-app
spec:
containers:
- name: dotnet-app
image: acrhermesprod.azurecr.io/dotnet-app:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
env:
- name: ASPNETCORE_URLS
value: http://+:8080
- name: ConnectionStrings__Default
valueFrom:
secretKeyRef:
name: dotnet-app-secrets
key: ConnectionStrings__Default
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 20
periodSeconds: 20
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: dotnet-app
namespace: dotnet-app
spec:
selector:
app: dotnet-app
ports:
- port: 80
targetPort: 8080
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dotnet-app
namespace: dotnet-app
annotations:
kubernetes.io/ingress.class: nginx
spec:
tls:
- hosts:
- app.example.com
secretName: dotnet-app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: dotnet-app
port:
number: 80
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: dotnet-app
namespace: dotnet-app
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: dotnet-app
minReplicas: 2
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
5. Poznámky ke scénáři .NET 10
Doporučení: - pro web/API appku rovnou počítej s 2+ replikami - používej readiness a liveness endpointy - databázi drž mimo cluster - HPA zapni až když máš ověřené requesty, limity a chování aplikace
ČÁST C — Helm skeleton pro oba scénáře
6. Struktura chartu
my-app-chart/
Chart.yaml
values.yaml
templates/
namespace.yaml
secret.yaml
deployment.yaml
service.yaml
ingress.yaml
hpa.yaml
7. Chart.yaml
apiVersion: v2
name: my-app-chart
description: Reusable AKS chart for Hermes Agent or .NET app
type: application
version: 0.1.0
appVersion: "1.0.0"
8. Univerzální values.yaml
namespace: default
image:
repository: acrhermesprod.azurecr.io/app
tag: latest
pullPolicy: IfNotPresent
replicaCount: 1
service:
type: ClusterIP
port: 80
targetPort: 8080
containerPort: 8080
env:
ASPNETCORE_URLS: "http://+:8080"
secretEnv: {}
# secretEnv:
# OPENAI_API_KEY: "replace-me"
probes:
readiness:
path: /health
initialDelaySeconds: 10
periodSeconds: 10
liveness:
path: /health
initialDelaySeconds: 20
periodSeconds: 20
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
ingress:
enabled: true
className: nginx
host: example.com
tls:
enabled: true
secretName: app-tls
hpa:
enabled: false
minReplicas: 2
maxReplicas: 5
cpuAverageUtilization: 70
9. templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
namespace: {{ .Values.namespace }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ .Release.Name }}
spec:
containers:
- name: {{ .Release.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.containerPort }}
env:
{{- range $k, $v := .Values.env }}
- name: {{ $k }}
value: {{ $v | quote }}
{{- end }}
{{- range $k, $v := .Values.secretEnv }}
- name: {{ $k }}
valueFrom:
secretKeyRef:
name: {{ $.Release.Name }}-secret
key: {{ $k }}
{{- end }}
readinessProbe:
httpGet:
path: {{ .Values.probes.readiness.path }}
port: {{ .Values.containerPort }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
livenessProbe:
httpGet:
path: {{ .Values.probes.liveness.path }}
port: {{ .Values.containerPort }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
10. templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}
namespace: {{ .Values.namespace }}
spec:
type: {{ .Values.service.type }}
selector:
app: {{ .Release.Name }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
11. templates/secret.yaml
{{- if .Values.secretEnv }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-secret
namespace: {{ .Values.namespace }}
type: Opaque
stringData:
{{- range $k, $v := .Values.secretEnv }}
{{ $k }}: {{ $v | quote }}
{{- end }}
{{- end }}
12. templates/ingress.yaml
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}
namespace: {{ .Values.namespace }}
spec:
ingressClassName: {{ .Values.ingress.className }}
{{- if .Values.ingress.tls.enabled }}
tls:
- hosts:
- {{ .Values.ingress.host }}
secretName: {{ .Values.ingress.tls.secretName }}
{{- end }}
rules:
- host: {{ .Values.ingress.host }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}
port:
number: {{ .Values.service.port }}
{{- end }}
13. templates/hpa.yaml
{{- if .Values.hpa.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ .Release.Name }}
namespace: {{ .Values.namespace }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ .Release.Name }}
minReplicas: {{ .Values.hpa.minReplicas }}
maxReplicas: {{ .Values.hpa.maxReplicas }}
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.hpa.cpuAverageUtilization }}
{{- end }}
ČÁST D — Konkrétní values pro oba scénáře
14. values-hermes.yaml
namespace: hermes
replicaCount: 1
image:
repository: acrhermesprod.azurecr.io/hermes-agent
tag: latest
env:
ASPNETCORE_URLS: "http://+:8080"
secretEnv:
OPENAI_API_KEY: "replace-me"
HERMES_SECRET: "replace-me"
probes:
readiness:
path: /health
initialDelaySeconds: 10
periodSeconds: 10
liveness:
path: /health
initialDelaySeconds: 20
periodSeconds: 20
ingress:
enabled: true
className: nginx
host: hermes.example.com
tls:
enabled: true
secretName: hermes-tls
hpa:
enabled: false
15. values-dotnet.yaml
namespace: dotnet-app
replicaCount: 2
image:
repository: acrhermesprod.azurecr.io/dotnet-app
tag: latest
env:
ASPNETCORE_URLS: "http://+:8080"
secretEnv:
ConnectionStrings__Default: "replace-me"
probes:
readiness:
path: /ready
initialDelaySeconds: 10
periodSeconds: 10
liveness:
path: /health
initialDelaySeconds: 20
periodSeconds: 20
ingress:
enabled: true
className: nginx
host: app.example.com
tls:
enabled: true
secretName: dotnet-app-tls
hpa:
enabled: true
minReplicas: 2
maxReplicas: 5
cpuAverageUtilization: 70
16. Deploy příklady
helm install hermes ./my-app-chart -f values-hermes.yaml
helm install dotnet-app ./my-app-chart -f values-dotnet.yaml
17. Shrnutí doporučení
- Hermes Agent: typicky 1 replika, ingress jen pokud opravdu vystavuje HTTP
- .NET 10 app: typicky 2+ repliky, health probes, HPA až po ověření provozu
- databázi drž mimo cluster
- secrets řeš přes Key Vault, ne natvrdo v
values.yaml - tohle je dobrý starter baseline, ne finální enterprise deployment
18. Další navazující krok
Pokud bude potřeba, navazující materiál může být: - produkční Helm chart pro AKS - varianta s Azure Key Vault CSI Driver - varianta s cert-manager - varianta s ACR pull přes managed identity - CI/CD pipeline ACR → AKS