VPC에서 Amazon ECS 서비스 연결하기 위한 모범 사례
VPC에서 Amazon ECS 서비스 연결하기 위한 모범 사례
VPC 안에서 Amazon ECS 태스크를 사용해 모놀리식(monolithic) 애플리케이션을 독립적으로 배포·확장할 수 있는 여러 부분으로 나누고, 이 부분들 사이의 통신을 구성하는 다양한 방법과 장단점을 알아봐요.
출처: 문서
본문
VPC에서 Amazon ECS 태스크를 사용하면 모놀리식 애플리케이션을 보안 환경에서 독립적으로 배포·확장할 수 있는 여러 부분으로 나눌 수 있어요. 이런 아키텍처를 서비스 지향 아키텍처(SOA) 또는 마이크로서비스라고 불러요. 그러나 VPC 안팎의 모든 부분들이 서로 통신할 수 있게 만드는 것은 어려울 수 있어요. 통신을 쉽게 만들어 주는 몇 가지 접근 방식이 있으며, 각각 장단점이 달라요.
Service Connect 사용하기
Service Connect를 권장해요. 이는 서비스 검색, 연결성, 트래픽 모니터링을 위한 Amazon ECS 구성을 제공해요. Service Connect를 사용하면 애플리케이션이 짧은 이름(short name)과 표준 포트로 같은 클러스터나 다른 클러스터의 서비스(같은 리전의 VPC 간 포함)에 연결할 수 있어요. 자세한 내용은 Amazon ECS Service Connect 참고.
Service Connect를 사용할 때 Amazon ECS가 서비스 검색의 모든 부분을 관리해요:
- 검색 가능한 이름 생성
- 태스크가 시작되고 중지될 때 각 태스크의 항목을 동적으로 관리
- 이름을 검색하도록 구성된 에이전트를 각 태스크에서 실행
애플리케이션은 DNS 이름의 표준 기능을 사용해 이름을 조회하고 연결을 만들 수 있어요. 이미 이런 방식을 사용한다면 Service Connect를 쓰기 위해 애플리케이션을 수정할 필요가 없어요.
변경은 배포 중에만 발생 (Changes only happen during deployments)
각 서비스와 태스크 정의 안에 완전한 구성을 제공하면, Amazon ECS가 각 서비스 배포에서 이 구성의 변경을 관리해 하나의 배포에 있는 모든 태스크가 동일하게 동작하도록 보장해요.
예를 들어 DNS를 서비스 검색으로 사용할 때 흔한 문제는 마이그레이션 제어예요. DNS 이름을 새로운 대체 IP 주소를 가리키도록 바꾸면 모든 클라이언트가 새 서비스를 사용하기 시작할 때까지 최대 TTL 시간이 걸릴 수 있어요. Service Connect에서는 클라이언트 배포가 클라이언트 태스크를 교체해 구성을 업데이트해요. 배포 circuit breaker와 같은 배포 구성을 구성해 다른 배포와 같은 방식으로 Service Connect 변경에도 영향을 줄 수 있어요.
서비스 검색 사용하기 (Using service discovery)
서비스 간 통신의 또 다른 접근 방식은 서비스 검색을 이용한 직접 통신이에요. 이 방식에서는 Amazon ECS와 AWS Cloud Map 서비스 검색 통합을 사용할 수 있어요.
서비스 검색을 사용하면 Amazon ECS가 실행된 태스크 목록을 AWS Cloud Map과 동기화해, 해당 서비스의 하나 이상의 태스크 내부 IP 주소로 해석되는 DNS 호스트 이름을 유지해요. Amazon VPC의 다른 서비스들은 이 DNS 호스트 이름을 사용해 내부 IP 주소로 다른 컨테이너에 직접 트래픽을 보낼 수 있어요. 자세한 내용은 Service discovery 참고.
예를 들어 위 다이어그램에는 세 개의 서비스가 있어요. service-a-local은 컨테이너 하나를 가지며 두 개의 컨테이너를 가진 service-b-local과 통신해요. service-b-local은 또한 컨테이너가 하나인 service-c-local과 통신해야 해요. 세 서비스의 모든 컨테이너는 AWS Cloud Map의 내부 DNS 이름을 사용해 통신해야 하는 다운스트림 서비스 컨테이너의 내부 IP 주소를 찾을 수 있어요.
이 방식은 낮은 지연 시간을 제공해요. 언뜻 보기에도 컨테이너 사이에 추가 구성 요소가 없어 단순해요. 트래픽이 한 컨테이너에서 다른 컨테이너로 직접 이동하거든요.
이 접근 방식은 각 태스크가 고유한 IP 주소를 가지는 awsvpc 네트워크 모드에서 적합해요. 대부분의 소프트웨어는 IP 주소로 직접 해석되는 DNS A 레코드만 지원해요. awsvpc 네트워크 모드에서는 각 태스크의 IP 주소가 A 레코드예요. 그러나 bridge 네트워크 모드를 사용하면 여러 컨테이너가 같은 IP 주소를 공유할 수 있어요. 또한 동적 포트 매핑 때문에 컨테이너들이 그 단일 IP 주소에서 임의의 포트 번호를 할당받게 돼요. 이 경우 A 레코드만으로는 서비스 검색이 충분하지 않아요. IP 주소와 포트 번호를 모두 추적할 수 있는 SRV 레코드도 사용해야 해요. 이 레코드 유형은 IP 주소와 포트 번호를 모두 추적할 수 있지만 애플리케이션을 적절히 구성해야 해요. 사용하는 일부 사전 제작 애플리케이션은 SRV 레코드를 지원하지 않을 수 있어요.
awsvpc 네트워크 모드의 또 다른 장점은 각 서비스마다 고유한 보안 그룹을 가진다는 점이에요. 이 보안 그룹을 구성해 해당 서비스와 통신해야 하는 특정 업스트림 서비스의 인커넷 연결만 허용할 수 있어요.
서비스 검색을 통한 직접 서비스 간 통신의 주요 단점은 재시도(retry)와 연결 실패 처리를 위한 추가 로직을 직접 구현해야 한다는 점이에요. DNS 레코드는 캐시 시간을 제어하는 TTL(time-to-live) 기간을 가져요. DNS 레코드가 업데이트되고 캐시가 만료되어 애플리케이션이 최신 DNS 레코드를 사용하게 되는 데 시간이 걸려요. 그래서 애플리케이션이 더 이상 존재하지 않는 다른 컨테이너를 가리키도록 DNS 레코드를 해석하게 될 수도 있어요. 애플리케이션은 재시도를 처리하고 잘못된 백엔드를 무시하는 로직을 가져야 해요.
내부 로드 밸런서 사용하기 (Using an internal load balancer)
서비스 간 통신의 또 다른 접근 방식은 내부 로드 밸런서를 사용하는 것이에요. 내부 로드 밸런서는 VPC 안에 완전히 존재하며 VPC 안의 서비스에만 접근할 수 있어요.
로드 밸런서는 각 서브넷에 중복 리소스를 배포해 고가용성을 유지해요. serviceA의 컨테이너가 serviceB의 컨테이너와 통신해야 할 때 로드 밸런서에 연결을 열어요. 그러면 로드 밸런서가 serviceB의 컨테이너에 연결을 열어요. 로드 밸런서는 각 서비스 간의 모든 연결을 관리하는 중앙 집중식 장소 역할을 해요.
serviceB의 컨테이너가 중지되면 로드 밸런서는 그 컨테이너를 풀(pool)에서 제거할 수 있어요. 로드 밸런서는 또한 풀의 각 다운스트림 대상에 대해 상태 검사(health check)를 수행하고, 건강해질 때까지 풀에서 잘못된 대상을 자동으로 제거할 수 있어요. 애플리케이션은 다운스트림 컨테이너가 몇 개인지 알 필요가 없어져요. 그냥 로드 밸런서에 연결만 열면 돼요.
이 접근 방식은 모든 네트워크 모드에 유리해요. 로드 밸런서는 awsvpc 네트워크 모드에서 태스크 IP 주소를 추적할 수 있고, bridge 네트워크 모드에서는 IP 주소와 포트의 더 복잡한 조합도 추적할 수 있어요. 여러 컨테이너가 같은 Amazon EC2 인스턴스에 호스팅되어 있고 다른 포트를 사용하더라도 모든 IP 주소·포트 조합에 트래픽을 고르게 분산해요.
이 접근 방식의 단점 하나는 비용이에요. 고가용성을 위해 로드 밸런서는 각 가용 영역에 리소스가 필요해요. 로드 밸런서 비용과 로드 밸런서를 통과하는 트래픽 양에 대한 오버헤드를 지불해야 하므로 추가 비용이 발생해요.
하지만 여러 서비스가 하나의 로드 밸런서를 공유하면 오버헤드 비용을 줄일 수 있어요. 이는 Application Load Balancer를 사용하는 REST 서비스에 특히 적합해요. 트래픽을 다른 서비스로 라우팅하는 경로 기반 라우팅 규칙을 만들 수 있어요. 예를 들어 /api/user/*는 user 서비스의 컨테이너로, /api/order/*는 관련 order 서비스로 라우팅할 수 있어요. 이 방식으로 하나의 Application Load Balancer만 지불하고 API에 일관된 URL 하나를 가지게 돼요. 하지만 트래픽은 백엔드의 다양한 마이크로서비스로 분산될 수 있어요.