Infra Trouble Shooting - 4
db connection pool을 hpa로 급한불 끄기
by HOON
0
Posted on March 19, 2025, 11:31 a.m.
안녕하세요, 오늘은 프로젝트를 진행하면서 k8s의 HPA를 사용한 경험을 작성해보고자 합니다.
문제
우선, 저희는 백엔드 어플리케이션 코드가 hikari JDBC Datasource를 사용해서 DB Connection pool을 관리했습니다. 이후 해당 코드를 운영서버에 적재 후 작업을 하던 도중 MSA로 구현된 서비스 중 stock 서비스가 아예 다운된 걸 확인했습니다.
에러 로그를 파악하던 중 다음과같은 에러 로그를 발견했습니다.
2025-03-14T07:15:09.160Z ERROR 1 : 🚨 데이터 조회 오류: Unable to acquire JDBC Connection [HikariPool-1 - Connection is not available, request timed out after 30000ms (total=10, active=10, idle=0, waiting=63)] [n/a]
2025-03-14T07:15:09.160Z ERROR 1 : 🚨 데이터 조회 오류: Unable to acquire JDBC Connection [HikariPool-1 - Connection is not available, request timed out after 30000ms (total=10, active=10, idle=0, waiting=62)] [n/a]
2025-03-14T07:15:09.165Z INFO 1 : ✅ SSE 연결 종료 (사용자 보유 주식)
2025-03-14T07:15:09.163Z INFO 1 : ✅ SSE 연결 종료 (사용자 보유 주식)
바로 hikari connection pool이 기본값인 10개로 설정되어 있는 문제였습니다.
따라서 짧은 시간에 db 커넥션을 여러개 유지하다보면 connection pool이 발생하고 바로 서비스가 다운 된 것입니다.
저는 팀 내 인프라 배포 담당으로써 백엔드 팀과 별개로 해결 방법을 알아보던중
kubernetes pod를 여러개로 늘려 각각의 connection pool을 유지하도록 하는 방법을 생각했고,
그에따라 HPA를 사용해서 고가용성을 확보하도록 시도했습니다.
해결
우선 HPA를 적용하는 방법은 생각보다 간단했습니다.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: be-stock
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
#참고로 아래 deployment.yaml 파일에서 cpu request와 limit가 지정되어있어야 합니다.
spec
containers:
resources:
requests:
cpu: "200m"
limits:
cpu: "500m"
이런식으로 cpu가 부하가 생길때마다, 최소2개의 pod, 최대 10개의 pod까지 복제되도록 설정 할 수 있습니다.
하지만 저희는 cpu 부하가 아닌, db 커넥션 수를 추적하고 적절하게 pod를 복제하는 과정이 필요합니다.
따라서 Prometheus으로 커스텀 메트릭을 작성하고 추적해서 적절하게 pod를 스케일 아웃 하도록 하겠습니다.
먼저. Prometheus에서 메트릭을 수집하기위해 spring application에서 의존성을 추가해줄게요.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-registry-prometheus'
}
그리고 어플리케이션 코드에서 커스텀 메트릭을 등록하는 코드를 작성할게요
package com.shinhan.stock_module.metrics;
import com.zaxxer.hikari.HikariDataSource;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class CustomMetrics {
private final HikariDataSource hikariDataSource;
private final MeterRegistry meterRegistry;
public CustomMetrics(HikariDataSource hikariDataSource, MeterRegistry meterRegistry) {
this.hikariDataSource = hikariDataSource;
this.meterRegistry = meterRegistry;
}
@PostConstruct
public void init() {
// custom_db_connections라는 이름으로 활성 커넥션 수 Gauge 등록
Gauge.builder("custom_db_connections", hikariDataSource, ds -> ds.getHikariPoolMXBean().getActiveConnections())
.description("Number of active DB connections")
.register(meterRegistry);
}
}
추가로 어플리케이션에서 보내는 메트릭을 k8s에서 수신 할 수 있도록 Prometheus Adapter도 생성하겠습니다.
apiVersion: v1
kind: ConfigMap
metadata:
name: custom-metrics-config
namespace: custom-metrics
data:
config.yaml: |
rules:
- seriesQuery: 'custom_db_connections'
resources:
overrides:
namespace:
resource: namespace
pod:
resource: pod
name:
as: "custom_db_connections"
metricsQuery: 'custom_db_connections{pod!="",namespace="{{.Namespace}}"}'
마지막으로 이제 hpa 파일을 다시한번 작성해보겠습니다. 아까 cpu를 추적하는것과는 다르게 db connection 수를 추적해야겠죠?
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: be-stock-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: be-stock
minReplicas: 2
maxReplicas: 10
metrics:
- type: Pods
pods:
metric:
name: custom_db_connections
target:
type: AverageValue
averageValue: 50
자 준비는 끝났습니다. 생성한 hpa.yaml 파일을 적용하고 실제로 커넥션이 여러개 생성되도록 테스트 해보면
. . .
아래와 같이 be-stock 서비스가 기존 1개에서 3개로 늘어났고, HPA도 정상적으로 작동중인걸 확인 할 수 있습니다.
ubuntu@k8s-master:~/backend/prod-stock$ kubectl get pods -n be-namespace
NAME READY STATUS RESTARTS AGE
be-stock-74467fb997-2jlz5 2/2 Running 0 7m
be-stock-74467fb997-8x2dp 2/2 Running 0 6m
be-stock-74467fb997-k3f5p 2/2 Running 0 5m
ubuntu@k8s-master:~/backend/prod-stock$ kubectl get hpa -n be-namespace
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
be-stock-hpa Deployment/be-stock custom_db_connections: 65/50 2 10 3 10m
여기서 "65/50"은 Pod당 평균 커스텀 메트릭 값이 65이고, HPA 기준(averageValue: 50)을 초과했음을 의미합니다.
이렇게 인프라 파트에서 임시방편으로 해결했지만, 우선적으로는 백엔드 어플리케이션에서 db 커넥션 풀을 효과적으로 관리해주면 좋을 것 같네요.
감사합니다!
Leave a Comment: