rewrite URL shortener in Rust
This commit is contained in:
+140
@@ -0,0 +1,140 @@
|
||||
# Production migration and rollback
|
||||
|
||||
This runbook moves the titan deployment from the Python source tree at
|
||||
`/root/repo/urlshortener` to the published `sodium/ushort` image. The final
|
||||
server-side application directory is `/root/compose/ushort`; no source checkout
|
||||
is required on titan.
|
||||
|
||||
Do not cut over until the exact image tag has been built for both `linux/amd64`
|
||||
and `linux/arm64`, pushed, and recorded by digest.
|
||||
|
||||
## 1. Prepare without affecting production
|
||||
|
||||
Create this layout on titan:
|
||||
|
||||
```text
|
||||
/root/compose/ushort/
|
||||
├── docker-compose.yml
|
||||
├── config.toml
|
||||
└── data/
|
||||
```
|
||||
|
||||
Copy `deploy/docker-compose.yml` as the Compose file. Convert the live JSON
|
||||
settings to TOML using `config.example.toml` as the reference, with these
|
||||
cutover-specific rules:
|
||||
|
||||
- keep the live `base_url`, limits, retention, and rate-limit values;
|
||||
- set `db_path = "data/urlshort.db"`;
|
||||
- set `production = false` so the binary serves its embedded frontend; and
|
||||
- generate a new API key, because the old key existed in the legacy Git
|
||||
history.
|
||||
|
||||
Keep `config.toml` owned by `1001:1001` with mode `0400`, matching the
|
||||
container identity. Do not copy it into Git or a container image.
|
||||
|
||||
Validate the deployment file before stopping anything:
|
||||
|
||||
```bash
|
||||
cd /root/compose/ushort
|
||||
chown 1001:1001 config.toml
|
||||
chmod 0400 config.toml
|
||||
docker-compose config
|
||||
docker pull sodium/ushort:0.1.0
|
||||
```
|
||||
|
||||
## 2. Take a consistent database copy
|
||||
|
||||
The final copy must be made while the Python service is stopped so no committed
|
||||
row is missed and no SQLite journal is in flight:
|
||||
|
||||
```bash
|
||||
cd /root/repo/urlshortener
|
||||
docker-compose stop urlshort
|
||||
|
||||
cp -a data/urlshort.db /root/compose/ushort/data/urlshort.db
|
||||
cp -a data/urlshort.db /root/compose/ushort/data/urlshort.db.pre-rust
|
||||
chown -R 1001:1001 /root/compose/ushort/data
|
||||
chmod 0750 /root/compose/ushort/data
|
||||
chmod 0640 /root/compose/ushort/data/urlshort.db*
|
||||
```
|
||||
|
||||
Do not delete or alter the original database during the initial soak period.
|
||||
|
||||
## 3. Start and validate ushort locally
|
||||
|
||||
```bash
|
||||
cd /root/compose/ushort
|
||||
docker-compose up -d
|
||||
docker-compose ps
|
||||
docker-compose logs --tail=100 ushort
|
||||
|
||||
curl -i http://127.0.0.1:18082/s/api/health
|
||||
curl -I http://127.0.0.1:18082/s/
|
||||
```
|
||||
|
||||
Expected results are HTTP 200 health JSON and HTTP 200 HTML. Confirm the
|
||||
container does not restart and the database remains writable.
|
||||
|
||||
## 4. Switch nginx
|
||||
|
||||
Replace the old `include /root/repo/urlshortener/nginx.conf;` in the `xcel.me`
|
||||
vhost with the location blocks from the new `nginx.conf`. Those blocks proxy
|
||||
the frontend as well as API/redirect traffic to `127.0.0.1:18082`; they do not
|
||||
refer to a source or static-file directory.
|
||||
|
||||
Test before reload:
|
||||
|
||||
```bash
|
||||
docker exec nginx nginx -t
|
||||
docker exec nginx nginx -s reload
|
||||
```
|
||||
|
||||
Then validate through the public endpoint:
|
||||
|
||||
```bash
|
||||
curl -i https://xcel.me/s/api/health
|
||||
curl -I https://xcel.me/s/
|
||||
curl -I https://xcel.me/s/static/app.js
|
||||
```
|
||||
|
||||
Check that `/s/` is `no-store`, static assets are `no-cache`, an existing short
|
||||
code still returns the original 302 target, and its visit count increments once.
|
||||
|
||||
## 5. Soak and clean up
|
||||
|
||||
During the soak period, monitor:
|
||||
|
||||
```bash
|
||||
cd /root/compose/ushort
|
||||
docker-compose ps
|
||||
docker-compose logs --tail=200 ushort
|
||||
```
|
||||
|
||||
After the rollback window closes:
|
||||
|
||||
1. retain a protected database backup;
|
||||
2. remove the obsolete `/root/repo/urlshortener` source tree;
|
||||
3. remove its `/srv/urlshortener` bind mount from the global nginx Compose
|
||||
file; and
|
||||
4. recreate nginx and re-run `nginx -t`.
|
||||
|
||||
At that point, application source exists only in Gitea, while titan contains
|
||||
only the Compose file, TOML config, and data under `/root/compose/ushort`.
|
||||
|
||||
## Rollback
|
||||
|
||||
If validation fails before public traffic is enabled:
|
||||
|
||||
```bash
|
||||
cd /root/compose/ushort
|
||||
docker-compose down
|
||||
cd /root/repo/urlshortener
|
||||
docker-compose start urlshort
|
||||
```
|
||||
|
||||
Restore the old nginx include and reload nginx.
|
||||
|
||||
If public writes occurred after cutover, stop ushort first and copy its current
|
||||
database back to the legacy data path before starting Python; otherwise those
|
||||
new short URLs would be lost. The schema is deliberately identical, so no
|
||||
reverse schema migration is required.
|
||||
Reference in New Issue
Block a user