feat: Docker Compose 最小编排——postgres:18 + 双无状态服务容器(ADR-007)
- 新增 docker-compose.yml、两服务 Dockerfile(eclipse-temurin:17-jre、非 root、仅装 exec jar)与 deploy/init-secrets.sh(幂等生成 RS256 密钥对与 .env 随机机密,产物入 .gitignore) - 容器配置复用 application.yml.sample(SPRING_CONFIG_LOCATION 挂载)+ PATBOND_* 环境变量,与本机运行同一套约定;DB 端口不对宿主机发布 - spring-boot-maven-plugin 显式绑定 repackage(本工程无 starter-parent,此前 package 产物不可执行),exec classifier 保留普通 jar 供 auth 模块 E2E 依赖 - 验收:docker compose up -d --build 后完整冒烟通过(register→me→refresh→旧 token 重用 40102→/internal 无凭证 401→logout),down -v 无残留;./mvnw clean test 全绿 74 测试 - README 新增 Docker Compose 一节 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,3 +9,7 @@ target/
|
|||||||
# Local service configuration (copy the committed .sample to application.yml)
|
# Local service configuration (copy the committed .sample to application.yml)
|
||||||
patbond-*/src/main/resources/application.yml
|
patbond-*/src/main/resources/application.yml
|
||||||
!patbond-*/src/main/resources/application.yml.sample
|
!patbond-*/src/main/resources/application.yml.sample
|
||||||
|
|
||||||
|
# Docker compose 本地机密(deploy/init-secrets.sh 生成,绝不入库)
|
||||||
|
.env
|
||||||
|
deploy/keys/
|
||||||
|
|||||||
@@ -103,6 +103,34 @@ curl -X POST http://127.0.0.1:8081/api/v1/auth/register \
|
|||||||
Machine-specific values live in the git-ignored `application.yml` (copied from the
|
Machine-specific values live in the git-ignored `application.yml` (copied from the
|
||||||
committed `.sample`); never commit secrets to the samples.
|
committed `.sample`); never commit secrets to the samples.
|
||||||
|
|
||||||
|
## Docker Compose
|
||||||
|
|
||||||
|
MVP 编排(ADR-007):`postgres:18`(数据落 volume)+ 两个无状态应用容器。
|
||||||
|
配置与本机运行同一套约定 —— 容器内挂载 `application.yml.sample` 作为配置,
|
||||||
|
`PATBOND_*` 环境变量注入实际值。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 生成 RS256 密钥对与 .env(DB 口令、内部令牌;产物被 .gitignore 忽略)
|
||||||
|
./deploy/init-secrets.sh
|
||||||
|
|
||||||
|
# 2. 构建可执行 jar
|
||||||
|
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw -DskipTests package
|
||||||
|
|
||||||
|
# 3. 启动(首次会构建镜像)
|
||||||
|
docker compose up -d --build
|
||||||
|
|
||||||
|
# 冒烟
|
||||||
|
curl -s -X POST http://127.0.0.1:8081/api/v1/auth/register \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"username":"demo_user","password":"secret123"}'
|
||||||
|
|
||||||
|
# 停止(-v 同时删除数据库数据)
|
||||||
|
docker compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:数据库端口不对宿主机发布;`8082`(user)在 MVP 阶段直连暴露以提供
|
||||||
|
`/api/v1/me`,`/internal/**` 由服务间令牌保护,规模化阶段应改由网关统一入口。
|
||||||
|
|
||||||
## Services
|
## Services
|
||||||
|
|
||||||
### Auth Service
|
### Auth Service
|
||||||
|
|||||||
Executable
+25
@@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 生成 docker compose 运行所需的本地机密:RS256 密钥对 + .env(DB 口令、内部令牌)。
|
||||||
|
# 产物全部被 .gitignore 忽略,绝不入库;重复执行是幂等的(已存在则不覆盖)。
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
mkdir -p deploy/keys
|
||||||
|
if [ ! -f deploy/keys/jwt-private.pem ]; then
|
||||||
|
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out deploy/keys/jwt-private.pem
|
||||||
|
echo "已生成 deploy/keys/jwt-private.pem"
|
||||||
|
fi
|
||||||
|
openssl pkey -in deploy/keys/jwt-private.pem -pubout -out deploy/keys/jwt-public.pem
|
||||||
|
echo "已生成 deploy/keys/jwt-public.pem"
|
||||||
|
|
||||||
|
if [ ! -f .env ]; then
|
||||||
|
{
|
||||||
|
echo "PATBOND_DB_PASSWORD=$(openssl rand -hex 16)"
|
||||||
|
echo "PATBOND_INTERNAL_TOKEN=$(openssl rand -hex 32)"
|
||||||
|
} > .env
|
||||||
|
echo "已生成 .env(随机 DB 口令与内部令牌)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 容器内以 uid 10001 运行,密钥需可读
|
||||||
|
chmod 644 deploy/keys/jwt-public.pem deploy/keys/jwt-private.pem
|
||||||
|
echo "OK:deploy/keys/ 与 .env 就绪(均已被 .gitignore 忽略)"
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Patbond MVP 编排(ADR-007):应用容器无状态,PostgreSQL 数据落 volume。
|
||||||
|
# 使用步骤见 Readme.md「Docker Compose」一节:
|
||||||
|
# 1) ./deploy/init-secrets.sh 生成 RS256 密钥对与 .env(均不入库)
|
||||||
|
# 2) JAVA_HOME=... ./mvnw -DskipTests package
|
||||||
|
# 3) docker compose up -d --build
|
||||||
|
#
|
||||||
|
# 配置来源:容器内挂载各服务的 application.yml.sample 作为配置文件,
|
||||||
|
# 其中的 ${PATBOND_*} 占位由下方 environment 注入 —— 与本机运行同一套约定。
|
||||||
|
name: patbond
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:18
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${PATBOND_DB_NAME:-patbond}
|
||||||
|
POSTGRES_USER: ${PATBOND_DB_USER:-patbond}
|
||||||
|
POSTGRES_PASSWORD: ${PATBOND_DB_PASSWORD:?先运行 deploy/init-secrets.sh 生成 .env}
|
||||||
|
volumes:
|
||||||
|
# postgres:18 官方镜像的挂载点是 /var/lib/postgresql(含版本子目录)
|
||||||
|
- pgdata:/var/lib/postgresql
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${PATBOND_DB_USER:-patbond} -d ${PATBOND_DB_NAME:-patbond}"]
|
||||||
|
interval: 2s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 30
|
||||||
|
# 数据库不对宿主机发布端口;调试需要时可临时加 ports: ["15432:5432"]
|
||||||
|
|
||||||
|
user:
|
||||||
|
build: ./patbond-user
|
||||||
|
environment:
|
||||||
|
SPRING_CONFIG_LOCATION: file:/config/application.yml
|
||||||
|
PATBOND_DB_URL: jdbc:postgresql://postgres:5432/${PATBOND_DB_NAME:-patbond}
|
||||||
|
PATBOND_DB_USER: ${PATBOND_DB_USER:-patbond}
|
||||||
|
PATBOND_DB_PASSWORD: ${PATBOND_DB_PASSWORD:?先运行 deploy/init-secrets.sh 生成 .env}
|
||||||
|
PATBOND_INTERNAL_TOKEN: ${PATBOND_INTERNAL_TOKEN:?先运行 deploy/init-secrets.sh 生成 .env}
|
||||||
|
PATBOND_JWT_PUBLIC_KEY: /run/patbond/keys/jwt-public.pem
|
||||||
|
volumes:
|
||||||
|
- ./patbond-user/src/main/resources/application.yml.sample:/config/application.yml:ro
|
||||||
|
- ./deploy/keys:/run/patbond/keys:ro
|
||||||
|
# MVP 直连暴露 8082 供客户端访问 /api/v1/me;/internal/** 已有服务间鉴权,
|
||||||
|
# 规模化阶段应由网关统一入口并停止直接暴露本端口。
|
||||||
|
ports:
|
||||||
|
- "${PATBOND_USER_PORT:-8082}:8082"
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
auth:
|
||||||
|
build: ./patbond-auth
|
||||||
|
environment:
|
||||||
|
SPRING_CONFIG_LOCATION: file:/config/application.yml
|
||||||
|
PATBOND_USER_SERVICE_URL: http://user:8082
|
||||||
|
PATBOND_INTERNAL_TOKEN: ${PATBOND_INTERNAL_TOKEN:?先运行 deploy/init-secrets.sh 生成 .env}
|
||||||
|
PATBOND_JWT_PRIVATE_KEY: /run/patbond/keys/jwt-private.pem
|
||||||
|
volumes:
|
||||||
|
- ./patbond-auth/src/main/resources/application.yml.sample:/config/application.yml:ro
|
||||||
|
- ./deploy/keys:/run/patbond/keys:ro
|
||||||
|
ports:
|
||||||
|
- "${PATBOND_AUTH_PORT:-8081}:8081"
|
||||||
|
depends_on:
|
||||||
|
- user
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pgdata:
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Runtime image only — build the jar first: ./mvnw -pl patbond-auth -am package
|
||||||
|
# Stateless by design (ADR-007): no local state, config via env / mounted files.
|
||||||
|
FROM eclipse-temurin:17-jre
|
||||||
|
RUN useradd --system --uid 10001 patbond
|
||||||
|
USER patbond
|
||||||
|
WORKDIR /app
|
||||||
|
COPY target/patbond-auth-1.0.0-SNAPSHOT-exec.jar app.jar
|
||||||
|
EXPOSE 8081
|
||||||
|
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
|
||||||
@@ -88,6 +88,22 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<!-- No spring-boot-starter-parent in this build, so the
|
||||||
|
executable-jar repackaging must be bound explicitly. -->
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<!-- Keep the plain jar as the main artifact so other
|
||||||
|
modules can depend on this one (patbond-auth's
|
||||||
|
E2E test does); the runnable fat jar gets the
|
||||||
|
-exec classifier and is what the Dockerfile ships. -->
|
||||||
|
<classifier>exec</classifier>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Runtime image only — build the jar first: ./mvnw -pl patbond-user -am package
|
||||||
|
# Stateless by design (ADR-007): no local state, config via env / mounted files.
|
||||||
|
FROM eclipse-temurin:17-jre
|
||||||
|
RUN useradd --system --uid 10001 patbond
|
||||||
|
USER patbond
|
||||||
|
WORKDIR /app
|
||||||
|
COPY target/patbond-user-1.0.0-SNAPSHOT-exec.jar app.jar
|
||||||
|
EXPOSE 8082
|
||||||
|
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
|
||||||
@@ -98,6 +98,22 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<!-- No spring-boot-starter-parent in this build, so the
|
||||||
|
executable-jar repackaging must be bound explicitly. -->
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<!-- Keep the plain jar as the main artifact so other
|
||||||
|
modules can depend on this one (patbond-auth's
|
||||||
|
E2E test does); the runnable fat jar gets the
|
||||||
|
-exec classifier and is what the Dockerfile ships. -->
|
||||||
|
<classifier>exec</classifier>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|||||||
Reference in New Issue
Block a user