Lesson 07 Deployments & StatefulSets
Theme:
Lesson 07 · Slide 1

Deployments & StatefulSets

Templating workloads that power your Kubernetes applications.

Lesson 07 · Slide 2

Deployment Template Anatomy

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "mychart.fullname" . }}
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "mychart.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "mychart.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - containerPort: {{ .Values.service.port }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
Notes: The | default .Chart.AppVersion pattern keeps image tags in sync with chart versions when no explicit tag is set.
Lesson 07 · Slide 3

Resources, Probes & Environment

resources:
  requests:
    cpu: {{ .Values.resources.requests.cpu | quote }}
    memory: {{ .Values.resources.requests.memory | quote }}
  limits:
    cpu: {{ .Values.resources.limits.cpu | quote }}
    memory: {{ .Values.resources.limits.memory | quote }}
livenessProbe:
  httpGet:
    path: {{ .Values.probes.liveness.path }}
    port: http
  initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
readinessProbe:
  httpGet:
    path: {{ .Values.probes.readiness.path }}
    port: http
startupProbe:
  httpGet:
    path: {{ .Values.probes.startup.path }}
    port: http
  failureThreshold: 30
  periodSeconds: 10
env:
  - name: APP_ENV
    value: {{ .Values.env.appEnv | quote }}
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: {{ include "mychart.fullname" . }}-secret
        key: db-password
  - name: CONFIG_FLAG
    valueFrom:
      configMapKeyRef:
        name: {{ include "mychart.fullname" . }}-config
        key: feature-flag
Lesson 07 · Slide 4

Update Strategies

RollingUpdate (default)

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: {{ .Values.strategy.maxSurge | default "25%" }}
    maxUnavailable: {{ .Values.strategy.maxUnavailable | default "25%" }}
  • Zero-downtime deploys
  • maxSurge: extra pods during update
  • maxUnavailable: pods that can go down

Recreate

strategy:
  type: Recreate
  • Kill all old pods first, then create new
  • Brief downtime expected
  • Use when app cannot run two versions concurrently
Notes: Init containers run before main containers start. Use them for migrations, config fetching, or waiting on dependencies.
initContainers:
  {{- if .Values.initContainers.waitForDB.enabled }}
  - name: wait-for-db
    image: busybox:1.36
    command: ['sh', '-c', 'until nc -z {{ .Values.database.host }} {{ .Values.database.port }}; do sleep 2; done']
  {{- end }}
Lesson 07 · Slide 5

StatefulSet — Stable Identity & Storage

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: {{ include "mychart.fullname" . }}
spec:
  serviceName: {{ include "mychart.fullname" . }}-headless
  replicas: {{ .Values.statefulset.replicas }}
  podManagementPolicy: {{ .Values.statefulset.podManagementPolicy | default "OrderedReady" }}
  selector:
    matchLabels:
      {{- include "mychart.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "mychart.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          volumeMounts:
            - name: data
              mountPath: /var/lib/data
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: [ "ReadWriteOnce" ]
        storageClassName: {{ .Values.statefulset.storageClass | quote }}
        resources:
          requests:
            storage: {{ .Values.statefulset.storageSize | default "10Gi" }}
Notes: Each pod gets podname-0, podname-1, etc. with a unique PVC. Pods are created in order and terminated in reverse order with OrderedReady.
Lesson 07 · Slide 6

PodDisruptionBudget

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: {{ include "mychart.fullname" . }}
spec:
  {{- if .Values.pdb.minAvailable }}
  minAvailable: {{ .Values.pdb.minAvailable }}
  {{- else }}
  maxUnavailable: {{ .Values.pdb.maxUnavailable | default 1 }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "mychart.selectorLabels" . | nindent 6 }}
Lesson 07 · Slide 7

Topology Spread Constraints

topologySpreadConstraints:
  {{- range .Values.topologySpreadConstraints }}
  - maxSkew: {{ .maxSkew }}
    topologyKey: {{ .topologyKey }}
    whenUnsatisfiable: {{ .whenUnsatisfiable }}
    labelSelector:
      matchLabels:
        {{- include "mychart.selectorLabels" $ | nindent 8 }}
  {{- end }}

Example values:

topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
  - maxSkew: 1
    topologyKey: kubernetes.io/hostname
    whenUnsatisfiable: ScheduleAnyway
Notes: Topology constraints distribute pods evenly across failure domains. Use DoNotSchedule for strict enforcement or ScheduleAnyway for best-effort.
Lesson 07 · Slide 8

Summary

Next: Lesson 08 →

1 / ?