devops-kubernetes
Kubernetes when it is warranted: Deployments with requests, limits, probes and security contexts, Services, Ingress and the Gateway API, ConfigMaps and Secrets with an external secret store, autoscaling, disruption budgets, Helm versus Kustomize, namespaces and RBAC, network policies, and GitOps. Read before writing or changing Kubernetes manifests or charts.
- License 许可
- Apache-2.0
- 所属仓库
- enowdev/enowxcli
- 最近更新
- 2026-10-06
- Stars
- ★ 67
- Forks
- 21
01 前言 Front Matter
- name
- devops-kubernetes
- description
- Kubernetes when it is warranted: Deployments with requests, limits, probes and security contexts, Services, Ingress and the Gateway API, ConfigMaps and Secrets with an external secret store, autoscaling, disruption budgets, Helm versus Kustomize, namespaces and RBAC, network policies, and GitOps. Read before writing or changing Kubernetes manifests or charts.
03 SKILL.md 正文
Kubernetes
The generated manifest is a Deployment of app:latest with one replica, no
requests or limits, no probes, running as root on a writable filesystem, next
to a committed Secret that is only base64, a LoadBalancer Service per app,
and kubectl apply from a laptop. It works until the first node drain, noisy
neighbour or leaked repository. This is what a workload needs to survive
rollouts, node loss and review, and how to package and ship it. One part of
devops; the whole is in the devops skill.
1. When, and which cluster
- Not for a few services run by a small team: a PaaS or ECS costs a fraction
to operate (
devops-platforms). It pays off with many services and teams, or where the organisation already runs it. Use a managed control plane (GKE Autopilot or EKS Auto Mode when nobody wants to run nodes); k3s or Talos only when self-hosting is a requirement. - Upstream is at 1.37 (August 2026), three minors a year, about 14 months of
patches each; managed providers trail by one or two. Upgrade at least
yearly, one minor at a time, after
kubentorplutofinds removed APIs. - Ingress NGINX (
kubernetes/ingress-nginx) was retired in March 2026, with no more fixes. Use the Gateway API with a maintained implementation (Envoy Gateway, Cilium, Istio, NGINX Gateway Fabric, Traefik, Kong, the cloud's).
2. The Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
labels: { app.kubernetes.io/name: api }
spec:
replicas: 3
revisionHistoryLimit: 5
selector:
matchLabels: { app.kubernetes.io/name: api }
strategy:
rollingUpdate: { maxSurge: 25%, maxUnavailable: 0 }
template:
metadata:
labels: { app.kubernetes.io/name: api }
spec:
serviceAccountName: api
automountServiceAccountToken: false
terminationGracePeriodSeconds: 30
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
seccompProfile: { type: RuntimeDefault }
containers:
- name: api
image: ghcr.io/acme/api:1.4.2@sha256:<digest>
ports: [{ name: http, containerPort: 8080 }]
envFrom:
- configMapRef: { name: api-config }
- secretRef: { name: api-secrets }
resources: { requests: { cpu: 250m, memory: 256Mi }, limits: { memory: 512Mi } }
startupProbe: { httpGet: { path: /healthz, port: http }, periodSeconds: 5, failureThreshold: 30 }
readinessProbe: { httpGet: { path: /readyz, port: http }, periodSeconds: 5, failureThreshold: 3 }
livenessProbe: { httpGet: { path: /healthz, port: http }, periodSeconds: 10, failureThreshold: 3 }
lifecycle: { preStop: { sleep: { seconds: 5 } } }
securityContext: { allowPrivilegeEscalation: false, readOnlyRootFilesystem: true, capabilities: { drop: ["ALL"] } }
volumeMounts: [{ name: tmp, mountPath: /tmp }]
volumes: [{ name: tmp, emptyDir: {} }]
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector: { matchLabels: { app.kubernetes.io/name: api } }
- Requests always: scheduling and the autoscaler's maths use them. A memory
limit always, 1 to 2 times the request, with the runtime sized below it
(
--max-old-space-size,-XX:MaxRAMPercentage=75,GOMEMLIMIT), since the kernel kills the process at the limit. CPU limits rarely: throttling adds latency even on an idle node; keep them for batch work or where policy demands (Go 1.25 and later read the CPU limit forGOMAXPROCS). - Size requests from measurement (p95 over a week, VPA recommendations,
kubectl top); in-place resize is stable since 1.35. - Probes: startup covers the slowest boot (30 x 5s here); readiness checks
what a request needs, with a short timeout; liveness checks only the
process (
backend-observability). No heavy work in any of them. - The
preStopsleep (stable since 1.34, nosleepbinary needed) lets endpoints drop the pod beforeSIGTERM; the grace period covers the sleep plus the app's drain (devops-deploy). - At least 2 replicas (3 for anything that matters) spread over zones, and no
replicasfield once an HPA owns it (GitOps and the HPA would fight). The image by digest, a numeric user matching the image (devops-containers).
3. Services and the Gateway API
apiVersion: v1
kind: Service
metadata: { name: api }
spec:
selector: { app.kubernetes.io/name: api }
ports: [{ name: http, port: 80, targetPort: http }]
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata: { name: api }
spec:
parentRefs: [{ name: public, namespace: gateway }]
hostnames: ["api.example.com"]
rules:
- matches: [{ path: { type: PathPrefix, value: / } }]
backendRefs: [{ name: api, port: 80 }]
ClusterIPServices inside and one Gateway outside, not aLoadBalancerper app. The platform owns the Gateway (its namespace, HTTPS listeners,allowedRoutes); app teams own their HTTPRoutes.- TLS through cert-manager: set
config.gatewayAPI.enabled=truein its Helm values, annotate the Gateway withcert-manager.io/cluster-issuer: letsencrypt, and each HTTPS listener'scertificateRefsnames the Secret it fills. On an Ingress, the same annotation plus atls:block. - Timeouts, body limits and rate limits are per implementation (policies or
filters); set them for uploads and long requests (
devops-networking).
4. Configuration and secrets
- A ConfigMap change must roll the pods: Kustomize's
configMapGeneratorhashes the name; Helm charts add achecksum/configpod annotation. - A Secret is base64, not encryption; plain Secret manifests never go into git. Pull them from a secret manager with External Secrets Operator, or keep them encrypted in git with Sealed Secrets or SOPS:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: api-secrets }
spec:
refreshInterval: 1h
secretStoreRef: { kind: ClusterSecretStore, name: aws-secrets-manager }
target: { name: api-secrets }
dataFrom:
- extract: { key: prod/api }
- Secrets encrypted at rest (KMS on managed clusters), readable by few subjects. Cloud access through workload identity (EKS Pod Identity or IRSA, GKE Workload Identity Federation, Azure Workload Identity), never keys.
5. Scaling and disruption
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: api }
spec:
scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: api }
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource: { name: cpu, target: { type: Utilization, averageUtilization: 70 } }
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: api }
spec:
maxUnavailable: 1
selector: { matchLabels: { app.kubernetes.io/name: api } }
- CPU utilisation is measured against the request, so requests must be real; 60 to 75% is the usual target. Request rate or queue depth through KEDA or a metrics adapter; KEDA also scales workers to zero on an empty queue. The default 300s scale-down window stops flapping; keep it.
- A PDB with
minAvailableequal to the replica count blocks every drain and upgrade;maxUnavailable: 1does not. Nodes follow pending pods (Cluster Autoscaler or Karpenter); aPriorityClassprotects what matters.
6. Packaging: Kustomize or Helm
| Kustomize | Helm | |
|---|---|---|
| Best for | Your own apps: plain YAML, a base and overlays | Configurable software for others; third-party components |
| Per environment | overlays/staging, overlays/prod (image digest, replicas, config) |
values-staging.yaml, values-prod.yaml |
| Render | kubectl kustomize deploy/overlays/prod |
helm template api ./chart -f values-prod.yaml |
| Lint | kubeconform on the output | helm lint --strict, kubeconform on the output |
- Helm 4 is current (server-side apply for new releases). Pin chart versions,
prefer OCI charts and the upstream project's chart (Bitnami's point at
images without free updates); label with the
app.kubernetes.io/*set.
7. Namespaces, RBAC, network policies
- A namespace per application or team and environment; production in its own
cluster when the blast radius matters. RBAC: a ServiceAccount per workload,
Roles per namespace, no
cluster-adminfor apps or CI; check withkubectl auth can-i --list --as=system:serviceaccount:shop:api -n shop. - Pod Security Admission: label namespaces
pod-security.kubernetes.io/enforce: restricted; the pod above passes. - Network policies: deny by default, allow DNS, then each needed path (from the gateway's namespace in, to the database out). They need an enforcing CNI (Cilium, Calico, GKE Dataplane V2, the EKS VPC CNI with policies on); without one they are silently ignored.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: default-deny }
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
egress:
- to: [{ namespaceSelector: {}, podSelector: { matchLabels: { k8s-app: kube-dns } } }]
ports: [{ port: 53, protocol: UDP }, { port: 53, protocol: TCP }]
8. Jobs, CronJobs, migrations
- CronJobs:
concurrencyPolicy: Forbid, an explicittimeZone,startingDeadlineSeconds, andbackoffLimit,activeDeadlineSecondsandttlSecondsAfterFinishedon the job, so stuck runs end and old ones go. - Migrations run as a Job before the rollout: an Argo CD
PreSynchook, a Helmpre-upgradehook, or a CI step that waits (kubectl wait --for=condition=complete job/migrate --timeout=10m). Never an init container: it runs in every pod, racing (devops-deploy).
9. GitOps
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata: { name: api-prod, namespace: argocd }
spec:
project: shop
source:
repoURL: https://github.com/acme/deploy.git
targetRevision: main
path: apps/api/overlays/prod
destination: { server: https://kubernetes.default.svc, namespace: shop }
syncPolicy:
automated: { prune: true, selfHeal: true }
- The cluster pulls its state from git (Argo CD, or Flux
GitRepositoryandKustomization). CI pushes the image and commits its digest; CI holds no cluster credentials, a rollback is agit revert, and self-heal undoes hand edits. Logs to stdout as JSON, metrics by ServiceMonitor or OTLP, alerts on restarts andOOMKilled(devops-observability).
Check it
- Offline, with your cluster's version (the second schema location covers
CRDs such as HTTPRoute and ExternalSecret), plus
helm lint --strictandkube-linter lint deploy/ortrivy config deploy/:
kubectl kustomize deploy/overlays/prod | kubeconform -strict -summary \
-kubernetes-version 1.35.0 -schema-location default \
-schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json'
- With read access:
kubectl diff -k deploy/overlays/prodandkubectl apply --dry-run=server -k deploy/overlays/prod; a real apply only when the task says so. - In staging:
kubectl rollout status deployment/api --timeout=5m, pods over zones, no probe failures inkubectl get events --sort-by=.lastTimestamp.
Avoid
latest or tag-only images; no requests, no memory limit, or tight CPU
limits on latency-sensitive services; liveness probes that check the
database; no preStop sleep; root users and writable filesystems; Secrets in
git as base64; one LoadBalancer per service; new work on Ingress NGINX;
replicas fighting an HPA; a PDB that blocks every drain; migrations in init
containers; cluster-admin for CI; network policies on a CNI that ignores
them; kubectl apply to production from a laptop; Kubernetes for one small
service.
04 同仓库更多 Skills
demo-skill
Project-scope demo skill for tests.
writing
Writing words people read: the specific thing instead of claims, words to drop, sentences, interface copy (buttons, labels, errors, empty states, confirmations), page copy, notifications and emails, alt text and link text, numbers and dates, words that translate well, documentation and voice. Read before writing a page's copy, an interface's text or a document.
ui
Designing or restyling an interface: direction, layout, type, colour, icons, components, states, responsive behaviour. Read before building a page, a screen or a set of components.
ui-themes
Light and dark themes, and colour that holds in both: tokens with a value per theme, and what each kind of content needs to stay visible (icons, SVG drawings and line art, logos, photos, screenshots, charts, code, maps, borders, shadows, focus, form controls), switching without a flash, and checking both. Read before building a second theme or a theme toggle, and before any drawing, logo or chart on a page that has two themes.
ui-structure
Structure before styling: the blueprint written before any interface code (the screen's job, the regions its kind of page needs, their order and why, the layout skeleton in numbers, the components and their states), the anatomy and order of a homepage, a dashboard, a list, a detail page, settings, a form, sign-in, pricing, docs, an article and a checkout, and how to turn the blueprint into code (tokens, layout primitives, components, sections, page). Read first, before building or rebuilding any page or screen.
ui-stack-vue
Building interfaces in Vue 3 and Nuxt: project layout, script setup with typed defineProps, defineEmits and defineModel, attribute fallthrough, slots and scoped slots, template refs, ref, reactive and computed, watch versus watchEffect, composables and VueUse, provide and inject, Pinia, keyed v-for, transitions, Nuxt 4 (app directory, pages, layouts, useFetch and useAsyncData keys, $fetch in handlers, server routes, runtime config, useSeoMeta, NuxtImg, middleware), VeeValidate forms, Reka UI and Nuxt UI, testing, vue-tsc, and the traps. Read when the project uses Vue or Nuxt.