12W - VPC Lattice 기반 gateway api

개요

드디어 EKS 스터디의 마지막 주차.
이번에는 비교적 최근에 나온 vpc lattice를 gateway api로 활용하는 실습을 진행한다.
안 그래도 이전에 해보려다 시간이 부족해서 못 했는데 이번 주제로 나와서 좋았다.
다만 라티스에 대한 이해도가 부족해서, 감을 잡는데 조금 오래 걸렸다.
이에 따라 실습은 대체로 분석 위주로 진행하며, 헤매는 부분도 더러 있을 것이다.

사전 지식

VPC Lattice

VPC 라티스는 여러 VPC나 여타 다른 리소스들을 통합하여 관리할 수 있는 완전 관리형 네트워킹 서비스이다.
일종의 서비스 메시로, 네트워크를 통합적으로 구성할 수 있도록 도와준다.
그렇다고 흔히 이야기되는 서비스 메시와는 조금 결이 다른 느낌인데, 그래도 서비스 메시에서 보통 활용되는 기본 개념들을 비슷하게 가지고 있다.

구체적으로 어떤 기능을 가지고 있는지는 라티스를 구성하는 컴포넌트들을 보면 조금 더 구체화된다.

Lattice 구성 요소


라티스는 네트워크를 통합 관리하기 위한 서비스인 만큼 기존 리소스들을 한 단계 추상화시켜 개념 짓는다.
image.png
이 그림은 위의 서비스라는 컴포넌트가 구체적으로 어떻게 생겼는지 조금 더 구체적으로 보여준다.

서비스 네트워크(Service Network)

라티스의 모든 구성요소를 묶는 총체적인 그룹 경계를 서비스 네트워크라고 한다.
이 네트워크 안에 여러 컴포넌트(서비스, 리소스)들을 등록하면, 이 서비스 네트워크에 연결된 클라이언트는 해당 컴포넌트들에 통신을 보낼 수 있게 된다.
즉 서비스 네트워크는 모든 요소를 포함하는 하나의 클러스터라고 보면 되겠다.
구체적으로 여기에 연결될 수 있는 컴포넌트는 다음의 것이 있다.

이것들이 무엇인지는 아래에서 바로 볼 거고, 각 컴포넌트들은 서비스 네트워크 내에서 고유하게 식별될 수 있는 도메인 이름을 부여받는다.
이를 통해 클라이언트는 원하는 컴포넌트에 통신을 할 수 있게 되는 것이다.

클라이언트는 서비스 네트워크에 소속된 VPC를 연결해야 통신이 가능해진다.
이때 여러 개의 VPC, 또 다른 계정 간 VPC를 하나의 서비스 네트워크에 연결하는 것이 가능하다.

서비스 디렉토리(Service Directory)

서비스 메시에 모든 서비스의 엔드포인트를 등록하는 서비스 레지스트리가 있듯이 서비스 네트워크에는 서비스 디렉토리가 있다.
이 디렉토리에 각 컴포넌트의 IP가 담기게 되는데, 이 IP도 도메인 이름처럼 각 컴포넌트마다 서비스 네트워크에서 고유하게 할당된다.
그럼 이 IP는 어디에서 할당받는가?
AWS에서 관리하는 관리형 접두사 목록의 IP 대역으로부터 받는다![1]
그래서 VPC에서 서비스 네트워크에 연결해서 컴포넌트와 통신을 하려면 일단 VPC 차원에서 보안 그룹으로 해당 IP 대역으로의 아웃바운드를 허용해야 한다.
마찬가지로 등록되는 컴포넌트의 보안 그룹도 해당 IP 대역으 인바운드를 허용해야 한다.
(대충 169.254~로 시작하는, IMDS와 비슷한 정도의 IP 주소풀을 준다.)

서비스 네트워크에는 서비스 디렉토리라고 하는 등록된 모든 서비스들을 내부에서 고유한 접근할 수 있도록 식별자를 관리하는 저장소가 위치해있다.
이를 통해 클라이언트는 원하는 컴포넌트에 통신을 할 수 있게 되는 것이다.

서비스(Service)

|800
서비스는 라티스를 통해 구성되는 논리적 네트워크 단위이다.
조금 더 구체적으로 서비스로서 등록될 수 있는 것들은 다음의 것들이 있다.

서비스는 결국 서비스 네트워크 안에서 통신이 가능한 어플리케이션, 프로세스 등을 의미한다고 보면 되겠다.

라티스 서비스는 위 그림과 같이 하위로 몇 가지 구성 요소를 더 가지고 있다.

인증 정책(Auth Policy)

서비스 네트워크에서 서비스에 대한 접근 정책을 정의하는 리소스이다.
IAM 기반으로 라티스 서비스에 접근할 주체를 지정하는 것이 가능하다.

예시 흐름


실습에서 진행할 아키텍처를 간단하게 보면서 먼저 감을 잡는 시간을 가진다.

서비스 등록 과정

어떤 서비스를 등록하는 과정은 다음과 같다.

gateway api contoller를 사용한다면 위 과정은 컨트롤러가 알아서 gateway api 리소스에 맞춰서 설정해준다.
dns의 경우 external dns를 사용해 작업이 되도록 세팅할 수 있다.

트래픽 흐름

이제 클라이언트가 실제로 서비스에 접근할 때 어떻게 트래픽이 이어지고, 이를 위해 어떤 세팅들이 필요한지 알아보자.

위 통신 구조 상에서 라티스는 요청과 응답을 매개하는 프록시 역할을 수행하게 된다.
양쪽은 결국 라티스에서 관리하는 ip에 트래픽을 보내게 되기 때문에 실질적으로 두 vpc가 같은 대역을 쓰던 말던 제대로 통신이 가능하게 된다.

AWS Gateway API Controller

AWS에서 게이트웨이API를 지원하는 애드온.
ALB Controller와 다르게 위 라티스를 조작하는 식으로 동작한다.
이것 때문에 흔히 생각하는 gateway api와 조금 다르게 동작한다.

gateway api는 기본적으로는 남북 트래픽을 조금 더 잘 관리할 수 있도록, 인그레스를 대체하기 위해 만들어진 api이다.
당연히 aws gateway api controller 역시 그러한 동작을 수행하긴 하는데, 라티스에서는 더 단지 쿠버네티스 클러스터가 하나의 클러스터가 아니라 그런 클러스터들을 여러 개 서비스 네트워크 붙일 수 있도록 하는데 초점을 맞춘다.
즉 달리 말해 게이트웨이 api관련 리소스를 만들면 클러스터로 치면 남북 트래픽을 설정하는 것 같지만, 라티스 서비스 네트워크 상에서는 동서 트래픽을 설정하는 꼴이란 것이다.
그래서 외부 통신을 위해서는 사실 그대로 ALB Controller를 사용해야만 한다;;

대신 다음의 기능을 가지고 있다.

동작 원리


이 컨트롤러를 설치하면 기본적으로 알아서 GatewayClass가 세팅된다.
여기에 클러스터 관리자가 Gateway를 만들면 이것은 곧 서비스 네트워크와 1:1 매핑된다.
말 그대로 test1이란 이름으로 게이트웨이를 만들었다면, 실제 AWS 측에서도 test1이란 이름의 서비스 네트워크가 있어야만 한다.
여기에 route 관련 리소스를 만들면 이것은 라티스 서비스로서 구성된다.
route 리소스에 세팅된 hosts, match, backendRef 등의 필드가 전부 라티스 서비스의 리스너, 룰, 타겟그룹으로서 구성된다.
타겟그룹은 구체적으로는 backendRef의 서비스가 가지는 엔드포인트들로 구성이 된다고 보는 게 조금 더 명확한 표현이겠다.

실습 진행

간단한 서버 통신


첫번째 라티스 활용 실습은 위와 같이 기본적인 eks 클러스터를 구축하고, 다른 vpc에서 접근하여 통신하는 것이다.[2]
이번에도 최대한 실습을 잘 따라가면서 내용을 정리하는 것을 주목적으로 삼는다.

git clone https://github.com/aws-ia/terraform-aws-eks-blueprints.git

블루프린트 레포 파일을 받는다.
image.png
/patterns/vpc-lattice/client-server-communication/main.tf에서 리전 정보만 바꿔주면 기본 준비는 끝이다.

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

terraform apply -auto-approve

aws eks update-kubeconfig --name client-server-communication --alias client-server-communication --region ap-northeast-2

코드 분석

image.png
client 파일에는 eks와 통신하기 위한 ec2와 vpc에 대한 세팅이 담겨 있다.
SSM으로 세션 연결을 할 수 있도록 정책이 포함돼있다.
image.png
vpc 엔드포인트가 뚫리는데, SSM을 사용하기 위한 세팅이며, 이를 위한 보안 그룹도 세팅된다.

image.png
다음으로 eks는 1.30버전으로 별도의 vpc에 프라이빗 서브넷 쪽에 만들어진다.
image.png
프라이빗 서브넷에 eks가 배치되니 기본적으로 인터넷으로 나가기 위한 nat 게이트웨이도 만든다.
image.png
다음으로는 게이트웨이api 컨트롤러와 external dns를 설치한다.
external dns 부분은 아래에서 다루겠다.
image.png
그 다음 데모 어플리케이션을 헬름을 통해 배포하는 것을 확인할 수 있다.
차트는 같은 디렉토리에서 찾아볼 수 있다.

apiVersion: gateway.networking.k8s.io/v1beta1
kind: GatewayClass
metadata:
  name: amazon-vpc-lattice
spec:
  controllerName: application-networking.k8s.aws/gateway-api-controller
---
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
---
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: /

해당 차트에는 기본적인 게이트웨이 api 리소스들에 대한 양식이 정의돼있다.
게이트웨이에 정의된 80번 포트는 httpRoute에서 받아 데모 서비스의 8090포트로 보내도록 돼있다.
image.png
마지막으로 클러스터 노드들의 보안 그룹에 lattice의 ip 대역을 받을 수 있도록 룰을 추가한다.

image.png
lattice 파일에서는 라티스 서비스 네트워크를 만들고, 여기에 각 vpc를 연결한다.
데모 어플리케이션이 배포된 이후 2분을 강제로 기다리도록 세팅돼 있는데, 이걸 depends 하는 블록이 아무것도 없다.
그냥 프로비저닝 단계에서 다른 리소스들이 배포될 동안 시간을 버는 리소스라 생각하면 될 것 같다.
image.png
마지막으로 데모 어플리케이션으로 사용할 도메인에 대해 클라이언트 vpc에서 route53을 사용하도록 세팅한다.
image.png
eks 파일에서 external dns는 이 도메인을 위해서 존재한다.
즉, example.com은 external dns를 통해 route53에 등록되고 클라이언트 vpc는 해당 존의 영향을 받게 되므로 데모 주소를 현재 아키텍쳐 내에서 질의 받을 수 있게 되는 구조이다.

클라이언트에서 데모 어플리케이션 통신

image.png
배포된 환경을 분석하기 이전에 제대로 배포됐는지 확인해보자.
블루프린트 예제는 기본적으로 직접 세팅할 게 없다보니 실습 후, 사후 분석을 하는 방식으로 진행하게 되더라.

kubectl logs -f deployment/server -n apps --all-containers=true --since=1m

통신이 제대로 되는지 확인하기 위해 서버 쪽에서도 로그를 잠시 걸어둔다.

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

ssm으로 들어간 인스턴스에서는 그냥 요청을 날리기만 하면 된다.
image.png
응답이 제대로 파드로부터 오는 것이 확인된다.
image.png
서버 파드를 보아도 라티스에서 실행되는 헬스체킹들이 보이나 제대로 curl 요청을 받은 것을 확인할 수 있다.

2025/04/26 11:20:41 Receiving %!(EXTRA *http.Request=&{GET / HTTP/1.1 1 1 map[Accept:[*/*] User-Agent:[curl/8.3.0] X-Amzn-Lattice-Network:[SourceVpcArn=arn:aws:ec2:ap-northeast-2::vpc/vpc-037b672c4bad7e46c] X-Amzn-Lattice-Target:[ServiceArn=arn:aws:vpc-lattice:ap-northeast-2::service/svc-06159a634e1a2693d; ServiceNetworkArn=arn:aws:vpc-lattice:ap-northeast-2::servicenetwork/sn-0e258440c1c304c69; TargetGroupArn=arn:aws:vpc-lattice:ap-northeast-2::targetgroup/tg-07f926c2415770d89] X-Amzn-Source-Vpc:[vpc-037b672c4bad7e46c] X-Forwarded-For:[10.1.2.114]] {} <nil> 0 [] false server.example.com map[] map[] <nil> map[] 169.254.171.196:7250 / <nil> <nil> <nil> 0xc0001c8940})

해당 로그를 조금 더 자세히 보면 들어온 요청의 헤더값들을 확인할 수 있다.
일단 요청이 온 vpc의 arn 정보와 라티스 서비스 arn도 나온다.
image.png
해당 도메인으로의 질의는 라티스를 거친다는 것을 간략하게 짐작할 수 있다.

콘솔을 통한 분석

그럼 본격적으로 어떻게 환경이 구성됐길래 이게 가능한 건지도 알아보자.
image.png
먼저 vpc 콘솔에서 라티스에 서비스 네트워크를 들어가보면 하나의 서비스 네트워크가 생성된 것을 확인할 수 있다.
사진으로 찍진 않았으나 테라폼에서 설정했던 두 개의 vpc 연결도 확인할 수 있다.
이 서비스 네트워크에 등록된 하나의 서비스가 보이는데, 이것이 gateway api controller에 의해 만들어진 것으로 짐작할 수 있다.
image.png
해당 서비스 연결 id를 보면 도메인 이름이 두 개 있는 것을 볼 수 있다!
그 중 하나는 http route에서 설정한 도메인이다.
image.png
아예 해당 서비스에 들어가서 보면 라우팅 탭에 위에서 게이트웨이api 리소스로 설정했던 값들이 반영된 것이 보인다.
route에서 match 쪽 필드 그대로 리스너 룰로 만들어졌고, 어떤 서버로 포워딩을 하는 것을 확인할 수 있다.
image.png
구체적으로 해당 리소스는 타겟그룹으로, 이 타겟 그룹에는 데모 파드들이 위치한다.
image.png
위 파드들은 route에서 설정했던 서비스의 엔드포인트들이다.

하지만 이 정도로는 만족할 수 없다.
image.png
위의 설정들에 나온 도메인 이름이 구체적으로 ip 추적이 가능한 이유는 위에서 만들었던 route53 덕분이다.
보다시피 server.example.com에 대해 cname 레코드가 만들어진 것을 볼 수 있다.
그리고 이에 대한 값은 서비스가 기본으로 가지던 이름이다.
image.png
이를 기반으로 다시 라티스 서비스를 들어가보면 조금 더 명확해진다.
이 서비스는 위 route53에 의해 질의가 될 수 있도록 세팅돼있는 상태이다.
image.png
클라이언트 vpc에서 도메인 질의를 했을 때 나왔던 169로 시작하는 ip는 aws에서 관리하는 ip 대역이다.
조금 더 구체적으로는 aws 관리형 접두사 리스트라고 해서, 서비스마다 부여하는 ip 대역이 있다.[3]
이 대역으로 가는 트래픽은 aws 측으로 넘어가는데 이 부분부터는 aws에서 알아서 트래픽이 우리가 설정한 리소스나 엔드포인트로 갈 수 있도록 도와준다.
마치 인스턴스에서 IMDS를 얻어오는 것과 같은 원리라고 보면 되겠다.

gateway api controller 동작 추적

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

도대체 gateway api controller는 무슨 일을 했던 걸까?
로그를 뜯어본다.
image.png
가장 초반에는 실행이 시작되며 게이트웨이api 리소스에 와치를 걸고, 이후에 해당 리소스들이 생성되자 콘솔에서 봤던 것들이 정직하게 만들어지는 것을 확인할 수 있다.
image.png
이러한 동작을 할 수 있는 이유는 위와 같이 라티스에 대한 조작, 그리고 필요한 정보를 조회하기 위한 읽기 권한을 가지고 있기 때문이다.

리소스 정리

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

실습에 사용했던 것들을 일일히 지워준다.
image.png
이때 실습을 통해 세팅된 각 게이트웨이 리소스가 안 지워지는 이슈가 있을 수도 있는데, 직접적으로 정리해주는 게 좋다.[4]

멀티 클러스터 보안 통신 구축

이제 생소한 라티스와 게이트웨이api 맛보기는 끝났다.
조금 더 복잡한 아키텍처를 구성하고, 클러스터 내부에서 어떻게 설정이 이뤄졌는지도 조금 더 상세하게 보도록 한다.

이번에는 이렇게 아키텍처를 구성한다![5]
보기만 해도 어지러운데, 차근차근 뜯어보자.
먼저 전체 구성의 목표는 멀티 클러스터 간 보안 통신을 하는 것이다.[6]
각 클러스터는 다른 VPC에 위치하고 있는 상태에서 암호 통신을 할 것이다.
이때 흔한 서비스 메시가 그렇듯이, 각 클러스터의 어플리케이션은 통신 간 암호화와 보안에 대해 신경 쓸 필요 없게 세팅한다.
그래서 어플리케이션 코드의 수정을 하지 않을 수 있도록 각 어플리케이션 앞단에 사이드카 프록시로 엔보이를 둘 것이다.
이 엔보이를 배치하기 위해, 각 워크로드가 배치될 때 Mutating Admission Webhook을 해주는 Kyverno를 둔다.
키베르노는 배치되는 워크로드에 두 가지 작업을 한다.

그리고 이렇게 배치된 엔보이가 해주는 역할은 구체적으로 다음과 같다.

그럼 위 아키텍쳐에 나온 번호에 맞춰 각 리소스와 동작을 정리하자면,

  1. HttpRoute 설정
    1. 라티스 gateway api로 서비스를 노출한다.
    2. 백엔드 서비스와 경로, 라우팅 규칙을 명시한다.
    3. 키베르노 정책을 설정한다.
  2. 엔보이 사이드카 주입
    4. aws 신원을 통해 aws api로의 요청을 서명한다.Automatically signs AWS API requests with AWS credentials
    5. 서비스 간 통신을 암호화한다.
  3. PCA
    1. 인증서를 관리한다.
    2. 구체적으로 라티스의 커스텀 도메인에 대한 인증서를 설정한다.
    3. 이를 기반으로 TLS 통신이 가능하게 한다.
  4. IAM 정책
  5. External DNS
    1. gateway api 컨트롤러의 DNS엔드포인트를 모니터링한다.
    2. 이에 대한 DNS 레코드를 Route53에 만들고 업데이트한다.
  6. App1 → App2 요청 흐름
    1. 서비스 네트워크를 통해 라우팅된다.
    2. IAM 정책을 통해 인증된다.
    3. 위에서 설정된 인증서를 통해 암호화 통신을 한다.
  7. App2 → App1 응답 흐름
    1. 사실 그냥 위와 같다.

환경 세팅

기본 환경 세팅


이번 실습도 몇 가지 단계를 거쳐서 세팅을 해야 하는데, 먼저 두 클러스터가 사용하게 될 기본 인프라를 구성해야 한다.
https를 위한 기본적인 인증서 세팅, 도메인, 라티스 세팅을 진행해야 한다.
image.png
environment 디렉토리에 main.tf에서 리전을 수정해준다.

cd terraform-aws-eks-blueprints/patterns/vpc-lattice/cross-cluster-pod-communication/environment/
terraform init
terraform apply --auto-approve

일단 기본 환경을 배포한다.
image.png
여기에는 기본적인 route53과, 라티스에 통신을 하기 위해 사용되는 기본적인 롤이 들어있다.
이름을 보면 알겠지만, 이 롤은 나중에 엔보이를 위해 쓰인다.

각 클러스터 세팅

다음으로는 각 클러스터를 구축해야 한다.
image.png
cluster 디렉토리에서도 마찬가지로 main.tf에서 리전을 수정해주자.
보다시피 클러스터의 ip 대역을 동일하게 세팅한다.
대역 충돌이 발생하지 않는 라티스의 장점을 간접적으로 체험해볼 수 있으렷다.

cd ../cluster/
./deploy.sh cluster1
eval `terraform output -raw configure_kubectl`

./deploy.sh cluster2
eval `terraform output -raw configure_kubectl`

다음 각 클러스터를 배포한다.
image.png
해당 스크립트는 테라폼 워크스페이스를 분리해서 각각을 적용하도록 돼있다.

클러스터 코드 분석

테라폼 코드로 보면 전체적으로 이전 실습과 비슷하다.
image.png
그러나 주의 깊게 볼 부분이 있는데, 이번에는 컨트롤러를 설치할 때 사용할 서비스 네트워크를 명시한다.
두 클러스터가 배포될 때 둘다 lattice-gateway를 사용하게 되어 결과적으로 같은 서비스 네트워크를 사용한다.
image.png
다만 뮤테이팅을 걸기 위해 키베르노를 설치한다.
image.png
이번에는 단계를 나누어 플랫폼 차트를 먼저 배포하는 것을 볼 수 있다.
데모 어플리케이션이 배포되기 이전에 설정돼야 하는 키베르노 정책 리소스와 게이트웨이 리소스를 먼저 배포하는 것이다.
여기에 allowedCluster 부분이 설정돼있는데, 코드를 뜯어본 바로는 이 부분은 사실 없어도 된다.
(구체적으로는 플랫폼 차트 values 파일에 해당 필드가 없어도 된다.)
image.png
게이트웨이 리소스에 조금 변화가 있는데, 이번에는 네임스페이스를 지정하면서 셀렉터를 기반으로 매칭한다.
image.png
tls 세팅에 대해서는 두 가지 진입점을 만드는데, 이것도 실습 세팅 상에서는 사실 custom domain쪽만 있으면 된다.
image.png
여기에 aws gateway api controller만의 리소스인 IAMAuthPolicy도 볼 수 있다.
게이트웨이에 라티스의 서비스를 호출할 수 있는 권한을 정의한다.
image.png
여기에 키베르노의 리소스도 정의돼있다.
보다시피 초기화 컨테이너로 iptables를 건드려서 트래픽이 엔보이를 향하도록 두고 있고, 동시에 엔보이도 배포한다.
iptables 명령어는 다음의 설정을 담고 있다.

즉, 나가는 모든 요청은 엔보이를 거치게 된다는 것이다.
근데 여기에서 조금 주의해서 봐야할 부분이 있다고 생각이 든다.
내가 iptables를 제대로 보고 있는 게 맞다면, 이건 들어오는 트래픽에 대해서는 작동하지 않는다.
아까 위에서 어플리케이션은 http로 통신을 한다고 했는데, 요청을 보내는 측에서 http 요청을 보내는 것이 https로 변환되는 것만 제대로 보장되는 세팅인 것이다.
하지만 위에서 httproute에 tls 관련 라우팅 시 터미네이션이 일어나도록 세팅됐기 때문에 결과적으로는 요청을 받는 측도 http 요청을 받게 될 것이다.

그럼 엔보이는 어떤 식으로 세팅이 되는가?
해당 설정은 여기에서 확인할 수 있다.[7]
image.png
일단 이건 엔보이 설정 파일이다.
http 필터에 흥미로운 게 두 개 보인다.
일단 아래에는 SigV4 서명 설정 필터가 여기에 있는데, 특정 헤더를 제외한 트래픽에 대해 서명을 진행하는 설정이 담겨있다.
그리고 그 위로 포워드 프록시로서의 필터가 들어가는데, dns 질의를 127.0.0.1로 거는 것이 보인다..
사실 이 부분은 명확하게 아직 파악을 못 했다.
image.png
다만 일반적인 엔보이 세팅에 기반해 추측을 하자면, 단서는 클러스터 쪽 설정에 있다.
클러스터에는 엔드포인트가 있어야 하는데, 여기에는 엔드포인트가 보이지 않는다.
대신 클러스터 타입에 대한 정의가 내려져 있는데, 여기에도 동적 포워드 프록시로 세팅이 들어간다.
즉 현재 엔보이가 포워드 프록시로서 동작하고 있으며 실제 트래픽을 보내야 하는 업스트림 호스트를 dns 질의를 통해 찾아 보내도록 하는 세팅이라는 것이다.

클러스터 쪽에 tls 설정이 들어간 것이 보인다.

cat /etc/envoy/envoy.yaml.in | envsubst \$AWS_REGION,\$JWT_AUDIENCE,\$JWT_JWKS,\$JWT_ISSUER,\$JWKS_HOST,\$APP_DOMAIN > /etc/envoy/envoy.yaml
aws acm-pca get-certificate-authority-certificate --certificate-authority-arn $CA_ARN --region $AWS_REGION --output text > /etc/pki/ca-trust/source/anchors/internal.pem
update-ca-trust extract

cat /etc/envoy/envoy.yaml
/usr/local/bin/envoy --base-id 1 -l trace -c /etc/envoy/envoy.yaml

실제 엔보이가 실행될 때는 이 스크립트를 거친다.
위에 환경변수로서 세팅된 값들을 먼저 갈아치우고, PCA로부터 인증서를 받아온다.
(이게 아까 기본 환경 세팅 부분에서 봤던 롤이 사용되는 부분이다.)
그리고 컨테이너 속 신뢰된 CA 정보를 업데이트한다.
결과적으로 앞으로 통신할 때 사용되는 인증서를 서명한 CA에 대해 엔보이는 정말 신뢰할 수 있는 신뢰 기관으로서 인식하게 될 것이다.

image.png
위 플랫폼 차트가 배포된 이후에 데모 어플리케이션 차트가 배포된다.
image.png
디플로이먼트에서는 실행 프로세스의 그룹이 1000이 되도록 세팅한다.
결과적으로 데포 어플리케이션의 트래픽은 위 iptables 규칙 상에서 8080포트로 보내질 것이다.
image.png
httproute는 여기에 있는데, 위에서 본 두 개의 tls 게이트웨이 중 커스텀 도메인 부분만 활용하는 것을 확인할 수 있다.
image.png
이번에는 httproute에 대한 IAMAuthPolicy 리소스도 만든다.
여기에서 allowed 관련 필드가 사용되는데, 이 httpRoute로 들어올 수 있는 트래픽의 명세를 정의하는 것으로 보인다.
이 설정은 라티스 서비스로 들어가는 트래픽에 대해 위 조건을 만족시키지 않는 요청을 거부시킬 것이다.

확인

kubectl --context eks-cluster1 \
  exec -ti -n apps deployments/demo-cluster1-v1 -c demo-cluster1-v1 \
  -- curl demo-cluster2.example.com

image.png
각 클러스터에서 서로를 향해 요청을 날렸을 때 응답이 돌아온다면 성공이다.

kubectl --context eks-cluster1 exec -ti -n apps deployments/demo-cluster1-v1 -c demo-cluster1-v1 -- curl demo-cluster1.example.com

image.png
그러나 보다시피 자신을 향한 요청은 성공하지 못한다.
이건 위에서 IAMAuthPolicy에 서로의 클러스터에 대해서만 통신을 허용했기 때문이다.

kubectl --context eks-cluster1 exec -ti -n apps deployments/demo-cluster1-v1 -c demo-cluster1-v1 -- curl demo-cluster1-v1 

image.png
물론 위 통신은 라티스를 통하는 도메인을 이용했기 때문에 막히는 거고, 그냥 클러스터 내부의 서비스 도메인을 이용해 요청하면 당연히 통신이 가능하다.

apiVersion: v1
kind: Pod
metadata:
  name: debug
  namespace: apps
spec:
  containers:
  - image: nicolaka/netshoot
    name: debug
    command: [sh,-c,"tail -f /dev/null"]
  terminationGracePeriodSeconds: 1

간단하게 테스트를 할 수 있는 파드를 배포해보자.

keti -n apps debug -- curl demo-cluster2.example.com 

image.png
이 요청 역시 실패하는데, 이 파드는 위 엔보이가 세팅돼있지 않기에 라티스로 통신할 권한이 없기 때문이다.

하지만 언뜻 생각하면 이상하게 느껴지기도 한다.
일단 IAMAuthPolicy 상에서는 클러스터와 네임스페이스만 따지기 때문에 위 파드를 제한을 받을 일이 없다.
image.png
image.png
또한 eks 클러스터 콘솔에서 파드 아이덴티티 설정에 들어가보면, apps 네임스페이스의 default 서비스어카운트를 가지는 파드는 이 파드 아이덴티티를 가지고 aws 리소스와 통신을 할 수 있다.
이때 파드 아이덴티티는 이 조건을 만족하는 파드들이 해당 롤의 권한을 가진다는 의미 정도밖에 되지 않는다.
image.png
서비스 네트워크 액세스 정책을 보면 anonymous 주체는 통신을 할 수 없도록 돼있다.
방금 만든 디버그용 파드는 SigV4 서명이 돼있지 않기에 권한은 있을지라도 정확하게 신원이 누구인지 확인이 되지 않는다.
그렇기 때문에 익명으로서 요청이 인식되고 라티스의 서비스를 호출할 권한이 없다고 인식되어버리는 것이다.

하지만 데모 어플리케이션의 통신 주체는 엔보이이다.
엔보이는 위에서 보았듯이 http 요청에 대해 SigV4 서명을 받는 필터를 거친 후에 프록시 작업을 수행한다.
그렇기 때문에 엔보이의 신원은 명확하게 인식되고, 이로부터 통신이 가능해지게 되는 것이다.

그럼 어떻게 해야 내가 만든 커스텀 워크로드가 통신을 할 수 있을까?

apiVersion: apps/v1
kind: Deployment
metadata:
  name: debug
  namespace: apps
spec:
  selector:
    matchLabels:
      app: debug
  replicas: 1
  template:
    metadata:
      labels:
        app: debug
    spec:
      containers:
      - image: nicolaka/netshoot
        name: debug
        command: [sh,-c,"tail -f /dev/null"]
      terminationGracePeriodSeconds: 1

image.png
키베르노의 정책으로 디플로이먼트면 뮤테이팅이 발동하게 했으니 이렇게만 세팅하면 당연히 주입이 일어날 거라 생각했는데, 왜인지 주입이 일어나지 않았다.

리소스 정리

./destroy.sh cluster2
./destroy.sh cluster1

SN=$(aws vpc-lattice list-service-networks --query 'items[?name==`lattice-gateway`].id' --output text)
if [ -n "$SN" ]; then
    aws vpc-lattice delete-service-network --service-network-id "$SN"
fi
cd ../environment
terraform destroy -auto-approve

결론

스터디 내용만 보고 실습을 먼저 진행해서 감을 잡고 작동 원리와 문서를 정리하려고 했는데, 상당히 난항을 겪었다.
게이트웨이 api를 이스티오에 적용하는 것을 먼저 해보다보니 자연스럽게 이스티오 기준으로 생각을 하면서 진행했던 것이 화근이었던 것 같다.
그걸 떠나서도 이번 실습이 AWS에 빠삭하지 않는 내게 꽤나 어려웠다는 것도 한 몫한 것 같다.
이번에 얻었던 수확 중 하나는 라티스에 대한 기본적인 사용법, 그리고 AWS sdk를 거치지 않는 요청은 파드 아이덴티티를 통하더라도 SigV4 서명이 되지 않는 다는 것.

이번 내용은 조금 더 개인 정리가 필요할 것 같다.

번외 - gateway 리소스로 새로운 서비스 네트워크 만들기?

apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
  name: test
  namespace: lattice-gateway
spec:
  gatewayClassName: amazon-vpc-lattice
  listeners:
    - name: http-listener
      port: 81
      protocol: HTTP
      allowedRoutes:
        kinds:
          - kind: HTTPRoute
        namespaces:
          from: Selector
          selector:
            matchLabels:
              allow-attachment-to-infra-gw: "true"

게이트웨이는 서비스 네트워크를 나타낸다고 했으니, 임의로 하나를 만들면 새로운 서비스 네트워크가 만들어질 것이라고 생각했다.
image.png
그러나 막상 해보니 서비스 네트워크를 찾을 수 없다면서 게이트웨이는 계속 준비되지 않았다.
image.png
여태까지 보니까 게이트웨이의 이름은 곧 서비스 네트워크의 이름이다.
그래서 서비스 네트워크를 만들어봤다.
image.png
그러자 성공적으로 게이트웨이가 초기화됐다.
여기에서 알 수 있었던 것은, AWS에서는 게이트웨이를 만든다고 해서 실제 리소스가 생성되는 것이 아니란 것이었다.
이것은 이스티오에서 게이트웨이 api를 사용할 때와 매우 다른 동작이다.
AWS에서는 gateway api로 라티스를 전체적으로 관리할 수 있도록 설계하고 만든 게 아닌 것으로 보인다.
오히려 이미 존재하는 라티스의 서비스 네트워크를 기반으로 그 위에 서비스를 설정할 수 있도록 만든 것이다.
image.png
게이트웨이 리소스가 활성화되도 vpc 결합조차 일어나지 않는다.
image.png
혹시 vpc 연결을 하는 리소스가 있지 않을까 해서 찾아보니 있긴 있었다.
어쩌면 서비스 네트워크가 알아서 만들어지지 않는다는 점에서 내가 너무 비약적으로 추측을 하고 있는 것일 수도 있겠다.

aws gateway api controller에 대한 생각 정리

아직 완전히 정리되지는 않았지만, 중간 중간 들었던 생각을 잠시 나열한다.

생소해서 그런 것도 있겠지만 라티스를 기반으로 하는 gateway api에는 조금 이질감이 들었다.

gateway api는 다양한 설정과 기능을 담을 수 있도록 계속 개발이 진행되고 있는 만큼, 지금 섣불리 판단하기는 이르다고 본다.
무엇보다 아직 내가 aws gateway api controller에 대해 함부로 판단을 할 수 있을 만큼의 지식이 없다.

이전 글, 다음 글

다른 글 보기

이름 index noteType created
1W - EKS 설치 및 액세스 엔드포인트 변경 실습 1 published 2025-02-03
2W - 테라폼으로 환경 구성 및 VPC 연결 2 published 2025-02-11
2W - EKS VPC CNI 분석 3 published 2025-02-11
2W - ALB Controller, External DNS 4 published 2025-02-15
3W - kubestr과 EBS CSI 드라이버 5 published 2025-02-21
3W - EFS 드라이버, 인스턴스 스토어 활용 6 published 2025-02-22
4W - 번외 AL2023 노드 초기화 커스텀 7 published 2025-02-25
4W - EKS 모니터링과 관측 가능성 8 published 2025-02-28
4W - 프로메테우스 스택을 통한 EKS 모니터링 9 published 2025-02-28
5W - HPA, KEDA를 활용한 파드 오토스케일링 10 published 2025-03-07
5W - Karpenter를 활용한 클러스터 오토스케일링 11 published 2025-03-07
6W - PKI 구조, CSR 리소스를 통한 api 서버 조회 12 published 2025-03-15
6W - api 구조와 보안 1 - 인증 13 published 2025-03-15
6W - api 보안 2 - 인가, 어드미션 제어 14 published 2025-03-16
6W - EKS 파드에서 AWS 리소스 접근 제어 15 published 2025-03-16
6W - EKS api 서버 접근 보안 16 published 2025-03-16
7W - 쿠버네티스의 스케줄링, 커스텀 스케줄러 설정 17 published 2025-03-22
7W - EKS Fargate 18 published 2025-03-22
7W - EKS Automode 19 published 2025-03-22
8W - 아르고 워크플로우 20 published 2025-03-30
8W - 아르고 롤아웃 21 published 2025-03-30
8W - 아르고 CD 22 published 2025-03-30
8W - CICD 23 published 2025-03-30
9W - EKS 업그레이드 24 published 2025-04-02
10W - Vault를 활용한 CICD 보안 25 published 2025-04-16
11W - EKS에서 FSx, Inferentia 활용하기 26 published 2025-04-18
11주차 - EKS에서 FSx, Inferentia 활용하기 26 published 2025-05-11
12W - VPC Lattice 기반 gateway api 27 published 2025-04-27

관련 문서

이름 noteType created
Amazon VPC Lattice knowledge 2025-04-23
AWS Gateway API Controller knowledge 2025-04-27

참고


  1. https://www.gateway-api-controller.eks.aws.dev/latest/guides/environment/ ↩︎

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

  3. https://docs.aws.amazon.com/vpc/latest/userguide/working-with-aws-managed-prefix-lists.html ↩︎

  4. https://www.gateway-api-controller.eks.aws.dev/latest/guides/getstarted/#cleanup ↩︎

  5. https://aws-ia.github.io/terraform-aws-eks-blueprints/patterns/network/cross-cluster-pod-communication/ ↩︎

  6. https://aws.amazon.com/ko/blogs/containers/secure-cross-cluster-communication-in-eks-with-vpc-lattice-and-pod-identity-iam-session-tags/ ↩︎

  7. https://github.com/aws-samples/amazon-eks-security-immersion-day/blob/mainline/docker/envoy/envoy.yaml.in ↩︎