현재 상황
ECS 서비스를 등록하던 중, 계속적으로 대상 그룹 상태가 Unhealthy라고 뜨게 되는 상황을 발견했다. 이 상황으로 인해 헬스 체크 뿐만 아니라 API 엔드포인트 요청조차도 불가능한 상황이 되었다.
Cloudwatch에서 로그 봤을 때, product-service라는 ecs 서비스는 정상적으로 돌아가는 상황임을 알 수 있다.

문제

위와 같이 등록된 대상이 "Unhealthy" 상태로 뜨고, 헬스 체크가 실패했다는 메시지가 표시됨.
포스트맨에서 실서버 헬스 체크 했을 경우, 502/503 에러가 계속적으로 뜨는 현상이 발견되었다. 그리고 product-service에서 헬스 체크가 계속적으로 실패하다보니 결국에는 ECS 서비스의 배포 상태도 "롤백 실패"로 끝나게 되는 상태였다.
http://rushdeal-alb-{숫자}.ap-northeast-2.elb.amazonaws.com:80/api/v1/actuator/health

시도한 방법들
해당 ecs 서비스에 해당하는 Task Definition에서 상태 검사 유예 기간을 300초로 늘려주었다. (원래 120초였음)

현재 발생한 503 에러의 핵심은 : "로드밸런서(ALB)가 정해진 경로로 신호를 보냈는데 대답을 못 들었다"는 것이다. 또한 더 나아가서, 이것이 대상 그룹(Target Group)에 대한 헬스 체크 문제인지, 아니면 컨테이너 상의 헬스 체크에도 문제인지에 대해서 궁금증을 갖게되었다.
이에 대해 좀 더 알아보기 위해 Container Health Check와 Target Group Health Check의 차이를 찾아보았는데 자세한 내용은 아래와 같다.
1. Container Health Check vs Target Group Health Check
현재 발생하는 503 에러를 해결하기 위해 두 설정의 차이를 명확히 이해해야 합니다.
- Target Group Health Check (ALB 담당) - [503 에러의 범인]
- 목적: "이 컨테이너로 유저 트래픽을 보내도 안전한가?"
- 작동: ALB가 외부에서 컨테이너의 포트(8020)로 헬스 체크 경로(예: /actuator/health)에 신호를 보냅니다.
- 실패 시: 로드밸런서가 대상을 제외하며 유저는 503 에러를 봅니다.
- 해결: 보안 그룹 개방, SecurityConfig 권한 허용, 대상 그룹 경로 수정.
- Container Health Check (ECS/Docker 담당)
- 목적: "이 프로세스 자체가 살아있는가?"를 체크합니다.
- 작동: 컨테이너 내부에서 자기 자신에게 명령어를 날립니다. (예: curl -f http://localhost:8020/health)
- 실패 시: ECS가 해당 태스크를 즉시 종료하고 새로 띄웁니다. (무한 재시작 발생)
- 결론: 지금 503 에러 해결을 위해 이걸 건드릴 필요는 없습니다. (지금 문제는 네트워크 통로와 경로 문제이므로, 이걸 설정한다고 503이 해결되지는 않습니다. 오히려 더 복잡해집니다.)
2. iTerm에서 상태 확인하는 AWS CLI 명령어
터미널에서 아래 명령어를 입력하여 현재 상태를 확인하세요. (AWS CLI가 설치 및 로그인되어 있어야 합니다.)
① 대상 그룹(Target Group) 상태 확인 (503의 직접적 이유)
ALB가 왜 이 대상을 Unhealthy라고 하는지 구체적인 에러 메시지를 볼 수 있습니다.
aws elbv2 describe-target-health \
--target-group-arn [여기에_대상_그룹_ARN_복사]
- 출력 확인: TargetHealth 항목의 Description을 보세요.
- Health check failed with HTTP code [404]: 경로가 틀림 (지금 가장 유력)
- Connection refused: 포트나 보안 그룹 문제
② ECS 태스크 상태 상세 확인 (롤백/중지 원인)
태스크가 왜 자꾸 죽는지, 어떤 헬스 체크에서 걸렸는지 보여줍니다.
aws ecs describe-tasks \
--cluster [클러스터명] \
--tasks [태스크_ID]
- 출력 확인: lastStatus, stoppedReason, healthStatus를 확인하세요.
AWS CLI를 통해 Target Group에 대한 상태를 확인해보았다.
503 에러 원인 확인 (Target Group 상태)
아래는 ALB가 왜 대상을 Unhealthy로 보는지 구체적인 이유(HTTP 코드 등)를 출력한다.
aws elbv2 describe-target-health \
--target-group-arn [본인의_대상_그룹_ARN]
{
"TargetHealthDescriptions": [
{
"Target": {
"Id": "10.0.xx.xxx",
"Port": 8020,
"AvailabilityZone": "ap-northeast-2b"
},
"HealthCheckPort": "8020",
"TargetHealth": {
"State": "draining",
"Reason": "Target.DeregistrationInProgress",
"Description": "Target deregistration is in progress"
},
"AdministrativeOverride": {
"State": "no_override",
"Reason": "AdministrativeOverride.NoOverride",
"Description": "No override is currently active on target"
}
},
{
"Target": {
"Id": "10.0.xx.xxx",
"Port": 8020,
"AvailabilityZone": "ap-northeast-2a"
},
"HealthCheckPort": "8020",
"TargetHealth": {
"State": "unhealthy",
"Reason": "Target.FailedHealthChecks",
"Description": "Health checks failed"
},
"AdministrativeOverride": {
"State": "no_override",
"Reason": "AdministrativeOverride.NoOverride",
"Description": "No override is currently active on target"
}
}
]
}
- 중점 확인: TargetHealth 내부의 Reason과 Description을 보세요.
- HTTP code [403]: 100% SecurityConfig 문제입니다.
- Connection refused: 포트(8020)가 다르거나 보안 그룹 문제입니다.
현재 로그에서는 Description: Health checks failed라고만 나오고 구체적인 HTTP 상태 코드(예: 403, 404)가 출력되지 않는 상태이다. 로드밸런서(ALB)가 애플리케이션으로부터 어떠한 HTTP 응답도 받지 못했기 때문이다. 즉, TCP 연결 단계에서 문제가 발생(Connection Refused)했거나 응답 시간 초과(Timeout)가 발생했을 가능성이 높다.
즉, 위 로그에서 Description에 Health checks failed만 뜨는 이유는 HTTP 통신 단계(L7)까지 가지 못하고 연결 단계(L4)에서 문제가 발생했기 때문이다.
의심되었던 부분
그런데 해당 ECS 서비스의 로그를 보니 tomcat에서 8080포트로 실행되었다는 것을 발견했다. 분명히 해당 ECS 서비스의 포트는 환경변수와 기타 다른 AWS 콘솔 설정에는 8020으로 설정되어있는데 말이다..
해당 서비스의 Task Definition을 봐도 "PRODUCT_SERVER_PORT" 환경변수는 분명히 8020으로 설정되어 있었다.(application-prod.yml에 server.port 부분을 ${PRODUCT_SERVER_PORT}라고 설정해두었었다.)
그러면 뭐가 문제일까?

왜 PRODUCT_SERVER_PORT=8020이 적용되지 않았을까?
Spring Boot는 환경 변수를 읽을 때 다음과 같은 규칙이 있다.
표준 방식: Spring Boot는 SERVER_PORT라는 이름의 환경 변수는 별도 설정 없이도 자동으로 server.port에 매핑된다.
커스텀 방식: PRODUCT_SERVER_PORT라는 이름을 쓰려면, application.yml 안에 반드시 server.port: ${PRODUCT_SERVER_PORT}라고 명시되어 있어야 한다. => 이건 이미 설정되어 있음!! 그런데 application-prod.yml에만 설정되어 있었다.
유력한 문제 원인: 현재 로그에 8080이 찍힌다면, application.yml 설정이 누락되었거나 SPRING_PROFILES_ACTIVE=prod 설정이 누락되어 기본값(8080)으로 돌아간 것이다.
그러고보니 ECS 서비스의 Task Definition에서 SPRING_PROFILES_ACTIVE 설정값에 대해서는 추가한 적이 없었다. 해당 태스크 정의에서 "새 개정 생성"을 하여 누락된 환경 변수 값들을 추가해주고 새로운 태스크 버전을 만들었다.
다시 ECS 서비스에서 방금 새로 만든 태스크 정의가 바로 적용되도록 "새 배포 강제 적용"을 해주었다. (태스크 정의 리비전을 방금 만든 최신 번호로 선택)

그리고 좀 기다려보다가 ECS 서비스 로그를 확인해보니 Tomcat started on port 8020.. 으로 정상적으로 뜨기 시작했다.
"의심되었던 부분" 소제목 바로 아래 사진의 로그는 애플리케이션이 8020 대신 8080포트로 실행되었다는 것을 보여준다. 로드밸런서(ALB)는 8020으로 신호를 보내고 있는데 앱은 8080에서 실행하고 있으니, 패킷이 전달되지 않아 Unhealthy와 502/503에러가 발생하는 것이다.

해결
드디어... 대상 상태가 정상으로 떴다!!!!! 원인은..

대상 그룹 탭에서도 Healthy 상태가 나오는 것을 확인할 수 있다.

그래도 헬스 체크 시에 503이 나온다면?
위와 같이 대상 그룹의 Health Check가 Healthy로 되었음에도 실제 Postman을 통해 actuator health check를 했을 경우에 503에러가 나거나, 실제 서비스 API 요청 시 403 에러가 나오고 있는 상황이었다.
대상 그룹(Target Group)이 Healthy라는 것은 ALB와 컨테이너 사이의 통신은 성공했다는 뜻인데 Healthy임에도 ALB 주소로 접속했을 때 503이 뜨는 이유는, ALB 리스너가 "이 경로로 들어온 요청을 어느 대상 그룹으로 보낼지" 모르는 상태이기 때문이다.
서비스 API 요청 시 403 에러가 뜨는 경우에는 ALB를 통과해 앱에 도착했지만, Spring Security가 요청을 막은 것이다.
| 헬스 체크 | GET http://[ALB-DNS]/actuator/health | 200 OK (ALB 리스너 규칙에 /actuator/* 추가 시) |
| 상품 API | GET http://[ALB-DNS]/api/v1/products | 200 OK |
위의 503에러를 수정하기 위해서 아래와 같이 추가적으로 설정을 하였다.
EC2 -> Load Balancer -> 리스너 및 규칙 -> 규칙 편집
조건 경로에서 /actuator/*를 설정해주었다.

체크리스트
모든 ECS 서비스에 대해 아래와 같은 체크리스트에 따라 수정/설정하도록 했다. 좀 Rough하게 적어둔 것이긴 하지만..
[RUSH_CREW] AWS ECS 서비스 배포 확인 사항
< 코드 단 확인 사항 >
======= application-prod.yml
server:
tomcat:
accesslog:
enabled: true
pattern: "%h %l %u %t \"%r\" %s %b" # 표준 Apache 로그 형식
port: ${PRODUCT_SERVER_PORT:8020}
data:
redis:
host: ${TIMEDEAL_REDIS_HOST:localhost}
port: ${TIMEDEAL_REDIS_PORT:6382}
ssl:
enabled: false # 스프링 부트에서 SSL(TLS) 사용안함 (데이터 전송 시 암호화 체크 안했기 때문에 false 설정)
=======SecurityConfig.java
.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
// 경로별 권한 설정
// 보통 @PreAuthorize로 처리하므로 모두 허용하거나 authenticated로 설정
// Actuator나 헬스 체크 경로는 열어두는 것이 좋음
.requestMatchers("/actuator/**", "/health").permitAll()
.requestMatchers("/api/v1/products/**").permitAll()
.anyRequest().authenticated()
<aws 콘솔 단 확인 사항>
========== 리스너 규칙
"“/actuator./*”" 추가
==========레디스 있을 경우, ecs 서비스 수정
서비스 업데이트 -> 네트워킹 -> 보안 그룹 추가(rushdeal-app-sg)
=========태스크 정의
SPRING_PROFILES_ACTIVE => prod
SERVER_PORT={}
==========상태 검사 유예 시간
1. ECS 클러스터(rush_deal_msa) > 서비스(Services) 탭 > rushdeal-payment-service 클릭.
2. 우측 상단 [서비스 업데이트(Update service)] 버튼 클릭.
3. 배포 구성(Deployment configuration) 섹션 (스크롤 조금 내리면 있음):
* 상태 검사 유예 기간(Health check grace period): 기존 120 ➔ 300 입력.
=========OPENFEIGN 사용해서 msa 내부 통신시
기존 ECS 서비스 삭제 하고 재생성 시, 서비스 검색에서 꼭 네임 스페이스 설정해주기!
“서비스 검색에서는 Amazon Route 53을 사용하여 서비스의 네임스페이스를 생성하며, 이 네임스페이스는 DNS를 통해 찾을 수 있습니다”
“
@FeignClient(name = "user-service", url = "http://user-service.rushdeal.local:8060")
public interface UserFeignClient {
@PostMapping("/api/v1/users")
“ => 이렇게 통신 할 때 꼭 필요 (url 네임스페이스 주소로 통신)
드디어 /actuator/health가 정상적으로 통신되었음을 알 수 있다!

바로 아래 포스팅의 해결방법처럼 상태 검사 유예 기간 체크를 300초로 설정해주는 것도 큰 도움이 된다! (기존에는 120초인가 그 정도로 설정되어 있었을 때에는 타임아웃 발생했었어서 Health Check가 계속 비정상으로 된 적도 있었음)
ECS 배포시 Health Check로 인해 Task 갱신이 안되는 문제
AWS ECS 배포시 Task가 Running 상태가 되면 elb에서 정해진 health check path로 health check를 하고 unhealthy이면 방금 생성한 task가 삭제된다.만약 빌드하는 시간동안 health check가 들어오는 바람에 응답을 주
velog.io
https://blogger903.tistory.com/71
AWS ECS Health Check - 반드시 알아야 할 기본 사항
AWS ECS Health Check - 반드시 알아야 할 기본 사항서문AWS ECS를 사용할 때 가장 중요한 것은 기본적인 사항을 제대로 이해하는 것입니다. 특히 Health Check 설정은 안정적인 서비스 운영을 위한 핵심 요
blogger903.tistory.com
https://blog.templ.es/post/ecs-healthcheck-troubleshooting/
ECS 컨테이너 Health Check가 계속 실패하는 이유 - Temple's Hideout
ECS 컨테이너에 Health Check를 적용하는 도중 겪은 트러블슈팅 경험 2025. 04. 28 컨테이너의 백엔드 애플리케이션이 잘 돌아가고 있는지 확인하는 Health Check를 위해 CMD-SHELL, curl -f http://localhost:3000/healt
blog.templ.es
[ECS Fargate][TroubleShoot] Health Check 실패 (Unhealthy Host 0,1 무한반복)
1. 이슈상황 1-1. 특정 ECS Service에서 Task가 생성되고 지워지는 현상이 반복되는 현상이 지속적으로 발생하였으며 Stopped 된 사유는 Scaling activity로 인해 발생한 것으로 확인됨 1-2. ECS Service와 연결된
anggeum.tistory.com
'Project' 카테고리의 다른 글
| Antigravity를 활용한 포트폴리오 사이트 개발기 (0) | 2026.03.20 |
|---|---|
| [RUSH_CREW] 12. ECR & ECS로 무중단배포하기 (3) (0) | 2025.12.22 |
| [SURFING THE GANGWON] 기상청 API 호출 성능 최적화 (0) | 2025.10.01 |