Designs where the deploy workflow puts things on the Pi
Infrastructure design for U6: single Pi split by directory not port, systemd --user services instead of sudo, a releases/current/shared layout that keeps the customer website outside every atomic switch, and a health check through the existing reverse proxy before pruning anything. Flags that user-level systemd needs enable-linger or the service dies the moment the deploy SSH session closes.
This commit is contained in:
+219
@@ -0,0 +1,219 @@
|
||||
# Infrastructure Design Plan — U6 Deploy Workflow
|
||||
|
||||
## Prerequisite note (deliberate deviation)
|
||||
|
||||
Functional Design and NFR Design are **skipped** for U6 per the execution plan (`unit-of-work.md`,
|
||||
`aidlc-state.md`) — U6 is a Pipeline-type unit (YAML, no C# logic), and its behavioural contract is
|
||||
already fully specified by FR-02, FR-03, FR-04, FR-06, FR-08, FR-20, ASM-01, ASM-03, ASM-04, D-02,
|
||||
D-05, D-09, D-26, D-27 and the U5↔U6 shared-interface constraint in
|
||||
`unit-of-work-dependency.md` ("U5 with U6 — one interface, two files"). Infrastructure Design is the
|
||||
right entry point: it maps that contract onto an actual host layout.
|
||||
|
||||
## Steps
|
||||
|
||||
- [x] Step 1: Analyze design artifacts (requirements.md §§ Decisions/FR/ASM, unit-of-work.md U6,
|
||||
unit-of-work-dependency.md, reference project `.gitea/workflows/` at
|
||||
`K:\Development\SlpSoftware\Projects\SlpSoftware`)
|
||||
- [x] Step 2: Create this plan
|
||||
- [x] Step 3: Generate context-appropriate questions (below)
|
||||
- [x] Step 4: Store plan (this file)
|
||||
- [x] Step 5: Notify user in chat
|
||||
- [x] Step 6: Collect and analyze answers (12/12 answered, no contradictions — Q1/Q10 initially
|
||||
looked like a conflict, same-port two-instance clash, resolved: port assignment is a
|
||||
host/nginx-level fact outside the workflow's concern, so "directory-only" split is consistent)
|
||||
- [x] Step 7: Generate infrastructure design artifacts (`infrastructure-design.md`,
|
||||
`deployment-architecture.md`)
|
||||
- [x] Step 8: Present completion message
|
||||
- [ ] Step 9: Wait for explicit approval
|
||||
- [ ] Step 10: Record approval and update progress
|
||||
|
||||
## What the reference project (`SlpSoftware`) already establishes — not re-asked
|
||||
|
||||
- Reusable `workflow_call` deploy workflow, invoked with `secrets: inherit`
|
||||
- `sshpass` + `scp` in a plain shell step (container SCP actions fail on the Podman runner with a 409
|
||||
attach error) — D-05
|
||||
- A `config` job that turns `env:` values into job outputs, because the `env` context is unavailable
|
||||
inside a reusable workflow's `with:` block
|
||||
- Gitea Actions **variables** for paths/environment names, **secrets** for host/credentials
|
||||
(`PI_MAIN_HOST`, `PI_MAIN_PORT`, `PI_MAIN_USERNAME`, `PI_MAIN_PASSWORD` — reusable per ASM-07)
|
||||
|
||||
None of that needs to be re-decided. What the reference project does **not** cover — because it only
|
||||
ever uploads a static `dist/` folder to a fixed path — is everything below: this unit publishes a
|
||||
running .NET process, must switch releases atomically without a moment of downtime for the
|
||||
customer's website, must back up a database first, and must be able to restart the process and prove
|
||||
it came back healthy.
|
||||
|
||||
---
|
||||
|
||||
## Category: Deployment Environment
|
||||
|
||||
### Question 1
|
||||
Is production the **same Raspberry Pi** as test (different port/directory), or a **second,
|
||||
separate** Pi?
|
||||
|
||||
A) Same Pi, different port and directory per environment
|
||||
B) Separate Pi per environment
|
||||
C) Not decided yet — design for either (parameterize host via Gitea variables/secrets, don't assume)
|
||||
|
||||
[Answer]: A, but only a different directory
|
||||
|
||||
### Question 2
|
||||
What OS architecture is the Pi running, for the `dotnet publish` runtime identifier?
|
||||
|
||||
A) `linux-arm64` (Raspberry Pi 4/5, 64-bit OS)
|
||||
B) `linux-arm` (32-bit OS)
|
||||
C) `linux-x64` (not a Pi / emulated)
|
||||
D) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A, but I might need a windows package later as well for other client API's. For now it is on the Pi
|
||||
|
||||
---
|
||||
|
||||
## Category: Compute Infrastructure
|
||||
|
||||
### Question 3
|
||||
How is the published process managed/restarted on the Pi (ASM-03 assumes systemd)?
|
||||
|
||||
A) systemd service, one unit per environment (e.g. `slpmodularcms-test.service`,
|
||||
`slpmodularcms-production.service`), restarted via `sudo systemctl restart <unit>` over SSH
|
||||
B) systemd service, but restart is a manual/documented step, not automated in the workflow
|
||||
C) Some other process manager (pm2, supervisor, a custom script) — describe below
|
||||
D) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A, but if there are better or more convenient ways to manage .net processes let me know
|
||||
|
||||
### Question 4
|
||||
Passwordless `sudo systemctl restart` for the deploy user — is that already configured, or does it
|
||||
need to be part of the one-time host setup documented in Operations?
|
||||
|
||||
A) Already configured
|
||||
B) Needs to be added — document it as a one-time host-setup step in Operations
|
||||
C) Avoid sudo entirely — run the app as the deploy user's own systemd **user** service
|
||||
(`systemctl --user restart`), no elevation needed
|
||||
D) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: C
|
||||
|
||||
### Question 5
|
||||
Should the workflow **publish** (`dotnet publish -r <RID> --self-contained ...`) framework-dependent
|
||||
or self-contained?
|
||||
|
||||
A) Self-contained (Pi doesn't need a separately-installed .NET runtime; larger artifact)
|
||||
B) Framework-dependent (assumes .NET 10 runtime already installed on the Pi, per ASM-03; smaller
|
||||
artifact)
|
||||
C) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: B
|
||||
|
||||
---
|
||||
|
||||
## Category: Storage Infrastructure
|
||||
|
||||
### Question 6
|
||||
Release directory layout on the host, given `deploy_path` as the single input (FR-02) — how should
|
||||
`releases/`, the atomic `current` pointer, and the persistent `wwwroot/web/` be arranged underneath
|
||||
it?
|
||||
|
||||
A) `{deploy_path}/releases/{timestamp-or-sha}/` per release, `{deploy_path}/current` symlink switched
|
||||
atomically to point at one release, `{deploy_path}/shared/wwwroot-web/` persistent and
|
||||
symlinked into each new release as `wwwroot/web`
|
||||
B) Same as A, but the persistent website content lives at a path **outside** `deploy_path` entirely
|
||||
(e.g. a fixed host path unrelated to the deploy target), to make it structurally impossible for
|
||||
any future workflow change to prune it by mistake
|
||||
C) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
### Question 7
|
||||
How many previous releases should be retained after pruning (FR-06: "at least the previous one")?
|
||||
|
||||
A) 2 (current + 1 previous)
|
||||
B) 3 (current + 2 previous)
|
||||
C) 5
|
||||
D) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
### Question 8
|
||||
Database backup before production deploy (FR-20) — is it automatable from the Gitea runner over SSH
|
||||
(ASM-04 assumes the database is reachable from the *application*, not necessarily from the runner)?
|
||||
|
||||
A) Automatable: the deploy workflow runs a backup command over SSH on the Pi itself (e.g.
|
||||
`sqlcmd`/`sqlpackage` or a SQL Server backup script already on the host), before the atomic switch
|
||||
B) Not automatable from the runner: emit a clear, verifiable manual step in the deployment
|
||||
instructions instead (FR-20's documented fallback), and the workflow does not attempt it
|
||||
C) Not sure yet — design the workflow with an explicit backup step that can be a placeholder script
|
||||
the user fills in during Operations
|
||||
D) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
---
|
||||
|
||||
## Category: Networking Infrastructure
|
||||
|
||||
### Question 9
|
||||
Post-switch health verification (FR-06 implies confirming the restart succeeded) — how should the
|
||||
workflow check `/health`?
|
||||
|
||||
A) `curl` from the Gitea runner against the environment's public HTTPS URL
|
||||
B) `curl` executed **on the Pi itself** over the same SSH connection, against `localhost:<port>`
|
||||
(works even if the public URL is only reachable through a reverse proxy not yet configured, and
|
||||
doesn't depend on external DNS/TLS)
|
||||
C) Both — localhost check on the Pi first, then a public-URL check as a secondary confirmation
|
||||
D) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
### Question 10
|
||||
Is there an existing reverse proxy (nginx, as in the reference project) in front of this
|
||||
application, or does Kestrel serve requests directly?
|
||||
|
||||
A) Kestrel serves directly on a fixed port per environment (e.g. `5000` test, `5001` production) —
|
||||
no reverse proxy for this app
|
||||
B) Existing nginx reverse proxy in front, forwarding to Kestrel on a local port
|
||||
C) Not decided yet / out of scope for this workflow — document the assumption and let Operations
|
||||
confirm the real topology
|
||||
D) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: B
|
||||
|
||||
---
|
||||
|
||||
## Category: Monitoring Infrastructure
|
||||
|
||||
### Question 11
|
||||
If the post-switch health check (Question 9) fails, what should the workflow do?
|
||||
|
||||
A) Fail the job loudly (red pipeline) but leave the atomic switch as-is — no automatic rollback;
|
||||
manual rollback via Operations' documented procedure
|
||||
B) Automatically re-point `current` back to the previous release and restart, then fail the job
|
||||
C) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
---
|
||||
|
||||
## Category: Shared Infrastructure
|
||||
|
||||
### Question 12
|
||||
Gitea Actions variable/secret naming for the deploy target — reuse the reference project's
|
||||
`PI_MAIN_*` secrets (ASM-07), or introduce project-specific names since this repo has its own
|
||||
test **and** production paths on the same or a different host?
|
||||
|
||||
A) Reuse `PI_MAIN_HOST` / `PI_MAIN_PORT` / `PI_MAIN_USERNAME` / `PI_MAIN_PASSWORD` as-is (same
|
||||
values already configured for the reference repo, since it's the same Pi)
|
||||
B) New, project-scoped secrets (e.g. `SLPMODULARCMS_PI_HOST`, etc.) even if the values happen to be
|
||||
identical today, so the two repositories' deploy targets can diverge independently later
|
||||
C) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]: A, but rename PI_MAIN_HOST to PI_MAIN_ADDRESS
|
||||
|
||||
---
|
||||
|
||||
## Self-Validation
|
||||
|
||||
- [x] Every question uses lettered multiple-choice options
|
||||
- [x] Every question ends in an explicit `Other` option
|
||||
- [x] Every question has an `[Answer]:` tag
|
||||
- [x] No process/approval question is included here (those stay in chat)
|
||||
Reference in New Issue
Block a user