release(main): 集成 nginx 移除里程碑

This commit is contained in:
Cheng Zhou
2026-03-30 21:03:35 +08:00
parent 956c47218e
commit 5677f6823a
22 changed files with 287 additions and 160 deletions
+4 -4
View File
@@ -10,10 +10,10 @@
- [ ] `cd frontend && npm run build`
## 1.1 腾讯云私有镜像校验
- [ ] `dev` 推送后,`<IP>:5000/ctms/ctms-backend``ctms-nginx``dev-latest` / `dev-<short_sha>` 标签已出现在私有仓库
- [ ] `release` 推送后,`<IP>:5000/ctms/ctms-backend``ctms-nginx``rc-release` / `rc-<short_sha>` 标签已出现在私有仓库
- [ ] `release/*` 推送后,`<IP>:5000/ctms/ctms-backend``ctms-nginx``rc-<release-branch>` / `rc-<short_sha>` 标签已出现在私有仓库
- [ ] `main` 推送后,`<IP>:5000/ctms/ctms-backend``ctms-nginx``latest` / `sha-<short_sha>` 标签已出现在私有仓库
- [ ] `dev` 推送后,`<IP>:5000/ctms/ctms-backend``ctms-frontend``dev-latest` / `dev-<short_sha>` 标签已出现在私有仓库
- [ ] `release` 推送后,`<IP>:5000/ctms/ctms-backend``ctms-frontend``rc-release` / `rc-<short_sha>` 标签已出现在私有仓库
- [ ] `release/*` 推送后,`<IP>:5000/ctms/ctms-backend``ctms-frontend``rc-<release-branch>` / `rc-<short_sha>` 标签已出现在私有仓库
- [ ] `main` 推送后,`<IP>:5000/ctms/ctms-backend``ctms-frontend``latest` / `sha-<short_sha>` 标签已出现在私有仓库
- [ ] GitHub Actions 已成功 SSH 登录腾讯云服务器并执行远程构建脚本
- [ ] 部署机已执行 `docker login <IP>:5000`,且使用的是私有仓库 `htpasswd` 账号密码
@@ -4,24 +4,25 @@
**Decisions**
- Keep single-host `docker-compose` deployment.
- Convert the frontend to a static build served directly by Nginx.
- Keep the frontend as an independently deployable runtime image.
- Keep PostgreSQL and the existing `pg_data` persistence model.
- Keep one backend container and remove development-only runtime behavior.
- Update the existing `docker-compose.yaml` in place instead of introducing a separate production file.
- Leave public ingress to an external reverse proxy outside this repository.
**Architecture**
- `db` remains a long-lived PostgreSQL service with the existing bind-mounted data directory.
- `backend` runs from the backend image default command and exposes only the application port needed inside the compose network.
- `nginx` becomes the public entrypoint. It serves the built frontend assets and proxies `/api/` and `/health` to the backend service.
- The frontend is no longer a long-running service in production. Its build artifacts are produced during the image build and copied into the Nginx image.
- `backend` runs from the backend image default command and exposes port `8000`.
- `frontend` runs from its own image and serves the SPA on a dedicated frontend port.
- An external reverse proxy becomes the public entrypoint and routes browser traffic to `frontend` and API traffic to `backend`.
**Cleanup Scope**
- Remove development-only compose services such as toolbox/init containers.
- Remove development-only backend startup overrides such as `debugpy` and `uvicorn --reload`.
- Remove development-only frontend runtime behavior such as `npm run dev`, bind mounts, and exposed Vite ports.
- Remove development-only frontend runtime behavior such as bind mounts and hot-reload-only startup behavior.
- Keep data persistence and core service names stable where practical to limit operational change.
**Operational Notes**
- Updating `docker-compose.yaml` in place means local development will no longer use the previous hot-reload setup by default.
- Frontend SPA routing must be handled by Nginx with `try_files`.
- Verification is done through `docker compose config`, image builds, container startup, and HTTP health checks.
- Frontend SPA routing and same-origin `/api` behavior must be handled by the external reverse proxy.
- Verification is done through `docker compose config`, image builds, container startup, and direct HTTP health checks.
@@ -2,11 +2,11 @@
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Convert the repository's only `docker-compose.yaml` from development deployment to production deployment using static frontend assets served by Nginx.
**Goal:** Convert the repository's only `docker-compose.yaml` from development deployment to production deployment using standalone frontend and backend runtime images.
**Architecture:** The backend runs as a production FastAPI container, the frontend is built into static assets during image build, and Nginx serves the SPA while reverse proxying backend routes. PostgreSQL remains persistent through the existing `pg_data` bind mount.
**Architecture:** The backend runs as a production FastAPI container, the frontend is built into a standalone runtime image that serves the SPA itself, and an external reverse proxy routes browser and API traffic to the correct container. PostgreSQL remains persistent through the existing `pg_data` bind mount.
**Tech Stack:** Docker Compose, FastAPI, Vue/Vite, Nginx, PostgreSQL
**Tech Stack:** Docker Compose, FastAPI, Vue/Vite, Node.js, PostgreSQL
---
@@ -19,27 +19,27 @@
Update the Dockerfile so a Node build stage runs dependency install and `npm run build`, then a lightweight stage exposes the built `dist` directory for downstream use instead of running `npm run dev`.
**Step 2: Keep the output suitable for Nginx consumption**
**Step 2: Keep the output suitable for frontend runtime consumption**
Ensure the final stage stores frontend assets in a predictable directory such as `/usr/share/nginx/html`.
Ensure the final stage stores frontend assets in a predictable directory and starts a static server on a stable port.
**Step 3: Verify the image definition is syntactically valid**
Run: `docker compose config`
Expected: compose renders successfully with no Dockerfile-related errors.
### Task 2: Productionize Nginx Runtime
### Task 2: Productionize Frontend Runtime
**Files:**
- Modify: `nginx/nginx.conf`
- Modify: `frontend/Dockerfile`
**Step 1: Add static frontend serving**
Set the web root to the built frontend assets and add `try_files $uri $uri/ /index.html;` for SPA routing.
Use the runtime image to expose a frontend port and serve the compiled SPA directly.
**Step 2: Keep backend reverse proxy behavior**
**Step 2: Leave reverse proxy behavior to external infrastructure**
Preserve `/api/` and `/health` proxying to `backend:8000` with standard forwarded headers.
Do not embed repository-owned reverse proxy configuration; document that `/` routes to `frontend` and `/api` plus `/health` route to `backend`.
**Step 3: Verify config structure through compose-backed startup**
@@ -55,13 +55,13 @@ Expected: compose renders successfully.
Delete `frontend`, `frontend-init`, and `backend-init`, and remove bind mounts, debug ports, and development commands that are only useful for hot reload or scaffolding.
**Step 2: Wire backend and nginx for production**
**Step 2: Wire backend and frontend for production**
Allow the backend to use its Dockerfile default command, keep the database dependency, and expose only the ports needed for the production stack.
**Step 3: Build frontend assets into nginx**
**Step 3: Add standalone frontend service**
Point the Nginx service build to a production image definition that includes the built frontend output.
Point the frontend service build to `frontend/Dockerfile` and give it a registry-backed image reference.
**Step 4: Verify rendered compose**
@@ -79,12 +79,12 @@ Document that `docker-compose.yaml` is now the production deployment entrypoint.
**Step 2: Add minimal verification commands**
Document `docker compose up -d --build`, plus health checks for `/` and `/health`.
Document `docker compose up -d --build`, plus direct health checks for the frontend and backend containers.
**Step 3: Verify behavior**
Run: `docker compose up -d --build`
Expected: `db`, `backend`, and `nginx` start successfully.
Expected: `db`, `backend`, and `frontend` start successfully.
### Task 5: End-to-End Verification
@@ -103,8 +103,8 @@ Expected: PASS
**Step 3: Check HTTP endpoints**
Run: `curl -i http://127.0.0.1/`
Run: `curl -i http://127.0.0.1:4173/`
Expected: `200 OK` and HTML payload
Run: `curl -i http://127.0.0.1/health`
Run: `curl -i http://127.0.0.1:8000/health`
Expected: successful backend health response
@@ -25,7 +25,7 @@
**Operational Flow**
1. Start PostgreSQL with an empty data directory.
2. Run the one-shot backend initialization command.
3. Start long-lived backend and Nginx services.
3. Start long-lived backend and frontend services behind the external reverse proxy.
4. Log in with `admin@huapont.cn / admin123`, then change the password.
**Non-Goals**
@@ -163,7 +163,7 @@ Expected: migrations run successfully and the protected admin is ensured.
**Step 4: Validate service health**
Run: `docker compose up -d`
Expected: `db`, `backend`, and `nginx` start successfully.
Expected: `db`, `backend`, and `frontend` start successfully.
Run: `curl -i http://127.0.0.1/health`
Run: `curl -i http://127.0.0.1:8000/health`
Expected: `200 OK`
@@ -0,0 +1,21 @@
# Remove In-Repo Nginx Design
**Goal:** Remove all in-repository Nginx runtime, build, publish, and documentation responsibilities while keeping the frontend as an independently deployable image behind an external reverse proxy.
**Decisions**
- Remove the `nginx/` directory and every repository-owned Nginx configuration reference.
- Keep `docker-compose.yaml` as the deployment entrypoint, but change the production topology to `frontend + backend + db`.
- Keep `frontend` as a standalone runtime image that serves the built SPA directly.
- Keep `backend` as the only API container and let the external reverse proxy route `/api` and `/health` to it.
- Update historical docs so they describe the current topology instead of preserving obsolete Nginx-based guidance.
**Architecture**
- `db` remains the persistent PostgreSQL service.
- `backend` remains the FastAPI service exposed on port `8000`.
- `frontend` becomes a standalone container image built from `frontend/Dockerfile` and serves the compiled SPA on its own port.
- The external reverse proxy is now the only public entrypoint and is responsible for routing browser traffic to `frontend` and API traffic to `backend`.
**Operational Notes**
- The frontend still assumes same-origin API access, so the external proxy must route `/api/*` and `/health` to `backend`.
- The repository publish flow should only build and push `ctms-backend` and `ctms-frontend`.
- Verification should prove that no runtime, CI, or document references to repository-managed Nginx remain.
@@ -0,0 +1,114 @@
# Remove In-Repo Nginx Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Remove repository-managed Nginx and replace it with a standalone frontend image plus backend image that are intended to sit behind an external reverse proxy.
**Architecture:** The compose stack runs `db`, `backend`, and `frontend`. The frontend image builds the Vite SPA and serves it directly from its own container, while the backend continues serving the API on port `8000`. External infrastructure performs the only reverse proxy routing.
**Tech Stack:** Docker Compose, FastAPI, Vue/Vite, Node.js, PostgreSQL, GitHub Actions
---
### Task 1: Define the frontend runtime image
**Files:**
- Modify: `frontend/Dockerfile`
**Step 1: Keep the multi-stage frontend build**
Retain the existing build stage so `npm ci` and `npm run build` still produce `dist`.
**Step 2: Turn the final image into a runnable frontend container**
Use a lightweight runtime image that copies `dist`, exposes a stable frontend port, and starts a static file server suitable for SPA delivery.
**Step 3: Verify Dockerfile shape**
Run: `sed -n '1,200p' frontend/Dockerfile`
Expected: final stage exposes a frontend port and contains a runnable `CMD`.
### Task 2: Replace nginx topology with frontend topology
**Files:**
- Modify: `docker-compose.yaml`
**Step 1: Remove the `nginx` service**
Delete the Nginx image, build, ports, and volume mounts.
**Step 2: Add a `frontend` service**
Build from `frontend/Dockerfile`, define a private-registry-backed `FRONTEND_IMAGE` default, and expose the frontend runtime port.
**Step 3: Verify rendered compose**
Run: `docker compose config`
Expected: only `db`, `backend`, `backend-init`, and `frontend` render.
### Task 3: Update image publishing
**Files:**
- Modify: `scripts/build-and-push-registry.sh`
- Modify: `.github/workflows/publish-images.yml`
**Step 1: Publish `frontend` instead of `nginx`**
Build and push `${REGISTRY_HOST}/ctms/ctms-frontend` alongside `${REGISTRY_HOST}/ctms/ctms-backend`.
**Step 2: Keep branch-based tag logic unchanged**
Preserve the existing `dev`, `release`, `release/*`, and `main` tag conventions.
**Step 3: Verify script syntax**
Run: `bash -n scripts/build-and-push-registry.sh`
Expected: PASS
### Task 4: Rewrite docs to match the new topology
**Files:**
- Modify: `README.md`
- Modify: `docs/guides/release-checklist.md`
- Modify: `docs/plans/2026-03-27-production-compose-design.md`
- Modify: `docs/plans/2026-03-27-production-compose-implementation.md`
- Modify: `docs/plans/2026-03-27-production-init-design.md`
- Modify: `docs/plans/2026-03-27-production-init-implementation.md`
- Modify: `docs/plans/2026-03-30-tcloud-private-registry-design.md`
- Modify: `docs/plans/2026-03-30-tcloud-private-registry.md`
**Step 1: Remove repository-managed Nginx language**
Replace every statement that says Nginx is the public entrypoint, serves the SPA, or is a published runtime image.
**Step 2: Document the external reverse proxy expectation**
Describe the runtime as frontend and backend containers behind an external proxy.
**Step 3: Verify search results**
Run: `rg -n --hidden -g '!/.git' -S "nginx|ctms-nginx|NGINX_IMAGE|Nginx" README.md docker-compose.yaml .github scripts docs`
Expected: no active repository-managed Nginx references remain.
### Task 5: Remove obsolete nginx files and verify end-to-end cleanup
**Files:**
- Delete: `nginx/Dockerfile`
- Delete: `nginx/nginx.conf`
**Step 1: Delete the obsolete files**
Remove the Nginx directory because nothing in the repository should depend on it anymore.
**Step 2: Validate compose and script syntax**
Run: `docker compose config`
Expected: PASS
Run: `bash -n scripts/build-and-push-registry.sh`
Expected: PASS
**Step 3: Validate frontend and backend image references**
Run: `sed -n '1,160p' README.md`
Expected: deployment instructions mention `frontend` and `backend`, not `nginx`.
@@ -1,9 +1,9 @@
# Tencent Cloud Private Registry Design
**Goal:** Add CI that automatically builds and pushes private `backend` and `nginx` images to a self-hosted Docker Registry running on a Tencent Cloud CVM, with `dev` publishing test tags, `release` and `release/*` publishing release-candidate tags, and `main` publishing formal tags.
**Goal:** Add CI that automatically builds and pushes private `backend` and `frontend` images to a self-hosted Docker Registry running on a Tencent Cloud CVM, with `dev` publishing test tags, `release` and `release/*` publishing release-candidate tags, and `main` publishing formal tags.
**Decisions**
- Publish only two runtime images: `<registry-host>/ctms/ctms-backend` and `<registry-host>/ctms/ctms-nginx`.
- Publish only two runtime images: `<registry-host>/ctms/ctms-backend` and `<registry-host>/ctms/ctms-frontend`.
- Keep the private registry on the Tencent Cloud CVM as a self-hosted `registry:2` instance protected by `htpasswd`.
- Trigger automation on pushes to `dev`, `release`, `release/*`, and `main`.
- Use GitHub Actions only as the orchestrator. Actual Docker build and push happen on the Tencent Cloud server over SSH.
@@ -18,34 +18,33 @@
- A new GitHub Actions workflow triggers on pushes to `dev`, `release`, `release/*`, and `main`.
- The workflow authenticates to the Tencent Cloud server using SSH credentials stored in GitHub Secrets.
- The workflow syncs the repository contents to a fixed build directory such as `/opt/ctms-build/<repo>`.
- A server-local script logs in to the private registry, builds `backend` and `nginx`, applies branch-specific tags, and pushes the images to the registry.
- A server-local script logs in to the private registry, builds `backend` and `frontend`, applies branch-specific tags, and pushes the images to the registry.
**Registry Naming**
- Backend image: `<registry-host>/ctms/ctms-backend`
- Nginx image: `<registry-host>/ctms/ctms-nginx`
- `frontend` is intentionally not published because the current runtime topology already builds the frontend assets into the `nginx` image.
- Frontend image: `<registry-host>/ctms/ctms-frontend`
**Tagging Strategy**
- `dev` branch pushes publish:
- `<registry-host>/ctms/ctms-backend:dev-latest`
- `<registry-host>/ctms/ctms-backend:dev-<short_sha>`
- `<registry-host>/ctms/ctms-nginx:dev-latest`
- `<registry-host>/ctms/ctms-nginx:dev-<short_sha>`
- `<registry-host>/ctms/ctms-frontend:dev-latest`
- `<registry-host>/ctms/ctms-frontend:dev-<short_sha>`
- `release` branch pushes publish:
- `<registry-host>/ctms/ctms-backend:rc-release`
- `<registry-host>/ctms/ctms-backend:rc-<short_sha>`
- `<registry-host>/ctms/ctms-nginx:rc-release`
- `<registry-host>/ctms/ctms-nginx:rc-<short_sha>`
- `<registry-host>/ctms/ctms-frontend:rc-release`
- `<registry-host>/ctms/ctms-frontend:rc-<short_sha>`
- `release/*` branch pushes publish:
- `<registry-host>/ctms/ctms-backend:rc-<release-branch>`
- `<registry-host>/ctms/ctms-backend:rc-<short_sha>`
- `<registry-host>/ctms/ctms-nginx:rc-<release-branch>`
- `<registry-host>/ctms/ctms-nginx:rc-<short_sha>`
- `<registry-host>/ctms/ctms-frontend:rc-<release-branch>`
- `<registry-host>/ctms/ctms-frontend:rc-<short_sha>`
- `main` branch pushes publish:
- `<registry-host>/ctms/ctms-backend:latest`
- `<registry-host>/ctms/ctms-backend:sha-<short_sha>`
- `<registry-host>/ctms/ctms-nginx:latest`
- `<registry-host>/ctms/ctms-nginx:sha-<short_sha>`
- `<registry-host>/ctms/ctms-frontend:latest`
- `<registry-host>/ctms/ctms-frontend:sha-<short_sha>`
**Server Requirements**
- Docker installed on the Tencent Cloud server.
@@ -64,7 +63,7 @@
- `REGISTRY_PASSWORD`
**Compose Integration**
- `docker-compose.yaml` should define `image:` for `backend`, `backend-init`, and `nginx`, with defaults based on private registry variables such as `REGISTRY_HOST`.
- `docker-compose.yaml` should define `image:` for `backend`, `backend-init`, and `frontend`, with defaults based on private registry variables such as `REGISTRY_HOST`.
- Existing `build:` blocks stay in place so local `docker compose up -d --build` continues to work.
- Deployment hosts can export `REGISTRY_HOST`, then run `docker login`, `docker compose pull`, `docker compose run --rm backend-init`, and `docker compose up -d`.
@@ -2,11 +2,11 @@
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Add CI that triggers remote builds on a Tencent Cloud CVM and pushes private `backend` and `nginx` images into a self-hosted Docker Registry protected by `htpasswd`, for `dev`, `release`, `release/*`, and `main`.
**Goal:** Add CI that triggers remote builds on a Tencent Cloud CVM and pushes private `backend` and `frontend` images into a self-hosted Docker Registry protected by `htpasswd`, for `dev`, `release`, `release/*`, and `main`.
**Architecture:** GitHub Actions runs on pushes to `dev`, `release`, `release/*`, and `main`, connects to the Tencent Cloud server over SSH, syncs the repository into a fixed build directory, and invokes a repository-owned shell script that logs in to the private registry, builds the images locally on the server, tags them according to branch, and pushes them.
**Tech Stack:** GitHub Actions, SSH, Docker, self-hosted `registry:2`, Docker Compose, FastAPI, Nginx
**Tech Stack:** GitHub Actions, SSH, Docker, self-hosted `registry:2`, Docker Compose, FastAPI, Node.js
---
@@ -65,7 +65,7 @@ Fail fast for unsupported branches.
Log in to `${REGISTRY_HOST}` using the supplied username and password, then build:
- `${REGISTRY_HOST}/ctms/ctms-backend`
- `${REGISTRY_HOST}/ctms/ctms-nginx`
- `${REGISTRY_HOST}/ctms/ctms-frontend`
**Step 3: Push both tags for both images**
@@ -86,7 +86,7 @@ Expected: PASS
Set:
- `backend.image` to `${BACKEND_IMAGE:-${REGISTRY_HOST:-127.0.0.1:5000}/ctms/ctms-backend:latest}`
- `backend-init.image` to `${BACKEND_IMAGE:-${REGISTRY_HOST:-127.0.0.1:5000}/ctms/ctms-backend:latest}`
- `nginx.image` to `${NGINX_IMAGE:-${REGISTRY_HOST:-127.0.0.1:5000}/ctms/ctms-nginx:latest}`
- `frontend.image` to `${FRONTEND_IMAGE:-${REGISTRY_HOST:-127.0.0.1:5000}/ctms/ctms-frontend:latest}`
Keep the current `build:` blocks intact.