flowardΒ·vibes
Home / 04 β€” Kubernetes Manifests 3 min read

04 β€” Kubernetes Manifests

What Kubernetes is: the system that runs your containerised app in production. It restarts your app if it crashes, runs multiple copies for reliability, and rolls out new versions without downtime. What a manifest is: a YAML file that tells Kubernetes what to run and how.

4.1 Deployment β€” kubernetes/deployment.yaml

Keeps 2+ copies of your app running at all times.

Rules

  • replicas: 2 minimum
  • CPU and memory requests and limits defined
  • livenessProbe β€” detects a frozen process so Kubernetes can restart it. Point it at /healthz, which must not depend on the database (see guide 08 β€” a DB outage must not restart-loop your app).
  • readinessProbe β€” tells Kubernetes when the pod may receive traffic. Point it at /readyz, which should include the DB connectivity check.
  • All secrets referenced via secretKeyRef β€” never plain-text values
  • securityContext enforcing non-root (defence in depth on top of the Dockerfile USER)
  • The container port, Service targetPort, probe ports, and the app's APP_PORT must all agree.

Example

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  replicas: 2                    # Always run 2 copies for high availability
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      securityContext:
        runAsNonRoot: true       # Refuse to start if the image tries to run as root
                                 # (requires a NUMERIC `USER` in the Dockerfile β€” see guide 03)
      containers:
        - name: my-app
          # ⚠ The pipeline replaces this with the correct ECR image URI on deploy
          image: <AWS_ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/my-app:latest
          ports:
            - containerPort: 3000   # Must match EXPOSE in the Dockerfile and APP_PORT
          resources:
            requests:
              cpu: "100m"           # 0.1 core minimum
              memory: "128Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"
          env:
            - name: APP_PORT
              valueFrom:
                configMapKeyRef:
                  name: my-app-config
                  key: app-port
            - name: APP_ENV
              valueFrom:
                configMapKeyRef:
                  name: my-app-config
                  key: app-env
            - name: LOG_LEVEL
              valueFrom:
                configMapKeyRef:
                  name: my-app-config
                  key: log-level
            - name: DB_HOST
              valueFrom:
                secretKeyRef:
                  name: my-app-secrets   # Created by the DevOps team
                  key: db-host
            - name: DB_PORT
              valueFrom:
                secretKeyRef:
                  name: my-app-secrets
                  key: db-port
            - name: DB_NAME
              valueFrom:
                secretKeyRef:
                  name: my-app-secrets
                  key: db-name
            - name: DB_USER
              valueFrom:
                secretKeyRef:
                  name: my-app-secrets
                  key: db-user
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: my-app-secrets
                  key: db-password
          livenessProbe:
            httpGet:
              path: /healthz       # Process-alive only β€” no DB dependency
              port: 3000
            initialDelaySeconds: 15
            periodSeconds: 20
          readinessProbe:
            httpGet:
              path: /readyz        # Ready to serve β€” may include DB check
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10

(If your app only implements a single /healthz, you may point both probes at it β€” but then /healthz must NOT fail when the database is down, or Kubernetes will restart healthy pods in a loop during a DB outage.)

4.2 Service β€” kubernetes/service.yaml

Gives your app a stable internal address and load-balances across replicas.

apiVersion: v1
kind: Service
metadata:
  name: my-app-svc
spec:
  selector:
    app: my-app          # Must match the Deployment's pod labels
  ports:
    - protocol: TCP
      port: 80           # Port other services use inside the cluster
      targetPort: 3000   # Must match containerPort in the Deployment
  type: ClusterIP        # Internal only β€” DevOps handles external access via Ingress

4.3 ConfigMap β€” kubernetes/configmap.yaml (only if needed)

Non-sensitive config only. Never put a secret in a ConfigMap β€” they are not encrypted.

apiVersion: v1
kind: ConfigMap
metadata:
  name: my-app-config
data:
  app-port: "3000"
  app-env: "production"
  log-level: "info"

Naming convention

Use your app's name consistently: Deployment metadata.name, container name, Secret <app>-secrets, ConfigMap <app>-config, Service <app>-svc. The pipeline's DEPLOYMENT_NAME and CONTAINER_NAME variables must match.

Prompt tip

"Write Kubernetes manifests at kubernetes/deployment.yaml, kubernetes/service.yaml, and kubernetes/configmap.yaml. Deployment: 2 replicas, CPU/memory requests and limits, runAsNonRoot, liveness probe on /healthz (no DB dependency), readiness probe on /readyz, all secrets via secretKeyRef from a Secret named <app>-secrets, non-sensitive config via configMapKeyRef from <app>-config."