1. 장애 현상 및 스택 트레이스 분석
Kubernetes 기반 마이크로서비스에서 설정 정보를 ConfigMap으로 주입하는 과정에서 com.kubernetes.client.informer.v1.configmap.ConfigMapInformer.get() 호출 시 런타임 예외가 발생한다. 핵심 스택 트레이스는 다음과 같다.
org.springframework.boot.env.YamlPropertySourceLoader.load() threw exception
com.fasterxml.jackson.databind.JsonMappingException: Cannot map JSON array to Map<CharSequence, Object>
(expected a Map with key type String and value type Object, but found a JSON array)
이 오류는 HTTP 500 상태 코드와 함께 데이터 조회 API 응답 지연으로 이어지며, 주요 원인은 구성 파서가 배열(Array) 구조를 키-값 맵(Map) 구조로 변환하는 과정에서 타입 불일치가 발생했음을 의미한다.
2. 근본 원인 진단
쿠버네티스 ConfigMap의 data 필드는 본질적으로 Map<String, String> 형식을 따른다. 그러나 YAML 작성 시 들여쓰기 오류로 인해 단일 키 아래에 리스트 형식이 중첩되거나, Spring Boot의 YamlPropertySourceLoader가 해당 구조를 파싱하는 과정에서 내부 Jackson 컨버터가 배열 노드를 Map 인터페이스로 매핑하려 시도하며 실패한다. 또한, 로그에 출력된 password: "***"는 마스킹 처리의 결과일 뿐 실제 파싱 실패의 직접적 원인은 구성 파일의 구조적 모순이다.
3. 해결 절차
3.1. 구성 데이터 구조 재정의
배열 형식을 제거하고, Spring Boot 설정 바인딩 규칙에 맞는 평면 키 구조로 ConfigMap을 재구성한다. 변수명과 계층 구조를 명시적으로 분리하여 파싱 안정성을 높인다.
apiVersion: v1
kind: ConfigMap
metadata:
name: svc-db-parameters
namespace: prod-cluster
data:
db.conn.endpoint: "10.0.12.45"
db.conn.port: "3306"
db.conn.account: "app_svc_reader"
db.conn.secret-ref: "db-credential-token"
db.pool.max-capacity: "25"
db.pool.conn.timeout: "3000"
3.2. 애플리케이션 설정 바인딩 최적화
Java 측에서 @ConfigurationProperties를 활용할 때, 중첩 객체 대신 단일 플랫 구조로 매핑하거나 명시적 Getter/Setter를 제공하여 역직렬화 경로를 명확히 한다.
@Configuration
@ConfigurationProperties(prefix = "db")
@Validated
public class DataSourceRegistry {
private final ConnectionParams conn = new ConnectionParams();
private PoolConfig pool = new PoolConfig();
@Data
public static class ConnectionParams {
private String endpoint;
private Integer port;
private String account;
private String secretRef;
}
@Data
public static class PoolConfig {
private Integer maxCapacity;
private Integer connTimeout;
}
// 명시적 Getter 설정
public ConnectionParams getConn() { return conn; }
public PoolConfig getPool() { return pool; }
public void setPool(PoolConfig poolConfig) { this.pool = poolConfig; }
}
3.3. 리소스 업데이트 및 검증
기존의 부분 패치(kubectl patch) 대신 완전한 매니페스트 적용으로 상태 불일치를 방지한다.
# 기존 리소스 완전 교체
kubectl apply -f svc-db-parameters.yaml -n prod-cluster
# 파싱 결과 검증
kubectl exec -it deployment/app-worker -- \
java -jar /opt/app/config-validator.jar \
--spring.config.location=file:/etc/config/
4. 운영상 권장 사항
- 민감 정보 분리: 비밀번호, 토큰 등은 반드시 Kubernetes Secret으로 격리하고, ConfigMap에는 메타데이터 또는 참조 키만 저장한다.
- YAML 구문 검증:
kubeval또는pre-commit훅을 파이프라인에 도입하여 배포 전 구조적 오류를 사전 차단한다. - 인포머 캐시 동기화:
ConfigMapInformer의 리소스 버전을 명시적으로 관리하여 캐시 무효화 타이밍과 파싱 시도 시점을 일치시킨다.