RT 서비스는 MSA를 채택중임. 각각의 MS들을 각각의 EC2에 올렸음.
즉 delivery-service, garage-service, post-service가 있다고 할 때, 하나의 EC2에 서비스 하나를 올렸음.
물론 도커기반으로 프로젝트를 돌렸음. 그림으로 보면 아래와 같다.
ECS를 적용한 이유
회원 수가 점차 늘어났고, 특히 특정 서비스에서의 트래픽이 다른 서비스에 비해 현저히 많은 것을 RT서비스에 로그 시각화 분석을 위한 ELK를 적용하고 나서 확인했다. 물론 지금의 트래픽 정도라면 충분히 감당할 수 있지만, 나중에 스케일 아웃을 해야하는 상황에 위와 같은 구조는 유연하지 못하다고 판단하였다. 그리고 또한 개인적으로 컨테이너 기반의 컨테이너 오케스트레이션 서비스를 적용해보고 싶은 개인적인 욕심도 있었다.
하지만 쿠버네티스는 적용하기에는 많은 시간이 필요할 것 같았다. 즉 언제 모든 RT의 서버를 쿠버네티스화할 수 있을지를 전혀 가늠이 되지 않았다. 그러던 중 검색과 인프런에서 진행하는 프리온보딩을 통해 ECS라는 AWS의 서비스를 알게 되었고, 마침 현재 RT의 아키텍처와 리소스 대부분이 AWS의 서비스들을 사용하고 있으며 서버 어플리케이션이 도커 컨테이너 기반으로 돌아가고 있기 때문에 ECS를 적용하는데 오랜 시간이 걸리지 않을 것 같았다.
즉 요약하면 아래와 같은 4가지 이유로 인해 ECS를 적용했다.
1. 특정 서비스에 트래픽이 더 많은 것을 보고 스케일 아웃을 해야할 시에 유연하게 대처할 수 있도록 미리 아키텍처를 구성하고 싶었다.
2. 컨테이너 오케스트레이션을 사용하고 싶었던 개인적인 욕심
3. 쿠버네티스의 높은 진입장벽
4. EKS 혹은 쿠버네티스에 비해 ECS가 적용하기 쉽다는 점
그래서 ECS란?
AWS ECS(Elastic Container Service)는 AWS의 관리형 컨테이너 오케스트레이 서비스입니다. 컨테이너화된 어플리케이션을 쉽게 배포, 운영하고 관리할 수 있도록 도와주는 서비스입니다.
컨테이너는 어플리케이션과 해당 종속성을 격리된 환경으로 패키징하는 기술로, 이를 사용하면 애플리케이션의 이식과 확장성을 향상시킬수 있습니다. ECS는 이러한 컨테이너를 관리하고 실행하는 기능을 제공합니다.
- Chatp GPT
라고 챗지피티는 설명한다. 나의 경험으로 간단히 말해보자면 A서비스를 스케일 아웃하기 위해서는 EC2를 또 하나 만들어서 해당 EC2안에 도커를 설치하고, CI/CD를 연결해야 한다. 하지만 ECS를 쓰면 우리가 직접 EC2의 리소스를 구성하고 생성할 필요가 없다. ECS의 Service를 정의하고 해당 서비스에서 컨테이너당 CPU와 메모리 크기만을 할당하고 서비스에 몇개의 테스트를 만들지만 결정하면 된다. A서비스가 트래픽이 많아 더 많은 서버를 증설하고 싶다면 태스크의 수를 늘려주면 되는 것이다.
또한 컨테이너를 사용한다면 도커 이미지를 ECR(Elastic Container Registry)에 docker hub에 도커 이미지를 푸쉬하듯이 저장해두고, ECS가 ECR의 이미지를 pull해서 컨테이너를 실행시키면 되는 것이다.
이제 ECS에 상세히 조사해보자.
먼저 ECS에 대해 알아보기 전에 컨테이너 오케스트레이션이 뭔지 알아보자.
컨테이너 오케스트레이션
ECS는 컨테이너 오케스트레이션을 제공한다. 컨테이너 오케스트레이션은 아래와 같은 기능을 제공해준다.
1. 컨테이너 클러스터링
- 여러 대의 노드를 하나의 클러스터로 묶어 어플리케이션을 분산하여 실행하고, 자원을 효율적으로 활용하는 기능.
- 여러 대의 물리적인 또는 가상의 서버를 하나의 시스템처럼 동작하게 하는 기술
- 컨테이너를 실행하는 호스트의 자원을 효율적으로 분배, 컨테이너가 안정적으로 실행되도록 함.
- 여러 대의 컨테이너를 묶어 하나의 서버처럼 사용할 수 있도록 지원
2. 서비스 디스커버리
- 컨테이너를 자동으로 발견하고, 서비스 이름과 IP주소등을 관리하여, 어플리케이션 간의 연결을 관리하는 기능
- 클라우드 환경에서 컨테이너 생성, 배치, 이동에 따른 IP, Port 정보 업데이트 및 관리
3. 자동 스케일링(Auto Scaling)
- 어플리케이션의 트래픽 양에 따라 자동으로 컨테이너 수를 조절하여, 자원 사용량을 최적화하고, 가용성을 보장하는 기능
4. 로드 밸런싱
- 여러 대의 노드에서 실행 중인 컨테이너들을 조절하여, 트래픽을 균등하게 분배하여, 어플리케이션의 성능을 최적화하는 기능
5. 롤아웃과 롤백
- 새로운 버전의 어플리케이션을 롤아웃하고, 이전 버전으로 롤백하는 기능
6. 자동 복구
- 컨테이너나 노드의 장애시 자동으로 복구하는 기능
7. 모니터링과 로깅
- 컨테이너나 노드의 상태를 모니터링하고, 로그를 수집하여, 어플리케이션의 성능과 문제점을 분석하는 기능
8. 보안과 네트워크 기능
- 컨테이너와 노드의 보안을 관리하고, 네트워크 설정을 관리하는 기능
다양한 기능을 보면 내가 ECS를 적용하면서 기대했던 효과들 보다 훨씬 많은 기능을 제공하는 것을 볼 수 있다.
위와 같은 기능을 하는 컨테이너 오케스트레이션을 제공하는 컨테이너 오케스트레이션의 툴에는 대표적으로 GCP의 GKE(Google Kubernetes Engine), AWS의 EKS(Elastic Kubernetes Service), ECS(Elastic Container Service)가 있다.
ECS 중에서도 몇가지 서비스 종류가 있는데, 본인은 Fargate를 사용했다.
ECS Fargate는 컨테이너가 실행될 호스트 컴퓨팅에대한 고민을 하지 않아도되는 서버리스 서비스이다.
Fargate
fargate를 사용하면 컨테이너 어플리케이션을 배포하고 실행할 때 EC2의 인스턴스를 관리할 필요 없이 컨테이너를 쉽게 실행할 수 있다. AWS가 컨테이너 실행에 필요한 하드웨어 및 인프라 관리를 대신해주는 것이다. 즉 fargate를 사용하면 컨테이너 어플리케이션을 효율적으로 실행하고 관리할 수 있으며, 인프라 구축 및 관리에 필요한 복잡성을 줄이는 데 도움을 준다.
ECS에는 몇가지 주요 개념이 있다. 하나씩 알아보자.
먼저 그림으로 보면 아래와 같다.
Cluster
클러스터는 도커 컨테이너를 실행할 수 있는 논리적인 공간. 즉 컨테이너 실행 환경을 정의하는 리소스 집합으로 컨테이너화된 어플리케이션을 실행하고 관리하는 데에 사용된다. 클러스터는 여러 개의 EC2 인스턴스 혹은 AWS Fargate 작업 인스턴스로 구성된다.
즉 클러스터는 컨테이너화된 어플리케이션을 실행하는데 사용되는 가상 컴퓨팅 리소스의 모음이다.
Task
task는 컨테이너를 실행하는 최소단위이다. task는 최소 1개 이상의 컨테이너로 구성될 수 있으며 특정 task 내의 컨테이너는 모두 같은 클러스터 안에서 실행된다.
즉 task는 컨테이너 애플리케이션의 실행 단위로, 여러 개의 관련된 컨테이너들이 하나의 그룹으로 묶여 있는 것. 이를 통해 서로 다른 컨테이너들을 함께 실행하고, 복잡한 애플리케이션을 구성할 수 있다.
Task Definition
위에서 설명한 Task를 정의한 파일이라고 생각하면 된다. task는 컨테이너 기반이다. 해서 도커의 네트워크, 실행 명령, 볼륨, 환경 설정 등을 여기서 미리 정의할 수 있다. 이들을 정의하면 정의된 task definition에 따라 task가 실행되는 것이다.
Service
위에서 설명한 Task Definition을 이용하여 Task의 실행 유형, 타입, task 작업 개수, 배포 방식, 배치 방식 오토 스케일링 설정과 로드 밸런서 설정을 할 수 있게 하여 Task를 실행 및 관리하게 해준다. 즉 Task를 관리하는 상위 그룹 개념으로 보면 된다.
ECS를 적용한 RT 서비스의 그림은 아래와 같을 것이다.
위의 예시에서는 post service가 많은 트래픽이 있다고 판단하여 post service의 컨테이너는 3개를 실행중인 상황이다.
CI/CD를 추가한 그림은 아래와 같다.
1. 로컬에서 코딩후 github에 push
2. 젠킨스가 github의 리퍼지토리 pull & docker image build
3. 젠킨스가 docker hub와 AWS ECR에 도커 이미지 push
4. 젠킨스가 ECS에 ECR의 latest 이미지를 반영하여 컨테이너 다시 띄우도록 command 날림
5. 새로운 버전이 반영됨
이제 한번 직접 만들어 보자.
클러스터부터 만들어보면 아래와 같다.
AWS ECS Console -> 클러스터 -> 클러스터 생성
아래에 보이는 것과 같이 클러스터 이름, VPC, 서브넷을 선택한다. (VPC는 이미 생성했다고 가정)
인프라는 AWS Fargate.
생성을 하고 나면 아래와 같이 클러스터가 생성된다.
이번에는 docker image를 저장할 ECR(Elastic Container Registry)를 만들어보자.
AWS ECR Console -> 리포토리 생성하기
본인은 private으로 만들었고, 원하는 리포지토리 이름을 입력해주면 된다.
생성하고 나면 프라이빗 리포지토리 목록에 방금 생성한 리포지토리가 보일 것이다.
그리고 [푸시 명령 보기]을 클릭하면 해당 리포지토리에 이미지를 푸쉬하기 위한 명령어를 친절하게 알려준다.
본인은 해당 명령을 젠킨스 pipeline에서 실행할 것이다.
이제 Task Definition을 만들어보자.
AWS ECS 콘솔 -> 태스크 정의 -> 새 테스트 정의 생성(본인은 Json으로 생성 하지 않았음)
여기서 태스크의 크기, 태스크 역할, 컨테이너 설정, 로그, 도커 구성등을 설정해주면 된다. 맨 처음에 있는 시작 유형은 AWS Fargate를 선택하면 된다.
태스크 역할은 ecsTaskExecutionRole을,
컨테이너의 이미지 URI는 ECR에서 생성한 리포지토리의 URI를 선택해주면 된다. 이때 맨 마지막에 latest 태그를 붙여줘야 최신 이미지를 가져온다는 것에 주의한다.
또한 본인은 이전에 스프링의 환경 변수를 docker 명령어인 -v 옵션으로 넘겨주었는데, 여기서는 환경변수에 대문자_와 같은 법칙을 사용해줘야 한다.
예로 yml파일 기준으로 spring.profiles.active는 아래와 같이 하면 된다.
이제 서비스를 만들면 된다.
AWS ECS Console -> 원하는 클러스터 선택 -> 서비스 생성
이때 특별한 것은 없고,
배포 구성 - 어플리케이션 유형은 [서비스] 선택
원하는 서비스 이름 입력, 원하는 테스크 개수 입력
네크워킹은 본인의 VPC를 선택하고 원하는 서브넷을 선택한다. (보안 그룹 역시 마찬가지)
로드밸런싱
본인은 NLB를 사용하기 때문에 NLB를 선택하고 본인의 로드 밸런서를 선택한다. 그리고 리스너, 대상 그룹은 새로 생성하면 알아서 만들어준다.
이제 생성을 한다.
++ 본인이 매우 애먹은 것이 있는데, 바로 VPC 엔드포인트이다.
위에서 생성한 클러스터, 서비스등은 VPC안에 들어있지만 ECR은 VPC안에 들어있지 않다. (AWS의 모니터링 툴도 마찬가지)
해서 이들에 VPC안에 존재하는 서비스가 VPC 외부에 있는 서비스에 접근을 하거나 혹은 그 반대를 위해서는 반드시 VPC 엔드포인트 설정을 해줘야 한다. 본인은 아래와 같이 VPC 엔드 포인트를 설정해주었다.
VPC EndPoint
AWS의 VPC 엔드포인트는 AWS 클라우드 내의 리소스에 안전하게 액세스할 수 있는 방법을 제공하는 서비스이다.
VPC 엔드포인트는 VPC 내에서 다른 AWS 서비스나 S3 버킷과 같은 AWS 리소스로의 트래픽을 라우팅할 때 인터넷을 거치지 않고 직접 연결할 수 있게 해준다. 이는 보안, 네트워크 퍼포먼스, 데이터 전송 비용 개선에 도움이 된다.