Run the sync server as a plain container; stop cleanly on SIGTERM
Drop the Compose/Caddy example: production runs on Kubernetes, and locally the container only needs a forwarded port and a volume. The README now gives the `docker run` steps and what a Kubernetes deployment needs (one replica, since it's a single SQLite file; a volume at /data; TLS at the ingress). The server only shut down on Ctrl-C, so `docker stop` and pod termination, which send SIGTERM, waited out the grace period and killed it. It now shuts down gracefully on either. Also ignore `.env`, which can hold the OIDC client secret.
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
# Where users reach the server, exactly as the reverse proxy serves it.
|
||||
# Settings for the mtgc-server container, for `docker run --env-file`. Copy to
|
||||
# .env (which git ignores) and fill in. See the README's "Running the sync
|
||||
# server" for the full list.
|
||||
|
||||
# Where users reach the server, exactly as they reach it: through your ingress
|
||||
# in production, or the forwarded port locally (http://localhost:8080).
|
||||
MTGC_PUBLIC_URL=https://mtgc.example.com
|
||||
|
||||
# Username/password accounts, managed with `mtgc-server user ...`.
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Replace with the hostname in MTGC_PUBLIC_URL. Caddy obtains the certificate.
|
||||
mtgc.example.com {
|
||||
encode gzip
|
||||
reverse_proxy mtgc-server:8080
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
# Example deployment: mtgc-server behind Caddy, which provides HTTPS.
|
||||
#
|
||||
# cp .env.example .env # then fill it in
|
||||
# docker compose up -d
|
||||
# docker compose exec mtgc-server mtgc-server user add yourname
|
||||
|
||||
services:
|
||||
mtgc-server:
|
||||
build:
|
||||
context: ../../..
|
||||
dockerfile: crates/mtgc-server/Dockerfile
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
volumes:
|
||||
- mtgc-data:/data
|
||||
|
||||
caddy:
|
||||
image: caddy:2
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- caddy-data:/data
|
||||
|
||||
volumes:
|
||||
mtgc-data:
|
||||
caddy-data:
|
||||
@@ -9,7 +9,7 @@
|
||||
//! ```
|
||||
//!
|
||||
//! The `user` commands read the same `MTGC_DB` as the server, so in a container
|
||||
//! they run with `docker compose exec mtgc-server mtgc-server user add me`.
|
||||
//! they run with `docker exec -it <container> mtgc-server user add me`.
|
||||
|
||||
use std::io::{BufRead, IsTerminal};
|
||||
|
||||
@@ -63,15 +63,29 @@ fn serve() -> anyhow::Result<()> {
|
||||
.await
|
||||
.with_context(|| format!("binding {bind}"))?;
|
||||
tracing::info!("listening on {bind}");
|
||||
axum::serve(listener, router(state))
|
||||
.with_graceful_shutdown(async {
|
||||
let _ = tokio::signal::ctrl_c().await;
|
||||
})
|
||||
.await?;
|
||||
axum::serve(listener, router(state)).with_graceful_shutdown(shutdown_signal()).await?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Resolves on Ctrl-C, or on SIGTERM — what `docker stop` and Kubernetes send.
|
||||
/// As PID 1 in a container the process gets no default signal handling, so
|
||||
/// without catching SIGTERM it would sit out the grace period and be killed.
|
||||
async fn shutdown_signal() {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
let mut term = signal(SignalKind::terminate()).expect("install SIGTERM handler");
|
||||
tokio::select! {
|
||||
_ = tokio::signal::ctrl_c() => {}
|
||||
_ = term.recv() => {}
|
||||
}
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
let _ = tokio::signal::ctrl_c().await;
|
||||
tracing::info!("shutting down");
|
||||
}
|
||||
|
||||
/// Account management needs only the database, not the rest of the server's
|
||||
/// configuration, so it works before OIDC (say) is set up.
|
||||
fn open_db() -> anyhow::Result<rusqlite::Connection> {
|
||||
|
||||
Reference in New Issue
Block a user