Rollouts
A rollout moves a change to the agents of one fleet in a controlled way. There are two kinds:
- a config rollout brings agents to a new rendered configuration (a new desired hash), and
- a version rollout brings collectors to a new collector version, using an executor.
How a rollout proceeds is described by a RolloutPolicy. The rules below are implemented by the rollout engine,
which runs on the leader replica. The design is in
docs/architecture.md and in the
server design.
apiVersion: myriarch.dev/v1alpha1
kind: RolloutPolicy
metadata:
name: canary-then-all
spec:
waves:
- name: canary
selector: {matchAttributes: {labels.wave: canary}} # an explicit canary wave
- name: rest
percent: 100
requireApproval: true # starts only after an operator approves
maxConcurrent: 5 # targets in progress at the same time (0 = unlimited)
healthWait: 5m # how long a target must stay healthy after success
applyTimeout: 30m # the longest time until success is reported
offlineGracePeriod: 30m # how long a wave waits for offline agents
failureThreshold: {maxFailed: 1}
onFailure: pause # pause (default) | rollback
timezoneAttribute: site.timezone
defaultTimezone: UTC
maintenanceWindows:
- {days: [Mon, Tue, Wed, Thu, Fri], start: "22:00", end: "05:00"}
- {days: [Sat, Sun], start: "00:00", end: "00:00", timezone: UTC}
This is the policy from examples/quickstart/policies/. All fields and their
defaults are in the schema reference.
The how-to guides Roll out a configuration change, Roll out a collector version and Upgrade the OpenTelemetry Collector on many hosts show these rules at work.
States
A rollout has one of these statuses: pending, running, waiting_approval, paused, completed, failed,
aborted, rolling_back, rolled_back or superseded. The statuses completed, aborted, rolled_back and
superseded are final. failed is not final but has no automatic transition: a person decides (rollback, abort or
retry-failed). A wave is pending, waiting_approval, running, completed, failed or skipped. A target is
pending, waiting_window, in_progress, verifying, succeeded, failed, skipped, cancelled or rolled_back.
Which policy a fleet uses
A fleet names one policy for configuration changes (rollout.configPolicy) and one for version changes
(rollout.versionPolicy). Without a reference the policy named default is used. If no object with that name exists,
the built-in policy applies: one wave of 100 percent, maxConcurrent: 50, healthWait: 60s, offlineGracePeriod: 15m,
failureThreshold.maxFailed: 1, onFailure: pause, no maintenance windows, and the default applyTimeout of the kind
(10 minutes for configuration, 30 minutes for versions). A rollout stores a snapshot of the policy with all defaults
filled in, so later edits in Git do not change a running rollout.
Waves
- Every wave has either a
selectoror apercent(1 to 100), otherwise the policy is invalid (wave_invalid). Thepercentvalues of the waves must strictly increase. - When a rollout is created, its targets are distributed over the waves (a snapshot):
- A selector wave takes all targets that are not yet assigned and whose attribute view matches.
- A percent wave takes not yet assigned targets in a stable pseudo-random order (ascending by
sha256(rolloutID || agentID)) until the cumulative share of all targets reachesceil(percent x total / 100). Targets of earlier selector waves count toward that share. - If the last wave does not reach 100 percent, a final wave
restwith 100 percent is appended implicitly. This also happens when the last wave is a selector wave. - Waves without targets are marked
skipped.
- Waves run one after the other. Wave n+1 starts when every target of wave n has a final state (
succeeded,failed,skippedorcancelled) and the failure threshold was not reached. - With
requireApprovalthe wave (and the rollout) goes towaiting_approvaland starts only afterPOST /api/v1/rollouts/{id}/actions/approvewith{"wave": n}, which the UI offers as a button.
Targets and success
A target is one agent within a rollout.
- Start (
pendingtoin_progress). A target starts only if the agent is online, its local maintenance window is open, andmaxConcurrentis not exhausted. For a configuration, the desired hash of the agent is set to the target hash and pushed. For a version, the executor’sStartis called (an updater package offer, a webhook or an AWX job). - Success reported (
in_progresstoverifying). For a configuration the agent reportsremote_config_status = APPLIEDfor the target hash. For a version the collector reportsservice.versionequal to the target version. Success is never reported by an executor. - Success (
verifyingtosucceeded). The agent stays online and healthy forhealthWait.- An update or a configuration change restarts the collector. An unhealthy report that was sent before the target
entered
verifying, from the old process that is stopping, does not count as a failure. The target waits, for at most 2 minutes, for the health report of the new process. - The normal start-up of the new process is no failure either. The real supervisor first reports the new version with
healthy=falseand no status, then the collector reportsStatusStarting, and only thenStatusOK. Such start-up reports (unhealthy with the statusStatusStarting,StatusStoppingor no status, and withoutlastError) let the target wait inverifyingfor at mostapplyTimeout. If the process is not healthy by then, the target fails. - Any other unhealthy report (an error status or a
lastError) fails the target. - Crash loops are detected for version rollouts: if the collector was healthy once on the new version and then falls back to a start-up report more than once, the target fails. A single restart after the update, for example because a configuration was applied, is allowed.
- An update or a configuration change restarts the collector. An unhealthy report that was sent before the target
entered
- Failure (
failed). The agent reportsFAILED(configuration), or the updater reportsINSTALL_FAILED. Or the agent becomes unhealthy or goes offline during the health wait,applyTimeoutis exceeded, or the executor returns an error. - Offline. Targets that are offline at their planned start wait. After
offlineGracePeriod, counted from the start of the wave, they becomeskipped. - Abort. Targets that have not started become
cancelled. - One of each kind per fleet. At most one configuration rollout and one version rollout run at the same time for a
fleet, independent of each other. If a version update restarts the collector of a host while its configuration target
is in the health wait, the configuration target fails. Use
retry-failed, or finish the version rollout first. - Supersede. When the target changes (a new rendered hash or a new target version), a new rollout replaces the
running one. This applies to rollouts in
pending,running,waiting_approvalandpaused. Forpausedrollouts, failed targets count as well, so that a fix in Git replaces a paused rollout. Rollouts infailed(for example because the fleet was removed from Git) and inrolling_backare never replaced automatically. A person confirms withabort, or the rollback runs to its end. resumeafter a failure-threshold pause acknowledges the failures so far (the rollout annotationackedFailureslists per wave the acknowledged failed attempts as<agentId>#<attempt>).retry-failedresets failed targets and increases the attempt counter, so a new failure of the same agent counts toward the threshold again.- Starting a version through a webhook or AWX is at-least-once. A deterministic
X-Myriarch-DeliveryID lets the receiver de-duplicate calls.
Health wait
The health wait is the time that a target must stay healthy after it has reported success. It is the safety margin of a rollout: a collector that crashes, restarts in a loop or turns unhealthy shortly after a change fails its target while the rollout is still working on its current wave.
- Rule. A target enters
verifyingwhen its agent reports the new configuration (APPLIED) or the new version. The server stores that moment asverifiedAtof the target; for a version rollout it is also the start of the stephealth_wait. The target becomessucceededwhen the agent is still online, still reports the target configuration or version, is healthy andnow - verifiedAt >= healthWait. The health wait therefore ends atverifiedAt + healthWait. - Value.
healthWaitisspec.healthWaitof the rollout policy. A policy that sets none, or0, waits 60 seconds. A rollout keeps the value of its policy snapshot, so an edit of the policy in Git changes neither a rollout that already runs nor its health waits. The wait is a trade-off: a longer wait gives a bad change more time to show itself before the rollout goes on, and it makes every round ofmaxConcurrenttargets last that much longer. The two Quickstarts (Docker Compose and Kubernetes on kind) use the demo policies inexamples/quickstart/policies/withhealthWait: 30s,maxConcurrent: 0(unlimited) and no maintenance windows, so that a rollout iscompletedabout a minute after you approve the last wave. Those are demo values. The policies ofgitops-template/config/policies/are production-like:2mfor a configuration,5mfor a version, andmaxConcurrent: 5; their comments say why. When a rollout “hangs” in the Quickstart, look at the health wait first: it is the time a target spends inverifying. - When it ends. The engine checks the rule on every tick (
rollouts.tickInterval, 5 seconds by default, and sooner after agent events), so a target finishes up to one tick after its wait has ended. It can take longer when the agent has not yet reported the health of the new process: the old process that is stopping and the normal start-up of the new one neither fail the target nor let it succeed (see Targets and success). - Failure during the wait. A target whose agent goes offline, reports an error or no longer reports the target
configuration or version fails at once, and the wait does not start again within an attempt.
retry-failedstarts a new attempt with a new wait. - Pause.
pausestops new targets from starting, but a target that is inverifyingkeeps its wait and the engine evaluates it to the end, also while the rollout ispausedorwaiting_approval. The engine does not evaluate a rollout in the statusfailed: its targets stay inverifyinguntil an operator aborts, rolls back or retries the rollout. A rollout that rolls back resets its targets and does not let them finish.
Available from v0.2.0. The rollout page shows the remaining time:
- Where. In the status column of the target list, for every target in
verifying. In the step Health wait of the progress of a version target. On the card of a wave, as the number of its targets in the health wait and the time until the wait that ends last is over. - At 0 the page says “finishing…” until the engine has evaluated the target. The count does not stop while the
rollout is paused, because the engine does not stop either, and it disappears when the target reaches a final status. The
page shows no remaining time for a rollout that the engine does not evaluate (the statuses
failed,rolling_backand the final ones). - Source. The step and the wave card also say where the wait comes from, in a line like
Policy canary-then-all · healthWait 5m · policies/canary-then-all.yaml@9f3c2a17. The policy name links to the policy. Path and commit belong to the policy snapshot of the rollout and can differ from the policy as it is now. The built-in policy has no file, and the line says so instead of the path. - No field for the end. The API has no field for the end of the wait. The browser computes
verifiedAtpluspolicy.healthWaitfrom the targets ofGET /api/v1/rollouts/{id}/targetsand fromGET /api/v1/rollouts/{id}, with the rule above (60 seconds when the snapshot has no value), and counts down with its own clock. A browser clock that differs from the clock of the server shifts the display, not the decision of the engine.
Maintenance windows
- A target may start only while the current time is inside at least one window. Running targets are not interrupted. Without windows a target may start at any time.
- The time zone is the
timezoneof the window.agent(the default) means the value of the agent attribute named bytimezoneAttribute(defaultsite.timezone). If that is missing or not a valid zone, the policy’sdefaultTimezoneis used, then the server settingrollouts.defaultTimezone(defaultUTC), then UTC. Times are computed with the IANA database, including daylight saving time. The zone nameLocalis never accepted, so the result does not depend on the server host. dayslists the weekdays on which a window begins (MontoSun, empty means every day).- If
endis not later thanstart, the window crosses midnight and ends on the next day (22:00to05:00). Withstartequal toendthe window lasts 24 hours fromstart. - Targets outside a window have the status
waiting_window.
Failure threshold, pause and rollback
- The threshold is checked per wave. It is reached if
maxFailed > 0and the number of failed targets is at leastmaxFailed, ormaxFailedPercent > 0andfailed x 100 >= maxFailedPercent x targets of the wave. The wave becomesfailed. If both values are 0,maxFailed: 1applies. If onlymaxFailedPercentis set, only the percentage rule is used. onFailure: pausepauses the rollout (paused, with the reason instatusReasonand the notificationrollout.paused). Running targets are evaluated to the end, no new ones start. The operator choosesresume,retry-failed,abortorrollback.onFailure: rollbackrolls back automatically. All targets inin_progress,verifying,succeededorfailedare reset to their starting state: for a configuration the desired hash returns to thefromhash, for a version the package offer or executor call is repeated with the previous version. They end asrolled_back, and the rollout goes throughrolling_backtorolled_back(notificationrollout.rolled_back).- The rollout status
failed(notificationrollout.failed) arises only when a rollback cannot finish withinapplyTimeout, or when the rollout cannot continue at all, for example when the executor is misconfigured and all startable targets of a wave fail inStartorValidate, or when the policy or the fleet disappeared. - A rollback contradicts Git. The agent shows
driftuntil Git is reverted or a new commit starts the rollout again. Targets that the rollback cancelled without ever starting them are not rolled out again either (see below), otherwise the rejected change would reach the rest of the fleet wave by wave. Agents that moved to another fleet meanwhile are not touched by the rollback and becomeskipped.
When do rollouts arise?
The planner runs on the leader replica after every applied revision and additionally once a minute. Runs are serialized. There is at most one active rollout per fleet and kind.
The target per agent (only assigned collectors without an assignment error):
- Configuration. The assigned hash. An agent is relevant if its desired hash is set and differs from the assigned one. (Agents without a desired hash get the bootstrap rule, see Fleets.)
- Version. The effective target version of the fleet (Fleets: an active choice in
the UI, else
collector.version). An agent is relevant if the fleet’s executor is notnone, an effective target exists (so not on hold) and the agent’s version differs from it.
Exclusions for a relevant agent. The last target of the agent for this target is:
failedorrolled_back. The change failed there. Only a new commit, a new version orretry-failedby an operator starts it again.cancelledin a rollout with the statusaborted,rolling_backorrolled_back. An operator or the failure policy stopped or rolled back on purpose. The change is rolled out again only with a new target.
A target that was cancelled because of superseded is not excluded. A skipped target of a completed
configuration rollout leads to the catch-up (no rollout) described in
Fleets. For versions there is no catch-up. Such agents are
relevant as usual (stragglers or a new rollout, so that maintenance windows and maxConcurrent apply). One exception
applies: an agent whose version target became skipped because of offlineGracePeriod gets a new rollout only after
it has connected again since. Otherwise a permanently switched-off edge machine would trigger a new rollout, with its
rollout.started and rollout.completed notifications, after every completed rollout.
The procedure per fleet and kind:
- Before anything else the planner reconciles the choices made in the UI with Git (rules K4, K1, K2a and K2b from Fleets) and writes the effective target version to the member agents.
- If an active rollout exists whose open targets (
pending,waiting_window,in_progress,verifying) have a different target than their agent’s current target (a new commit with another hash, a changed target version, a new choice in the UI, a hold), that rollout becomessuperseded. Its open targets are cancelled, and for versionsCancelis called on the executor. Continue with step 3. - If an active rollout exists and all its open targets still have the current target, relevant agents that have no target in it yet (stragglers, for example agents that connected later) are added to the last wave. Done.
- Without an active rollout, a new rollout is created with all relevant, not excluded agents. Its
toRefis the current commit or target version, and its targets carry thefromRefandtoRefhashes or versions. If no agent is relevant, no rollout is created.
Origin. Every rollout records what triggered it: gitops (the target comes from Git: a new commit, or
collector.version, also after an operator took a choice back), ui (an operator chose the target version, with the
operator’s name as createdBy and the reason as requestReason) or system. A rollout caused by the UI uses the policy
from update.selection.rolloutPolicy (default rollout.versionPolicy).
Operator actions
| Action | Effect |
|---|---|
pause | Stops starting new targets. |
resume | Continues a paused rollout and acknowledges the failures so far. |
approve | Releases a wave that requires approval. |
abort | Stops the rollout. Targets that have not started are cancelled. |
rollback | Returns started targets to their starting state. |
retry-failed | Resets failed targets and runs them again. |
They are available at /api/v1/rollouts/{id}/actions/<action> and need the permission rollouts:operate, which the roles
operator and admin have.
Live progress per target
For version rollouts the engine derives a progress record for each running target on every tick (the progress
field of the rollout target), stores it and freezes it with the final status of the target.
phase:offered,downloading,verifying,installing,restarting,health_check,rolling_back,health_wait,succeeded,failed,rolled_backorcancelled.source:updater(steps reported by themyriarch-updater),package_status(only the OpAMP package status of the updater is known, for example with an older or simulated updater) orexecutor(webhook, AWX or manual:offered, theninstalling, which means “external update, waiting for version X”, thenhealth_wait).steps[]in the fixed orderoffered,downloading,verifying,installing,restarting,health_check, optionallyrolling_back, thenhealth_wait, each with astatus(pending,running,done,failedorskipped),startedAt,finishedAtanddetail. AlsodownloadPercentfrom the package status, anderror,attempt,fromVersionandtoVersion.
The updater reports its steps as state, not as events: as the health component dev.myriarch.update with the
children version (the offered version, with a leading v), from and step:<name>, each with a status, start time,
status time and last_error. The server assigns the reported attempt to a target when version == "v" + toRef and
the attempt did not begin earlier than 2 minutes before the target (a tolerance for clock differences). Otherwise the
attempt counts as not yet begun (offered). Missed intermediate reports do no harm.
The rollout page lists the targets with running work first (in progress and verifying, then waiting for a maintenance window, failed and rolled back, pending, succeeded, skipped, cancelled) and offers the wave order as an alternative.
The status of the target always has the last word: verifying means the collector reports the version and the health
wait (healthWait) is running (health_wait). A final status resolves all steps. After a rollback on the host the
phase is rolled_back and health_wait is skipped. The read API resolves targets that were ended from outside, by
abort, supersede or rollback, to a consistent final picture. The reporting protocol is described in the
updater protocol.