카테고리 없음

[AEWS3기-12주차] Amazon VPC Lattice for Amazon EKS

james_janghun 2025. 4. 21. 18:22

이 포스팅은 가시다님의 AEWS 3기 스터디 포스팅내용입니다.

해당 글에서는 Service Discovery에서 Service Mesh까지의 변천사를 알아보고,

AWS 내에서 EKS 클러스터의 Gateway API를 어떻게 Lattice 서비스와 연결하는지 관계를 살펴보겠습니다.

 

또한 실습으로 다른 VPC 에서 EC2 서버를 생성하여 어떻게 Lattice 서비스를 통해 호출이 가능한지도 확인해보겠습니다.

 

배경

이번 AWS VPC Lattice에 대해서는 아래 블로깅을 참조하길 바랍니다.

https://devops-james.tistory.com/537

 

 

EKS와 같이 마이크로서비스가 많이 혼재되어 있는 서비스에서는 Service Discovery가 매우 중요합니다.

서로 간의 호출구조 파악이 어렵고 아주 빠른 agile방식으로 개발을 하고 있기 때문에 인프라 담당자가 조금이라도 방심하면 바로 일정 서비스가 마비될 수 있습니다.

 

Service Discovery란?

  • 서비스의 IP 주소 및 포트 정보를 동적으로 관리
  • 각 서비스 인스턴스를 지속적으로 Health Check
  • 서비스 관련 메타데이터와 구성 정보를 저장하고 관리

 

Service Discovery의 패턴 2가지

1) Client Side 방식

요청하는 Client 가 서비스 레지스트리를 직접 쿼리하여 호출하고자하는 Server의 엔드포인트를 조회하는 방식

https://microservices.io/i/servicediscovery/client-side-discovery.jpg

 

특징

클라이언트가 직접 서비스 레지스트리를 쿼리하여 서비스 위치를 찾는 방식(예: Netflix Eureka, AWS CloudMap)으로 클라이언트는 서비스 레지스트리를 찾아서 엔드포인트로 요청합니다. 엔드포인트만 잘 등록해놓으면 됩니다. 다만 client 에서 서버로 바로 호출하는 구조를 가지고 있기 때문에 (운영자 입장에서) 해당 텔레메트리 정보 등을 시각화하거나 분석하는데 제약이 있습니다.

 

2) Server Side 방식

Client는 프록시에 요청을 던지고 라우터(프록시)가 서비스 레지스트리를 쿼리하고 클라이언트 요청을 적절한 서비스로 라우팅하는 방식

https://microservices.io/i/servicediscovery/server-side-discovery.jpg

 

 

특징

즉 클라이언트는 프록시를 호출하여 프록시가 서버쪽으로 요청을 전달하는 방식이다. 따라서 네트워크 홉이 추가되지만 프록시에 sidecar 등을 통해서 텔레메트리 정보를 획득할 수 있다. 다만 프록시라는 홉이 추가되고, 설정 및 관리가 중요합니다.

 

Service Mesh

필요성

Service Discovery 방식으로 진행하더라도 결국 마이크로 서비스가 늘어나면서 네트워크 복잡성 증가 및 공통 기능 적용(서킷브레이커, 재시도 로직, 타임아웃, 인증 등)의 요구사항 이 생기게 됨에 따라 ServiceMesh의 필요성이 증가하게 되었습니다.

 

 

기존의 서비스 디스커버리는 클라이언트가 서버의 엔트포인트를 쿼리해서 요청하는 방식에 그쳤다면 서비스 매시는 여기에 트래픽 관리, 보안 등 여러가지 기능을 더 추가한 것입니다. (일반적으로 서버사이드 디스커버리 방식에서 발전했다고 볼 수 있습니다.)

 

작동방식

Service Mesh는 기본적으로 각 애플리케이션에 sidecar 등의 프록시를 두어 해당 프록시를 통해 모든 요청이 통제되고 관리되도록 합니다.

 

예를들어 A가 B 애플리케이션에 요청할 때는 분홍색으로 프록시 컨테이너에 요청을 넣는 방식으로 서로 소통합니다.

App Mesh - envoy proxy 사용

 

Service Mesh의 한계 

- 동-서 트래픽(서비스 간 내부 통신)에 초점을 맞추다보니 북-남 트래픽(외부-내부 통신)에 제한적

 

- 사이드카 프록시 배포가 필수적이므로 멀티 VPC 클라우드 환경에서 네트워크 복잡도 증가

 

=> 멀티클러스터를 사용하는 경우, 다중 VPC를 사용하는 경우, 여러 AWS 계정을 사용하는 경우 등 다양한 리소스의 통신제약

 

Gateway API의 등장 (kubernetes)

이러한 한계를 극복하기 위해 SIG-NETWORK 커뮤니티에서 Gateway API가 고안되었습니다. Gateway API는 다음과 같은 목표를 가지고 설계되었습니다.

  • 역할 기반 설계: 인프라 관리자, 클러스터 운영자, 애플리케이션 개발자 각각의 요구사항을 반영할 수 있도록 설계
  • 범용성: HTTP, HTTPS, gRPC 등 다양한 프로토콜을 지원하고 확장성 있게 설계
  • 표준화: Kubernetes Ingress와 같이 포터블한 표준 API가 되도록 설계
  • 확장성: 멀티 클러스터 환경 및 다양한 VPC 간의 원활한 네트워크 통합이 가능하도록 설계

 

주요 구성요소

Gateway API는 계층적 구조로 설계되어 인프라 관리자와 애플리케이션 개발자의 역할과 책임을 명확히 구분합니다. 

 

GatewayClass

- 공통 구성을 가진 게이트웨이 집합을 정의하며, 이 클래스를 구현하는 컨트롤러에 의해 관리됩니다.

- 게이트웨이는 각기 다른 컨트롤러에 의해 다양한 구성으로 구현될 수 있습니다.

    게이트웨이는 반드시 해당 클래스를 구현하는 컨트롤러의 이름을 포함하는 GatewayClass를 참조해야 합니다.

 

Gateway

- 외부 트래픽을 수신하고 내부 서비스로 라우팅하는 진입점을 정의합니다.

- 하나 이상의 Route 리소스와 연결될 수 있습니다.

 

Routes

- 트래픽을 특정 서비스로 매핑하는 프로토콜 별 규칙을 정의합니다.

- HTTPRoute, TLSRoute, TCPRoute, UDPRoute, GRPCRoute 등이 있습니다.

- Gateway 리소스에 연결되어 트래픽을 대상 서비스로 전달합니다.

https://gateway-api.sigs.k8s.io/

Gateway API Controller

Gateway API도 다른 Kubernetes 컴포넌트와 마찬가지로 Controller를 통해 동작합니다. AWS에서는 AWS Gateway API Controller를 제공하고 있습니다. 해당 공식문서의 표현을 빌리자면 다음과 같습니다.

Amazon Elastic Kubernetes Service (EKS) is a managed service that you can use to run Kubernetes on AWS without needing to install, operate, and maintain your own Kubernetes control plane or nodes. EKS's implementation of the Gateway API is through AWS Gateway API Controller which provisions Amazon VPC Lattice Resources for gateway(s), HTTPRoute(s) in EKS clusters.

 

즉 EKS 내에서 Gateway API Controller를 구현하려면 AWS VPC Lattice를 프로비저닝하여 연결한다고 합니다. 따라서 해당 리소스를 사용하려면 VPC Lattice도 같이 학습해야합니다.

 

Gateway API 구성 요소와 VPC Lattice 오브젝트 간 매핑 관계

VPC Lattice에서는 다음과 같이 매핑 관계를 가지고 있습니다. 해당 부분을 잘 살펴보는 것도 핵심이 되겠습니다.

 

 

실습

실습은 해당 워크샵으로 진행합니다. 먼저 싱글 클러스터를 통해서 EKS Gateway API와 AWS Lattice의 매핑구조를 살펴보는데 초점을 둡니다.

https://aws-ia.github.io/terraform-aws-eks-blueprints/patterns/network/client-server-communication/

 

아키텍처 소개

싱글 EKS 클러스터를 가지는 VPC가 있고 이를 호출할 Client(EC2)는 다른 VPC에 존재합니다.

EC2 클라이언트는 PC Lattice를 통해 호출하도록 구성되어있습니다.

 

프로비저닝

다음 코드를 클론하여 테라폼 코드를 실행합니다.

git clone https://github.com/aws-ia/terraform-aws-eks-blueprints.git
cd terraform-aws-eks-blueprints/patterns/vpc-lattice/client-server-communication

 

테라폼 수행 

terraform init
terraform apply -target="module.client_vpc" -auto-approve
terraform apply -target="module.cluster_vpc" -auto-approve
terraform apply -target=aws_route53_zone.primary -auto-approve

terraform apply -target="module.client_sg" -auto-approve
terraform apply -target="module.endpoint_sg" -auto-approve

terraform apply -target="module.client" -auto-approve
terraform apply -target="module.vpc_endpoints" -auto-approve

terraform apply -target="module.eks" -auto-approve
terraform apply -target="module.addons" -auto-approve

 

 

결과확인

 

Client - EC2

먼저 Client쪽에서는 EC2를 생성합니다. t2.micro 서버로  SSM 호출을 위한 Role을 지정하였습니다.

Client이므로 특별한 설정은 없습니다. 코드상에서는 EKS 클러스터와 다른 VPC에 있다 정도만 확인하시면 됩니다.

module "client" {
  source  = "terraform-aws-modules/ec2-instance/aws"
  version = "~> 5.0"

  name = "client"

  instance_type               = "t2.micro"
  subnet_id                   = module.client_vpc.private_subnets[0]
  create_iam_instance_profile = true
  iam_role_description        = "IAM role for client"
  iam_role_policies = {
    AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
  }
  vpc_security_group_ids = [module.client_sg.security_group_id]

  tags = local.tags
}

 

EKS 클러스터

여기서 확인할 것은 apps 네임스페이스에 pod2개가 서버로 존재하며, 해당 서버로 호출할 예정입니다.

또한 AWS gateway Api Controller pod가 설치되어있다는게 중요합니다.

 

EKS는 노드그룹 3개를 생성했습니다. 사실 중요한 것은 아닙니다.

EC2와 EKS 노드 그룹 생성확인

 

코드를 조금 더 살펴봅시다.

set 부분에 보면 value = "https://vpc-lattice.${local.region}.amazonaws.com" 이렇게 VPC Lattice의 엔드포인트 정보를 가져오는 것을 확인할 수 있습니다. 또한 External DNS에서 example.com 도메인 정보를 가져옵니다.

module "addons" {
  source  = "aws-ia/eks-blueprints-addons/aws"
  version = "~> 1.16"

  cluster_name      = module.eks.cluster_name
  cluster_endpoint  = module.eks.cluster_endpoint
  cluster_version   = module.eks.cluster_version
  oidc_provider_arn = module.eks.oidc_provider_arn

  enable_aws_gateway_api_controller = true
  aws_gateway_api_controller = {
    chart_version           = "v1.0.3"
    create_namespace        = true
    namespace               = "aws-application-networking-system"
    source_policy_documents = [data.aws_iam_policy_document.gateway_api_controller.json]
    set = [
      {
        name  = "clusterName"
        value = module.eks.cluster_name
      },
      {
        name  = "log.level"
        value = "debug"
      },
      {
        name  = "clusterVpcId"
        value = module.cluster_vpc.vpc_id
      },
      {
        name  = "defaultServiceNetwork"
        value = ""
      },
      {
        name  = "latticeEndpoint"
        value = "https://vpc-lattice.${local.region}.amazonaws.com"
      }
    ]
    wait = true
  }
  enable_external_dns            = true
  external_dns_route53_zone_arns = try([aws_route53_zone.primary.arn], [])
  external_dns = {
    set = [
      {
        name  = "domainFilters[0]"
        value = "example.com"
      },
      {
        name  = "policy"
        value = "sync"
      },
      {
        name  = "sources[0]"
        value = "crd"
      },
      {
        name  = "sources[1]"
        value = "ingress"
      },
      {
        name  = "txtPrefix"
        value = module.eks.cluster_name
      },
      {
        name  = "extraArgs[0]"
        value = "--crd-source-apiversion=externaldns.k8s.io/v1alpha1"
      },
      {
        name  = "extraArgs[1]"
        value = "--crd-source-kind=DNSEndpoint"
      },
      {
        name  = "crdSourceApiversion"
        value = "externaldns.k8s.io/v1alpha1"
      },
      {
        name  = "crdSourceKind"
        value = "DNSEndpoint"
      }
    ]
  }

  tags = local.tags
}

 

 

이제 VPC Lattice와 연결포인트를 잡아봅시다.

1. GatewayClass

GatewayApi 구축의 최상단으로 amazon-vpc-lattice를 메타데이터로 하는 GatewayClass를 생성합니다. 해당 내용은 쿠버네티스에서만 구현됩니다.

apiVersion: gateway.networking.k8s.io/v1beta1
kind: GatewayClass
metadata:
  name: amazon-vpc-lattice
spec:
  controllerName: application-networking.k8s.aws/gateway-api-controller

 

2. Gateway

위에서 만든 GatewayClass에서 Gateway 리소스를 생성합니다. 해당 Gateway부터는 VPC Lattice의 Service Network정보와 매핑됩니다. 해당 내용을 살펴보면 리스너를 HTTP 80으로 두고있습니다.

apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
  name: my-services
  namespace: apps
spec:
  gatewayClassName: amazon-vpc-lattice
  listeners:
    - name: http
      protocol: HTTP
      port: 80

 

이제부터 콘솔에서 확인이 가능합니다. VPC Lattice에 서비스 네트워크 보시면 확인이 가능합니다.

3. HTTP Route 설정

앞서 만든 Gateway 설정에서 HTTP 80으로 리스너설정을 하였습니다. 해당 리스너로 들어온 트래픽에 대한 라우팅 설정입니다.

잘보면 server.example.com에 대해서 my-services라는 Lattice 서비스를 타고 쿠버네티스 Service 8090포트로 라우팅 되는 것을 확인할 수 있습니다.

apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: server
  namespace: apps
spec:
  hostnames:
    - server.example.com
  parentRefs:
    - name: my-services
      sectionName: http
  rules:
    - backendRefs:
        - name: server
          kind: Service
          port: 8090
      matches:
        - path:
            type: PathPrefix
            value: /

 

AWS Lattice 콘솔에서는 server-apps: 라우팅 정보에서 확인할 수 있습니다.

 

server-apps-80-http 리스너 설정에 접두사 "/" 부분이 잘 들어간 것을 확인할 수 있습니다.

 

실제로 타겟정보인 k8s-apps-server-hcwajmmvgh 를 들어가보면 VPC Lattice 항목의 대상그룹으로 연결됩니다.

여기에 있는 IP정보는 pod 정보로 자동으로 서비스 디스커버리를 해줍니다.

 

예를들어 pod 하나를 강제로 죽여서 pod IP를 바꾸게되면 빠르게 자동으로 변경되는 것을 확인할 수 있습니다. (서비스 디스커버리 확인)

 

 

클라이언트 - 서버 호출 실습

클라이언트 서버에서 SSM을 활용해 서버에 접속합니다.

 

example.com이라는 도메인은 Route53에 프라이빗 도메인으로 등록되어 있습니다.

 

해당 정보를 잘 살펴보면 serrver.example.com은 CNAME으로 "http://server-apps-024b4a97240738db2.7d67968.vpc-lattice-svcs.ap-northeast-2.on.aws/" 라는 server-app의 서비스로 연결되어 있습니다.

 

 

해당 도메인은 server-apps 라우팅 정보에 등록된 도메인과 동일합니다.

 

따라서 이 도메인을 호출하게 되면 자동으로 target으로 호출되게 되는 것입니다.

다만 Lattie service의 호스팅 영역 ID와 Route53의 호스팅 영역 ID가 달랐는데 이 부분은 VPC Lattie의 경우 AWS에서 내부적으로 관리하는 도메인에 연계되어 있기 때문에 다르다는 것은 참고하기 바랍니다.

 

 

실제로 도메인을 통해서 호출이 되는지 확인해봅시다. 서버에서 EKS로 호출을 날려보겠습니다.

잘 호출되는 것을 확인할 수 있습니다.

curl -i http://server.example.com

 

 

왼쪽이 서버 -> 오른쪽 EKS 클러스터 pod로그(서버로그)

 

 

서버에서 nslookup으로 해당 도메인에 대해서 확인해보면 vpc latte 서비스로 연결된 것도 확인할 수 있습니다.

nslookup server.example.com

 

 

 

AWS Gateway API Controller의 동작 체크 / 로그 확인

다음 명령어를 통해서 AWS Gateway API Controller의 로그를 저장해서 볼 수 있다.

kubectl logs deployment/aws-gateway-api-controller-aws-gateway-controller-chart -n aws-application-networking-system --all-containers=true > lattice.log

 

apigw 로그

대상그룹으로 lattice 엔드포인트 정보가 표출됩니다.

externaldns 로그

example.com 도메인에 대한 정보를 필터링하고 있습니다.

 

실습 리소스 정리

terraform destroy -target="module.client_vpc" -auto-approve
terraform destroy -target="module.cluster_vpc" -auto-approve
terraform destroy -target=aws_route53_zone.primary -auto-approve

terraform destroy -target="module.client_sg" -auto-approve
terraform destroy -target="module.endpoint_sg" -auto-approve

terraform destroy -target="module.client" -auto-approve
terraform destroy -target="module.vpc_endpoints" -auto-approve

terraform destroy -target="module.eks" -auto-approve
terraform destroy -target="module.addons" -auto-approve

terraform destroy -auto-approve

 

이상으로 싱글 클러스터를 통해서 Lattice 서비스와 EKS Gateway API 연결을 알아보았습니다.