scripts(books-vds-portainer-migration): Phase 1 adapter ready
Clone-adapted from .wiki/concepts/portainer-stack-management-vds.md § Migration script. Diffs from VDS-infra version: - PORTAINER_URL = portainer.kzntsv.site - Auth via X-API-Key (PAT works, no JWT fallback needed) - DIR prefix /usr/docker/<stack> (not /opt/stacks/<stack>) - Down step via `docker rm -f` by compose-project label (bypasses docker-compose v1 ContainerConfig bug entirely; no compose binary on path required) - Refuses traefik/portainer migration (management plane) Idempotent: delete-then-create against existing stack by name on endpoint 1. Re-runs are safe. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
84
scripts/books-vds-portainer-migration/README.md
Normal file
84
scripts/books-vds-portainer-migration/README.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# books-vds-portainer-migration
|
||||
|
||||
Migrates SSH-compose stacks at `/usr/docker/<stack>/` on books VDS (`89.253.255.133`) to Portainer-managed via API. Adapted from canonical pattern: [`.wiki/concepts/portainer-stack-management-vds.md`](../../.wiki/concepts/portainer-stack-management-vds.md).
|
||||
|
||||
Related task: [`books-vds-stacks-to-portainer`](../../.tasks/books-vds-stacks-to-portainer.md).
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# 1. Get PAT from pass
|
||||
export BOOKS_PORTAINER_API_KEY=$(pass show books-vds/full-env BOOKS_PORTAINER_API_KEY)
|
||||
|
||||
# 2. Copy script to VDS
|
||||
scp -i ~/.ssh/id_ed25519_books_ops migrate.sh root@89.253.255.133:/tmp/
|
||||
|
||||
# 3. Migrate one stack at a time, verify between
|
||||
ssh -i ~/.ssh/id_ed25519_books_ops root@89.253.255.133 \
|
||||
"BOOKS_PORTAINER_API_KEY='$BOOKS_PORTAINER_API_KEY' bash /tmp/migrate.sh proxy-chain"
|
||||
```
|
||||
|
||||
## Ladder (lowest → highest blast radius)
|
||||
|
||||
1. **proxy-chain** — internal-only, no traefik exposure → smoke for the script
|
||||
2. **imgproxy** — 2-container compose, serves `imgproxy.kzntsv.site`
|
||||
3. **minio** — read-mostly, books-api retries OK
|
||||
4. **mongo** — shared, used by books-api + books-task-runner. **VERIFY retry-tolerance first**
|
||||
5. **books-db** — MariaDB, books-api connection-pool reconnects
|
||||
6. **elasticsearch** — currently "not used" per user. Verify before recreate.
|
||||
|
||||
## Smoke per stack (after each migration)
|
||||
|
||||
```bash
|
||||
# proxy-chain — internal smoke (no public endpoint)
|
||||
docker exec proxy-chain wget -qO- localhost:8000 || echo "$?"
|
||||
|
||||
# imgproxy — serves imgproxy.kzntsv.site
|
||||
curl -sS -o /dev/null -w "%{http_code}\n" https://imgproxy.kzntsv.site/
|
||||
|
||||
# minio
|
||||
curl -sS -o /dev/null -w "%{http_code}\n" https://minio.kzntsv.site/minio/health/live
|
||||
|
||||
# mongo
|
||||
docker exec mongo mongo --quiet --eval 'db.adminCommand({ping:1}).ok' \
|
||||
-u root -p "$(pass show books-vds/full-env | grep MONGO_PWD)"
|
||||
|
||||
# books-db
|
||||
docker exec books-db mariadb -uroot -p"$(pass show books-vds/full-env | grep BOOKS_DB_PWD)" -e 'SELECT 1'
|
||||
|
||||
# elasticsearch
|
||||
docker exec elasticsearch curl -sS localhost:9200/_cluster/health | jq .status
|
||||
```
|
||||
|
||||
## What the script does
|
||||
|
||||
1. **Backup** `docker-compose.yml.bak-pre-portainer-migration-<date>` (idempotent — keeps first backup).
|
||||
2. **Transform** compose: strip `env_file:` (defensive — books VDS has none), absolutize `./` paths to `/usr/docker/<stack>/`.
|
||||
3. **Parse** `.env` into API env array (no-op for books VDS — no `.env` files).
|
||||
4. **Delete** existing Portainer stack with same name on endpoint 1 (idempotency for re-runs).
|
||||
5. **Down** ad-hoc containers via `docker rm -f` by `com.docker.compose.project` label (bypasses docker-compose v1 `ContainerConfig` bug).
|
||||
6. **POST** `/api/stacks/create/standalone/string?endpointId=1` — Portainer pulls images, creates network attachments, applies traefik labels through docker socket events.
|
||||
|
||||
## Diffs from VDS-infra script (`portainer-stack-management-vds.md`)
|
||||
|
||||
| Aspect | VDS-infra | books VDS |
|
||||
|---|---|---|
|
||||
| Portainer URL | `portainer.vds.kzntsv.site` | `portainer.kzntsv.site` |
|
||||
| Endpoint ID | 1 (same) | 1 |
|
||||
| Stack dir prefix | `/opt/stacks/<stack>/` | `/usr/docker/<stack>/` |
|
||||
| Auth | JWT (PAT 401'd) | **X-API-Key** (PAT works) |
|
||||
| Compose down | `cd $DIR && docker compose down` | **`docker rm -f` by label** (skips compose binary entirely) |
|
||||
| `env_file:` strip | required (most stacks had .env) | defensive (books VDS has none) |
|
||||
|
||||
## Safety
|
||||
|
||||
- **Re-runnable.** Delete-then-create idempotency means re-running for the same stack just rebuilds it (data binds preserved, names preserved, network re-attached).
|
||||
- **Compose file preserved on disk** (in `/usr/docker/<stack>/` + dated backup). Portainer manages lifecycle but doesn't move the file.
|
||||
- **Bind paths preserved** — `/usr/docker/<stack>/data` etc. stay at the same disk path. Backup pipeline (`scripts/books-vds-backup-daily-kreknin/`) doesn't need adjustment.
|
||||
- **Skips traefik / portainer** by name — refuses to migrate management plane.
|
||||
|
||||
## Post-migration
|
||||
|
||||
- Update [`.wiki/entities/books-vds.md`](../../.wiki/entities/books-vds.md) — flip each stack from "SSH-managed" to "Portainer-managed" table.
|
||||
- Extend or fork [`.wiki/concepts/portainer-stack-management-vds.md`](../../.wiki/concepts/portainer-stack-management-vds.md) with books VDS section + gotchas if new ones encountered.
|
||||
- Close task as 🟢.
|
||||
Reference in New Issue
Block a user