Kubernetes YAML a Helm pro oba AKS scénáře

Navazující interní materiál k AKS průvodci pro dva scénáře:

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í


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