--- name: tsh-implementing-kubernetes description: 'Kubernetes deployment patterns, Helm charts, and cluster management. Use when deploying applications to K8s, designing workload configurations, implementing scaling strategies, or managing cluster resources.' user-invocable: false --- # Kubernetes Patterns ## When to Use - Deploying applications to Kubernetes - Designing Deployment, StatefulSet, or Job configurations - Implementing auto-scaling (HPA, VPA, KEDA) - Creating or modifying Helm charts - Setting up ingress, networking, and service mesh - Configuring resource requests, limits, and QoS ## Project Detection Check which Kubernetes tooling the project uses: - `helm/` or `Chart.yaml` → Helm charts - `kustomize/` or `kustomization.yaml` → Kustomize - `k8s/` or `kubernetes/` with `*.yaml` → Raw manifests - `skaffold.yaml` → Skaffold for local dev - `argocd/` or `Application` resources → ArgoCD GitOps - `flux-system/` or `Kustomization` CRD → Flux GitOps Use `context7` to look up Kubernetes API versions and syntax. ## Workload Type Decision | Workload Type | Use When | |---------------|----------| | **Deployment** | Stateless apps, web servers, APIs | | **StatefulSet** | Databases, stateful apps needing stable identity | | **DaemonSet** | Node-level agents (logging, monitoring) | | **Job** | One-time tasks, batch processing | | **CronJob** | Scheduled recurring tasks | ## Deployment Configuration ### Resource Management ```yaml resources: requests: # Scheduler uses for placement memory: "256Mi" cpu: "100m" limits: # Kubelet enforces these memory: "512Mi" cpu: "500m" ``` **Rules:** - Always set requests (required for scheduling) - Set memory limits to prevent OOM impact on node - CPU limits optional (can cause throttling) - Request:Limit ratio of 1:2 is good starting point ### QoS Classes | Class | Condition | Eviction Priority | |-------|-----------|-------------------| | **Guaranteed** | requests == limits (all containers) | Last to evict | | **Burstable** | requests < limits | Medium | | **BestEffort** | No requests or limits | First to evict | **Rule:** Production workloads should be Guaranteed or Burstable, never BestEffort. ### Probes Configuration ```yaml livenessProbe: # Restarts container if fails httpGet: path: /healthz port: 8080 initialDelaySeconds: 15 periodSeconds: 10 failureThreshold: 3 readinessProbe: # Removes from Service if fails httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 startupProbe: # Delays liveness until startup complete httpGet: path: /healthz port: 8080 failureThreshold: 30 periodSeconds: 10 ``` **Rules:** - Always configure readinessProbe (graceful traffic handling) - Use startupProbe for slow-starting apps (instead of long initialDelaySeconds) - livenessProbe should check app health, not dependencies ### Pod Disruption Budget ```yaml apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: api-pdb spec: minAvailable: 2 # OR maxUnavailable: 1 selector: matchLabels: app: api ``` **Rule:** Always create PDB for production workloads to ensure availability during node drains. ### Pod Anti-Affinity ```yaml affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: api topologyKey: kubernetes.io/hostname ``` **Rule:** Spread replicas across nodes/zones for high availability. ## Scaling Strategies ### Horizontal Pod Autoscaler (HPA) ```yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: api-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: api minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 behavior: scaleDown: stabilizationWindowSeconds: 300 # Prevent flapping ``` ### Scaling Decision Matrix | Scaling Type | Use When | Tool | |--------------|----------|------| | CPU-based | General compute workloads | HPA | | Memory-based | Memory-intensive apps | HPA | | Custom metrics | Queue depth, request rate | HPA + Prometheus Adapter | | Event-driven | Message queues, scheduled jobs | KEDA | | Vertical | Right-sizing requests/limits | VPA | ## Helm Chart Structure ``` mychart/ ├── Chart.yaml # Chart metadata ├── values.yaml # Default values ├── values-dev.yaml # Environment overrides ├── values-prod.yaml ├── templates/ │ ├── _helpers.tpl # Template helpers │ ├── deployment.yaml │ ├── service.yaml │ ├── ingress.yaml │ ├── hpa.yaml │ ├── pdb.yaml │ └── configmap.yaml └── charts/ # Dependencies ``` ### Helm Best Practices ```yaml # values.yaml - use structured defaults replicaCount: 2 image: repository: myapp tag: "" # Override in CI, not here pullPolicy: IfNotPresent resources: requests: memory: "256Mi" cpu: "100m" limits: memory: "512Mi" # Enable/disable optional components autoscaling: enabled: true minReplicas: 2 maxReplicas: 10 ``` **Rules:** - Don't hardcode image tags in values.yaml (set in CI) - Use `{{ include "mychart.fullname" . }}` for resource names - Provide sensible defaults, override per environment ## Ingress Configuration ### Ingress Class Decision | Ingress Controller | Use When | |-------------------|----------| | **nginx-ingress** | General purpose, widely supported | | **AWS ALB** | AWS-native, integrated with WAF/ACM | | **Traefik** | Simple setup, automatic HTTPS | | **Istio Gateway** | Service mesh already in use | ### Ingress Example (nginx) ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: api-ingress annotations: nginx.ingress.kubernetes.io/ssl-redirect: "true" cert-manager.io/cluster-issuer: "letsencrypt-prod" spec: ingressClassName: nginx tls: - hosts: - api.example.com secretName: api-tls rules: - host: api.example.com http: paths: - path: / pathType: Prefix backend: service: name: api port: number: 80 ``` ## Security Context ```yaml securityContext: runAsNonRoot: true runAsUser: 1000 runAsGroup: 1000 fsGroup: 1000 seccompProfile: type: RuntimeDefault containers: - name: app securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL ``` **Rule:** Always run as non-root with minimal capabilities in production. ## Process 1. **Discover context** → Check existing K8s manifests, Helm charts, Kustomize 2. **Choose workload type** → Deployment, StatefulSet, Job based on requirements 3. **Configure resources** → Set requests/limits based on profiling or estimates 4. **Add probes** → Configure readiness, liveness, and startup probes 5. **Enable scaling** → Add HPA/KEDA based on scaling requirements 6. **Add resilience** → PDB, pod anti-affinity, topology spread 7. **Configure security** → Security context, network policies 8. **Validate** → `kubectl apply --dry-run=server`, helm template ## Checklist - [ ] Resource requests and limits defined - [ ] Readiness and liveness probes configured - [ ] PodDisruptionBudget created for production - [ ] Pod anti-affinity or topology spread configured - [ ] HPA configured for variable workloads - [ ] Security context with non-root user - [ ] Image pull policy appropriate (Never use `latest` in prod) - [ ] Labels consistent (`app`, `version`, `environment`) - [ ] Namespace isolation per environment ## Anti-Patterns | Don't | Do | |-------|-----| | Use `latest` image tag | Pin specific versions or SHA | | Skip resource requests | Always set requests for scheduling | | Single replica in production | Minimum 2 replicas with PDB | | Run as root | Use non-root user with minimal caps | | Missing readiness probe | Configure probes for graceful traffic | | `kubectl apply` in production | GitOps with ArgoCD/Flux | | Hardcode values in manifests | Use Helm values or Kustomize overlays | | Ignore pod eviction | Set PDB to maintain availability | ## Related Skills - `tsh-implementing-observability` - For K8s monitoring and logging setup - `tsh-implementing-ci-cd` - For K8s deployment pipelines - `tsh-managing-secrets` - For K8s secret management patterns - `tsh-implementing-terraform-modules` - For provisioning K8s clusters