fix(deploy): verify Docker clone updates against the served release
This commit is contained in:
1 parent
3bac126ace
commit
cbaa115d56
15 files changed
+321
-187
No files matched your search
@@ -137,79 +137,89 @@ pm2 stop next 2>/dev/null || true
|
||||
|
||||
# 3. Ensure the write directories are owned by www-data (UID/GID 33) so the
|
||||
# container user can write imports/uploads to them.
|
||||
sudo chown -R 33:33 ./public/nitro-assets ./public/swf ./storage
|
||||
sudo mkdir -p ./public/nitro-assets ./public/swf ./storage /var/www/Gamedata
|
||||
sudo chown -R 33:33 ./public/nitro-assets ./public/swf ./storage /var/www/Gamedata
|
||||
|
||||
# 4. Build and start
|
||||
docker compose up -d --build
|
||||
# 4. Build, migrate and start a release tied to the Git commit.
|
||||
# Requires Linux, Bash, Git, flock and Docker Compose v2; no host Node/pnpm.
|
||||
bash scripts/docker-update.sh
|
||||
|
||||
# 5. Run migrations. The slim runtime image has no source/tsx, so run migrations
|
||||
# on the HOST (against the same database) before/after starting the container.
|
||||
pnpm db:migrate # or: npm run db:migrate / yarn db:migrate
|
||||
|
||||
# 6. Check logs
|
||||
# 5. Follow logs
|
||||
docker compose logs -f cms
|
||||
```
|
||||
|
||||
The CMS will be available at `http://localhost:3002`.
|
||||
The CMS listens on port 3002 using Linux host networking. Existing database,
|
||||
Redis, emulator and shared gamedata services must already be configured.
|
||||
Create the mounted directories before installation; do not mount the checkout,
|
||||
`.next` or `node_modules` over `/app` inside the production container.
|
||||
|
||||
> **Reverse proxy:** This setup is designed to run behind the existing nginx on the host. nginx serves the Nitro/Octane client (`/client/`, `/nitro-client/`) and `/gamedata/` directly from `/var/www/Octane/dist` and `/var/www/Gamedata`, and proxies `/` to the CMS on `127.0.0.1:3002`. Point `APP_URL`/`NEXT_PUBLIC_APP_URL` at the public site URL.
|
||||
### Updating a Docker clone
|
||||
|
||||
### Automatic Updates
|
||||
|
||||
Updating is fully scripted — no need to touch Docker or nginx by hand. The
|
||||
script `scripts/docker-update.sh` pulls the latest `main`, runs CMS migrations
|
||||
on the host, rebuilds the image, recreates the container and waits for a healthy
|
||||
status. It also makes sure a stale host-side PM2 CMS (`pm2 stop next`) stays
|
||||
stopped so it can't clash on port 3002.
|
||||
|
||||
**Automatically (recommended):** a nightly cron job is already configured on a
|
||||
production server that installed this setup. It runs the script every night at
|
||||
03:30 and appends to `logs/docker-update.cron.log`:
|
||||
From your clone, run:
|
||||
|
||||
```bash
|
||||
crontab -e
|
||||
# 30 3 * * * /var/www/atom-nexst/scripts/docker-update.sh >> /var/www/atom-nexst/logs/docker-update.cron.log 2>&1
|
||||
# Also verifies that your public domain serves the expected commit:
|
||||
CMS_PUBLIC_URL=https://your-hotel.example bash scripts/docker-update.sh
|
||||
```
|
||||
|
||||
**Manually** — to update right now (same steps as the cron runs):
|
||||
The script follows the **current branch's configured Git upstream**. It refuses
|
||||
local uncommitted/untracked work, pulls fast-forward only and restarts itself if
|
||||
the updater changed. It does not reset or delete local work. Configure the
|
||||
upstream to your fork/branch if that is where you receive updates.
|
||||
|
||||
It builds a commit-tagged image and runs migrations in the matching builder
|
||||
container, using the clone's `.env`; no host dependency installation is needed.
|
||||
Only after build and migrations succeed does it recreate the CMS service. It
|
||||
compares the running image ID, image revision and `/api/health` release with the
|
||||
expected Git commit. With `CMS_PUBLIC_URL`, it also checks the domain through
|
||||
your proxy/CDN. A healthy response from another release is an error.
|
||||
|
||||
`git pull` alone updates source files, not an existing container. `docker compose
|
||||
restart` restarts the same image. `docker compose pull` downloads registry images;
|
||||
it does not fetch changes to this Git-built CMS. Use the updater for releases.
|
||||
A clone does **not** install an automatic scheduler. If desired, explicitly add a
|
||||
cron entry with the absolute path of **your** checkout; keep logs in its `logs`
|
||||
directory. Do not configure both CI and Compose updates for the same instance.
|
||||
The updater refuses an active `epicnext-cms-app` CI-managed container.
|
||||
|
||||
Logs: `logs/docker-update.log`. A failure after recreation leaves the candidate
|
||||
running for diagnosis and returns nonzero; this Compose updater does not promise
|
||||
automatic application or database rollback. Persistent volumes are preserved.
|
||||
Before production migrations, retain your normal database backup. The existing
|
||||
CI deploy continues to restore its previous container on failed verification.
|
||||
|
||||
### Diagnose an update that is not visible
|
||||
|
||||
```bash
|
||||
cd /var/www/atom-nexst
|
||||
./scripts/docker-update.sh
|
||||
# log: logs/docker-update.log
|
||||
git rev-parse HEAD
|
||||
docker inspect --format '{{.Image}}' epicnext-cms
|
||||
docker inspect --format '{{index .Config.Labels "org.opencontainers.image.revision"}}' epicnext-cms
|
||||
curl -fsS http://127.0.0.1:3002/api/health
|
||||
curl -fsS https://your-hotel.example/api/health
|
||||
```
|
||||
|
||||
The script aborts safely (exit 1) if the working tree has uncommitted changes so
|
||||
a `git pull` can never clobber local edits, and leaves the container running if
|
||||
health fails so you can debug it (exit 3). Failed runs are reported in the log;
|
||||
an exit of 0 means the CMS is healthy on the new commit.
|
||||
Both HTTP responses must report the expected `release`. `unknown` means the image
|
||||
was built without a commit ID. If the local endpoint is current but the public
|
||||
one is old, check nginx's upstream, other CMS processes and proxy/CDN caching.
|
||||
Health responses must not be cached. The release is compiled into Next.js and
|
||||
cannot be changed simply by injecting a new variable into an old container.
|
||||
|
||||
### Docker Commands
|
||||
### Docker commands
|
||||
|
||||
| Command | Description |
|
||||
| ------------------------------------------ | ------------------------------------ |
|
||||
| `docker compose up -d --build` | Build and start in background |
|
||||
| `docker compose down` | Stop and remove containers |
|
||||
| `docker compose logs -f cms` | Follow CMS logs |
|
||||
| `docker compose exec cms sh` | Open a shell in the CMS container |
|
||||
| `docker compose restart cms` | Restart the CMS container |
|
||||
| `docker compose pull && docker compose up -d --build` | Update and redeploy |
|
||||
| `pnpm db:migrate` (on the **host**) | Run database migrations (slim image has no source) |
|
||||
| `./scripts/docker-update.sh` | Full automated update (manual or cron) |
|
||||
| `pnpm db:up` / `pnpm db:down` | Start / stop the `mariadb-turbo` bulk-load container |
|
||||
| Command | Purpose |
|
||||
| --- | --- |
|
||||
| `bash scripts/docker-update.sh` | Pull, build, migrate, recreate and verify |
|
||||
| `docker compose logs -f cms` | View CMS logs |
|
||||
| `docker compose exec cms sh` | Open a shell in the running CMS |
|
||||
| `docker compose restart cms` | Restart the existing release |
|
||||
| `docker compose down` | Stop services; does not update code |
|
||||
| `pnpm db:up` / `pnpm db:down` | Manage the optional MariaDB service |
|
||||
|
||||
### How Package Manager Detection Works
|
||||
|
||||
The Dockerfile checks for lockfiles in this order:
|
||||
|
||||
1. **`pnpm-lock.yaml`** → uses pnpm (fastest, recommended)
|
||||
2. **`yarn.lock`** → uses yarn
|
||||
3. **`package-lock.json`** → uses npm
|
||||
4. **No lockfile** → falls back to `npm install`
|
||||
|
||||
This means you can use any package manager on your host machine — the Docker build will automatically match.
|
||||
|
||||
> Override the detection explicitly with `docker compose build --build-arg PACKAGE_MANAGER=pnpm` (or `npm` / `yarn`).
|
||||
For a manual build, export `CMS_RELEASE=$(git rev-parse HEAD)` before
|
||||
`docker compose build cms`; deployment and migration verification remain your
|
||||
responsibility. Docker uses the committed pnpm lockfile and pinned Node version.
|
||||
The build cache can stay enabled: copying changed source invalidates the
|
||||
application build layer. Deleting all Docker cache is not an update mechanism.
|
||||
|
||||
### Volumes
|
||||
|
||||
@@ -235,7 +245,8 @@ Only the CMS (and the optional avatar imager container, see [Avatar Imaging](#av
|
||||
**Container user & write permissions:** the CMS container runs as `www-data` (UID/GID 33) by default to match the host owner of `/var/www/Gamedata`. Ensure the other write volumes (`./public/nitro-assets`, `./public/swf`, `./storage`) are also owned by `www-data` on the host:
|
||||
|
||||
```bash
|
||||
sudo chown -R 33:33 ./public/nitro-assets ./public/swf ./storage
|
||||
sudo mkdir -p ./public/nitro-assets ./public/swf ./storage /var/www/Gamedata
|
||||
sudo chown -R 33:33 ./public/nitro-assets ./public/swf ./storage /var/www/Gamedata
|
||||
sudo chmod -R o+rX ./public/nitro-assets ./public/swf ./storage
|
||||
```
|
||||
|
||||
|
||||
Reference in new issue
Block a user