autumn release init --target kubernetes writes a Helm chart and a Kustomize base for your app. Both use the Autumn probe and shutdown contract, so a rolling update drops no requests.

Shell
autumn release init --target kubernetes

The command writes the usual Dockerfile, .dockerignore and autumn.production.toml.example, and also:

PathWhat it is
deploy/helm/A Helm chart
deploy/kustomize/base/A Kustomize base: Deployment, Service, PodDisruptionBudget

Build and push the image first. See the deployment guide.


Install

With Helm:

Shell
helm upgrade --install shop deploy/helm \
  --set image.repository=registry.example.com/shop \
  --set image.tag=1.4.0 \
  --set 'trustedHosts[0]=shop.example.com'

trustedHosts is required. The prod profile does not start without [security.trusted_hosts], so the chart refuses to render without it. The chart also trusts the pod IP: Prometheus scrapes a pod by its IP, and sends the IP as the Host header.

With Kustomize, make an overlay that uses the base. Set the image, the Secret and your host names in the overlay. The base sets AUTUMN_SECURITY__TRUSTED_HOSTS__HOSTS to a placeholder host and the pod IP. Then apply the overlay:

Shell
kubectl apply -k deploy/kustomize/overlays/prod

Put AUTUMN_DATABASE__PRIMARY_URL and AUTUMN_SECURITY__SIGNING_SECRET in a Secret. Load it with envFrom (Helm value envFrom). The prod profile does not start without a signing secret.


What the manifests set

ItemValueWhy
startupProbe/startup, every 2 s, 60 failuresHolds the other probes until startup is complete.
livenessProbe/liveRestarts only a stuck process. A down dependency does not restart the pod.
readinessProbe/ready, every 2 s, 1 failureRemoves the pod from the Service at the first 503.
preStopsleep 5Gives the endpoints controller time before SIGTERM.
AUTUMN_SERVER__PRESTOP_GRACE_SECS5After SIGTERM, /ready returns 503 for this time.
AUTUMN_SERVER__SHUTDOWN_TIMEOUT_SECS30In-flight requests and shutdown hooks finish in this time.
terminationGracePeriodSeconds505 + 5 + 30 + a buffer of 10. It is longer than the drain window.
PodDisruptionBudgetmaxUnavailable: 1A node drain removes one pod at a time.
Rolling updatemaxUnavailable: 0, maxSurge: 1A new pod is ready before an old pod stops.
Securitynon-root UID 10001, no privilege escalation, all capabilities droppedMatches the image user.
automountServiceAccountTokenfalseThe app does not call the Kubernetes API.

Do not point a probe at /health. It fails when a dependency is down. See Cloud-Native Autumn for the probe contract.

The grace period stays in step

In the Helm chart, set the four shutdown.* values:

Yaml
shutdown:
  preStopSleepSeconds: 5
  prestopGraceSeconds: 5
  shutdownTimeoutSeconds: 30
  bufferSeconds: 10

The chart sets the two AUTUMN_SERVER__* variables from these values. It sets terminationGracePeriodSeconds to their sum. The chart refuses to render when bufferSeconds is less than 1.

In the Kustomize base, the four numbers are literals. A comment in deployment.yaml gives the sum. Change them together.


Metrics

Each pod has prometheus.io/* annotations for metrics.path (/actuator/prometheus) on the app port. Change metrics.path when you change the [actuator] prefix.

With prometheus-operator, set metrics.podMonitor.enabled=true to render a PodMonitor. The chart then removes the annotations, so Prometheus does not scrape a pod two times. Set metrics.podMonitor.labels to the labels your Prometheus selects on, for example release: kube-prometheus-stack. A PodMonitor finds the Flagger primary and canary pods too. It sets the job label to the release name, so use --selector 'job="<release>"' with autumn slo generate.

Keep /actuator/* off the public internet. Block it at your Ingress.


Canary deploys

The chart supports Argo Rollouts and Flagger. Turn on one of them, not both. The chart refuses to render with both.

ValueEffect
rollout.enabled=trueRenders an Argo Rollouts Rollout with canary steps (rollout.steps), not a Deployment.
flagger.enabled=trueRenders a Flagger Canary for the Deployment. Flagger makes the Services, so the chart does not.

The Argo Rollouts canary has no traffic router, so the weight is a pod count. With 2 replicas, setWeight: 20 and setWeight: 50 each give 1 canary pod (about 33 % of the traffic). Use more replicas for finer steps.

Flagger needs a traffic source to shift weight. Set flagger.provider. For an ingress provider (for example nginx), also set flagger.ingressRef. For a mesh or the Gateway API, put the provider fields (for example gatewayRefs) in flagger.service. With the plain kubernetes provider, Flagger does a blue/green test. A canary with no traffic gives no metric values, and Flagger fails it. Add a load test in flagger.webhooks when your app has little traffic.

The canary analysis comes from your SLOs. Run autumn slo generate, apply its templates, and pass deploy/slo/helm-values.yaml to Helm:

Shell
autumn slo generate --app shop
kubectl apply -f deploy/slo/argo-analysis-template.yaml
helm upgrade --install shop deploy/helm \
  -f deploy/slo/helm-values.yaml --set rollout.enabled=true \
  --set 'trustedHosts[0]=shop.example.com'

The values set analysis.templateName, analysis.metricTemplates and analysis.maxBurnRate (14.4). A canary fails when it burns its error budget faster than that. See SLOs as Code.

With Argo Rollouts, the chart sets AUTUMN_DEPLOY_VERSION to the rollouts-pod-template-hash label. The version metric label then tells the canary pods from the stable pods. Promotion does not change the label. A later analysis cannot mix old and new pods.


Validation in CI

The Kubernetes manifests job in this repository runs scripts/check-k8s-manifests.sh. It renders the templates and runs:

  • helm lint --strict with the default values, each canary mode, and the PodMonitor.
  • helm template | kubeconform -strict for each of those modes.
  • kustomize build | kubeconform -strict on the base.
  • kubeconform on the generated SLO templates, and promtool on the rules.

Run the same check on your own chart:

Shell
helm lint --strict deploy/helm --set 'trustedHosts[0]=shop.example.com'
helm template shop deploy/helm --set 'trustedHosts[0]=shop.example.com' \
  | kubeconform -strict -summary

Object names are at most 55 characters, so Flagger can add -primary or -canary. Use nameOverride or fullnameOverride to set them.

Not included

  • A database migration Job. Run autumn migrate once before you roll out a release that needs it. See the deployment guide.
  • An Ingress. Add one for your ingress controller.