Spring Cloud Config 중앙 설정 관리

Spring Cloud Config란

Spring Cloud Config는 마이크로서비스 환경에서 설정을 중앙 집중 관리하는 서버-클라이언트 구조의 프로젝트입니다. 각 서비스에 분산된 application.yml을 Git 리포지토리, Vault, JDBC 등 외부 저장소에서 통합 관리하고, 서비스 재시작 없이 런타임에 설정을 갱신할 수 있습니다.

환경별(dev, staging, prod) 설정 분리, 민감 정보 암호화, 설정 변경 이력 추적이 기본 제공되어, 12-Factor App의 “Config” 원칙을 완벽히 구현합니다.

Config Server 구축

// build.gradle.kts
dependencies {
    implementation("org.springframework.cloud:spring-cloud-config-server")
    implementation("org.springframework.boot:spring-boot-starter-security")
}
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication.class, args);
    }
}
# application.yml (Config Server)
server:
  port: 8888

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/myorg/config-repo
          default-label: main
          search-paths:
            - '{application}'        # 서비스별 디렉토리
          clone-on-start: true       # 시작 시 클론
          timeout: 10
          # 프라이빗 리포 인증
          username: ${GIT_USERNAME}
          password: ${GIT_TOKEN}
        # 다중 리포지토리
        # repos:
        #   payment:
        #     pattern: payment-*
        #     uri: https://github.com/myorg/payment-config

Git 리포지토리 구조:

config-repo/
├── application.yml              # 모든 서비스 공통 설정
├── application-prod.yml         # 프로덕션 공통
├── order-service/
│   ├── order-service.yml        # order-service 기본 설정
│   ├── order-service-dev.yml    # 개발 환경
│   └── order-service-prod.yml   # 프로덕션
├── payment-service/
│   ├── payment-service.yml
│   └── payment-service-prod.yml
└── user-service/
    └── user-service.yml

Config Client 연동

// build.gradle.kts (각 마이크로서비스)
dependencies {
    implementation("org.springframework.cloud:spring-cloud-starter-config")
    implementation("org.springframework.boot:spring-boot-starter-actuator")
}
# application.yml (order-service)
spring:
  application:
    name: order-service           # Config Server에서 이 이름으로 설정 조회
  config:
    import: "configserver:http://config-server:8888"
  cloud:
    config:
      fail-fast: true             # Config Server 연결 실패 시 앱 시작 중단
      retry:
        initial-interval: 1000
        max-interval: 10000
        max-attempts: 6
        multiplier: 1.5

management:
  endpoints:
    web:
      exposure:
        include: refresh, health, info

설정 우선순위 (높은 것이 우선):

순위 소스 예시
1 서비스별+프로파일 order-service-prod.yml
2 서비스별 기본 order-service.yml
3 공통+프로파일 application-prod.yml
4 공통 기본 application.yml

런타임 설정 갱신: @RefreshScope

서비스 재시작 없이 설정을 변경하는 핵심 기능입니다:

@RestController
@RefreshScope  // 이 빈은 /actuator/refresh 호출 시 재생성됨
public class FeatureFlagController {

    @Value("${feature.new-checkout:false}")
    private boolean newCheckoutEnabled;

    @Value("${rate-limit.max-requests:100}")
    private int maxRequests;

    @GetMapping("/features")
    public Map<String, Object> getFeatures() {
        return Map.of(
            "newCheckout", newCheckoutEnabled,
            "maxRequests", maxRequests
        );
    }
}
# Git에서 설정 변경 후, 개별 서비스에 갱신 요청
curl -X POST http://order-service:8080/actuator/refresh

# 응답: 변경된 프로퍼티 키 목록
["feature.new-checkout", "rate-limit.max-requests"]

Spring Cloud Bus: 전체 서비스 일괄 갱신

서비스 인스턴스가 수십 개일 때 개별 /refresh는 비효율적입니다. Spring Cloud Bus는 메시지 브로커를 통해 모든 인스턴스에 갱신 이벤트를 브로드캐스트합니다:

// build.gradle.kts
dependencies {
    implementation("org.springframework.cloud:spring-cloud-starter-bus-kafka")
    // 또는 spring-cloud-starter-bus-amqp (RabbitMQ)
}
# application.yml
spring:
  cloud:
    bus:
      enabled: true
  kafka:
    bootstrap-servers: kafka:9092
# Config Server에 한 번만 요청하면 전체 서비스가 갱신됨
curl -X POST http://config-server:8888/actuator/busrefresh

# 특정 서비스만 갱신
curl -X POST http://config-server:8888/actuator/busrefresh/order-service

동작 흐름:

  • 1단계: Git에서 설정 파일 수정 → 커밋/푸시
  • 2단계: /actuator/busrefresh 호출 (또는 Git Webhook 연동)
  • 3단계: Config Server가 Kafka/RabbitMQ에 RefreshRemoteApplicationEvent 발행
  • 4단계: 모든 서비스 인스턴스가 이벤트 수신 → @RefreshScope 빈 재생성

민감 정보 암호화

Config Server는 대칭키/비대칭키 암호화를 지원합니다:

# Config Server — 대칭키 설정
encrypt:
  key: ${ENCRYPT_KEY}    # 환경 변수로 주입

# 암호화 API
curl http://config-server:8888/encrypt -d "my-db-password"
# → AQA1b2c3d4e5f6...

# 복호화 API
curl http://config-server:8888/decrypt -d "AQA1b2c3d4e5f6..."
# → my-db-password

Git 리포지토리에 암호화된 값을 저장합니다:

# order-service-prod.yml (Git에 저장)
spring:
  datasource:
    url: jdbc:postgresql://db-prod:5432/orders
    username: order_app
    password: '{cipher}AQA1b2c3d4e5f6...'  # 암호화된 값

payment:
  api-key: '{cipher}AQBx7y8z9...'

클라이언트가 설정을 조회하면 Config Server가 자동으로 복호화하여 평문으로 전달합니다. Git에는 암호문만 저장되므로 리포 유출 시에도 안전합니다.

Vault 백엔드

민감 정보를 더 안전하게 관리하려면 HashiCorp Vault를 백엔드로 사용합니다:

# Config Server — Vault + Git 복합 백엔드
spring:
  profiles:
    active: git, vault
  cloud:
    config:
      server:
        git:
          uri: https://github.com/myorg/config-repo
        vault:
          host: vault.infra
          port: 8200
          scheme: https
          backend: secret
          default-key: application
          kv-version: 2
          authentication: TOKEN
          token: ${VAULT_TOKEN}

비밀번호·API 키는 Vault에, 나머지 설정은 Git에 저장하는 하이브리드 구성이 권장됩니다.

JDBC 백엔드

Git이나 Vault 대신 데이터베이스에 설정을 저장할 수도 있습니다:

-- 설정 테이블
CREATE TABLE properties (
    id BIGSERIAL PRIMARY KEY,
    application VARCHAR(128) NOT NULL,
    profile VARCHAR(128) NOT NULL,
    label VARCHAR(128) NOT NULL,
    key VARCHAR(256) NOT NULL,
    value TEXT NOT NULL,
    UNIQUE(application, profile, label, key)
);

-- 데이터 예시
INSERT INTO properties VALUES
(1, 'order-service', 'prod', 'main', 'server.port', '8080'),
(2, 'order-service', 'prod', 'main', 'feature.new-checkout', 'true');
# Config Server — JDBC 백엔드
spring:
  profiles:
    active: jdbc
  cloud:
    config:
      server:
        jdbc:
          sql: "SELECT key, value FROM properties WHERE application=? AND profile=? AND label=?"
  datasource:
    url: jdbc:postgresql://config-db:5432/config
    username: config_user
    password: ${DB_PASSWORD}

Config Server 고가용성

프로덕션에서는 Config Server를 다중 인스턴스로 운영합니다:

# K8s Deployment — Config Server 2 replicas
apiVersion: apps/v1
kind: Deployment
metadata:
  name: config-server
spec:
  replicas: 2
  template:
    spec:
      containers:
        - name: config-server
          image: config-server:latest
          ports:
            - containerPort: 8888
          readinessProbe:
            httpGet:
              path: /actuator/health
              port: 8888
            periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: config-server
spec:
  selector:
    app: config-server
  ports:
    - port: 8888

클라이언트에서는 서비스 디스커버리 또는 K8s Service를 통해 접근하므로, 어떤 인스턴스에 연결되든 동일한 설정을 받습니다.

보안 설정

Config Server는 모든 서비스의 설정을 보유하므로 반드시 인증을 적용합니다:

# Config Server — Basic Auth
spring:
  security:
    user:
      name: ${CONFIG_USER}
      password: ${CONFIG_PASSWORD}

# 클라이언트
spring:
  cloud:
    config:
      username: ${CONFIG_USER}
      password: ${CONFIG_PASSWORD}

모니터링과 로깅

# Config Server 엔드포인트
GET /order-service/prod          # order-service 프로덕션 설정 조회
GET /order-service/prod/main     # label(branch) 지정
GET /encrypt                     # 암호화
GET /decrypt                     # 복호화
POST /actuator/busrefresh        # 전체 갱신

# 설정 조회 로그 (Config Server)
logging:
  level:
    org.springframework.cloud.config: DEBUG

정리

Spring Cloud Config는 마이크로서비스의 설정을 Git/Vault/JDBC에서 중앙 관리하고, @RefreshScope + Cloud Bus로 런타임 갱신을 실현합니다. 핵심은 민감 정보 암호화, 환경별 설정 분리, 재시작 없는 설정 변경의 세 가지입니다. K8s ConfigMap/Secret과 병행하면 인프라 설정은 K8s에, 애플리케이션 비즈니스 설정은 Config Server에 분리하는 것이 운영상 가장 효율적입니다.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux