target-tracking-service에 Dockerfile이 없던 상태에서 시작해, 로컬 docker compose 전체 스택 검증 → AI 프로바이더를 OpenAI에서 Gemini로 전환 → 기존 GitOps(K3s + ArgoCD) 파이프라인에 실제로 태우기까지 진행한 기록.

1. 시작 상태

2. Dockerfile & docker-compose 구성

2.1 멀티스테이지 Dockerfile

eclipse-temurin:21-jdk-alpine(빌드) → eclipse-temurin:21-jre-alpine(런타임) 2단계. non-root 유저(spring)로 실행, app.jar 하나만 최종 이미지에 남기는 방식으로 이미지 크기를 줄임.

2.2 docker-compose에 앱 서비스 추가 + Kafka 리스너 이중화

기존 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092 하나로는, 컨테이너로 뜨는 앱이 kafka:9092로 초기 접속한 뒤 브로커가 되돌려주는 주소가 localhost라 재연결이 끓기는 문제가 있었다. 이중화로 해결:

KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:29092,PLAINTEXT_HOST://0.0.0.0:9092
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092

3. 빌드 중 발견한 기존 버그 — build.gradle이 이미 깨져 있었음

Dockerfile을 시도하기 전에 로컬 ./gradlew build부터 실패하는 걸 확인했다. Docker와 무관하게 특정 커밋(Spring AI 1.0.0 + pgvector 의존성 추가)부터 컴파일 자체가 안 되고 있던 상태.

원인: spring-ai-openai-spring-boot-starter, spring-ai-pgvector-store-spring-boot-starter는 Spring AI 1.0.0 마일스톤(M6 이전) 시절 아티팩트 이름이고, 1.0.0 GA부터는 spring-ai-starter-model-openai, spring-ai-starter-vector-store-pgvector로 개명되었다. 로컬 Gradle 캐시의 spring-ai-bom-1.0.0.pom을 직접 열어서 실제 아티팩트 목록을 확인해 검증.

4. AI 프로바이더 전환: OpenAI → Gemini

4.1 전환 이유

Gemini Developer API(Google AI Studio)는 신용카드 없이 무료 티어 제공(gemini-2.5-flash 계열: 1,500 req/day, 1M TPM). OpenAI는 사실상 전액 유료. 포트폴리오 성격상 무료 티어가 있는 쪽이 합리적이라 판단해 전환.

4.2 버전/아티팩트 확인 과정

Spring AI 1.0.0 GA에는 Gemini Developer API용 스타터가 없고(spring-ai-vertex-ai-gemini만 존재 — GCP 프로젝트/서비스어카운트가 필요한 Vertex AI 경로), API 키 기반 무료 티어를 쓰려면 spring-ai-starter-model-google-genai가 필요한데 이건 1.1.0부터 추가되었다는 걸 Maven Central의 spring-ai-bom 각 버전 POM을 직접 curl로 훑어서 확인. 최신 GA는 2.0.0이지만 기존 코드와의 호환 리스크를 줄이기 위해 같은 1.x 라인의 최신 GA인 1.1.8로 올림.

4.3 실제 설정