mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-10-26 05:30:18 +00:00
d07cfc34d1
* docs(tutorials): refresh the create and approve a Blueprint walkthrough Walk the 0.98.0 Blueprint editor, rollout preview, and detail sheet end to end and update the steps to match: the empty-catalog button, the staged label tags, the GitOps authority and evidence block, the approval state shown in the sheet, the audit rows each action leaves, and the real port-conflict recovery. Permissions are described as the stack and node permissions they are, and all seven screenshots are retaken. * docs(tutorials): refresh the connect a Git source walkthrough Walk the 0.98.0 Git Source sheet from an unlinked stack to an applied commit and update the steps to match: the toolbar and Anatomy entry points, the Custom CA field, the four apply behaviors, the first-pull Add row in the change plan, the Accepted answer and stage row, and the exact ref error. Add the rule that a linked stack must be detached before its repository or ref changes. All six screenshots are retaken. * docs(tutorials): refresh the catch and fix drift walkthrough Walk a real image drift through detection, re-check, redeploy, and dismiss on 0.98.0 and update the text to match: the redeploy confirmation's consequence label, the health gate entry on the Activity timeline, and the Dismiss toast with Undo. Dismissing and restoring a finding is noted as needing the same stack permission as redeploying. All seven screenshots are retaken. * docs(tutorials): refresh the move a Blueprint to a new node walkthrough Walk a real migration between a retiring proxy node and the hub on 0.98.0 and update the tutorial to match: the Placement control tab, the toasts on pin and cordon, Cordon in the node details sheet, the Needs confirmation status block before the rollout is confirmed, the transient Completion unknown state after it, and the audit rows including the stack the apply creates. All eleven screenshots are retaken. * docs(tutorials): refresh the missing required variable guardrail walkthrough Walk the guardrail on 0.98.0 from a clean deploy to a refused Save & Deploy and Update and back to a healthy update, and update the steps to match: the toast and the Update failed chip that carry the message, the readiness dialog wording, the Environment tab's missing count, the Deploy Guardrails section as it reads now, and the activity entries. Adds the screenshots for the blocked Save & Deploy and the failure chip, and retakes the rest. * docs(tutorials): refresh the deploy from CI webhook walkthrough Run the webhook end to end on 0.98.0, including a signed trigger and the newline-signed request the troubleshooting section describes, and update the text to match: the Action list, including Git source sync with a pointer to the Webhooks page, the new form, callout, and Recent executions states, and the uptime and log wording on the redeployed stack. All five screenshots are retaken with the trigger URL and secret replaced by generic values. * docs(tutorials): refresh the enroll a remote node walkthrough Enroll a real Pilot agent on 0.98.0 and update the steps to match: the Pilot enrollment dialog and its Done button, the tunnel states after the agent starts, the switcher and Fleet cards beside the nodes already enrolled, the pencil that opens Edit node, and the hub-only view list, which now includes GitOps. The example compose directory is changed to a dedicated media path, and all six screenshots are retaken with host addresses, seal pins, and the token replaced by generic values. * docs(tutorials): refresh the Custom OIDC single sign-on walkthrough Run the whole flow on 0.98.0 against a real Keycloak 26 over a private CA and update the text to match: the login button is labelled with the Display Name rather than a fixed phrase, the Active badge and header counts after Save, the stored-secret state after Save, the Log Out path, what happens when the browser is already signed in at the identity provider, and the full list of fields to leave blank. All six screenshots are retaken with the issuer shown as a generic host. * docs(tutorials): refresh the auto-patch with a label walkthrough Run the label-driven update schedule twice on 0.98.0 and update the text to match: the Labels submenu states, the Current matches panel that lists every node, the run time and per-node details in the Execution history, the second "all images up to date" run, and the node's Updates chip as the proof the pending update cleared. The Update board example is replaced because other stacks on a real fleet keep it from ever being empty. All eight screenshots are retaken.
125 lines
10 KiB
Plaintext
125 lines
10 KiB
Plaintext
---
|
|
title: Catch and Fix a Container That's Drifted From Its Compose File
|
|
sidebarTitle: Catch and fix drift
|
|
description: Read a real Drift finding after a manual out-of-band container change, then redeploy from the finding to bring the running state back in line with the compose file.
|
|
---
|
|
|
|
Say an on-call engineer hotfixes a container directly on the host during an incident: change the image tag, run `docker compose up -d`, done, the site's back up. Nobody thinks to also make the same edit through Sencho, so the compose file it deploys from tomorrow never learns about tonight's fix. This walks through catching exactly that: read a real Drift finding after an out-of-band container change, confirm what's different, and redeploy from the finding to bring the running state back in line with the compose file.
|
|
|
|
This tutorial covers the runtime drift detection described on the Drift feature page: the status badge, the findings list, the drift ledger, and resolving a finding with the redeploy button on the finding itself. It does not cover Blueprint's separate policy-based drift mode (observe/suggest/enforce), which reconciles drift on Blueprint-deployed stacks automatically instead of leaving it for you to redeploy; see [Blueprint Model](/features/blueprint-model). It also does not cover network-attachment drift findings, which reuse this same mechanism but compare against the Networking tab instead of an image; see [Compose Networking](/features/compose-networking).
|
|
|
|
## Prerequisites
|
|
|
|
- Any role that can view a stack, including the read-only **viewer** and **auditor** roles, can open the Drift tab and click **re-check**. Actually resolving drift means redeploying, which needs the `stack:deploy` permission for this stack (the same permission Start, Stop, Restart, and Update already require); the redeploy button does not appear without it, and dismissing or restoring a finding needs the same permission.
|
|
- The stack must have been deployed through Sencho at least once. Drift's "since last deploy" signal has nothing to compare against until then, and shows **No deploy baseline** instead of a useful state.
|
|
- No plan requirement and nothing to turn on. Drift detection runs automatically as soon as a stack has a compose file and Docker is reachable on the node.
|
|
- Shell or SSH access to the host, if you want to reproduce this tutorial's own out-of-band change yourself. Any real manual Docker operation that changes a running container without going back through Sencho works just as well.
|
|
|
|
This tutorial uses a small single-service stack called `landing-page`:
|
|
|
|
```yaml
|
|
services:
|
|
app:
|
|
image: nginx:1.27-alpine
|
|
restart: always
|
|
ports:
|
|
- "8096:80"
|
|
```
|
|
|
|
Deploy this stack (or adapt an existing one) before continuing.
|
|
|
|
<Steps>
|
|
<Step title="Open the Drift tab and confirm the baseline">
|
|
Click the stack in the sidebar to open it, then switch to the **Drift** tab in the right-hand panel (scroll the tab row if it doesn't fit). Right after a deploy through Sencho, the status reads **in sync** ("Runtime matches the compose file.") and the card below it reads **matches last deploy** ("The compose source is unchanged since the last deploy."). This is the state the rest of this tutorial gets you back to.
|
|
|
|
<Frame>
|
|
<img src="/images/tutorials/resolve-stack-drift/drift-baseline-in-sync.png" alt="The Drift tab showing In sync status (Runtime matches the compose file) and Matches last deploy below it, with no findings or drift history." />
|
|
</Frame>
|
|
</Step>
|
|
|
|
<Step title="Make an out-of-band change on the host">
|
|
Simulate the incident hotfix: on the host, point the compose file at a different image tag, bring it up directly with Compose, then revert the file without redeploying through Sencho.
|
|
|
|
```bash
|
|
cd /path/to/landing-page
|
|
sed -i 's/nginx:1.27-alpine/nginx:1.29-alpine/' compose.yaml
|
|
docker compose up -d
|
|
sed -i 's/nginx:1.29-alpine/nginx:1.27-alpine/' compose.yaml
|
|
```
|
|
|
|
The running container is now on `nginx:1.29-alpine`, but the compose file on disk is back to declaring `nginx:1.27-alpine`, exactly as if someone had made a quick fix and never told Sencho about it.
|
|
</Step>
|
|
|
|
<Step title="Read the drift finding">
|
|
Reopen the Drift tab. Just opening it re-runs the comparison, so the status flips to **drifted · 1 finding** ("Runtime differs from the compose file."), while **matches last deploy** stays exactly as it was: the file itself never changed, only what's running under it. Under **Findings**, the `app` service shows an **image** finding with the expected and actual images side by side, and a line underneath saying what fixes it. Because the running image is newer than the one the Compose file names, the line offers two ways out: update Compose to keep the newer image (**Open stack editor**), or redeploy to go back to the compose image.
|
|
|
|
<Frame>
|
|
<img src="/images/tutorials/resolve-stack-drift/drift-detected-image-finding.png" alt="The Drift tab showing Drifted status with 1 finding, Matches last deploy still green below it, and a Findings section with an app image finding: compose nginx:1.27-alpine to running nginx:1.29-alpine." />
|
|
</Frame>
|
|
|
|
This is the moment the two signals' independence actually matters: a stack can be drifted at runtime while its file is unchanged, or the reverse. Here it's drifted with an unchanged file, because the fix happened directly against Docker, not through a compose edit.
|
|
</Step>
|
|
|
|
<Step title="Log the finding with re-check">
|
|
Click **re-check**. A **Drift history** section appears with the same finding marked **OPEN** and a detected timestamp, and the header now reads "checked just now." The status badge above it, though, still says **drifted**.
|
|
|
|
<Frame>
|
|
<img src="/images/tutorials/resolve-stack-drift/drift-recheck-still-drifted.png" alt="The Drift tab still showing Drifted status after clicking re-check, with a new Drift history section showing an app image finding marked OPEN, detected just now." />
|
|
</Frame>
|
|
|
|
Re-check is a read plus a ledger write: it records the finding into the stack's history and logs a **Drift detected** entry to the Activity tab, but it never touches the running container. Nothing about the actual deployment has changed yet.
|
|
</Step>
|
|
|
|
<Step title="Resolve it: redeploy from the finding">
|
|
This tutorial brings the container back to what the Compose file says, so open the **...** menu on the finding and choose **Redeploy to compose image**. It sits in the menu rather than leading because it moves the service back to an older image; for a finding where the running image is not newer, **Redeploy to match compose** is the button on the row.
|
|
|
|
<Frame>
|
|
<img src="/images/tutorials/resolve-stack-drift/drift-redeploy-menu.png" alt="The Drift tab with the app image finding and its overflow menu open, showing Redeploy to compose image." />
|
|
</Frame>
|
|
|
|
A **Redeploy landing-page** confirmation names the consequence: "The running app image is newer than the one compose names. Redeploying moves it back to the older compose image." Its footer reads **Recreates differing services**. Click **Redeploy**.
|
|
|
|
<Frame>
|
|
<img src="/images/tutorials/resolve-stack-drift/drift-redeploy-confirm.png" alt="The Redeploy landing-page confirmation, explaining that redeploying moves the running app image back to the older compose image, with a Recreates differing services label and Cancel and Redeploy buttons." />
|
|
</Frame>
|
|
|
|
Sencho runs the same deploy as the editor's **Deploy** button, with the same progress view, and recreates the `app` container from the compose file, which puts it back on `nginx:1.27-alpine`. If you would rather keep the newer image, click **Open stack editor** instead, change the image in the Compose file to match, and save; the finding clears the same way.
|
|
</Step>
|
|
</Steps>
|
|
|
|
## Verify it worked
|
|
|
|
Check from two independent surfaces.
|
|
|
|
Back on the **Drift** tab, the status returns to **in sync**, and the drift history entry that was **OPEN** a moment ago now reads **RESOLVED**, with both a detected and a resolved timestamp:
|
|
|
|
<Frame>
|
|
<img src="/images/tutorials/resolve-stack-drift/drift-resolved-in-sync.png" alt="The Drift tab back to In sync status, with the drift history entry now marked RESOLVED, showing both a detected and a resolved timestamp." />
|
|
</Frame>
|
|
|
|
On the **Activity** tab, which lists most-recent-first, the new **landing-page deployed** entry (with a health gate entry above it once the container passes its check) sits above a **Drift resolved** entry, which sits above the **Drift detected** entry from earlier in this tutorial: the finding was detected, then resolved by the redeploy.
|
|
|
|
<Frame>
|
|
<img src="/images/tutorials/resolve-stack-drift/activity-drift-resolved.png" alt="The Activity tab showing, in order: landing-page health gate passed, landing-page deployed by admin, Drift resolved on landing-page: 1 finding cleared, Drift detected on landing-page: 1 new finding, and the earlier health gate and landing-page deployed entries." />
|
|
</Frame>
|
|
|
|
## If something goes wrong
|
|
|
|
**Clicking re-check doesn't fix anything, and the status still says drifted.** That's not a bug, it's the read-only design: re-check only reconciles the ledger (recording what it currently sees, as in the screenshot above) so the finding shows up in history and on the Activity timeline. It never changes what Docker is running. If you want the drifted container gone, redeploy it from the finding as in the last step above (or **Save & Deploy** from the compose editor); that's the only action that actually reconciles the runtime with the compose file.
|
|
|
|
**A finding is in the way and you know it is expected.** Click **Dismiss** on the row to set it aside for the team until it changes. A toast reads `Dismissed. It returns if it changes.` with an **Undo** button. The finding moves under **1 dismissed**, where **Restore** brings it back, and the status keeps reading **drifted** because dismissing a finding does not change what is running.
|
|
|
|
## Related
|
|
|
|
<CardGroup cols={2}>
|
|
<Card title="Stack Drift" icon="magnifying-glass-chart" href="/features/stack-drift">
|
|
Full mechanics: every finding type, image comparison rules, port ranges, the drift ledger, and troubleshooting.
|
|
</Card>
|
|
<Card title="Stack Activity" icon="clock-rotate-left" href="/features/stack-activity">
|
|
Drift detected and resolved events sit in the same timeline as deploys and restarts.
|
|
</Card>
|
|
<Card title="Blueprint Model" icon="drafting-compass" href="/features/blueprint-model">
|
|
Blueprints manage drift differently, with a separate observe/suggest/enforce policy mode.
|
|
</Card>
|
|
</CardGroup>
|