# Example: K3s Deployment Skill This is a complete example of a DevOps skill for K3s/Kubernetes deployments. --- ```yaml --- name: k3s-deploy description: Generates K3s deployment manifests with proper resource limits, probes, and HPA. Use when deploying applications to Kubernetes. argument-hint: [app-name] [replicas] [image] allowed-tools: Read, Write, Edit, Glob, Grep, Bash disable-model-invocation: true --- # K3s Deployment Generator Generate production-ready Kubernetes manifests for K3s clusters with proper resource limits, health probes, and autoscaling. ## When to Use - Deploying new applications to K3s - Creating or updating Deployment manifests - Setting up Services and Ingress - Configuring Horizontal Pod Autoscaling ## Prerequisites - K3s cluster running and accessible - kubectl configured with cluster access - Container image available in registry ## Instructions ### Step 1: Parse Arguments Extract from `$ARGUMENTS`: - **app-name**: Name for the deployment (kebab-case) - **replicas**: Initial replica count (default: 2) - **image**: Container image with tag ### Step 2: Generate Deployment Create `deployment.yaml`: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: ${APP_NAME} namespace: default labels: app: ${APP_NAME} version: v1 spec: replicas: ${REPLICAS} revisionHistoryLimit: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 selector: matchLabels: app: ${APP_NAME} template: metadata: labels: app: ${APP_NAME} version: v1 annotations: prometheus.io/scrape: "true" prometheus.io/port: "8000" prometheus.io/path: "/metrics" spec: serviceAccountName: ${APP_NAME} securityContext: runAsNonRoot: true runAsUser: 1000 fsGroup: 1000 containers: - name: ${APP_NAME} image: ${IMAGE} imagePullPolicy: IfNotPresent ports: - name: http containerPort: 8000 protocol: TCP env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace envFrom: - configMapRef: name: ${APP_NAME}-config - secretRef: name: ${APP_NAME}-secrets resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "500m" livenessProbe: httpGet: path: /health/live/ port: http initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /health/ready/ port: http initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 3 startupProbe: httpGet: path: /health/live/ port: http initialDelaySeconds: 10 periodSeconds: 5 failureThreshold: 30 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL volumeMounts: - name: tmp mountPath: /tmp volumes: - name: tmp emptyDir: {} affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: ${APP_NAME} topologyKey: kubernetes.io/hostname topologySpreadConstraints: - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: ScheduleAnyway labelSelector: matchLabels: app: ${APP_NAME} ``` ### Step 3: Generate Service Create `service.yaml`: ```yaml apiVersion: v1 kind: Service metadata: name: ${APP_NAME} namespace: default labels: app: ${APP_NAME} spec: type: ClusterIP ports: - name: http port: 80 targetPort: http protocol: TCP selector: app: ${APP_NAME} ``` ### Step 4: Generate ConfigMap Create `configmap.yaml`: ```yaml apiVersion: v1 kind: ConfigMap metadata: name: ${APP_NAME}-config namespace: default data: LOG_LEVEL: "INFO" ENVIRONMENT: "production" ``` ### Step 5: Generate HPA Create `hpa.yaml`: ```yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: ${APP_NAME} namespace: default spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: ${APP_NAME} minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80 behavior: scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 10 periodSeconds: 60 scaleUp: stabilizationWindowSeconds: 0 policies: - type: Percent value: 100 periodSeconds: 15 - type: Pods value: 4 periodSeconds: 15 selectPolicy: Max ``` ### Step 6: Generate ServiceAccount Create `serviceaccount.yaml`: ```yaml apiVersion: v1 kind: ServiceAccount metadata: name: ${APP_NAME} namespace: default ``` ### Step 7: Generate Ingress (Traefik) Create `ingress.yaml`: ```yaml apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: ${APP_NAME} namespace: default spec: entryPoints: - websecure routes: - match: Host(\`${APP_NAME}.example.com\`) kind: Rule services: - name: ${APP_NAME} port: 80 middlewares: - name: ${APP_NAME}-ratelimit tls: certResolver: letsencrypt --- apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: ${APP_NAME}-ratelimit namespace: default spec: rateLimit: average: 100 burst: 50 ``` ## Patterns & Best Practices ### Resource Sizing Start conservative and adjust based on metrics: | Workload Type | CPU Request | CPU Limit | Memory Request | Memory Limit | |---------------|-------------|-----------|----------------|--------------| | Web App | 100m | 500m | 128Mi | 256Mi | | API Server | 200m | 1000m | 256Mi | 512Mi | | Worker | 500m | 2000m | 512Mi | 1Gi | | Database | 1000m | 4000m | 1Gi | 4Gi | ### Probe Configuration - **startupProbe**: Use for slow-starting apps (Django, Java) - **livenessProbe**: Detect deadlocks, restart if failed - **readinessProbe**: Detect temporary unavailability, remove from LB ### Labels Convention ```yaml labels: app: my-app # Required: app name version: v1.2.3 # Recommended: version component: api # Optional: component type part-of: platform # Optional: parent application managed-by: kubectl # Optional: management tool ``` ## Validation ```bash # Dry run to validate manifests kubectl apply --dry-run=client -f . # Show diff before applying kubectl diff -f . # Apply with record kubectl apply -f . --record # Watch rollout kubectl rollout status deployment/${APP_NAME} # Check events kubectl get events --field-selector involvedObject.name=${APP_NAME} ``` ## Common Pitfalls - **No Resource Limits**: Can cause node resource exhaustion - **Missing Probes**: Leads to traffic to unhealthy pods - **Wrong Probe Paths**: Causes constant restarts - **No Anti-Affinity**: All pods on same node = single point of failure - **Aggressive HPA**: Causes thrashing; use stabilization windows ## Rollback Procedure ```bash # View rollout history kubectl rollout history deployment/${APP_NAME} # Rollback to previous version kubectl rollout undo deployment/${APP_NAME} # Rollback to specific revision kubectl rollout undo deployment/${APP_NAME} --to-revision=2 ``` ```