接下來把開發的 Rust/Axum API 打包成可重現的容器影像,搭配 docker-compose 啟動 Postgres、Redis 以及 API,方便在開發/測試或小型生產環境快速部署。
這個範例假設你的 Cargo.toml、src/、migrations/ 在專案目錄。程式名稱以 cargo 的 package.name(例如 sqlx_connect_demo)為準。
Dockerfile
# ---- Build stage ----
FROM rust:1.88 as builder
WORKDIR /usr/src/app
# 安裝 musl 工具鏈與必要套件
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential musl-tools musl-dev pkg-config libssl-dev ca-certificates \
&& rm -rf /var/lib/apt/lists/*
RUN rustup target add x86_64-unknown-linux-musl
# 複製 Cargo 檔以利用 Docker cache
COPY Cargo.toml Cargo.lock ./
# 建一個暫時的 main.rs 以便下載依賴(cache trick)
RUN mkdir -p src
RUN echo 'fn main() { println!("dummy"); }' > src/main.rs
# 預先抓取依賴
RUN cargo fetch
# 複製專案檔案(不複製 target,因為 target 的檔案大,複製又慢又久,再加上內容都是可下載的依賴套件)
COPY src ./src
COPY migrations ./migrations
COPY tests ./tests
COPY .env .env
# 以 musl target 建置 release binary
RUN cargo build --release --target x86_64-unknown-linux-musl
# ---- Runtime stage(使用 scratch) ----
FROM scratch
# 請把 sqlx_connect_demo 換成你 Cargo.toml 裡的 package.name
COPY --from=builder /usr/src/app/target/x86_64-unknown-linux-musl/release/sqlx_connect_demo /sqlx_connect_demo
# 複製 CA certs(如果程式需要 TLS)
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
EXPOSE 3000
ENTRYPOINT ["/sqlx_connect_demo"]
說明:
以下範例在同一個 compose network 內啟動三個服務:db、redis、api。檔名 docker-compose.yml:
services:
db:
image: postgres:18
restart: unless-stopped
environment:
POSTGRES_USER: myapp
POSTGRES_PASSWORD: myapp_pass
POSTGRES_DB: myapp_db
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
networks:
- mynet
redis:
image: redis:8
restart: unless-stopped
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
networks:
- mynet
api:
build:
context: .
dockerfile: Dockerfile
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
environment:
DATABASE_URL: postgresql://myapp:myapp_pass@db:5432/myapp_db
REDIS_URL: redis://redis:6379/
JWT_SECRET: change_me_in_production
RUST_LOG: info
ports:
- "3000:3000"
networks:
- mynet
volumes:
db_data:
redis_data:
networks:
mynet:
driver: bridge
說明重點:
我們先前的程式碼誤用了 macro! 這將會造成 build 錯誤,請把 handles delete_user 的 macro! 修改成:
let res = sqlx::query(
r#"
DELETE FROM users
WHERE id = $1 AND caller_id = $2
"#,
)
.bind(id)
.bind(caller_id)
.execute(&pool)
.await
.map_err(|e| internal_err(e))?;
另外,main.rs 中的 127.0.0.1 讓我們只能在 docker 容器內通訊,必須修改成 0.0.0.0,這樣暴露接口才能從外部連接。
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
.await
.unwrap();
把 Dockerfile 和 docker-compose.yml 放在跟 Cargo.toml 同一個資料夾中,就能開始了。
建立與執行:
local build 並啟動:
docker compose build
[+] Building 116.7s (22/22) FINISHED
=> [internal] load local bake definitions 0.0s
=> => reading from stdin 540B 0.0s
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 1.49kB 0.0s
=> WARN: FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 2) 0.1s
=> [internal] load metadata for docker.io/library/rust:1.88 1.5s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [builder 1/13] FROM docker.io/library/rust:1.88@sha256:af306cfa71d987911a781c37b59d7d67d934f49684058f96cf720 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 13.59kB 0.0s
=> CACHED [builder 2/13] WORKDIR /usr/src/app 0.0s
=> CACHED [builder 3/13] RUN apt-get update && apt-get install -y --no-install-recommends build-essential m 0.0s
=> CACHED [builder 4/13] RUN rustup target add x86_64-unknown-linux-musl 0.0s
=> CACHED [builder 5/13] COPY Cargo.toml Cargo.lock ./ 0.0s
=> CACHED [builder 6/13] RUN mkdir -p src 0.0s
=> CACHED [builder 7/13] RUN echo 'fn main() { println!("dummy"); }' > src/main.rs 0.0s
=> CACHED [builder 8/13] RUN cargo fetch 0.0s
=> [builder 9/13] COPY src ./src 0.1s
=> [builder 10/13] COPY migrations ./migrations 0.0s
=> [builder 11/13] COPY tests ./tests 0.0s
=> [builder 12/13] COPY .env .env 0.0s
=> [builder 13/13] RUN cargo build --release --target x86_64-unknown-linux-musl 113.1s
=> [stage-1 1/2] COPY --from=builder /usr/src/app/target/x86_64-unknown-linux-musl/release/sqlx_connect_demo /sq 0.1s
=> [stage-1 2/2] COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt 0.1s
=> exporting to image 0.3s
=> => exporting layers 0.2s
=> => writing image sha256:4c6bd3f093bbe92b20768bb7a45969d471e83477a715ebdc848b140beb18824f 0.0s
=> => naming to docker.io/library/sqlx_connect_demo-api 0.0s
=> resolving provenance for metadata file 0.0s
[+] Building 1/1
✔ sqlx_connect_demo-api Built 0.0s
docker compose up(關閉視窗或按下 Ctrl + C,程式就會結束)
[+] Running 2/3
✔ Container sqlx_connect_demo-db-1 Running 0.0s[+] Running 3/3
✔ Container sqlx_connect_demo-db-1 Running 0.0s
✔ Container sqlx_connect_demo-redis-1 Running 0.0s
✔ Container sqlx_connect_demo-api-1 Recreated 0.1s
Attaching to api-1
api-1 | 成功建立 PgPool
api-1 | 2025-10-10T01:47:13.461193Z INFO relation "_sqlx_migrations" already exists, skipping
api-1 | 成功完成 migrations
api-1 | 2025-10-10T01:47:20.711707Z INFO finished processing request latency=0 ms status=303
api-1 | 2025-10-10T01:47:20.732604Z INFO finished processing request latency=0 ms status=200
api-1 | 2025-10-10T01:47:20.804000Z INFO finished processing request latency=0 ms status=200
api-1 | 2025-10-10T01:47:20.806666Z INFO finished processing request latency=0 ms status=200
api-1 | 2025-10-10T01:47:20.825082Z INFO finished processing request latency=0 ms status=200
api-1 | 2025-10-10T01:47:20.840708Z INFO finished processing request latency=0 ms status=200
api-1 | 2025-10-10T01:47:20.863574Z INFO finished processing request latency=0 ms status=200
api-1 | 2025-10-10T01:47:20.980924Z INFO finished processing request latency=0 ms status=200
api-1 | 2025-10-10T01:47:21.442509Z INFO finished processing request latency=0 ms status=200
後台模式(不必一直開啟視窗):
檢查 log:
停止:
停止並移除(-v 會刪除 volumes):