Cél és előfeltételek
Az előző leckében ("JWT autentikáció") megépítettük a REST API-nk védelmét: bejelentkezés, token kiadás, auth middleware. Egy ilyen szolgáltatás azonban addig csak papíron "él", amíg a saját gépeden fut cargo run-nal. Ebben a leckében megtanulod, hogyan csomagolod be a Rust szervert egy Docker image-be, és hogyan teszed publikusan elérhetővé a Fly.io felhőplatformon.
Ehhez a leckéhez a következő korábbi ismeretekre lesz szükséged:
- axum
Router, handler-ek,State,Arcmegosztott állapot tokio::main,axum::servesqlxconnection pool és migrációkjsonwebtokenés az auth middleware felépítésetracing/tracing-subscriber- parancssori (bash) alapok,
cargo build --release
Két új dolgot fogsz megtanulni:
- Hogyan épül fel egy multi-stage Dockerfile, és miért ez az iparági alapértelmezés Rust szerverekhez.
- Hogyan zajlik egy Fly.io deploy az első parancstól a publikus URL-ig.
A Docker és a Fly.io telepítése (docker, flyctl) ennek a leckének nem témája - feltételezzük, hogy a Docker Desktop/Engine már fut a gépeden, a flyctl parancsot pedig a 4. lépésben telepítjük.
Miért van szükség konténerizált deployra?
A Rust nagy előnye, hogy a cargo build --release egyetlen, viszonylag kis méretű, natív binárist ad. Elsőre azt gondolhatnád, hogy elég csak ezt a fájlt felmásolni egy szerverre. A gyakorlatban azonban több probléma is felmerül:
- Eltérő futtatási környezet. A build gépeden lévő glibc-verzió, OpenSSL-könyvtárak vagy CA-tanúsítványok hiányozhatnak a célszerverről.
- Reprodukálhatóság. Egy Dockerfile pontosan leírja, milyen build-eszközökkel és milyen alap image-ből készül a szerver - ez ugyanazt az eredményt adja a laptopodon és a felhőben is.
- Platform-elvárás. A modern felhőplatformok (Fly.io, Railway, AWS Fargate, Google Cloud Run) mind konténer image-eket futtatnak, nem nyers binárisokat.
A Rust ökoszisztéma erőssége itt is megmutatkozik: mivel a végeredmény egy statikusan linkelt, kis méretű bináris, a Docker image-ünk is drámaian kisebb lehet, mint egy tipikus Node.js vagy Java alapú szerveré - ha jól építjük fel a Dockerfile-t.
Előkészítés: a projekt és a függőségek
Ha még nincs kéznél a JWT-leckéből származó projekt, hozz létre egy egyszerűsített demót, amivel ezt a leckét gyakorolhatod:
cargo new jwt-api --bin
cd jwt-api
A Cargo.toml függőségei (a korábbi leckékben megismert crate-ek):
[package]
name = "jwt-api"
version = "0.1.0"
edition = "2024"
[dependencies]
axum = "0.8"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tracing = "0.1"
tracing-subscriber = "0.3"
sqlx = { version = "0.9", features = ["runtime-tokio", "postgres", "tls-rustls"] }
jsonwebtoken = "10"
thiserror = "2"
A tls-rustls feature-t azért adtuk hozzá az sqlx-hez, mert a legtöbb felhős Postgres (így a Fly Postgres is) TLS-en keresztül fogadja a kapcsolatokat. Enélkül a sqlx::PgPool::connect hívás elakadhat éles adatbázis ellen.
1. lépés: .dockerignore
Feladat: hozz létre egy .dockerignore fájlt a projekt gyökerében, amely kizárja a build-artefaktumokat és a helyi konfigurációt a Docker build kontextusból. Miért fontos ez? Mert a docker build minden fájlt átküld a Docker daemon-nak a build kontextusból - ha ott van a target/ mappa (ami akár gigabyte-os is lehet), a build lassú és felesleges.
Megoldás
target/
.git/
.env
*.log
Dockerfile
.dockerignore
2. lépés: multi-stage Dockerfile
Feladat: írj egy két szakaszos (multi-stage) Dockerfile-t:
- az első szakasz (
builder) egy teljes Rust build-környezetből (rust:1.96-slim) lefordítja a projektet release módban, - a második szakasz (
runtime) egy minimális Debian image-ből csak a lefordított binárist és a szükséges futásidejű fájlokat (CA-tanúsítványok) tartalmazza.
A cél, hogy a végső image ne tartalmazza a Rust compiler-t, a forráskódot vagy a build-cache-t - csak azt, amire a szervernek futáskor szüksége van.
Ha a Cargo.toml/Cargo.lock-ot előbb másolod be, és egy üres main.rs-sel lefordítod a függőségeket, a Docker cache-elni tudja ezt a réteget. Így ha csak a forráskódot módosítod (a függőségeket nem), a következő build nem fordítja újra az összes crate-et.
Megoldás
# --- 1. szakasz: build ---
FROM rust:1.96-slim AS builder
WORKDIR /app
# Előbb csak a függőség-leírókat másoljuk be, hogy a Docker cache-elhesse
# a build lépést, amíg a Cargo.toml/Cargo.lock nem változik.
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs
RUN cargo build --release
RUN rm -rf src
# Most jöhet a valódi forráskód - a fenti réteg a cache-ből jön,
# csak a saját kódunkat kell újra lefordítani.
COPY src ./src
RUN touch src/main.rs && cargo build --release
# --- 2. szakasz: futtatás ---
FROM debian:bookworm-slim AS runtime
WORKDIR /app
# A ca-certificates a HTTPS/TLS kapcsolatokhoz szükséges gyökértanúsítványokat
# tartalmazza - ezekre a Postgres felé irányuló TLS-kapcsolathoz (sqlx
# `tls-rustls` feature) és minden kimenő HTTPS híváshoz szükség van.
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/jwt-api /usr/local/bin/jwt-api
ENV PORT=8080
EXPOSE 8080
CMD ["/usr/local/bin/jwt-api"]
A rust:1.96-slim image több száz megabyte, mert tartalmazza a teljes toolchaint. Ez rendben van, hiszen ez csak a builder szakasz - a végső image-ben ebből semmi nem marad, csak a lefordított bináris kerül át a COPY --from=builder sorral.
3. lépés: lokális build és futtatás tesztelése
Feladat: buildeld le az image-et lokálisan, futtasd le egy konténerben, és ellenőrizd egy curl hívással, hogy válaszol-e a /health endpoint.
Megoldás
docker build -t jwt-api:local .
docker run --rm -p 8080:8080 \
-e DATABASE_URL="postgres://user:pass@localhost:5432/jwtdb" \
-e JWT_SECRET="fejleszteshez-ideiglenes-titok" \
jwt-api:local
Egy másik terminálban:
curl http://localhost:8080/health
Sikeres válasz esetén egy {"status":"ok","version":"0.1.0"} jellegű JSON-t kapsz vissza.
4. lépés: a Fly.io CLI telepítése és az első deploy
A Fly.io egy olyan felhőplatform, amely a feltöltött Docker image-et egy könnyű virtuális gépként ("Fly Machine") indítja el, a hozzá tartozó publikus URL-lel és HTTPS-tanúsítvánnyal.
Feladat: telepítsd a flyctl parancssori eszközt, jelentkezz be, majd futtasd le a fly launch parancsot úgy, hogy a már meglévő Dockerfile-unkat használja (ne generáljon újat).
Megoldás
# Telepítés (macOS/Linux; Windows-on lásd a Fly.io dokumentációját)
curl -L https://fly.io/install.sh | sh
# Bejelentkezés / regisztráció
fly auth login
# Az alkalmazás inicializálása a jelenlegi mappában
fly launch --dockerfile Dockerfile --no-deploy
A fly launch interaktívan rákérdez az alkalmazás nevére és a régióra, majd létrehoz egy fly.toml konfigurációs fájlt. A --no-deploy kapcsolóval most még nem indítjuk el a deploy-t - előbb beállítjuk a titkos változókat.
Egy tipikus fly.toml így néz ki:
app = "jwt-api-demo"
primary_region = "fra"
[build]
dockerfile = "Dockerfile"
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
[[http_service.checks]]
interval = "15s"
timeout = "3s"
grace_period = "5s"
method = "get"
path = "/health"
A [[http_service.checks]] blokk pontosan a mi /health endpointunkat hívja rendszeresen - ha a szerver nem válaszol, a Fly.io újraindítja a machine-t.
5. lépés: titkos változók és az adatbázis csatlakoztatása
Feladat: állítsd be a DATABASE_URL és JWT_SECRET értékeket titkos (secret) változóként, majd futtasd le a valódi deploy-t, és nézd meg a naplókat.
Titkos adatokat (adatbázis-jelszó, JWT-titok) soha ne írj bele a fly.toml-ba vagy a Dockerfile-ba - azok könnyen a repóba kerülhetnek. A fly secrets set a Fly.io saját titokkezelőjébe menti az értéket, futásidőben környezeti változóként kapja meg a konténer.
Megoldás
fly secrets set \
DATABASE_URL="postgres://user:pass@adatbazis-host:5432/jwtdb" \
JWT_SECRET="egy-eleg-hosszu-veletlen-string"
fly deploy
fly status
fly logs
Sikeres deploy után a Fly.io kiad egy publikus címet a https://<az-app-neve>.fly.dev mintázat szerint - ezt a fly status parancs is kiírja. A /health endpointot innen kívülről is elérheted:
curl https://<az-app-neve>.fly.dev/health
Ha van saját Postgres adatbázisod a Fly.io-n (fly postgres create), a kapott csatlakozási URL-t másold be a DATABASE_URL secretbe - ekkor a tls-rustls feature miatt az sqlx automatikusan titkosított kapcsolatot épít fel hozzá.
Teljes referencia-megoldás
A demó API (amit konténerbe csomagoltunk) src/main.rs tartalma:
use axum::{extract::State, routing::get, Json, Router};
use serde::Serialize;
use std::sync::Arc;
/// Egyszerűsített alkalmazásállapot - egy valódi projektben itt lakna
/// az sqlx connection pool és a JWT-titkos kulcs is.
struct AppState {
version: &'static str,
}
#[derive(Serialize)]
struct HealthResponse {
status: &'static str,
version: &'static str,
}
async fn health(State(state): State<Arc<AppState>>) -> Json<HealthResponse> {
Json(HealthResponse {
status: "ok",
version: state.version,
})
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
let state = Arc::new(AppState { version: "0.1.0" });
let app = Router::new()
.route("/health", get(health))
.with_state(state);
let port = std::env::var("PORT").unwrap_or_else(|_| "8080".to_string());
let addr = format!("0.0.0.0:{port}");
tracing::info!("Szerver indul: {addr}");
let listener = tokio::net::TcpListener::bind(&addr)
.await
.expect("nem sikerült portot foglalni");
axum::serve(listener, app)
.await
.expect("a szerver leállt hibával");
}
Ehhez tartozik a fentebb bemutatott Cargo.toml, a .dockerignore, a multi-stage Dockerfile és a fly.toml. Ha mind a négy fájl megvan a projekt gyökerében, a teljes deploy folyamat három parancsra egyszerűsödik:
fly launch --dockerfile Dockerfile --no-deploy
fly secrets set DATABASE_URL="..." JWT_SECRET="..."
fly deploy
Bónusz-kihívások
- Vezess be egy plusz lépést a Dockerfile builder szakaszába, amely
cargo clippy -- -D warningséscargo testparancsokat futtat build közben - így egy hibás commit sosem jut el a runtime image-ig. - Állíts be automatikus deploy-t: hozz létre egy GitHub Actions workflow-t, amely minden
mainbranch-re történő push után lefuttatja afly deployparancsot egyFLY_API_TOKENGitHub secret felhasználásával.
Összefoglalás és mi jön legközelebb
Ezzel a leckével a JWT-védett API-d nem csak a laptopodon fut, hanem egy valódi, publikus URL-en elérhető, HTTPS-en keresztül védett szolgáltatásként üzemel a Fly.io-n. Megtanultad, hogyan válik szét a build és a futtatási réteg egy multi-stage Dockerfile-ban, és hogyan zajlik egy Fly.io deploy a fly launch-tól a fly secrets set-en át a fly deploy-ig.
Ez volt az útvonal utolsó gyakorlati leckéje. A következő, záró leckében ("Záróvizsga") összefoglaljuk az egész utat: a Future trait-től és a Tokio executor-tól kezdve az Axum routing-on és az sqlx-en át egészen a JWT-autentikációig és a mostani deploy-ig - és számot adhatsz arról, hogy mindezt önállóan is össze tudod rakni.