From ab0265c17b58c076c33a807d9aad1b4c40c766fe Mon Sep 17 00:00:00 2001 From: Lixi20 Date: Fri, 4 Sep 2026 14:51:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20Docker=20Compose=20=E6=9C=80=E5=B0=8F?= =?UTF-8?q?=E7=BC=96=E6=8E=92=E2=80=94=E2=80=94postgres:18=20+=20=E5=8F=8C?= =?UTF-8?q?=E6=97=A0=E7=8A=B6=E6=80=81=E6=9C=8D=E5=8A=A1=E5=AE=B9=E5=99=A8?= =?UTF-8?q?=EF=BC=88ADR-007=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 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 --- .gitignore | 4 +++ Readme.md | 28 ++++++++++++++++++ deploy/init-secrets.sh | 25 ++++++++++++++++ docker-compose.yml | 64 +++++++++++++++++++++++++++++++++++++++++ patbond-auth/Dockerfile | 9 ++++++ patbond-auth/pom.xml | 16 +++++++++++ patbond-user/Dockerfile | 9 ++++++ patbond-user/pom.xml | 16 +++++++++++ 8 files changed, 171 insertions(+) create mode 100755 deploy/init-secrets.sh create mode 100644 docker-compose.yml create mode 100644 patbond-auth/Dockerfile create mode 100644 patbond-user/Dockerfile diff --git a/.gitignore b/.gitignore index cf91eba..293d1d1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,7 @@ target/ # Local service configuration (copy the committed .sample to application.yml) patbond-*/src/main/resources/application.yml !patbond-*/src/main/resources/application.yml.sample + +# Docker compose 本地机密(deploy/init-secrets.sh 生成,绝不入库) +.env +deploy/keys/ diff --git a/Readme.md b/Readme.md index e886138..2d30a1f 100644 --- a/Readme.md +++ b/Readme.md @@ -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 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 ### Auth Service diff --git a/deploy/init-secrets.sh b/deploy/init-secrets.sh new file mode 100755 index 0000000..12cf051 --- /dev/null +++ b/deploy/init-secrets.sh @@ -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 忽略)" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..741aedc --- /dev/null +++ b/docker-compose.yml @@ -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: diff --git a/patbond-auth/Dockerfile b/patbond-auth/Dockerfile new file mode 100644 index 0000000..59559e8 --- /dev/null +++ b/patbond-auth/Dockerfile @@ -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"] diff --git a/patbond-auth/pom.xml b/patbond-auth/pom.xml index 370769a..2b0cf5b 100644 --- a/patbond-auth/pom.xml +++ b/patbond-auth/pom.xml @@ -88,6 +88,22 @@ org.springframework.boot spring-boot-maven-plugin + + + + + repackage + + + + exec + + + diff --git a/patbond-user/Dockerfile b/patbond-user/Dockerfile new file mode 100644 index 0000000..e53bdb8 --- /dev/null +++ b/patbond-user/Dockerfile @@ -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"] diff --git a/patbond-user/pom.xml b/patbond-user/pom.xml index 8fb5158..475217d 100644 --- a/patbond-user/pom.xml +++ b/patbond-user/pom.xml @@ -98,6 +98,22 @@ org.springframework.boot spring-boot-maven-plugin + + + + + repackage + + + + exec + + +