0. ECS로의 마이그레이션
0.1 ECS로의 마이그레이션이 필요한 경우
ECS는 AWS의 컨테이너 오케스트레이션 서비스로, Docker 컨테이너의 배포, 관리, 확장을 손쉽게 할 수 있도록 해주는 서비스이다.
컨테이너 단위로 서버를 관리하기 떄문에, EC2에 직접 앱을 띄우는 방식보다 유연성과 관리 자동화가 뛰어나다.
특히 다음과 같은 상황에서 효과적이다.
1) 배포 자동화가 필요할 때
- CI/CD 파이프라인을 통해 컨테이너 이미지를 바로 배포할 수 있다.
- EC2에서는 코드 변경 시 직접 git pull, systemctl restart 같은 수작업이 필요하지만, ECS는 이미지 태그 업데이트 → 배포로 자동화가 가능하다.
2) 스케일링이 필요한 경우
- 트래픽이 몰릴 때 자동으로 컨테이너 수를 늘리고 줄임 (Auto Scaling).
- EC2에서 ASG로 스케일링을 구현하는 것보다 훨씬 간단하고 효율적이다.
3) 서버 관리 부담을 줄이고 싶을 때
- ECS Fargate 모드를 쓰면 EC2 인스턴스를 직접 관리할 필요 없다.
- 서버 OS 패치, 보안 업데이트, 인스턴스 관리 부담을 덜 수 있다.
4) 마이크로서비스로 분리하려 할 때
- 서비스별로 독립 컨테이너를 띄우고, ECS Service/Task 단위로 관리하면 마이크로서비스 아키텍처를 쉽게 구현 가능.
5) 비용 최적화
- 트래픽이 일정하지 않다면 Fargate로 필요할 때만 컨테이너를 띄워서 과금할 수 있어 EC2 상시 가동 대비 저렴할 수 있다.
0.2 ECS로의 마이그레이션 방법
현재 백엔드를 EC2에서 직접 배포 중이기 때문에, 이를 컨테이너화(Docker)한 후, ECS에 올린다.
즉, EC2 내에서 Dockerfile 작성 -> Docker 이미지 빌드 -> ECR 업로드 과정으로 마이그레이션을 진행한다.
1. Docker 세팅
1.1 Docker 설치
EC2에 원격 접속한 뒤, 다음 명령어를 통해 Docker를 설치한다.
# 패키지 업데이트
sudo apt update
# https 관련 패키지 설치
sudo apt install apt-transport-https ca-certificates curl software-properties-common
# docker repository 접근을 위한 gpg 키 설정
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# docker repository 등록
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"
# 다시 업데이트
sudo apt update
# 도커 설치
sudo apt install docker-ce
# 설치 확인
docker --version
출처: https://everydayyy.tistory.com/121
1.2 Dockerfile 작성
FROM amazoncorretto:17-alpine
WORKDIR /app
COPY build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
다음과 같은 형식으로 Dockerfile을 작성한다.
- FROM amazoncorretto:17-alpine
- Amazon이 관리하는 JDK 17 (경량 Alpine Linux 기반) 이미지를 베이스로 사용.
- EC2의 OS가 Ubuntu여도 상관없어. 컨테이너 안에서는 Alpine 기반 Linux 환경이 따로 만들어짐.
- WORKDIR /app
- 컨테이너 안에서 /app이라는 작업 디렉토리를 만들어, 이후 명령은 이 디렉토리 안에서 실행됨.
- COPY build/libs/*.jar app.jar
- 로컬 서버(EC2)에 있는 JAR 파일을 컨테이너 이미지 안으로 복사.
- 예를 들어, ./build/libs/myapp-0.0.1-SNAPSHOT.jar → /app/app.jar 로 들어감.
- Docker는 호스트 머신의 절대경로에 접근할 수 없다. 따라서 Docker 빌드 시점의 파일 접근은 Dockerfile이 위치한 디렉토리를 기준으로한 상대경로로만 가능하다. 즉, Dockerfile과 JAR 파일이 같은 디렉토리 하위에 있어야한다.
- ENTRYPOINT ["java", "-jar", "app.jar"]
- 컨테이너가 실행될 때 어떤 명령을 기본으로 실행할지 설정.
- 여기서는 java -jar app.jar 명령으로 스프링부트를 실행하도록 지정.
/opt/app/
├── Dockerfile
└── build/
└── libs/
└── app-0.0.1-SNAPSHOT.jar
1.3 Docker 이미지 빌드 및 실행
Dockerfile을 기반으로 컨테이너 이미지를 만든다.
docker build -t my-backend .
- -t my-backend: 이미지 이름을 my-backend로 지정. (이미지 이름은 대문자가 들어가면 안된다.)
- . : 현재 디렉토리에서 Dockerfile을 찾아 빌드.
# Docker run
docker run -p 8080:8080 my-backend
이때, 사용하는 프로젝트에서 환경변수를 사용하는 경우,
Dockerfile과 같은 디렉토리에 .env 파일을 작성해서 환경변수를 가져오도록 할 수 있다.
.bashrc 파일에 작성하는 것과의 차이점은, export를 삭제하고 큰따옴표도 없앤다.
# .env 예시
SPRING_DATASOURCE_URL=jdbc:mysql://your-db-endpoint:3306/dbname
SPRING_DATASOURCE_USERNAME=username
SPRING_DATASOURCE_PASSWORD=password
이렇게 .env 파일을 작성했다면, 아래 명령어를 통해 .env 파일을 참조하여 실행하도록 할 수 있다.
# 포그라운드로 docker run
docker run --env-file .env -p 8080:8080 my-backend
# 백그라운드로 docker run
docker run -d --env-file .env -p 8080:8080 my-backend
백그라운드로 docker를 실행했다면, 다음 명령어를 통해 실행을 확인할 수 있다.
# 현재 실행중인 컨테이너 목록 확인
docker ps
# 로그 실시간 확인
docker logs -f my-backend
# 컨테이너 중지/실행
docker stop my-backend
docker start my-backend
# 컨테이너 삭제
docker rm my-backend
2. ECR로 이미지 업로드
2.1 IAM 권한 업데이트
현재 백엔드 배포중인 인스턴스에 연결된 IAM 역할로 "AmazonEC2ContainerRegistryFullAccess" 를 추가해준다.
iam 역할이 제대로 잘 적용이 되었다면, aws cli 설치 후 다음 명령어 실행시 이런 json 결과가 나온다.
aws ecr describe-repositories
{
"repositories": []
}
2.2 EC2 내에 aws cli 설치
# aws cli 설치
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
apt-get install unzip -y
unzip awscliv2.zip
sudo ./aws/install
# 설치 확인
aws --version
2.3 ECR 리포지토리 추가
ECS에서 이미지를 실행하려면, AWS의 ECR에 올려야한다.
aws ecr create-repository --repository-name my-repo
{
"repository": {
"repositoryArn": "arn:aws:ecr:ap-northeast-2:[AWS ID]:repository/my-backend",
"registryId": "[AWS ID]",
"repositoryName": "my-backend",
"repositoryUri": "[AWS ID].dkr.ecr.ap-northeast-2.amazonaws.com/my-backend",
"createdAt": "2025-08-01T12:34:46.299000+00:00",
"imageTagMutability": "MUTABLE",
"imageScanningConfiguration": {
"scanOnPush": false
},
"encryptionConfiguration": {
"encryptionType": "AES256"
}
}
}
- ECR(Elastic Container Registry)는 AWS의 Docker 이미지 저장소
- my-repo라는 리포지토리 생성
2.4 로그인 후 이미지 태깅 & 푸시
# 1. 로그인
aws ecr get-login-password --region ap-northeast-2 | \
docker login --username AWS --password-stdin <ECR_URI>
# 2. 로컬 이미지 -> ECR 용으로 태그 (형식: <레지스트리>/<레포지토리>:<태그>)
docker tag my-backend:latest <ECR_URI>/my-repo:latest
# 3. 푸시
docker push <ECR_URI>/my-repo:latest
- docker tag: 로컬 이미지를 ECR 주소 형식으로 이름 바꿈.
- docker push: 해당 이미지를 AWS ECR에 업로드.
위의 명령어를 통해 my-repo 리포지토리에 이미지가 정상적으로 푸시되었다면,
ECR 리포지토리에서 latest 이미지를 확인할 수 있다.
3. Elastic Container Service
3.1 ECS 클러스터 생성
ECS 클러스터란, ECS 리소스(서비스, 태스크, 인스턴스 등)를 논리적으로 그룹화한 단위이다.
ECS 클러스터에는 두 가지 종류가 있다.
- EC2 클러스터: 사용자가 직접 EC2 인스턴스를 관리하며 컨테이너가 EC2 위에서 실행됨.
- Fargate 클러스터: 서버리스 방식. 인프라 관리 없이 AWS가 컨테이너 실행을 위한 인프라를 자동으로 제공.
서버 관리를 최소화하여 운영 편의성을 위해 Fargate 클러스터를 선택했다.
3.2 ECS 태스크 정의
ECS 태스크란, 컨테이너 설정을 정의한 템플릿을 의미한다.
(Autoscaling Group이 EC2를 생성하기 위해 사용하는 시작템플릿과 비슷한 개념인것 같다.)
ECS > 태스크 정의 > 새 태스크 정의 생성
태스크의 이름을 작성하고,
클러스터에 맞춰서 'Fargate'로 시작 유형을 선택한다.
CPU는 필요에 따라 선택하면 되는데, 내가 사용하는 프로젝트는 처음에 1vCPU를 사용했더니 CPU 사용률 100%를 찍고 죽어버리는 문제가 있어서 2vCPU로 선택하였다.
태스크 역할(Task Role)은 컨테이너 내부 애플리케이션이 AWS 리소스에 접근할 때 사용하는 역할이고,
테스크 실행 역할(Task Execution Role)은 ECS가 컨테이너를 실행할 때 로그/이미지 풀링 등에 사용하는 역할을 말한다.
태스크 역할은 컨테이너가 필요로하는 AWS 리소스 접근권한을 갖는 역할을 주고.
태스크 실행 역할은 escTaskExecutionRole로 설정한다.
컨테이너의 이름을 작성하고,
ECR내의 사용가능한 이미지 URI를 넣어준다.
컨테이너 포트는 스프링 프로젝트이기 때문에 80과 8080을 매핑해주었고
리소스 할당은 v2CPU로 선택했다. (현재 컨테이너 하나만 만드므로)
나머지는 기본으로 설정하고, 태스크를 생성했다.
3.3 ECS 서비스 생성
ECS 서비스란, 특정 태스크 정의를 기반으로 지속적으로 태스크를 실행하고 유지시키는 구성요소이다.
예를 들면, 다음과 같은 역할을 수행한다.
- 지정한 태스크 개수(desired count)를 항상 유지.
- 태스크가 비정상 종료되면 자동으로 재시작.
- 로드밸런서(ALB/NLB)와 연결 가능 (트래픽 분산, 헬스체크)
- 블루/그린 배포, 롤링 업데이트 가능
클러스터 > 서비스 > 생성
3.2에서 만든 태스크를 선택하고,
서비스 이름을 작성한다.
컴퓨팅 옵션은 시작 유형을 선택한다.
배포 구성은 기본 설정 그대로 두었고,
나는 CodeDeloy를 하기 위해서 블루/그린 배포전략을 선택했다.
VPC와 보안그룹은 기존 EC2 서버에서 사용할 때 썼던 것으로 사용했고,
서브넷은 프라이빗 서브넷을 사용했다.
프라이빗 서브넷을 사용하기 때문에, 퍼블릭 IP는 꺼짐으로 설정했다.
사실 로드밸런서는 기존에 EC2 배포할때 사용하던 ALB를 그대로 사용하는 방식이라,
기존 것을 거의 그대로 사용하였다.
iam은 'AmazonEC2ContainerServiceRole'과 'AWS CodeDeployFullAccess' 권한을 가진 역할을 생성해서 선택했다.
블루/그린 배포 방식에서는 안정적인 배포를 위해 대상그룹이 2개가 필요한데,
이를 위해 대체용 대상그룹을 하나 생성해주었다.
4. 배포 확인
제대로 수행했다면, 블루/그린 배포 방식이기 떄문에 2개의 태스크가 실행중인것을 확인할 수 있다.
배포된 사이트도 기존과 동일하게 정상적으로 작동한다.
'AWS' 카테고리의 다른 글
[AWS][트러블슈팅] CodeDeploy 오류 해결 (ResourceInitializationError, Only target group in ECS deployment group) (1) | 2025.08.06 |
---|---|
[AWS][트러블슈팅] 프론트 -> 백엔드 API 요청 막힘 문제 해결 (2) | 2025.08.06 |
[AWS] SQS로 비동기로 요청 처리하기 (6) | 2025.07.25 |
[AWS][트러블슈팅]"사이트에 연결할 수 없음" 도메인 정지 문제 해결 (7) | 2025.07.24 |
[AWS] 리액트 프론트 배포(S3+CloudFront), 가비아 도메인 https 연결하기(route53) (2) | 2025.07.08 |