# Kubernetes & Container Orchestration ## Kubernetes Architecture Essentials ### Core Components - **Control Plane**: API Server, Scheduler, Controller Manager, etcd - **Worker Nodes**: Kubelet, Kube-proxy, Container Runtime - **Add-ons**: CoreDNS, Metrics Server, Ingress Controller ### Key Kubernetes Resources - **Workloads**: Pods, Deployments, StatefulSets, DaemonSets, Jobs, CronJobs - **Networking**: Services, Ingress, NetworkPolicies - **Configuration**: ConfigMaps, Secrets - **Storage**: PersistentVolumes, PersistentVolumeClaims, StorageClasses - **Access Control**: ServiceAccounts, Roles, RoleBindings, ClusterRoles, ClusterRoleBindings ## Production-Ready Deployment Pattern ### Deployment with Best Practices ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: myapp namespace: production labels: app: myapp version: v1.0.0 environment: production spec: replicas: 3 revisionHistoryLimit: 10 # Deployment strategy strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 # Zero-downtime deployment selector: matchLabels: app: myapp template: metadata: labels: app: myapp version: v1.0.0 annotations: prometheus.io/scrape: "true" prometheus.io/port: "8080" prometheus.io/path: "/metrics" spec: # Security context at pod level securityContext: runAsNonRoot: true runAsUser: 1000 fsGroup: 2000 seccompProfile: type: RuntimeDefault # Service account for pod identity serviceAccountName: myapp # Init container for setup tasks initContainers: - name: init-config image: busybox:1.36 command: ['sh', '-c', 'echo Initializing... && sleep 2'] securityContext: allowPrivilegeEscalation: false runAsNonRoot: true runAsUser: 1000 capabilities: drop: - ALL containers: - name: myapp image: myapp:1.0.0 imagePullPolicy: IfNotPresent # Resource limits and requests resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" # Container security securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 1000 capabilities: drop: - ALL # Health checks livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 3 successThreshold: 1 failureThreshold: 3 startupProbe: httpGet: path: /startup port: 8080 initialDelaySeconds: 0 periodSeconds: 10 failureThreshold: 30 # 5 minutes max startup time # Environment variables env: - name: ENV value: "production" - name: LOG_LEVEL value: "info" - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: POD_IP valueFrom: fieldRef: fieldPath: status.podIP # Environment from ConfigMap envFrom: - configMapRef: name: myapp-config - secretRef: name: myapp-secrets # Container ports ports: - name: http containerPort: 8080 protocol: TCP - name: metrics containerPort: 9090 protocol: TCP # Volume mounts volumeMounts: - name: config mountPath: /etc/myapp readOnly: true - name: secrets mountPath: /etc/secrets readOnly: true - name: tmp mountPath: /tmp - name: cache mountPath: /var/cache # Volumes volumes: - name: config configMap: name: myapp-config - name: secrets secret: secretName: myapp-secrets defaultMode: 0400 - name: tmp emptyDir: {} - name: cache emptyDir: {} # Pod scheduling affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - myapp topologyKey: kubernetes.io/hostname # Tolerations for node taints tolerations: - key: "node-role.kubernetes.io/spot" operator: "Exists" effect: "NoSchedule" ``` ### Service Configuration ```yaml apiVersion: v1 kind: Service metadata: name: myapp namespace: production labels: app: myapp annotations: service.beta.kubernetes.io/aws-load-balancer-type: "nlb" spec: type: LoadBalancer selector: app: myapp ports: - name: http port: 80 targetPort: 8080 protocol: TCP - name: https port: 443 targetPort: 8443 protocol: TCP # Session affinity (optional) sessionAffinity: ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 10800 --- apiVersion: v1 kind: Service metadata: name: myapp-headless namespace: production spec: clusterIP: None # Headless service for StatefulSets selector: app: myapp ports: - name: http port: 8080 targetPort: 8080 ``` ### Ingress with TLS ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: myapp namespace: production annotations: kubernetes.io/ingress.class: nginx cert-manager.io/cluster-issuer: letsencrypt-prod nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/rate-limit: "100" nginx.ingress.kubernetes.io/proxy-body-size: "10m" spec: tls: - hosts: - myapp.example.com secretName: myapp-tls rules: - host: myapp.example.com http: paths: - path: / pathType: Prefix backend: service: name: myapp port: number: 8080 ``` ## Configuration Management ### ConfigMap ```yaml apiVersion: v1 kind: ConfigMap metadata: name: myapp-config namespace: production data: # Simple key-value pairs app.env: "production" log.level: "info" # Multi-line configuration files application.yaml: | server: port: 8080 host: 0.0.0.0 database: max_connections: 100 timeout: 30s cache: ttl: 3600 max_size: 1000 ``` ### Secrets Management ```yaml # ❌ NOT RECOMMENDED - Base64 is not encryption! apiVersion: v1 kind: Secret metadata: name: myapp-secrets namespace: production type: Opaque data: db-password: cGFzc3dvcmQxMjM= # base64 encoded --- # ✅ BETTER - Use Sealed Secrets apiVersion: bitnami.com/v1alpha1 kind: SealedSecret metadata: name: myapp-secrets namespace: production spec: encryptedData: db-password: AgBqV7zJ8... # Encrypted with controller's public key --- # ✅ BEST - Use External Secrets Operator with AWS Secrets Manager apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: myapp-secrets namespace: production spec: refreshInterval: 1h secretStoreRef: name: aws-secrets-manager kind: SecretStore target: name: myapp-secrets creationPolicy: Owner data: - secretKey: db-password remoteRef: key: prod/myapp/db-password - secretKey: api-key remoteRef: key: prod/myapp/api-key ``` ## StatefulSets for Stateful Applications ```yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: postgres namespace: production spec: serviceName: postgres-headless replicas: 3 selector: matchLabels: app: postgres template: metadata: labels: app: postgres spec: securityContext: fsGroup: 999 runAsUser: 999 containers: - name: postgres image: postgres:15-alpine env: - name: POSTGRES_DB value: myapp - name: POSTGRES_USER valueFrom: secretKeyRef: name: postgres-secrets key: username - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: name: postgres-secrets key: password - name: PGDATA value: /var/lib/postgresql/data/pgdata ports: - name: postgres containerPort: 5432 resources: requests: memory: "1Gi" cpu: "500m" limits: memory: "2Gi" cpu: "1000m" livenessProbe: exec: command: - /bin/sh - -c - pg_isready -U $POSTGRES_USER -d $POSTGRES_DB initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: exec: command: - /bin/sh - -c - pg_isready -U $POSTGRES_USER -d $POSTGRES_DB initialDelaySeconds: 5 periodSeconds: 5 volumeMounts: - name: data mountPath: /var/lib/postgresql/data volumeClaimTemplates: - metadata: name: data spec: accessModes: ["ReadWriteOnce"] storageClassName: gp3 resources: requests: storage: 100Gi ``` ## Autoscaling ### Horizontal Pod Autoscaler (HPA) ```yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: myapp-hpa namespace: production spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: myapp minReplicas: 3 maxReplicas: 20 metrics: # CPU-based scaling - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 # Memory-based scaling - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80 # Custom metrics (requires metrics adapter) - type: Pods pods: metric: name: http_requests_per_second target: type: AverageValue averageValue: "1000" behavior: scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 50 periodSeconds: 60 scaleUp: stabilizationWindowSeconds: 0 policies: - type: Percent value: 100 periodSeconds: 15 - type: Pods value: 4 periodSeconds: 15 selectPolicy: Max ``` ### Vertical Pod Autoscaler (VPA) ```yaml apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: myapp-vpa namespace: production spec: targetRef: apiVersion: apps/v1 kind: Deployment name: myapp updatePolicy: updateMode: "Auto" # "Off", "Initial", "Recreate", or "Auto" resourcePolicy: containerPolicies: - containerName: myapp minAllowed: cpu: 100m memory: 128Mi maxAllowed: cpu: 2000m memory: 2Gi controlledResources: - cpu - memory ``` ## RBAC (Role-Based Access Control) ### ServiceAccount, Role, and RoleBinding ```yaml apiVersion: v1 kind: ServiceAccount metadata: name: myapp namespace: production --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: myapp-role namespace: production rules: - apiGroups: [""] resources: ["configmaps", "secrets"] verbs: ["get", "list"] - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: myapp-rolebinding namespace: production subjects: - kind: ServiceAccount name: myapp namespace: production roleRef: kind: Role name: myapp-role apiGroup: rbac.authorization.k8s.io ``` ### ClusterRole for Cluster-Wide Permissions ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: pod-reader rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["nodes"] verbs: ["get", "list"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: read-pods-global subjects: - kind: ServiceAccount name: myapp namespace: production roleRef: kind: ClusterRole name: pod-reader apiGroup: rbac.authorization.k8s.io ``` ## Network Policies ### Restrict Ingress Traffic ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: myapp-netpol namespace: production spec: podSelector: matchLabels: app: myapp policyTypes: - Ingress - Egress ingress: # Allow traffic from nginx ingress controller - from: - namespaceSelector: matchLabels: name: ingress-nginx - podSelector: matchLabels: app: nginx-ingress ports: - protocol: TCP port: 8080 # Allow traffic from prometheus for metrics - from: - namespaceSelector: matchLabels: name: monitoring - podSelector: matchLabels: app: prometheus ports: - protocol: TCP port: 9090 egress: # Allow DNS - to: - namespaceSelector: matchLabels: name: kube-system - podSelector: matchLabels: k8s-app: kube-dns ports: - protocol: UDP port: 53 # Allow database access - to: - podSelector: matchLabels: app: postgres ports: - protocol: TCP port: 5432 # Allow external HTTPS - to: - namespaceSelector: {} ports: - protocol: TCP port: 443 ``` ## Jobs and CronJobs ### Job for One-Time Task ```yaml apiVersion: batch/v1 kind: Job metadata: name: database-migration namespace: production spec: backoffLimit: 3 activeDeadlineSeconds: 600 # 10 minutes timeout template: metadata: labels: app: migration spec: restartPolicy: OnFailure containers: - name: migrate image: myapp:1.0.0 command: ["/app/migrate"] args: ["--direction", "up"] env: - name: DATABASE_URL valueFrom: secretKeyRef: name: myapp-secrets key: database-url resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" ``` ### CronJob for Scheduled Tasks ```yaml apiVersion: batch/v1 kind: CronJob metadata: name: backup-database namespace: production spec: schedule: "0 2 * * *" # Daily at 2 AM timeZone: "America/New_York" concurrencyPolicy: Forbid # Don't allow concurrent runs successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 1 jobTemplate: spec: backoffLimit: 2 activeDeadlineSeconds: 3600 # 1 hour timeout template: spec: restartPolicy: OnFailure containers: - name: backup image: postgres:15-alpine command: - /bin/sh - -c - | pg_dump -h $DB_HOST -U $DB_USER -d $DB_NAME | \ gzip > /backup/backup-$(date +%Y%m%d-%H%M%S).sql.gz envFrom: - secretRef: name: postgres-secrets volumeMounts: - name: backup mountPath: /backup volumes: - name: backup persistentVolumeClaim: claimName: backup-pvc ``` ## Helm Charts ### Chart Structure ``` myapp-chart/ ├── Chart.yaml ├── values.yaml ├── values-dev.yaml ├── values-prod.yaml ├── templates/ │ ├── deployment.yaml │ ├── service.yaml │ ├── ingress.yaml │ ├── configmap.yaml │ ├── secret.yaml │ ├── hpa.yaml │ ├── serviceaccount.yaml │ ├── NOTES.txt │ └── _helpers.tpl └── README.md ``` ### Chart.yaml ```yaml apiVersion: v2 name: myapp description: A Helm chart for MyApp type: application version: 1.0.0 appVersion: "1.0.0" keywords: - myapp - web maintainers: - name: DevOps Team email: devops@example.com dependencies: - name: postgresql version: "12.x.x" repository: https://charts.bitnami.com/bitnami condition: postgresql.enabled ``` ### values.yaml ```yaml replicaCount: 3 image: repository: myapp pullPolicy: IfNotPresent tag: "" # Defaults to chart appVersion imagePullSecrets: [] serviceAccount: create: true annotations: {} name: "" podAnnotations: prometheus.io/scrape: "true" prometheus.io/port: "8080" podSecurityContext: runAsNonRoot: true runAsUser: 1000 fsGroup: 2000 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 1000 capabilities: drop: - ALL service: type: ClusterIP port: 80 targetPort: 8080 ingress: enabled: true className: nginx annotations: cert-manager.io/cluster-issuer: letsencrypt-prod hosts: - host: myapp.example.com paths: - path: / pathType: Prefix tls: - secretName: myapp-tls hosts: - myapp.example.com resources: limits: cpu: 500m memory: 512Mi requests: cpu: 250m memory: 256Mi autoscaling: enabled: true minReplicas: 3 maxReplicas: 20 targetCPUUtilizationPercentage: 70 targetMemoryUtilizationPercentage: 80 nodeSelector: {} tolerations: [] affinity: {} postgresql: enabled: true auth: username: myapp database: myapp ``` ### Helm Commands ```bash # Install chart helm install myapp ./myapp-chart -n production # Install with custom values helm install myapp ./myapp-chart -n production -f values-prod.yaml # Upgrade release helm upgrade myapp ./myapp-chart -n production # Rollback helm rollback myapp 1 -n production # Uninstall helm uninstall myapp -n production # Template rendering (dry-run) helm template myapp ./myapp-chart -f values-prod.yaml # Lint chart helm lint ./myapp-chart ``` ## kubectl Command Reference ```bash # Get resources kubectl get pods -n production kubectl get deployments -n production -o wide kubectl get svc -n production # Describe resources kubectl describe pod myapp-123 -n production kubectl describe deployment myapp -n production # Logs kubectl logs myapp-123 -n production kubectl logs -f myapp-123 -n production # Follow kubectl logs myapp-123 -n production --previous # Previous container # Execute commands in pod kubectl exec -it myapp-123 -n production -- /bin/sh kubectl exec myapp-123 -n production -- env # Port forwarding kubectl port-forward svc/myapp 8080:80 -n production # Copy files kubectl cp myapp-123:/tmp/file.txt ./file.txt -n production # Scale deployment kubectl scale deployment myapp --replicas=5 -n production # Rollout management kubectl rollout status deployment/myapp -n production kubectl rollout history deployment/myapp -n production kubectl rollout undo deployment/myapp -n production # Apply/Delete manifests kubectl apply -f deployment.yaml kubectl delete -f deployment.yaml # Resource usage kubectl top nodes kubectl top pods -n production # Debug kubectl run debug --image=busybox:1.36 -it --rm --restart=Never -- sh ``` --- ## Best Practices Summary 1. **Always set resource requests and limits** 2. **Implement proper health checks** (liveness, readiness, startup) 3. **Use non-root containers** with security contexts 4. **Enable RBAC** and use service accounts 5. **Implement network policies** for zero-trust networking 6. **Use namespaces** for isolation 7. **Tag everything** with consistent labels 8. **Use ConfigMaps and Secrets** (never hardcode) 9. **Implement HPA** for auto-scaling 10. **Use readOnlyRootFilesystem** when possible