← 목록으로AWS

ECS Fargate 컨테이너 Health Check 기반 자동 재시작 구현

ECS에서 컨테이너는 Running인데 내부 앱이 죽는 문제를 Task Definition Health Check로 해결하고, 장애 시 자동 재시작을 구현하는 방법

AWSDevOpsDockerECS
2024-11-22

문제 정의

ECS Fargate 환경에서 컨테이너는 Running 상태인데 내부 애플리케이션이 죽는 문제가 발생했습니다.

컨테이너 상태: Running ✅
애플리케이션 상태: Dead ❌

→ ECS는 컨테이너가 살아있다고 판단
→ 서비스 장애 발생해도 감지 못함
→ 자동 재시작 안 됨

컨테이너 상태 ≠ 애플리케이션 상태라는 것이 핵심 문제입니다.


근본 원인

Dockerfile에 HEALTHCHECK를 정의해도 ECS는 이를 자동으로 사용하지 않습니다.

# Dockerfile에 있어도 ECS가 무시함
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1

ECS는 Task Definition에 정의된 healthCheck만 인식합니다.


해결: Task Definition에 healthCheck 추가

Health Check 설정

{
  "containerDefinitions": [
    {
      "name": "app",
      "image": "[ecr-repo]/[image]:[tag]",
      "healthCheck": {
        "command": ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"],
        "interval": 30,
        "timeout": 5,
        "retries": 3,
        "startPeriod": 60
      }
    }
  ]
}

동작 방식

30초마다 /health API 호출
    ↓
응답 실패 시 retry (최대 3회)
    ↓
3회 연속 실패 → unhealthy 판정
    ↓
ECS가 해당 컨테이너 자동 재시작

파라미터 설명

파라미터설명
commandcurl -f http://localhost:8080/health || exit 1헬스체크 명령어
interval30초체크 간격
timeout5초응답 대기 시간
retries3회실패 허용 횟수
startPeriod60초앱 기동 시간 (이 기간 내 실패는 무시)

애플리케이션 Health Endpoint 준비

Spring Boot Actuator 사용

# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health, info

/actuator/health 엔드포인트가 자동으로 제공됩니다.

# 정상 응답
curl http://localhost:8080/actuator/health
# {"status":"UP"}

Health Check 명령어를 Actuator에 맞게 수정:

{
  "command": ["CMD-SHELL", "curl -f http://localhost:8080/actuator/health || exit 1"]
}

커스텀 Health Endpoint

Actuator를 사용하지 않는 경우 간단한 엔드포인트를 직접 구현합니다.

@RestController
public class HealthController {
    @GetMapping("/health")
    public ResponseEntity<String> health() {
        return ResponseEntity.ok("OK");
    }
}

Docker 이미지 준비

Alpine 기반 이미지에는 curl이 없으므로 반드시 설치해야 합니다.

FROM eclipse-temurin:17-jre-alpine
 
# curl 설치 (Health Check에 필요)
RUN apk update && apk add --no-cache curl
 
COPY target/app.jar /app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]

curl이 없으면 Health Check 명령어가 실패하여 컨테이너가 계속 unhealthy로 판정됩니다.


주요 설정 포인트

startPeriod (가장 중요)

앱 기동 시간을 충분히 고려해야 합니다. Spring Boot 앱은 기동에 30~60초 이상 걸릴 수 있습니다.

startPeriod: 10초 (기본값)
→ 앱이 아직 뜨는 중에 Health Check 실패
→ unhealthy 오판 → 재시작 루프

startPeriod: 60초 (권장)
→ 60초 동안은 실패해도 무시
→ 앱 기동 완료 후부터 정상 체크

retries

너무 작으면 일시적 지연에도 재시작됩니다. 3회가 적절합니다.

dependsOn (멀티 컨테이너)

사이드카 패턴 등에서 컨테이너 간 실행 순서를 Health Check 기반으로 제어할 수 있습니다.

{
  "dependsOn": [
    {
      "containerName": "sidecar",
      "condition": "HEALTHY"
    }
  ]
}

ECS Exec (운영 디버깅)

실행 중인 컨테이너에 직접 접속하여 디버깅할 수 있습니다.

aws ecs execute-command \
  --cluster [cluster-name] \
  --task [task-id] \
  --container app \
  --interactive \
  --command "/bin/sh"

필요 조건:

  • Task Role에 AmazonSSMManagedInstanceCore 정책 연결
  • Task Definition에 enableExecuteCommand: true 설정
  • Service 생성/업데이트 시 --enable-execute-command 플래그

트러블슈팅

Health Check 계속 실패

원인 1: curl이 없음
→ Dockerfile에 curl 설치 추가

원인 2: startPeriod 부족
→ 앱 기동 시간보다 길게 설정 (60~120초)

원인 3: Health Endpoint 경로 불일치
→ /health vs /actuator/health 확인

ECS는 정상인데 앱이 죽음

원인: Task Definition에 healthCheck 미설정
→ ECS는 프로세스 존재 여부만 확인
→ 앱 레벨 상태는 체크하지 않음
→ healthCheck 추가로 해결

전체 구성 흐름

1. 앱에 /health 엔드포인트 구현 (또는 Actuator 사용)
2. Dockerfile에 curl 포함
3. Docker 이미지 빌드 → ECR Push
4. Task Definition에 healthCheck 설정 (startPeriod 충분히)
5. ECS Service 배포
6. 테스트: 앱 내부 장애 시 자동 재시작 확인

정리

  • ECS는 Docker HEALTHCHECK를 무시함 → Task Definition에 직접 설정 필요
  • /health 엔드포인트 + curl 기반 Health Check로 앱 상태 감지
  • startPeriod를 앱 기동 시간보다 충분히 길게 설정 (오판 방지)
  • unhealthy 판정 시 ECS가 자동으로 컨테이너 재시작
  • ECS Exec로 실행 중 컨테이너 디버깅 가능