Notes05-Application Lifecycle
Application Lifecycle
Rollouts and rollbacks, process arguments, configuration, multi-container Pods, probes, and the HorizontalPodAutoscaler.
Application Lifecycle Quick Notes
Rolling updates and rollbacks
A Deployment stores the Pod template. Editing the template (image, env, labels in the template) creates a new ReplicaSet. The old ReplicaSet is scaled down as the new one becomes Ready.
Strategy field | Default idea | Effect |
|---|---|---|
type: RollingUpdate | Default | Old and new Pods overlap |
maxUnavailable | Often | How many old Pods may be down during the rollout |
maxSurge | Often | How many extra Pods may exist above |
type: Recreate | No overlap | All old Pods terminate, then new ones start |
kubectl set image deployment/web nginx=nginx:1.28
kubectl rollout status deployment/web
kubectl rollout history deployment/web
kubectl rollout undo deployment/web
kubectl rollout undo deployment/web --to-revision=2
kubectl rollout pause deployment/web
kubectl rollout resume deployment/web
- History needs
revisionHistoryLimitabove 0 (default is 10).kubectl rollout undoscales the previous ReplicaSet back up. - A paused Deployment ignores further template changes until
rollout resume. kubectl rollout restart deployment/webbumps a template annotation so Pods are replaced even when the image tag did not change. Useful for picking up a ConfigMap that was edited in place.- Record the cause:
kubectl annotate deployment/web kubernetes.io/change-cause="bump nginx"sorollout historyshows a comment. - Stuck rollout: New Pods are not Ready (bad probe, image pull, pending PVC). The old ReplicaSet stays.
kubectl describe podon a new Pod, then fix the template. Progress deadline isprogressDeadlineSeconds(default 600). Past that, the Deployment condition isProgressing=False.
Command and args
Containers start from the image’s entrypoint and cmd. Kubernetes overrides those with two fields:
Kubernetes field | Overrides | Role |
|---|---|---|
| Image entrypoint | The executable |
| Image cmd | Arguments to that executable |
- Only
args: The image entrypoint runs, with your arguments. - Only
command: Your executable runs, with the image’s cmd as arguments (if the image set one). - Both: Your executable and your arguments. The image cmd is ignored.
containers:
- name: worker
image: busybox:1.36
command: ["sh", "-c"]
args: ["echo hello && sleep 3600"]
Docker’s ENTRYPOINT / CMD names are easy to mix up with these fields. In a Pod manifest the names are command and args. kubectl explain pod.spec.containers.command confirms the current wording.
Environment variables
env:
- name: COLOR
value: blue
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: CPU_REQUEST
valueFrom:
resourceFieldRef:
resource: requests.cpu
- name: PASSWORD
valueFrom:
secretKeyRef:
name: db
key: password
- name: CACHE
valueFrom:
configMapKeyRef:
name: web-config
key: cache_size
envFrom:
- configMapRef:
name: web-config
- secretRef:
name: db
env: You pick keys. A missing ConfigMap or Secret key prevents the container from starting (CreateContainerConfigError).envFrom: Every key in the object becomes an environment variable. A key that is not a valid env name is skipped.fieldRef: Pod metadata (metadata.name,metadata.namespace,spec.nodeName,status.podIP).- Order: Env vars are fixed when the container starts. Editing a ConfigMap does not change env vars in an already running container. Restart the Pod.
ConfigMaps
A ConfigMap holds non-secret configuration: property files, flags, small text.
kubectl create configmap web-config \
--from-literal=cache_size=10 \
--from-file=nginx.conf=./nginx.conf
kubectl create configmap web-env --from-env-file=app.envTwo ways to consume it:
- Environment (
env/envFrom) — copied at start. - Volume — files mounted into the container. Updates to the ConfigMap eventually appear in the mounted files (kubelet sync). The process must re-read the file, or you restart the Pod.
subPathmounts do not receive ConfigMap updates. Mount the directory if the app should see changes.immutable: truelocks the object. Replace it by creating a new ConfigMap and pointing the Pod at the new name.- Size: ConfigMaps are API objects, meant for small config (the etcd object limit is about 1 MiB). Large binaries belong on a volume.
Secrets
Same consumption model as ConfigMaps (env or volume), for credentials.
Type | Contents |
|---|---|
Opaque | Arbitrary keys. Default. |
kubernetes.io/tls |
|
kubernetes.io/dockerconfigjson | Registry auth, usually named via |
kubernetes.io/service-account-token | Legacy token Secret. Prefer projected ServiceAccount tokens. |
kubectl create secret generic db \
--from-literal=password='s3cr3t' \
--from-file=username=./user.txt
kubectl create secret tls web-tls --cert=tls.crt --key=tls.key
kubectl create secret docker-registry regcred \
--docker-server=ghcr.io --docker-username=me --docker-password='token'
- Encoding: Values in YAML
data:are base64, which hides them from a casual glance and is trivial to reverse (echo <value> | base64 -d).stringData:accepts plain text and is encoded on write. - Encryption at rest is a separate apiserver feature (encryption configuration against etcd). Creating a Secret does not turn that on.
- tmpfs: Secret volumes are stored in memory on the node, not in a world-readable disk file under the container’s writable layer.
- RBAC: Reading Secrets is a different verb from reading ConfigMaps.
viewand many app roles should not havegeton Secrets unless you mean it.
Multi-container Pods
Containers in one Pod share localhost and any volumes you mount into more than one of them.
Pattern | What the extra container does |
|---|---|
Sidecar | Helps the main container: ships logs from a shared volume, refreshes certificates, runs a mesh proxy |
Adapter | Rewrites the main container’s output into the format the cluster expects |
Ambassador | Proxies outbound calls (localhost:port in the app, the ambassador opens the remote connection) |
volumes:
- name: logs
emptyDir: {}
containers:
- name: app
image: myapp:1.2
volumeMounts:
- name: logs
mountPath: /var/log/app
- name: shipper
image: fluent-bit:3.0
volumeMounts:
- name: logs
mountPath: /var/log/app
readOnly: true
- Scheduling unit: Requests and limits of all containers are summed. The Pod is placed only if the node can hold the total.
- Probes and logs are per container. A sidecar in CrashLoop does not automatically restart the main container, and it does mark the Pod not-Ready if its own readiness probe fails.
Init containers
- Run to completion, in order, before app containers. The next init starts only after the previous one exits 0.
- Uses: Wait for a dependency, run a migration,
chowna volume, render config into anemptyDir. - Failure: A failing init container restarts according to the Pod
restartPolicy(defaultAlwaysrestarts it). App containers stay stopped until every init has succeeded. - Resources: The scheduler uses the maximum of (the highest init container request, the sum of app container requests), because inits and apps do not run at the same time. Still declare requests on inits so a huge init cannot surprise the node.
- Network and volumes: Same Pod IP and the same volume names.
initContainers:
- name: wait-db
image: busybox:1.36
command: ["sh", "-c", "until nc -z db 5432; do sleep 2; done"]
restartPolicy: Never on a bare Pod leaves a failed init in Init:Error / Init:CrashLoopBackOff without an infinite retry storm. Deployments still replace Pods according to their own rules.
Probes
Probe | Failure means |
|---|---|
startupProbe | The process is still starting. Liveness and readiness wait. Failure restarts the container. Give slow JVMs a high |
livenessProbe | The process is wedged. Kubelet restarts the container. |
readinessProbe | The process should not receive Service traffic. Kubelet removes the Pod from EndpointSlices. The container is left running. |
Handlers: httpGet, tcpSocket, grpc, exec.
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 2
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /live
port: 8080
periodSeconds: 10
startupProbe:
httpGet:
path: /live
port: 8080
periodSeconds: 5
failureThreshold: 30
- A liveness probe that shares a dependency with the app (it fails whenever the database blips) causes a restart loop. Readiness is the right probe for “I cannot serve yet.”
- No readiness probe: The Pod is added to the Service as soon as the container is started, including during a crash loop’s brief uptime.
startupProbe+ slow start: Set livenessfailureThresholdlow, and let startup cover the boot window (periodSeconds×failureThreshold).
Self-healing, stacked
- Process dies → kubelet restarts the container (
restartPolicy: Alwayson Deployment Pods). - Liveness fails → kubelet restarts the container.
- Readiness fails → Service stops sending traffic; the process keeps running.
- Pod is deleted or the node dies → ReplicaSet / Deployment creates a replacement.
- Too much CPU demand → HPA adds Pods (below).
HorizontalPodAutoscaler
HPA changes the replicas of a Deployment, ReplicaSet, or StatefulSet from a metric. It needs the Metrics API (metrics-server).
kubectl autoscale deployment web --cpu-percent=70 --min=2 --max=10
kubectl get hpa
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- Utilization is usage divided by the CPU request. Pods with no CPU request cannot scale on CPU utilization.
- Stabilization: HPA waits between scale events so a spike does not flap the replica count.
kubectl describe hpashows the current metric and the last scale time. - Cluster size: HPA adds Pods. It does not add nodes. Pending Pods after a scale-up mean nothing schedulable has enough free CPU and memory for the requests. Adding nodes is a separate controller (Cluster Autoscaler or Karpenter) watching for those Pending Pods.