컨테이너 오케스트레이션을 위한 백엔드 프레임워크의 견고한 심층 상태 확인 구현
Takashi Yamamoto
Infrastructure Engineer · Leapcell

소개
빠르게 발전하는 현대 소프트웨어 개발 환경에서 컨테이너화 및 마이크로서비스 아키텍처는 확장 가능하고, 탄력적이며, 유지 관리 가능한 애플리케이션을 구축하는 사실상의 표준이 되었습니다. Kubernetes, Docker Swarm, Amazon ECS와 같은 도구는 배포 및 관리를 간소화하지만, 그 효과는 중요하지만 종종 과소평가되는 구성 요소인 상태 확인에 달려 있습니다. 기본적인 상태 확인은 서비스가 실행 중인지 알려줄 수 있지만, 애플리케이션은 종속성 실패나 리소스 고갈로 인해 '가동' 상태이지만 핵심 기능을 전혀 수행하지 못할 수도 있습니다. 이때 심층 상태 확인이 등장합니다. 이는 단순한 프로세스 모니터링을 넘어서 애플리케이션의 내부 상태와 외부 종속성을 조사하여 운영 준비 상태에 대한 더 정확한 그림을 제공합니다.
이 글에서는 심층 상태 확인의 중요성, 백엔드 프레임워크 내에서 효과적으로 구현하는 방법, 그리고 이를 통한 견고한 컨테이너 오케스트레이션의 필수적인 역할에 대해 살펴보겠습니다.
핵심 개념 설명
구현 세부 정보에 들어가기 전에 심층 상태 확인과 컨테이너 오케스트레이터와의 상호 작용을 이해하는 데 중요한 몇 가지 핵심 용어를 명확히 하겠습니다.
- 상태 확인 (일반): 애플리케이션 또는 서비스 인스턴스의 운영 상태를 보고하는 엔드포인트입니다. 오케스트레이션 시스템은 이를 사용하여 컨테이너가 정상 상태이고 트래픽을 처리할 준비가 되었는지 결정합니다.
- 라이브니스 프로브(Liveness Probe): 오케스트레이터가 컨테이너가 실행 중인지 확인하는 데 사용됩니다. 라이브니스 프로브 실패 시 오케스트레이터는 일반적으로 컨테이너를 다시 시작합니다. 이는 교착 상태를 방지하고 프로세스가 응답하는지 확인합니다.
- 준비도 프로브(Readiness Probe): 오케스트레이터가 컨테이너가 트래픽을 수락할 준비가 되었는지 확인하는 데 사용됩니다. 준비도 프로브 실패 시 오케스트레이터는 서비스의 부하 분산 풀에서 컨테이너를 일시적으로 제거합니다. 이는 시작 중이거나 서비스가 일시적으로 요청을 처리할 수 없을 때(예: 데이터베이스 연결 설정) 중요합니다.
- 시작 프로브(Startup Probe): (Kubernetes 전용) 컨테이너 내부의 애플리케이션이 시작되었는지 나타내는 데 사용됩니다. 구성된 경우, 시작 프로브가 성공적으로 통과할 때까지 라이브니스 및 준비도 검사를 비활성화하여, 잠재적으로 긴 초기화 단계 동안의 조기 재시작 또는 서비스 제외를 방지합니다.
- 심층 상태 확인(Deep Health Check): 애플리케이션의 기본 기능 확인뿐만 아니라 중요한 내부 구성 요소 및 외부 종속성(예: 데이터베이스, 메시지 큐, 외부 API, 캐시)의 상태도 확인하는 고급 상태 확인입니다.
- 컨테이너 오케스트레이션 시스템: 컨테이너의 배포, 확장, 관리 및 네트워킹을 자동화하는 소프트웨어 플랫폼(예: Kubernetes, Docker Swarm)입니다. 원하는 애플리케이션 상태를 유지하기 위해 상태 확인에 크게 의존합니다.
백엔드 프레임워크에서 심층 상태 확인 구현
심층 상태 확인은 컨테이너 오케스트레이터가 트래픽 라우팅 및 서비스 재시작에 대해 지능적인 결정을 내릴 수 있도록 하여 궁극적으로 애플리케이션의 탄력성을 높입니다. 일반적인 백엔드 프레임워크인 Spring Boot(Java) 및 Express.js(Node.js)를 예로 사용하여 이러한 기능을 구현하는 방법을 살펴보겠습니다.
핵심 아이디어는 전용 HTTP 엔드포인트(예: Spring Boot의 /health/deep
또는 /actuator/health
)를 만드는 것입니다. 이 엔드포인트가 호출되면 중요한 내부 구성 요소 및 외부 종속성에 대한 일련의 검사를 수행합니다.
Spring Boot 예제 (Java)
Spring Boot Actuator는 상태 확인을 위한 훌륭한 지원을 제공합니다. 사용자 지정 상태 확인을 정의할 수 있는 확장 가능한 HealthIndicator
인터페이스가 포함되어 있습니다.
먼저 pom.xml
에 Spring Boot Actuator 종속성이 있는지 확인합니다.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
기본적으로 Actuator는 관련 종속성이 있는 경우 데이터베이스, Redis 등과 같은 일반적인 구성 요소에 대한 상태 확인을 제공합니다. 예를 들어 외부 API에 대한 심층 상태 확인을 구현하려면 사용자 지정 HealthIndicator
를 만들어야 합니다.
import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @Component public class ExternalApiServiceHealthIndicator implements HealthIndicator { private final RestTemplate restTemplate; private final String externalApiUrl; public ExternalApiServiceHealthIndicator(RestTemplate restTemplate) { this.restTemplate = restTemplate; // 실제 애플리케이션에서는 코딩 대신 구성을 통해 주입하세요. this.externalApiUrl = "http://external-api.example.com/status"; } @Override public Health health() { try { // 외부 서비스 자체의 상태 엔드포인트 또는 경량 엔드포인트에 대한 호출을 시도합니다. String response = restTemplate.getForObject(externalApiUrl, String.class); if (response != null && response.contains("UP")) { // 또는 JSON 응답 파싱 return Health.up().withDetail("externalApiUrl", externalApiUrl).build(); } else { return Health.down().withDetail("externalApiUrl", externalApiUrl) .withDetail("message", "External API reported unhealthy").build(); } } catch (Exception e) { return Health.down(e) .withDetail("externalApiUrl", externalApiUrl) .withDetail("message", "Failed to reach external API").build(); } } }
이제 /actuator/health
엔드포인트를 히트하면 Spring Boot Actuator가 사용자 지정 엔드포인트를 포함한 모든 HealthIndicator
를 집계하여 포괄적인 상태를 반환합니다. 오케스트레이터는 이 엔드포인트를 쿼리할 수 있습니다.
Kubernetes의 경우 배포 YAML은 다음과 같을 수 있습니다.
apiVersion: apps/v1 kind: Deployment metadata: name: my-backend-service spec: replicas: 3 selector: matchLabels: app: my-backend-service template: metadata: labels: app: my-backend-service spec: containers: - name: my-backend-service image: myrepo/my-backend-service:1.0.0 ports: - containerPort: 8080 livenessProbe: httpGet: path: /actuator/health/liveness # Spring Boot Actuator 전용 port: 8080 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /actuator/health/readiness # Spring Boot Actuator 전용 port: 8080 initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 2 startupProbe: # 애플리케이션 시작에 시간이 오래 걸리는 경우 httpGet: path: /actuator/health/startup port: 8080 initialDelaySeconds: 10 periodSeconds: 5 failureThreshold: 10 # 50초(10*5) 동안 시도한다는 의미
참고: Spring Boot Actuator 2.x는 /actuator/health/liveness
및 /actuator/health/readiness
엔드포인트를 제공하며, 이는 Kubernetes 라이브니스 및 준비도 프로브에 최적화되어 "시스템이 실행 중"이라는 것과 "시스템이 트래픽을 처리할 준비가 되었다"는 것을 분리합니다. /actuator/health
엔드포인트는 모든 상태 확인을 집계하고 전체 세부 정보를 표시합니다.
Express.js 예제 (Node.js)
Express.js를 사용하는 Node.js의 경우 일반적으로 심층 상태 확인을 위한 전용 라우트를 만듭니다. express-healthcheck
와 같은 라이브러리를 사용하거나 수동으로 구현할 수 있습니다.
const express = require('express'); const axios = require('axios'); // HTTP 요청을 만들기 위해 const app = express(); const port = 3000; // 데이터베이스 연결 확인 시뮬레이션 const checkDatabaseConnection = async () => { try { // 실제 앱에서는 클라이언트가 연결/쿼리를 시도하는 방식일 것입니다. const dbStatus = await new Promise(resolve => setTimeout(() => resolve(Math.random() > 0.1), 100)); // 90% 성공 확률 if (dbStatus) { return { status: 'UP', message: 'Database connected successfully' }; } else { return { status: 'DOWN', message: 'Database connection failed' }; } } catch (error) { return { status: 'DOWN', message: `Database check error: ${error.message}` }; } }; // 외부 API 확인 시뮬레이션 const checkExternalApi = async () => { const externalApiUrl = 'http://jsonplaceholder.typicode.com/posts/1'; // 공개 테스트 API try { const response = await axios.get(externalApiUrl, { timeout: 2000 }); // 타임아웃 설정 if (response.status === 200) { return { status: 'UP', message: 'External API responsive' }; } else { return { status: 'DOWN', message: `External API returned status: ${response.status}` }; } } catch (error) { return { status: 'DOWN', message: `External API check error: ${error.message}` }; } }; app.get('/health', async (req, res) => { const dbHealth = await checkDatabaseConnection(); const externalApiHealth = await checkExternalApi(); const overallStatus = (dbHealth.status === 'UP' && externalApiHealth.status === 'UP') ? 'UP' : 'DOWN'; res.status(overallStatus === 'UP' ? 200 : 503).json({ status: overallStatus, details: { database: dbHealth, externalApi: externalApiHealth } }); }); app.listen(port, () => { console.log(`Express deep health check listening on port ${port}`); });
Kubernetes의 경우 배포 YAML은 /health
을 가리키게 됩니다.
apiVersion: apps/v1 kind: Deployment metadata: name: my-nodejs-service spec: replicas: 3 selector: matchLabels: app: my-nodejs-service template: metadata: labels: app: my-nodejs-service spec: containers: - name: my-nodejs-service image: myrepo/my-nodejs-service:1.0.0 ports: - containerPort: 3000 livenessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 2
심층 상태 확인을 위한 주요 고려 사항
- 성능 영향: 심층 상태 확인은 가볍고 빠르게 실행되어 애플리케이션 성능에 영향을 미치지 않고 신속한 실패 감지를 보장해야 합니다. 상태 확인 내에서 무거운 계산이나 장기 실행 쿼리를 피하세요.
- 타임아웃: 외부 종속성 검사에 적절한 타임아웃을 설정합니다. 느린 종속성은 무한정 지속되는 대신 검사를 실패시켜야 합니다. 이는 Kubernetes 프로브
timeoutSeconds
에 중요합니다. - 세분성: 심층 상태 확인에 포함할 만한 가치가 있는 종속성을 결정합니다. 모든 단일 마이크로 종속성을 확인할 필요는 없으며, 서비스를 사용할 수 없게 만드는 것에 집중하세요.
- 라이브니스와 준비도의 구분: 심층 상태 확인은 둘 다에 사용될 수 있지만, 다른 수준의 '깊이'가 적절한지 고려하십시오. 서비스가 일시적인 종속성 문제를 복구할 수 있다면 라이브니스 프로브는 준비도 프로브보다 약간 덜 엄격할 수 있습니다. Spring Boot Actuator 2.x의
/liveness
및/readiness
분리는 이 좋은 예입니다. - 보안: 이러한 엔드포인트는 내부 상태를 노출하는 경우가 많습니다. 내부 네트워크 세그먼트에서만 액세스하거나 외부에서 모니터링을 위해 노출하는 경우 인증이 필요한지 확인하는 등 적절하게 보호하세요.
- 장애 주입 테스트: 종속성을 인위적으로 실패시켜 예상대로 작동하는지, 오케스트레이터가 올바른 조치를 취하는지 확인하기 위해 심층 상태 확인을 정기적으로 테스트하세요.
결론
심층 상태 확인은 탄력적이고 안정적인 마이크로서비스 아키텍처를 구축하는 데 있어 단순한 선택적 기능이 아니라 기본적인 구성 요소입니다. 애플리케이션의 내부 상태와 외부 종속성을 철저히 조사함으로써 컨테이너 오케스트레이션 시스템이 정보에 입각한 결정을 내리는 데 필요한 지능을 제공하여 높은 가용성과 견고한 시스템 동작을 보장합니다. 시연된 바와 같이 이러한 엔드포인트를 구현하는 것은 컨테이너화된 환경에서 운영 우수성을 향한 간단하지만 영향력 있는 단계입니다.