Skip to content

09. Installer Design

목적

Installer(Docker Compose) MVP의 실제 구현 범위와 설계 배경을 정의한다.

범위

installer/docker-compose.yml, collector/Dockerfile, console/Dockerfile. 루트의 INSTALLER.md는 CLI 도구(apmx install/upgrade/backup/doctor 등)까지 아우르는 장기 비전을 영어로 담고 있으나, CLAUDE.md가 "MVP only needs Docker Compose support"라고 명시한 대로 이번 구현은 docker compose up 한 번으로 Collector + PostgreSQL + Console이 뜨는 것까지만 다룬다. INSTALLER.md의 나머지 내용(CLI 래퍼, interactive 설치, upgrade/backup/restore/doctor 등)은 이번 범위 밖이다.

설계

구성

text
installer/
├── docker-compose.yml
├── .env.example
└── README.md

collector/Dockerfile     (멀티스테이지: eclipse-temurin JDK로 빌드 → JRE로 실행)
console/Dockerfile       (멀티스테이지: node로 빌드 → nginx로 정적 서빙)
console/nginx.conf       (React Router SPA fallback)

INSTALLER.md의 "Docker Layout"은 collector/, console/을 compose 파일 옆에 두는 평평한 구조를 제안하지만, 실제로는 각 모듈이 이미 갖고 있는 디렉터리에 Dockerfile을 그대로 두는 쪽을 택했다 - 빌드 대상과 Dockerfile을 같은 곳에 두는 게 더 일반적인 관례이고 디렉터리를 중복시키지 않는다.

서비스 3개

서비스이미지/빌드기본 포트(호스트)
postgrespostgres:16-alpine5432
collectorcollector/Dockerfile에서 빌드8080
consoleconsole/Dockerfile에서 빌드 (nginx)3000

INSTALLER.md의 "Default Ports" 중 Collector(8080)/Console(3000)/PostgreSQL(5432)은 그대로 따랐지만, 별도로 명시된 "REST API 8081"은 반영하지 않았다 - 실제 아키텍처는 REST API가 Collector와 같은 Spring Boot 프로세스/포트를 공유하기로 이미 결정되어 있다 (docs/02-architecture.md).

Java Agent는 Compose 구성에 포함하지 않는다. Agent는 대상 애플리케이션의 JVM 옵션 (-javaagent:...)으로 직접 붙이는 것이라 별도로 "설치"할 서버 프로세스가 아니다.

Collector 이미지: Gradle 멀티모듈 빌드 컨텍스트

collector/Dockerfile의 빌드 스테이지는 저장소 루트 전체를 빌드 컨텍스트로 받는다 (installer/docker-compose.ymlcontext: ..). :collector:bootJar만 빌드하면 되지만, Gradle은 settings.gradle.kts 구성 단계에서 선언된 모든 서브프로젝트 (common, agent:java-agent, examples:example-app)의 build.gradle.kts를 평가하기 때문에 최소한 그 파일들이 빌드 컨텍스트에 있어야 한다. 부분 COPY로 최적화하는 대신 전체를 COPY하는 단순한 방식을 택했다 (.dockerignorebuild/, node_modules/, .git 등은 제외해 컨텍스트 크기를 어느 정도 줄임).

Docker는 .dockerignore를 저장소 루트가 아니라 빌드 컨텍스트 루트에서 찾는다. Collector는 컨텍스트가 저장소 루트(..)라 루트의 .dockerignore가 적용되지만, Console은 컨텍스트가 ../console이므로 별도로 console/.dockerignore가 필요하다 (없으면 호스트에 남아있는 console/node_modules가 컨테이너 안에서 npm ci로 새로 설치한 것 위에 그대로 덮어써질 수 있다).

collector/build.gradle.ktstasks.jar { enabled = false }를 추가해 일반(plain) jar 생성을 껐다 - 이게 없으면 build/libs/에 실행 가능한 bootJar와 실행 불가능한 plain jar 두 개가 생겨, Dockerfile에서 COPY할 파일을 정확히 하나로 특정하기 애매해진다.

Console 이미지: 빌드 시점 API 주소

Vite는 VITE_* 환경변수를 빌드 시점에 JS 번들에 그대로 굽는다(런타임에 다시 읽지 않음). 그래서 console/DockerfileVITE_API_BASE_URL을 build arg로 받고, 이 값은 컨테이너 간 통신용 서비스명이 아니라 브라우저가 실제로 접속할 수 있는 Collector 주소(호스트에 노출된 포트, 기본 http://localhost:8080)여야 한다. COLLECTOR_PORT를 바꾸면 Console 이미지를 다시 빌드해야 한다 (installer/README.md에 안내).

반대로 Collector의 SPRING_DATASOURCE_URL은 컨테이너 간 통신이라 Docker 네트워크 서비스명(postgres)을 그대로 쓴다 - 서버끼리의 통신이므로 문제 없다.

CORS

Collector의 APMX_CONSOLE_ORIGIN 환경변수(→ apmx.console.allowed-origin 프로퍼티, io.apmx.collector.config.WebConfig)를 Console이 호스트에 노출되는 주소 (http://localhost:${CONSOLE_PORT})로 설정해, 브라우저에서의 CORS를 맞춘다.

예시

bash
cd installer
cp .env.example .env
docker compose up -d --build

자세한 사용법은 installer/README.md 참고.

TODO

  • Collector/Console 컨테이너 자체 HEALTHCHECK 추가 검토 (현재 베이스 이미지에 curl/wget이 없어 생략함 - postgrespg_isready로 헬스체크)
  • apmx install/upgrade/backup/doctor 같은 CLI 래퍼 (INSTALLER.md 장기 비전)
  • Windows/macOS, Kubernetes/Helm, 오프라인 설치, 업그레이드/롤백/백업
  • 이미지 태그/버전 관리 (현재는 docker compose up --build로 항상 로컬 소스 기준 재빌드)