문제 정의
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 1ECS는 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가 해당 컨테이너 자동 재시작
파라미터 설명
| 파라미터 | 값 | 설명 |
|---|---|---|
command | curl -f http://localhost:8080/health || exit 1 | 헬스체크 명령어 |
interval | 30초 | 체크 간격 |
timeout | 5초 | 응답 대기 시간 |
retries | 3회 | 실패 허용 횟수 |
startPeriod | 60초 | 앱 기동 시간 (이 기간 내 실패는 무시) |
애플리케이션 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로 실행 중 컨테이너 디버깅 가능