1. CI/CD pipeline에서 region mismatch
- ecr로 올릴때는 public registry라서 제공되는 지역 us-east-1로 AWS CLI 로그인을 해야한다
- ecs로 올릴때는 프로젝트의 지역으로 AWS CLI 로그인을 해야한다 (ap-northeast-2)
- Github Secrets로부터 가져옴
deploy-to-ecs:
runs-on: ubuntu-latest
needs: build-and-push
steps:
# AWS 자격 증명 설정
- name: AWS CLI 설정
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
2. Task Definition 시작 오류 - CannotPullImageManifestError

에러명: CannotPullImageManifestError: Error response from daemon: invalid reference format
jobs:
# ----------------------------------------------------------------
# JOB 1: Docker 이미지를 빌드하고 ECR에 푸시
# ----------------------------------------------------------------
build-and-push-to-ecr:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
env:
REPO_URI: ${{ secrets.ECR_REPOSITORY_URI }}
outputs:
image_tag: ${{ steps.prep.outputs.image_tag }} # <<<< 문제점
....
# ============== docker 빌드하고 ecr로 푸쉬 ==============
# 이미지 태그 구성 - 커밋 해시 7자리 + latest
- name: Set Image Tag
id: set-tag
run: echo "tag=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
- 알고보니 그냥 복붙 에러였는데, steps.prep이 아니라 밑에 Set Image Tag 부분에 steps.set-tag을 사용해야 했었다.
jobs:
# ----------------------------------------------------------------
# JOB 1: Docker 이미지를 빌드하고 ECR에 푸시
# ----------------------------------------------------------------
build-and-push-to-ecr:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
env:
REPO_URI: ${{ secrets.ECR_REPOSITORY_URI }}
outputs:
image_tag: ${{ steps.set-tag.outputs.image_tag }} # <<<< set-tag 으로 수정
....
# ============== docker 빌드하고 ecr로 푸쉬 ==============
# 이미지 태그 구성 - 커밋 해시 7자리 + latest
- name: Set Image Tag
id: set-tag
run: echo "tag=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
- 이제 deploy-to-ecs job이 실행될 때, set-tag id를 가진 step의 output을 제대로 가져올 수 있게 됐다
3. (오류는 아니지만) docker build 최적화
기존 코드:
# ============== docker 빌드하고 ecr로 푸쉬 ==============
# 이미지 태그 구성 - 커밋 해시 7자리 + latest
- name: Set Image Tag
id: set-tag
run: echo "tag=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
# Docker 빌드 (dockerfile 사용)
- name: Build Docker image
run: |
docker build -t "${{ env.REPO_URI }}:${{ steps.set-tag.outputs.tag }}" -t "${{ env.REPO_URI }}:latest" .
# ecr로 푸쉬
- name: Push to ECR
run: |
docker push "${{ env.REPO_URI }}:${{ steps.set-tag.outputs.tag }}"
docker push "${{ env.REPO_URI }}:latest"
- 캐싱이 제대로 되지 않는 상태였다..!
- docker build -> docker push를 해버려서 매번 빌드할떄 시간이 오래 걸렸었다.
수정 후:
# ============== docker 빌드하고 ecr로 푸쉬 ==============
# docker Buildx 설정 (캐싱을 위해 필요)
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# 이미지 태그 구성 - 커밋 해시 7자리 + latest
- name: Set Image Tag
id: set-tag
run: echo "image_tag=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
# Docker 빌드 (dockerfile 사용)
- name: Build and push Docker image with ECR cache
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ env.REPO_URI }}:${{ steps.set-tag.outputs.image_tag }}
${{ env.REPO_URI }}:latest
cache-from: type=registry,ref=${{ env.REPO_URI }}:buildcache
cache-to: type=registry,ref=${{ env.REPO_URI }}:buildcache,mode=max
platforms: linux/amd64

buildx를 사용하게 되면 이렇게 buildcache가 추가된다.
4. EC2 public 주소 접속 안됨 1 - MaxMetaspaceSize
모든게 정상적으로 되지만, EC2가 왠지 모르게 접속이 안됐었다..
그래서 로그를 확인해봤다:

2025-09-12T02:41:06.164Z WARN 1 --- [MoNew] [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Metaspace
결론은 Metaspace out of memory error이여서, Spring Boot가 JVM 메모리가 부족하다는 오류였다.
그래서 ecs가 reference하는 (S3에 올린) .env 파일에 MaxMetaspaceSize를 64에서 128로 올려주었다.
전:
# Spring Configuration
SPRING_PROFILES_ACTIVE=prod
...
# JVM Configuration
JAVA_TOOL_OPTIONS=-Xmx384m -Xms256m -XX:MaxMetaspaceSize=64m -XX:+UseSerialGC
후:
# JVM Configuration
JAVA_TOOL_OPTIONS=-Xmx384m -Xms256m -XX:MaxMetaspaceSize=128m -XX:+UseSerialGC
5. EC2 public 주소 접속 안됨 2 - MongoDB 연동 오류
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'batchConfig' defined in URL [jar:nested:/app/app.jar/!BOOT-INF/classes/!/com/sprint/team2/monew/domain/article/batch/config/BatchConfig.class]: Unsatisfied dependency
...
[org/springframework/boot/autoconfigure/data/mongo/MongoDatabaseFactoryDependentConfiguration.class]: Unsatisfied dependency expressed through method 'mongoTemplate' parameter 0: Error creating bean with name 'mongoDatabaseFactory' defined in class path resource [org/springframework/boot/autoconfigure/data/mongo/MongoDatabaseFactoryConfiguration.class]: Failed to instantiate [org.springframework.data.mongodb.core.MongoDatabaseFactorySupport]: Factory method 'mongoDatabaseFactory' threw exception with message: Database name must not be empty
장황하지만 뜯어보면 그냥 mongodb db 이름 설정을 까먹고 안적어주어서 생긴 오류다.
application-prod.yml에 이렇게 추가해주면 된다:
data:
mongodb:
uri: ${MONGODB_URL}
database: ${MONGODB_DB}
여기까지 하고... 드디어 베포가 돼었다...!

이제부터는 배포 후 생긴 오류들도 정리해보려고 한다.
1. MongoDB Atlas 커넥션 오류
로그인 페이지에 회원가입할때 MongoDB 쪽 오류가 났다....!
- 새로운 유저가 생성될 때 UserActivity를 만드는데 이건 mongodb 를 사용하기 떄문이다
{
"code": "DataAccessResourceFailureException",
"details": {},
"exceptionType": "DataAccessResourceFailureException",
"message": "Timed out while waiting for a server that matches WritableServerSelector. Client view of cluster state is {type=REPLICA_SET, servers=[{address=ac-fwt058t-shard-00-02.4sizz77.mongodb.net:27017, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketWriteException: Exception sending message}, caused by {javax.net.ssl.SSLException: Received fatal alert: internal_error}}, {address=ac-fwt058t-shard-00-01.4sizz77.mongodb.net:27017, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketWriteException: Exception sending message}, caused by {javax.net.ssl.SSLException: Received fatal alert: internal_error}}, {address=ac-fwt058t-shard-00-00.4sizz77.mongodb.net:27017, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketWriteException: Exception sending message}, caused by {javax.net.ssl.SSLException: Received fatal alert: internal_error}}]",
"status": 500,
"timestamp": "2025-09-12T05:18:02.410287676Z"
}
알고보니 MongoDB Atlas에서 커넥션을 허용 안해줘서 생긴 문제인 듯 하다.
Mongodb 로그인 > Security > Network Access에서 배포된 EC2링크를 여기에 추가해주었다.

추가했더니 이제 회원가입은 됐다~~
2. 활동 내역 페이지 먹통 - 태스크 실행 안되는 오류

잘 되나 싶었더니.. 처음에는 코드 상의 문제인 줄 알고 굉장히 해멨는데, 아무리 서비스 코드를 뜯어봤자 고칠 게 없어 보였다. 더 나아가 로컬에서는 잘만 됐기 때문에, 배포할때 어디 문제가 있던게 분명했다.
AWS 콘솔을 보니까 태스크가 무한 대기중이였고 새롭게 배포가 안되는 상황이였다.

태스크 무한 대기 중으로 마지막에는 timeout이 떴다 ㅋㅋ
더 자세히 알아보니까, ECS 클러스터의 컴퓨팅 리소스가 부족했었기 때문이었다.
- ECS의 서비스의 기본 배포 방식은 롤링 업데이트다 -> 서비스 중단을 막기 위해 기존 버전의 태스크를 중단하기 전에 새 버젼의 태스크를 먼저 실행시킨다.
- 그래서 배포가 진행되는 짧은 순간에는 기존 태스크 + 신규 태스크 2개가 동시에 실행된다!
- 결론적으로 원래는 1개의 태스크만 운영하더라고 배포 시점에는 일지적으로 2개의 리소스가 필요하지만, 현재 free tier는 t3.micro를 사용하고 있어서 새 태스크가 실행될 공간이 없어서 배포가 멈췄던 것이다.
기본적으로 이렇게 세팅이 되어있다:

- 아까 언급했던 대로 rolling update의 기본값으로 최대 실행 작업 비율은 200다
- 동시에 2개를 일지적으로 허용해야 해서 200이다
솔루션:
cd.yml 파일을 이렇게 수정했다
# 디버깅 스텝
- name: ECS_TASK_DEFINITION 확인
run: |
if [ -z "${{ secrets.ECS_TASK_DEFINITION }}" ]; then
echo "::error:: The ECS_TASK_DEFINITION secret is empty or not set."
exit 1
else
echo "ECS_TASK_DEFINITION secret is present."
echo "Length: ${#ECS_TASK_DEFINITION}"
# Show first few characters (safely masked by GitHub)
echo "Value starts with: ${ECS_TASK_DEFINITION:0:10}..."
fi
# 기존 ECS 태스크 정의 다운로드
- name: 기존 ECS 태스크 정의 다운로드
run: aws ecs describe-task-definition --task-definition "${{ secrets.ECS_TASK_DEFINITION }}" --query taskDefinition > task-def.json
# 기존 태스크 중지
- name: 서비스 중지 (Desired Count를 0으로 설정)
run: |
aws ecs update-service \
--cluster ${{ secrets.ECS_CLUSTER }} \
--service ${{ secrets.ECS_SERVICE }} \
--desired-count 0
# 서비스 중지 대기
- name: 서비스 중지 대기
run: |
aws ecs wait services-stable \
--cluster ${{ secrets.ECS_CLUSTER }} \
--service ${{ secrets.ECS_SERVICE }}
# 새 ECR 이미지로 태스크 정의 업데이트
- name: 새 ECR 이미지로 태스크 정의 업데이트
id: render-task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-def.json
container-name: ${{ secrets.ECS_CONTAINER_NAME }}
image: ${{ secrets.ECR_REPOSITORY_URI }}:${{ needs.build-and-push-to-ecr.outputs.image_tag }}
# 새 태스크 정의 등록
- name: 새 태스크 정의 등록
id: register-task-def
run: |
task_def_arn=$(aws ecs register-task-definition --cli-input-json file://${{ steps.render-task-def.outputs.task-definition }} --query 'taskDefinition.taskDefinitionArn' --output text)
echo "task_definition_arn=$task_def_arn" >> $GITHUB_OUTPUT
# 서비스 업데이트 및 재시작
- name: 서비스 업데이트 및 재시작
run: |
aws ecs update-service \
--cluster ${{ secrets.ECS_CLUSTER }} \
--service ${{ secrets.ECS_SERVICE }} \
--task-definition ${{ steps.register-task-def.outputs.task_definition_arn }} \
--desired-count 1 \
--force-new-deployment
# 새 배포 안정화 대기
- name: 새 배포 안정화 대기
run: |
aws ecs wait services-stable \
--cluster ${{ secrets.ECS_CLUSTER }} \
--service ${{ secrets.ECS_SERVICE }}
- 기존 서비스 강제 중지: aws ecs update-service --desired-count 0 명령어를 실행하여 현재 실행 중인 태스크를 0으로 만든다 -> 의도적으로 잠깐의 서비스 다운타임이 발생하지만, 클러스터의 모든 리소스를 확보할 수 있다
- 기존 태스크가 완전히 중지될 때까지 대기
- 새 버전으로 서비스 업데이트 + 재시작: 클러스터 리소스가 완전히 비워진 상태에서, 새로운 도커 이미지가 적용된 태스크 정의로 서비스를 업데이트하고 실행할 태스크 수를 다시 1로 설정한다
- 새 서비스 안정화 대기: 새로운 태스크가 완전히 실행되고 안정적인 상태가 될 때까지 기다린 후 배포를 종료한다
드디어 활동 내역에 가보면 페이지 먹통이 사라지고 서비스는 태스크 1개를 잘 실행중인 것을 볼 수 있다!!

'Codeit > 프로젝트' 카테고리의 다른 글
| Elastisearch 토이 프로젝트 만들어보기 (프로젝트를 위한 준비..) (0) | 2025.11.27 |
|---|---|
| [모두의 플리] 고도화 - redis를 이용한 분산환경 고려 (pub/sub) (0) | 2025.11.26 |
| [모두의 플리] 트러블 슈팅 - 웹소켓 환경에서 유저 정보 가져오기 (0) | 2025.11.26 |
| [모두의 플리] 트러블슈팅 - @AuthenticationPrincipal을 가진 컨트롤러 테스트 (0) | 2025.11.19 |
| [Monew] 트러블슈팅 - 뉴스 기사 백업 (0) | 2025.09.16 |