d1aa5daf2b
---ci--- project: atelier phase: 5 milestone: v0.2 status: complete requirements: covered: [ATELIER-36, ATELIER-37, ATELIER-38, ATELIER-39, ATELIER-40, ATELIER-41, ATELIER-42, ATELIER-43, ATELIER-44, ATELIER-45, ATELIER-46, ATELIER-47, ATELIER-48, ATELIER-49, ATELIER-50, ATELIER-51, ATELIER-52, ATELIER-53, ATELIER-54, ATELIER-55, ATELIER-56, ATELIER-57, ATELIER-58, ATELIER-59] partial: [] ---/ci---
5.7 KiB
5.7 KiB
Good Example: Kubernetes Deployment
A Kubernetes Deployment that follows Atelier's Kubernetes principles. Each aspect cites the principle it satisfies.
The Deployment
A stateless web service deployed as a Deployment with probes, resource contracts, RBAC, and a rolling update strategy — the canonical "production workload" pattern.
Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: api-prod
labels:
app: api
tier: web
spec:
replicas: 3
selector:
matchLabels:
app: api
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: api
tier: web
spec:
serviceAccountName: api
automountServiceAccountToken: false
containers:
- name: api
image: registry.example.com/api:v1.4.2 # pinned, not :latest
ports:
- containerPort: 8080
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
memory: 512Mi
readinessProbe:
httpGet:
path: /healthz/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /healthz/live
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: api-config
key: log_level
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: api-secrets
key: database_url
volumeMounts:
- name: config
mountPath: /etc/api
readOnly: true
volumes:
- name: config
configMap:
name: api-config
---
apiVersion: v1
kind: Service
metadata:
name: api
namespace: api-prod
spec:
selector:
app: api
ports:
- port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-deny-ingress
namespace: api-prod
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
tier: edge
What Makes It Good
Controller, Not Bare Pod (k8s P2 Pods are Mortal)
- A
Deploymentmanages the pods. If one dies, the controller replaces it. A bare pod has no recovery. - See
domains/kubernetes/workloads.md.
Resource Contracts (k8s P4 Requests and Limits are Contracts)
- Every container has CPU and memory requests and a memory limit. The workload is
Burstable, notBestEffort(first evicted under pressure). - See
domains/kubernetes/workloads.mdfor QoS classes.
Probes (k8s P5 Probes Drive Health)
readinessProbegates traffic: a pod that is not ready is removed from the Service's endpoints.livenessProberestarts a wedged container.- The probes check the workload's own health (
/healthz/ready,/healthz/live), not a dependency. A liveness probe that calls the database would cascade-restart on a DB blip. - See
domains/kubernetes/workloads.md.
Image Pinning (k8s P1 + IaC P5 Version Everything)
image: registry.example.com/api:v1.4.2— pinned to a version, not:latest. A pod restart pulls the same image it was built with.- See
domains/infrastructure-as-code/terraform.mdanddomains/devops/P7 Immutabilityfor the immutability angle.
RBAC (k8s P7 RBAC by Intent, Not Identity)
serviceAccountName: api— the workload runs as a dedicated ServiceAccount, not thedefaultshared identity.automountServiceAccountToken: false— the workload does not call the API, so it gets no token. Seedomains/kubernetes/rbac.md.- A matching
Role+RoleBinding(not shown) would grantget, list, watchonconfigmapsin this namespace — least privilege, scoped by intent.
Config and Secrets Separate (k8s P9 Config and Secrets are Separate)
LOG_LEVELfrom a ConfigMap (non-sensitive).DATABASE_URLfrom a Secret (sensitive). Both injected at runtime; neither baked into the image.- A configuration change does not require a rebuild. A secret rotation does not require an image redeploy.
- See
domains/kubernetes/rbac.mdanddomains/security/secrets.md.
Namespaces Bound Blast Radius (k8s P6 Namespaces Bound Blast Radius)
- The workload lives in
api-prod, notdefault. The namespace is the unit of quota, RBAC, and NetworkPolicy. A problem inapi-proddoes not leak to other workloads. - See
domains/kubernetes/networking.md.
NetworkPolicy Default-Deny (k8s P6, P7)
- The
NetworkPolicyallows ingress only from theedgenamespace. Without it, every pod in the cluster could reachapi. Default-deny is the baseline; allows are the exceptions. - See
domains/kubernetes/networking.md.
Roll Forward, Roll Back (k8s P10 Roll Forward Roll Back)
strategy: RollingUpdatewithmaxSurge: 1, maxUnavailable: 0— the rollout adds a new pod before removing an old one. Availability is maintained.kubectl rollout undo deployment/apireverts to the previous ReplicaSet. The rollback is tested before it is needed.- See
domains/kubernetes/workloads.mdanddomains/devops/P5 Progressive Delivery.
Cross-Domain Links
domains/devops/P4 Rollback First— the rollout strategy makes the deploy reversible.domains/security/authorization— the ServiceAccount + Role model is the k8s expression of least-privilege authorization.domains/observability/metrics— the probes are the platform's observability into the workload's health; the workload's own metrics complete the picture.