Kubernetes

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

yu3papa 2025. 4. 26. 13:10

K8S Gateway API 등장 배경

Kubernetes의 네트워킹은 마이크로서비스 아키텍처의 발전과 함께 진화해왔습니다.

한 개의 Kubernetes 클러스터 사용

단일 클러스터 내부에 배포되는 Kubernetes 서비스들은 클러스터 내부의 DNS 인 CoreDNS 및 Ingress 리소스를 통해서 내부 서비스 간 통신 및 외부로의 통신을 구현했습니다.

여러 개의 Kubernetes 클러스터를 사용

애플리케이션이 확장되고, 조직의 시스템의 개수가 증가하면서, 여러개의 K8S 클러스터를 사용하게 됩니다. EKS에서는 서비스들이 독자적인 VPC 및 Kubernetes 클러스터에 운영되기 시작하면서 클러스터 운영자들은 이런 VPC 간 / 클러스터 간의 애플리케이션 네트워킹에 대해서 고민하게 됩니다.

이러한 멀티 클러스터 환경에서는 Service Discovery가 중요해지게 됩니다.

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

이러한 Service Discovery 패턴은 2가지로 구분됩니다.

  • 클라이언트 사이드
    • 클라이언트가 직접 서비스 레지스트리를 쿼리하여 서비스 위치를 찾는 방식(예: Netflix Eureka, Spring Cloud)

  • 서버 사이드
    • 로드 밸런서가 서비스 레지스트리를 쿼리하고 클라이언트 요청을 적절한 서비스로 라우팅

AWS에서 EKS는 자신만의 VPC를 가지고 운영됩니다. 여러개의 EKS클러스터에서 사용하는 2개 이상의 VPC에서는

  • VPC Peering, PrivateLink, AWS Transit Gateway 등의 다양한 옵션을 통해 각 VPC를 연결합니다.

 

Service Mesh

마이크로 서비스가 늘어나면서 네트워크 복잡성 증가 및 공통 기능 적용(서킷브레이커, 재시도 로직, 타임아웃, 인증 등)의 요구사항 이 생기면서 Service Mesh 솔루션이 등장하게 됩니다.

  • Service Mesh
    • AWS AppMesh, Istio, Linkerd 등으로 트래픽 관리, 보안, 관찰성 강화
    • 마이크로서비스 간 통신의 복잡성 추상화
    • 서비스 디스커버리와 로드밸런싱의 고도화 및 자동화 (서비스 메시는 서비스 디스커버리를 기반으로 함)
  • 동작 원리
    • 컨테이너 애플리케이션 앞에 AWS AppMesh, Istio, Linkerd 등의 사이드카 프록시 컨테이너를 배치

istio 아키텍처

 

위에 열거한 네트워킹 방식에는 아래와 같은 한계점이 존재합니다.

Ingress와 Service Discovery, Service Mesh는 Kubernetes에서 트래픽 라우팅을 관리하는 대표적인 방식이었지만, 다음과 같은 한계점을 가지고 있었습니다.

  • Ingress의 한계
    • HTTP, HTTPS와 같은 L7 트래픽에 최적화 되어있어 gRPC 및 TPC, UDP와 같은 비 L7 프로토콜에 대한 라우팅 기능이 제한적이었습니다.
    • 고급 기능(인증, 속도 제한 정책, 고급 트래픽 관리 등)을 사용하려면 벤더별(AWS, NGINX, HAProxy 등) 사용자 정의 어노테이션이 필요하여 이식성과 표준화에 한계가 존재했습니다.
  • Service Mesh의 한계
    • 주로 동-서 트래픽(서비스 간 내부 통신)에 초점을 맞추어 설계되어 북-남 트래픽(외부-내부 통신)에 대한 기능이 제한적이였습니다.
      • Amazon EKS의 경우, VPC Peering 및 TGW가 필요 → 점점 늘어나는 설정들과 네트워크 리소스들
        여러 AWS 계정을 사용할 경우, 각각 교차 계정 액세스 설정이 필요 → 권한 관리의 어려움
      • 사이드카 프록시 배포가 필수적이며, 멀티 VPC 클라우드 환경에서 네트워크 복잡도가 증가하였습니다. (멀티 클러스터, 멀티 메시의 수많은 프록시 운영 및 관리의 어려움)

또한, 클라우드 환경에서 점차 마이크로 서비스가 확장되고 인프라 구성이 복잡해짐에 따라 아래와 같은 요구사항도 생겨났습니다.

  • 다양한 팀 간의 책임 분리(인프라 담당자, 애플리케이션 개발자, DevOps 엔지니어 등)가 필요해졌습니다.
  • 여러 VPC 또는 멀티 클러스터 환경에 걸친 리소스들을 일관되게 관리해야 했습니다.
  • 다양한 컴퓨팅 형태(인스턴스, 컨테이너, 서버리스 등)를 통합적으로 관리할 수 있는 네트워킹 레이어가 필요했습니다.

Gateway API의 등장

이러한 한계를 극복하기 위해 SIG-NETWORK 커뮤니티에서 [Gateway API](https://gateway-api.sigs.k8s.io/)가 고안되었습니다. Gateway API는 다음과 같은 목표를 가지고 설계되었습니다.

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

Gateway API의 주요 구성요소

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

이 계층적 구조를 통해 인프라 관리자는 GatewayClass와 Gateway를 관리하며 전체 인프라의 일관성을 유지하고, 애플리케이션 개발자는 다양한 Route를 통해 자신의 애플리케이션에 대한 세부 라우팅 규칙을 독립적으로 정의할 수 있습니다. 
AWS는 이러한 계층적 구조를 Amazon VPC Lattice와 통합하여 제공함으로써, 클라우드 네이티브 환경에서의 네트워킹 요구사항을 효과적으로 충족시킵니다.

Gateway API Controller

Gateway API도 다른 Kubernetes 컴포넌트와 마찬가지로 Controller를 통해 동작합니다. 여러 벤더 별 Gateway API Controller 구현체를 참조하고 싶다면 https://gateway-api.sigs.k8s.io/implementations/)를 확인해보세요. 

AWS에서는 AWS Gateway API Controller(https://gateway-api.sigs.k8s.io/implementations/#amazon-elastic-kubernetes-service)를 제공하고 있습니다.

 

Amazon VPC Lattice

https://docs.aws.amazon.com/ko_kr/vpc-lattice/latest/ug/what-is-vpc-lattice.html

Amazon VPC Lattice 는 AWS에서 제공하는 완전관리형 애플리케이션 네트워킹 서비스로, 다양한 컴퓨팅 환경에서 애플리케이션 서비스를 연결, 보호 및 모니터링하는 통합 솔루션입니다. VPC Lattice는 단일 VPC 내에서는 물론, 계정 간 여러 VPC에 걸쳐 서비스를 연결하는 데 활용할 수 있습니다.

현대적 애플리케이션은 마이크로서비스 아키텍처를 통해 여러 개의 소규모 모듈형 서비스로 분할되어 개발되는 추세입니다. 이러한 접근 방식은 개발 속도와 확장성 측면에서 많은 이점을 제공하지만, 다양한 마이크로서비스 간의 효율적인 연결과 관리에 대한 새로운 네트워킹 문제를 야기합니다. Amazon VPC Lattice는 이러한 과제를 해결하기 위해 네트워크 액세스, 트래픽 관리, 모니터링에 대한 일관된 정책을 정의하여 EC2 인스턴스, EKS 컨테이너, Lambda 서버리스 함수 등 다양한 컴퓨팅 서비스 간에 간단하고 표준화된 연결 방식을 제공합니다.

Amazon VPC Lattice 주요 구성 요소

 

Service

Service는 특정 작업이나 기능을 수행하는 독립적으로 배포가능한 소프트웨어 단위입니다. 이는 어떤 Amazon Virtual Private Cloud(VPC)나 계정에도 존재할 수 있으며, 다양한 유형의 컴퓨팅 환경(가상머신, 컨테이너, 서버리스 함수)에서 실행할 수 있습니다. 

 

서비스 구성은 다음과 같은 요소로 이루어집니다.

  • 리스너 : 리스너에서는 Service가 트래픽을 받아들일 포트와 프로포콜을 정의합니다. 지원되는 프로토콜은 HTTP/1.1, HTTP/2, gRPC이며, TLS가 활성화된 Service의 경우 HTTPS도 포함됩니다.
  • 규칙 : 리스너의 기본 구성 요소로, 요청을 대상 그룹의 대상들로 전달합니다. 각 규칙은 우선 순위, 하나 이상의 작업, 그리고 리스너가 클라이언트 요청을 라우팅하는 기준이 되는 하나 이상의 조건으로 구성됩니다.
  • 대상 그룹 : 애플리케이션이나 서비스를 실행하는 리소스들의 집합으로, 이를 대상 이라고도 합니다. 대상은 EC2 인스턴스, IP주소, Lambda 함수, 또는 Kubernetes Pod가 될 수 있습니다.

Service Network

  • VPC Lattice의 핵심 구성 요소로, 여러 서비스를 논리적으로 그룹화하고 이들 간의 통신을 관리합니다. 하나 이상의 VPC를 Service Network에 연결하여 해당 네트워크 내의 서비스 간 통신을 가능하게 합니다.
    • 여러 VPC와 서비스를 단일 네트워크로 통합
    • 계정 간, 리전 간 서비스 연결 지원
    • 중앙 집중식 액세스 제어 및 모니터링 제공
    • VPC 연결을 통한 Service Network 내 서비스에 대한 접근 관리

 

Auth Policy

  • Auth Policy는 VPC Lattice 서비스에 대한 액세스를 제어하는 IAM 기반 정책입니다. 이를 통해 특정 IAM 보안 주체나 역할이 서비스에 접근할 수 있는지 여부를 세밀하게 제어할 수 있습니다.
    • IAM 기반의 세분화된 접근 제어
    • 서비스 레벨 또는 서비스 네트워크 레벨에서 적용 가능
    • HTTP 메소드, 경로, 헤더 등을 기준으로 조건부 액세스 규칙 설정
    • AWS 리소스 간의 안전한 통신 보장

 

Service Directory

  • 모든 VPC Lattice 서비스를 중앙에서 검색하고 관리할 수 있는 카탈로그입니다. 개발자와 운영팀이 사용 가능한 서비스를 쉽게 찾고 접근할 수 있도록 지원합니다.
    • 사용 가능한 모든 서비스의 중앙 집중식 카탈로그 제공
    • 서비스 메타데이터 및 설명 관리
    • 서비스 검색 및 필터링 기능
    • 접근 가능한 서비스에 대한 가시성 제공

 

Amazon VPC Lattice의 장점

  • 여러 VPC, EC2 인스턴스, 컨테이너, 서버리스로 구성한 애플리케이션의 네트워크 구성을 중앙 집중화 할 수 있습니다.
  • 별도의 사이드카 프록시를 구성할 필요가 없습니다. (+ 완전 관리형 서비스)
  • 복잡한 네트워크 구성을 단순화 해서 쉽게 사용할 수 있습니다.
  • IAM 및 SigV4를 통해 각 애플리케이션으로의 보안 구성을 손쉽게 적용 할 수 있습니다.
  • CloudWatch, S3, Kinesis Data Firehose를 통해 쉽게 로깅, 트래픽 패턴 분석 등을 수행할 수 있습니다.

사용 사례 #1 : 단일 리전의 애플리케이션 간 연결

단일 AWS 리전에서 프로덕션, 개발 및 공유 서비스 애플리케이션 네트워킹을 사용하는 일반적인 엔터프라이즈 아키텍처ALT

 

사용 사례 #2 : 온프레미스에서 AWS에 배포된 애플리케이션으로 접근하는 경우

서비스 네트워크 엔드포인트가 있는 하이브리드 인그레스 VPC를 사용하여 온프레미스에서 Prod, Dev 및 Shared 서비스의 서비스 네트워크 접근하기

 

사용 사례 #3 : 여러 AWS 리전에 걸친 애플리케이션 간의 연결

리전 간 액티브-패시브 서비스 접속 구성

사용 사례 #4 : 다중 계정 간 애플리케이션의 연결

다중 계정 간 네트워크 연결 구성

AWS Gateway API Controller

  • https://www.gateway-api-controller.eks.aws.dev/latest/concepts/overview/
  • Gateway API에 의해 정의된 사용자 지정 리소스를 확장하여 Kubernetes API를 사용하여 VPC Lattice 리소스를 생성합니다. 이 Controller가 클러스터에 설치되면 Controller는 Gateway API의 리소스(Gateway 및 Route)의 생성을 감시하고 적절한 Amazon VPC Lattice 오브젝트를 프로비저닝 합니다. 이를 통해 사용자는 커스텀 코드를 작성하거나 사이드카 프록시를 관리할 필요 없이 Kubernetes API를 사용하여 VPC Lattice Service, VPC Lattice Service Network 및 Target Group을 구성할 수 있습니다.
  • AWS Gateway API Controller는 Amazon VPC Lattice와 통합되어 다음 작업을 수행할 수 있습니다.
    • VPC 및 계정 전반에 걸친 서비스 간 네트워크 연결을 원활하게 처리합니다.
    • 여러 Kubernetes 클러스터에 걸쳐 있는 VPC Lattice Service를 검색합니다.
    • 서비스 간 통신의 보안을 구성하기 위한 깊이 있는 방어 전략을 구현합니다.
    • 서비스 간 요청/응답 트래픽을 관찰합니다.

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

 

Amazon EKS에서 VPC Lattice로 Gateway API를 사용하기 위한 요구사항

 

[실습 1] Simple Client to Server communication

이번 실습에서는 
Amazon EKS Blueprints for Terraform : Amazon VPC Lattice - Simple Client to Server Communication ()
을 활용합니다.
https://aws-ia.github.io/terraform-aws-eks-blueprints/patterns/network/client-server-communication/

실습 개요

이 패턴에서는 Terraform을 활용하여 클라이언트 애플리케이션이 한 곳에서 실행되고 서버 애플리케이션이 다른 곳에서 실행되는 두 개의 별개의 VPC를 배포합니다. 서버 애플리케이션은 EKS 클러스터 내에 배포되며 두 애플리케이션 간의 연결을 설정하는 Amazon VPC Lattice를 통해 클라이언트 애플리케이션에 노출됩니다. 또한 Amazon Route53 및 External DNS Add-on을 사용하여 노출된 서비스에 대한 사용자 지정 도메인 이름을 구성하는 방법을 보여줍니다.

  • Terraform 코드 준비 및 프로비저닝
# 실습용 코드를 clone
[yu3papa@iworks ~]$ git clone https://github.com/aws-ia/terraform-aws-eks-blueprints.git

# /patterns/vpc-lattice/client-server-communication 디렉터리로 이동
[yu3papa@iworks ~]$ cd terraform-aws-eks-blueprints/patterns/vpc-lattice/client-server-communication

# main.tf 파일 내의 29번째 라인에서 region 값을 ap-northeast-2로 수정


# 실습 인프라를 프로비저닝 --> 약 30분 소요
[yu3papa@iworks client-server-communication]$ terraform init
[yu3papa@iworks client-server-communication]$ terraform apply -target="module.client_vpc" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform apply -target="module.cluster_vpc" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform apply -target=aws_route53_zone.primary -auto-approve
[yu3papa@iworks client-server-communication]$ terraform apply -target="module.client_sg" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform apply -target="module.endpoint_sg" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform apply -target="module.client" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform apply -target="module.vpc_endpoints" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform apply -target="module.eks" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform apply -target="module.addons" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform apply -auto-approve
...(생략)...
Apply complete! Resources: 6 added, 0 changed, 0 destroyed.

Outputs:

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

# 생성된 EKS 에 대한 kubeconfig 설정
[yu3papa@iworks client-server-communication]$ aws eks update-kubeconfig --name client-server-communication --alias client-server-communication --region ap-northeast-2
Added new context client-server-communication to /home/yu3papa/.kube/config

# EKS 클러스터의 POD 가 정상 RUNNING 중인지 확인
[yu3papa@iworks ~]$ kubectl get po -A
NAMESPACE                           NAME                                                              READY   STATUS    RESTARTS   AGE
apps                                server-6d44dd47-64pcs                                             1/1     Running   0          5m9s
apps                                server-6d44dd47-bf8cm                                             1/1     Running   0          5m9s
aws-application-networking-system   aws-gateway-api-controller-aws-gateway-controller-chart-6b9t572   1/1     Running   0          5m51s
aws-application-networking-system   aws-gateway-api-controller-aws-gateway-controller-chart-6bj9qc7   1/1     Running   0          5m51s
external-dns                        external-dns-555c676b8-zzb6z                                      1/1     Running   0          5m51s
kube-system                         aws-node-9j4gw                                                    2/2     Running   0          7m29s
kube-system                         aws-node-f8w62                                                    2/2     Running   0          7m29s
kube-system                         aws-node-jf6r8                                                    2/2     Running   0          7m28s
kube-system                         coredns-5b9dfbf96-gkrmh                                           1/1     Running   0          11m
kube-system                         coredns-5b9dfbf96-pc57p                                           1/1     Running   0          11m
kube-system                         kube-proxy-dbbkg                                                  1/1     Running   0          7m29s
kube-system                         kube-proxy-jmwc8                                                  1/1     Running   0          7m29s
kube-system                         kube-proxy-m7fgx                                                  1/1     Running   0          7m28s

 

프로비저닝 된 인프라 확인

VPC

  • Subnets

 

client 워크로드 EC2

 

VPC Lattice를 통한 Client to Server 통신 테스트

  • demo-application Gateway API 리소스 분석

  • client EC2 인스턴스에 Session Manager를 이용하여 연결

# kubectl 명령을 통해 server pod의 애플리케이션 로그를 실시간으로 모니터링
[yu3papa@iworks ~]$ kubectl logs -f deployment/server -n apps --all-containers=true --since=1m
Found 2 pods, using pod/server-6d44dd47-64pcs
2025/04/26 05:54:27 Receiving %!(EXTRA *http.Request=&{GET / HTTP/1.1 1 1 map[Accept:[*/*] Accept-Encoding:[*] Connection:[close] User-Agent:[VpcLattice-HealthChecker/1.0]] {} <nil> 0 [] true 169.254.171.196 map[] map[] <nil> map[] 169.254.171.196:2265 / <nil> <nil> <nil> 0xc000284ec0})
2025/04/26 05:54:29 Receiving %!(EXTRA *http.Request=&{GET / HTTP/1.1 1 1 map[Accept:[*/*] Accept-Encoding:[*] Connection:[close] User-Agent:[VpcLattice-HealthChecker/1.0]] {} <nil> 0 [] true 169.254.171.195 map[] map[] <nil> map[] 169.254.171.195:8441 / <nil> <nil> <nil> 0xc000284f00})
...(생략)...

# client의 터미널에서는 아래 명령을 수행하여 네트워크 통신 테스트를 수행
sh-4.2$ curl -i http://server.example.com
HTTP/1.1 200 OK
date: Sat, 26 Apr 2025 05:56:23 GMT
content-length: 52
content-type: text/plain; charset=utf-8

Requsting to Pod(server-6d44dd47-bf8cm): server pod

#### 실행결과는 아래와 같습니다.


 

통신 과정 분석

  • server.example.com 도메인에 DNS 질의해보면 Amazon VPC Lattice로 구성된 엔드포인트가 조회됩니다.
sh-4.2$ nslookup server.example.com
Server:         10.1.0.2
Address:        10.1.0.2#53

Non-authoritative answer:
server.example.com      canonical name = server-apps-0617bbfbd326d1971.7d67968.vpc-lattice-svcs.ap-northeast-2.on.aws.
Name:   server-apps-0617bbfbd326d1971.7d67968.vpc-lattice-svcs.ap-northeast-2.on.aws
Address: 169.254.171.1
Name:   server-apps-0617bbfbd326d1971.7d67968.vpc-lattice-svcs.ap-northeast-2.on.aws
Address: fd00:ec2:80::a9fe:ab01
  • Amazon VPC Lattice 확인

  • Amazon VPC Lattice Target Group 확인
    • VPC Lattice Service의 세부 항목 중, Routing 섹션을 클릭하면 아래와 같은 Listener 규칙과 함께 대상 그룹을 확인할 수 있습니다.

 

  • 대상 그룹 세부 정보

 

# 아래 kubectl 명령을 수행하면, 해당 IP address와 일치하는 server pod의 IP 목록을 조회할 수 있습니다.
[yu3papa@iworks ~]$ kubectl get po -n apps -o wide
NAME                    READY   STATUS    RESTARTS   AGE   IP            NODE                                             NOMINATED NODE   READINESS GATES
server-6d44dd47-64pcs   1/1     Running   0          38m   10.0.32.136   ip-10-0-36-64.ap-northeast-2.compute.internal    <none>           <none>
server-6d44dd47-bf8cm   1/1     Running   0          38m   10.0.31.48    ip-10-0-28-231.ap-northeast-2.compute.internal   <none>           <none>

# server pod에서 사용하는 service의 정보 및 Port 번호를 조회
[yu3papa@iworks ~]$ kubectl get svc -n apps
NAME     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
server   ClusterIP   172.20.173.117   <none>        8090/TCP   41m

--> 이를 통해 VPC Lattice로 구성된 DNS 엔드포인트는 EKS 클러스터 내의 server pod로 라우팅 된다는 것을 알 수 있습니다.

 

AWS Gateway API Controller의 동작 체크

이렇게 VPC Lattice를 통해 EKS 클러스터 내의 server pod로 라우팅 할 수 있는 환경을 구성하기 위해서는 EKS 클러스터 내에 GatewayClass, Gateway, Route 리소스를 생성해야 합니다.

AWS Gateway API Controller는 생성된 GatewayClass, Gateway, Route 리소스를 참조하여 필요한 VPC Lattice 네트워크 환경을 구성하게 됩니다.

 

# AWS Gateway API Controller의 로그를 lattice.log라는 파일로 저장
[yu3papa@iworks ~]$ kubectl logs deployment/aws-gateway-api-controller-aws-gateway-controller-chart -n aws-application-networking-system --all-containers=true > lattice.log
Found 2 pods, using pod/aws-gateway-api-controller-aws-gateway-controller-chart-6b9t572

# vi 명령을 통해 로그를 조회하면, AWS Gateway API Controller의 동작을 확인 해볼 수 있습니다.
[yu3papa@iworks ~]$ vi lattice.log

 

위 로그에 대한 설명

  • 로그를 보면, server라는 이름의 service와 my-services라는 이름의 gateway의 생성을 감지하여, reconcile 작업을 수행하는 것을 확인할 수 있습니다.
  • 이어서 server-apps라는 이름(HTTPRoute의 metadata.name 및 metadata.namespace를 사용)으로 구성된 Route 대상에 라우팅 하기 위해 server.example.com이라는 사용자 지정 도메인 이름을 세팅하고, VPC Lattice Service를 구성합니다.
  • 그리고 VPC Lattice Service에 대해 Listener rule을 생성하고 HTTPRoute 매니페스트 내용에 맞춰 PathPrefix를 구성하고, backendRefs에 대한 대상 그룹을 생성합니다.
  • 대상 그룹에는 Service를 참조하여 라우팅 대상 Pod의 IP 주소 및 Port 번호를 세팅합니다.

실습 리소스 정리

# 주의 사항
테라폼을 destroy 할때 Route53 의 example.com 도메인에 대한 "호스팅 영역" 삭제 에러가 발생하면 하위 레코드 삭제한 후 테라폼을 수행할것!!!
# 약 20분 소요
[yu3papa@iworks ~]$ cd ~/terraform-aws-eks-blueprints/patterns/vpc-lattice/client-server-communication
[yu3papa@iworks client-server-communication]$ terraform destroy -target="module.client_vpc" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform destroy -target="module.cluster_vpc" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform destroy -target=aws_route53_zone.primary -auto-approve
[yu3papa@iworks client-server-communication]$ terraform destroy -target="module.client_sg" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform destroy -target="module.endpoint_sg" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform destroy -target="module.client" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform destroy -target="module.vpc_endpoints" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform destroy -target="module.eks" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform destroy -target="module.addons" -auto-approve
[yu3papa@iworks client-server-communication]$ terraform destroy -auto-approve
...(생략)...

# kubeconfig 파일 삭제
[yu3papa@iworks ~]$ rm -fr ~/.kube/config

 

[실습 2] Multi Cluster secure communication

실습 개요

멀티 클러스터 환경 간에서 Amazon VPC Lattice를 활용한 안전한 통신 방법에 대해 실습을 진행

이 패턴에서는 Amazon VPC Lattice와 IAM authorization을 사용하여 서로 다른 VPC에 있는 두 EKS 클러스터 간의 안전한 멀티 클러스터 통신 방식을 보여줍니다. 

  • 이 솔루션에서는 Service Discovery가 어떻게 이루어지는지를 설명하고, VPC Lattice가 겹치는 CIDR을 가진 EKS 클러스터 간 통신을 어떻게 용이하게 하는지 강조합니다.
  • 이를 통해 프라이빗 NAT 게이트웨이와 Transit Gateway와 같은 네트워킹 구조가 필요하지 않도록 하는 방법을 소개합니다.
  • 클러스터 간 통신을 안전하게 수행하기 위해, 인증서 관리자(ACM)에서 발행하고 AWS Private CA에서 지원하는 TLS 인증서로 보호되는 Amazon Route53 Private Hosted Zone을 사용하여 Amazon VPC Lattice를 통해 설정됩니다.
  • 이를 위해 Kyverno를 사용하여 Envoy SigV4 proxy 컨테이너를 Pod에 추가하고, 여기에 PCA를 주입합니다.

주요 컴포넌트

인프라 구성 두 개의 VPC: VPC cluster 1과 VPC cluster 2가 독립적으로 구성
두 개의 EKS 클러스터: 각 VPC에 EKS Cluster 1과 EKS Cluster 2 배포
VPC Lattice: 두 VPC 간의 서비스 통신을 위한 중앙 관리형 연결 서비스
애플리케이션 구성 EKS Cluster 1 애플리케이션:
- App1 Service가 Pod로 배포됨
- Envoy sidecar 프록시를 통한 서비스 메시 구성
- HTTP Route를 통한 외부 트래픽 라우팅
EKS Cluster 2 애플리케이션:
- App2 Service가 Pod로 배포됨
- 마찬가지로 Envoy sidecar 프록시와 HTTP Route 구성
정책 및 보안 - Kyverno: 각 클러스터에 적용된 정책 관리 도구로 사이드카 주입 등을 관리
- IAM Auth Policy: 두 클러스터 간의 인증 및 권한 정책 설정
- Private Certificate Authority: 중앙 인증서 관리
- Certificate Manager: *.example.com 도메인을 위한 인증서 관리
네트워킹 AWS Gateway API Controller: 각 클러스터의 API 트래픽 제어
DNS 구성:
- ExternalDNS를 통한 DNS 레코드 관리
- `demo-cluster1.example.com` 및 `demo-cluster2.example.com`으로 각 클러스터 서비스 노출
- Amazon Route 53을 통한 도메인 관리
주요 흐름 및 프로세스 1. VPC Lattice를 통해 HTTPS 트래픽이 두 클러스터 간에 라우팅됨
2. 각 클러스터의 IAM Auth Policy가 접근 권한을 제어
3. Pod Identity 기능을 통해 Envoy SigV4 proxy 컨테이너에 ACM에 대한 권한 및 VPC Lattice service invoke 권한 부여
4. AWS Gateway API Controller가 각 클러스터의 인그레스 트래픽을 관리
5. ExternalDNS가 자동으로 DNS 레코드를 생성하여 서비스 디스커버리 지원
   
   
   
   
   

Terraform 코드 준비 및 프로비저닝

인프라 프로비저닝

  • 실습에 필요한 사전 환경(Route53, VPC Lattice, IAM Role, ACM 등)을 프로비저닝 합니다. 

 

# 실습용 코드를 clone
[yu3papa@iworks ~]$ git clone https://github.com/aws-ia/terraform-aws-eks-blueprints.git

# /patterns/cross-cluster-pod-communication/environment 디렉터리로 이동
[yu3papa@iworks ~]$ cd ~/terraform-aws-eks-blueprints/patterns/vpc-lattice/cross-cluster-pod-communication/environment/

# main.tf 파일 내의  region 값을 ap-northeast-2로 수정


# environment의 인프라를 프로비저닝
[yu3papa@iworks environment]$ terraform init
[yu3papa@iworks environment]$ terraform apply --auto-approve

 

cluster 프로비저닝

  • 실습에 사용할 두 개의 EKS 클러스터 및 데모 애플리케이션을 구성
# /patterns/cross-cluster-pod-communication/cluster 디렉터리로 이동
[yu3papa@iworks ~]$ cd ~/terraform-aws-eks-blueprints/patterns/vpc-lattice/cross-cluster-pod-communication/cluster

# main.tf 파일 내의  region 값을 ap-northeast-2로 수정


# 첫번째 EKS 클러스터를 배포
[yu3papa@iworks cluster]$ ./deploy.sh cluster1

# 배포 완료 후, 아래 명령으로 kubectl config 설정을 완료합니다.
[yu3papa@iworks cluster]$ eval `terraform output -raw configure_kubectl`
Added new context eks-cluster1 to /home/yu3papa/.kube/config

[yu3papa@iworks cluster]$ kubectl get no
NAME                                             STATUS   ROLES    AGE     VERSION
ip-10-0-19-4.ap-northeast-2.compute.internal     Ready    <none>   6m52s   v1.30.9-eks-5d632ec
ip-10-0-36-235.ap-northeast-2.compute.internal   Ready    <none>   6m51s   v1.30.9-eks-5d632ec
ip-10-0-8-113.ap-northeast-2.compute.internal    Ready    <none>   6m51s   v1.30.9-eks-5d632ec

# 2번째 EKS 클러스터를 배포
[yu3papa@iworks cluster]$ ./deploy.sh cluster2

# 배포 완료 후, 아래 명령으로 kubectl config 설정을 완료합니다.
[yu3papa@iworks cluster]$ eval `terraform output -raw configure_kubectl`
Added new context eks-cluster2 to /home/yu3papa/.kube/config

 

프로비저닝 된 인프라 확인

  • Amazon EKS 클러스터

eks-cluster1을 들어가보겠습니다.
Access > Pod Identity associations 순으로 콘솔을 이동하면 아래와 같은 내용을 확인할 수 있습니다. 
`vpc-lattice-sig4-client`라는 IAM role이 `apps`라는 네임스페이스의 `default`라는 Service account에 associate 된 것을 확인할 수 있습니다.

해당 IAM role을 클릭해보면 아래와 같이 IAM 콘솔로 이동하여 자세히 확인할 수 있습니다.
이 IAM role은 ACM의 PCA에 대한 여러 액세스 권한과 VPC Lattice Service에 대한 invoke 권한이 설정되어 있습니다.

 

  • VPC Lattice

VPC 콘솔 > PrivateLink and Lattice > Service networks 순으로 콘솔을 이동하면 아래와 같이 VPC Lattice Service network 콘솔로 이동할 수 있습니다.

 

VPC 콘솔 > PrivateLink and Lattice > Lattice services 순으로 콘솔을 이동하면 아래와 같이 VPC Lattice의 Service 콘솔로 이동할 수 있습니다. 각 Service가 커스텀 도메인 명에 매핑된 것을 확인할 수 있습니다.

그리고 아래와 같이 각 Service가 Private Hosted Zone에 associate 되어있고 PCA를 사용하고 있는 것을 확인할 수 있습니다.

  • EKS 클러스터 별 애플리케이션
# eks-cluster1으로 context를 스위칭
[yu3papa@iworks ~]$ kubectl config use-context eks-cluster1
Switched to context "eks-cluster1".

# POD 조회
[yu3papa@iworks ~]$ kubectl get po -A
NAMESPACE                           NAME                                                              READY   STATUS      RESTARTS   AGE
apps                                demo-cluster1-v1-59f7f697c-j9b8d                                  2/2     Running     0          154m
aws-application-networking-system   aws-gateway-api-controller-aws-gateway-controller-chart-57chnl5   1/1     Running     0          157m
aws-application-networking-system   aws-gateway-api-controller-aws-gateway-controller-chart-57lccxs   1/1     Running     0          157m
external-dns                        external-dns-5c6f9b9b87-7244v                                     1/1     Running     0          157m
kube-system                         aws-node-6jz29                                                    2/2     Running     0          155m
kube-system                         aws-node-nw4tc                                                    2/2     Running     0          155m
kube-system                         aws-node-qbmh2                                                    2/2     Running     0          155m
kube-system                         coredns-5fc547d667-7ldlp                                          1/1     Running     0          155m
kube-system                         coredns-5fc547d667-dvjq5                                          1/1     Running     0          155m
kube-system                         eks-pod-identity-agent-6dcw6                                      1/1     Running     0          155m
kube-system                         eks-pod-identity-agent-fsjcr                                      1/1     Running     0          155m
kube-system                         eks-pod-identity-agent-qx7rc                                      1/1     Running     0          155m
kube-system                         kube-proxy-6nnsd                                                  1/1     Running     0          155m
kube-system                         kube-proxy-jrq4d                                                  1/1     Running     0          155m
kube-system                         kube-proxy-lznfk                                                  1/1     Running     0          155m
kyverno                             kyverno-admission-controller-54b8bdb86f-fhpzg                     1/1     Running     0          158m
kyverno                             kyverno-background-controller-64fcf87c7b-ccmmb                    1/1     Running     0          158m
kyverno                             kyverno-cleanup-admission-reports-29094370-d24wt                  0/1     Completed   0          4m35s
kyverno                             kyverno-cleanup-cluster-admission-reports-29094370-hgf4j          0/1     Completed   0          4m35s
kyverno                             kyverno-cleanup-controller-5b4b8f645b-v8ctt                       1/1     Running     0          158m
kyverno                             kyverno-reports-controller-55b9787f78-wcl2z                       1/1     Running     0          158m


# 데모 애플리케이션 Pod(demo-cluster1-v1)의 구성을 확인
[yu3papa@iworks ~]$ kubectl describe po demo-cluster1-v1-59f7f697c-j9b8d  -n apps
Name:             demo-cluster1-v1-59f7f697c-j9b8d
Namespace:        apps
Priority:         0
Service Account:  default
Node:             ip-10-0-19-4.ap-northeast-2.compute.internal/10.0.19.4
Start Time:       Sat, 26 Apr 2025 16:39:38 +0900
Labels:           app=demo-cluster1-v1
                  pod-template-hash=59f7f697c
Annotations:      vpc-lattices-svcs.amazonaws.com/agent-inject: true
Status:           Running
IP:               10.0.19.0
IPs:
  IP:           10.0.19.0
Controlled By:  ReplicaSet/demo-cluster1-v1-59f7f697c
Init Containers:
  iptables-init:
    Container ID:  containerd://353649ba17a92b473a691b78bda2d59be276fe9cfc34daecdea4517092402b08
    Image:         public.ecr.aws/seb-demo/iptables:v1
    Image ID:      public.ecr.aws/seb-demo/iptables@sha256:32f68e35a3c5925c7ee4cc664411063579418975e99754e0b9a53b1405b49e03
    Port:          <none>
    Host Port:     <none>
    Command:
      /bin/sh
      -c
      iptables -t nat -N EGRESS_PROXY; iptables -t nat -A OUTPUT -p tcp -d 169.254.171.0/24 -j EGRESS_PROXY; iptables -t nat -A EGRESS_PROXY -m owner --gid-owner 0 -j RETURN; iptables -t nat -A EGRESS_PROXY -p tcp -j REDIRECT --to-ports 8080; iptables -t nat -L -n -v;

    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Sat, 26 Apr 2025 16:39:44 +0900
      Finished:     Sat, 26 Apr 2025 16:39:44 +0900
    Ready:          True
    Restart Count:  0
    Environment:
      AWS_STS_REGIONAL_ENDPOINTS:              regional
      AWS_DEFAULT_REGION:                      ap-northeast-2
      AWS_REGION:                              ap-northeast-2
      AWS_CONTAINER_CREDENTIALS_FULL_URI:      http://169.254.170.23/v1/credentials
      AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE:  /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-4qwsx (ro)
      /var/run/secrets/pods.eks.amazonaws.com/serviceaccount from eks-pod-identity-token (ro)
Containers:
  envoy-sigv4:
    Container ID:  containerd://fbddbd42b0529715971a6d3c532e99d0af08658b30c8f6769ad12875523280f6
    Image:         public.ecr.aws/seb-demo/envoy-sigv4:v0.5
    Image ID:      public.ecr.aws/seb-demo/envoy-sigv4@sha256:097a68853c38c9cc2cf44d1de31e10538dd5b312cbc9092b12d2e49f7f92fdee
    Port:          8080/TCP
    Host Port:     0/TCP
    Args:
      -l
      info
    State:          Running
      Started:      Sat, 26 Apr 2025 16:39:57 +0900
    Ready:          True
    Restart Count:  0
    Environment:
      APP_DOMAIN:                              example.com
      CA_ARN:                                  arn:aws:acm-pca:ap-northeast-2:390402552337:certificate-authority/98c5d70d-e5cf-435a-9b22-b36dff4d142e
      AWS_STS_REGIONAL_ENDPOINTS:              regional
      AWS_DEFAULT_REGION:                      ap-northeast-2
      AWS_REGION:                              ap-northeast-2
      AWS_CONTAINER_CREDENTIALS_FULL_URI:      http://169.254.170.23/v1/credentials
      AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE:  /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-4qwsx (ro)
      /var/run/secrets/pods.eks.amazonaws.com/serviceaccount from eks-pod-identity-token (ro)
  demo-cluster1-v1:
    Container ID:   containerd://5d6e79c3f983ec9a0749cfca1a6fb01a4ae4968338cb900bb1702b793e7947fa
    Image:          public.ecr.aws/seb-demo/http-server:latest
    Image ID:       public.ecr.aws/seb-demo/http-server@sha256:05b913f6c411303f8967ed556b12c80ca49c63a9a37005bc8651c18df54266a6
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Sat, 26 Apr 2025 16:40:14 +0900
    Ready:          True
    Restart Count:  0
    Environment:
      PodName:                                 Hello from demo-cluster1-v1
      AWS_STS_REGIONAL_ENDPOINTS:              regional
      AWS_DEFAULT_REGION:                      ap-northeast-2
      AWS_REGION:                              ap-northeast-2
      AWS_CONTAINER_CREDENTIALS_FULL_URI:      http://169.254.170.23/v1/credentials
      AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE:  /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-4qwsx (ro)
      /var/run/secrets/pods.eks.amazonaws.com/serviceaccount from eks-pod-identity-token (ro)
Conditions:
  Type                        Status
  PodReadyToStartContainers   True
  Initialized                 True
  Ready                       True
  ContainersReady             True
  PodScheduled                True
Volumes:
  eks-pod-identity-token:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  82649
  kube-api-access-4qwsx:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:                      <none>

# Init Containers를 통해 envoy-sigv4 컨테이너를 위한 IPTables 세팅이 수행되었습니다.


# envoy-sigv4 컨테이너와 데모 애플리케이션 컨테이너가 잘 배포되어 있습니다.

 

통신 테스트 및 동작방식 확인

# 두 클러스터 간 연결성을 확인하기 위해 아래의 명령을 실행
[yu3papa@iworks ~]$ kubectl --context eks-cluster1 \
  exec -ti -n apps deployments/demo-cluster1-v1 -c demo-cluster1-v1 \
  -- curl demo-cluster2.example.com
Requsting to Pod(demo-cluster2-v1-57c67665b6-9knl9): Hello from demo-cluster2-v1

# eks-cluster1의 데모 애플리케이션에서 그대로 eks-cluster1의 데모 애플리케이션으로 요청을 수행하면 권한 오류 메시지를 확인
[yu3papa@iworks ~]$ kubectl --context eks-cluster1 \
  exec -ti -n apps deployments/demo-cluster1-v1 -c demo-cluster1-v1 \
        -- curl demo-cluster1.example.com
AccessDeniedException: User: arn:aws:sts::390402552337:assumed-role/vpc-lattice-sigv4-client/eks-eks-cluste-demo-clust-bc1f1194-fc92-4e6e-ab46-cc2cd53e1f8e is not authorized to perform: vpc-lattice-svcs:Invoke on resource: arn:aws:vpc-lattice:ap-northeast-2:390402552337:service/svc-078dc8b191a4202ed/ because no service-based policy allows the vpc-lattice-svcs:Invoke action

# 에러가 발생하는 이유는, eks-cluster1의 IAMAuthPolicy에서 eks-cluster2 으로만 호출 가능하도록 했기 때문입니다.
[yu3papa@iworks ~]$ kubectl --context eks-cluster1 \
  get IAMAuthPolicy -n apps demo-cluster1-iam-auth-policy  \
  -o json | jq ".spec.policy | fromjson"
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::390402552337:root"
      },
      "Action": "vpc-lattice-svcs:Invoke",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalTag/eks-cluster-name": "eks-cluster2",
          "aws:PrincipalTag/kubernetes-namespace": "apps"
        }
      }
    }
  ]
}

# IAMAuthPolicy의 자세한 내용을 확인
# --> 해당 IAMAuthPolicy의 적용 대상(Target Ref)이 demo-cluster1 라는 이름을 가진, apps 네임스페이스의 HTTPRoute 리소스임을 확인

[yu3papa@iworks ~]$ kubectl describe IAMAuthPolicy demo-cluster1-iam-auth-policy -n apps
Name:         demo-cluster1-iam-auth-policy
Namespace:    apps
Labels:       app.kubernetes.io/managed-by=Helm
Annotations:  application-networking.k8s.aws/iam-auth-policy-resource-id: svc-078dc8b191a4202ed
              application-networking.k8s.aws/iam-auth-policy-resource-type: Service
              meta.helm.sh/release-name: demo-cluster1
              meta.helm.sh/release-namespace: apps
API Version:  application-networking.k8s.aws/v1alpha1
Kind:         IAMAuthPolicy
Metadata:
  Creation Timestamp:  2025-04-26T07:39:38Z
  Finalizers:
    application-networking.k8s.aws/iam-auth-policy
  Generation:        1
  Resource Version:  2700
  UID:               ad8cab45-4552-4c69-adb6-b1a986a8fe0f
Spec:
  Policy:  {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
              "AWS": "arn:aws:iam::390402552337:root"
            },
            "Action": "vpc-lattice-svcs:Invoke",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "aws:PrincipalTag/eks-cluster-name": "eks-cluster2",
                    "aws:PrincipalTag/kubernetes-namespace": "apps"
                }
            }
        }
    ]
}

  Target Ref:
    Group:      gateway.networking.k8s.io
    Kind:       HTTPRoute
    Name:       demo-cluster1
    Namespace:  apps
Status:
  Conditions:
    Last Transition Time:  2025-04-26T07:39:45Z
    Message:
    Observed Generation:   1
    Reason:                Accepted
    Status:                True
    Type:                  Accepted
Events:                    <none>

# 대상이 되는 HTTPRoute를 조회하기 위해 아래 명령을 수행
#  --> HTTPRoute로 라우팅 되는 대상이 demo-cluster1-v1 이라는 Service임

[yu3papa@iworks ~]$ kubectl describe HTTPRoute demo-cluster1 -n apps
Name:         demo-cluster1
Namespace:    apps
Labels:       app.kubernetes.io/managed-by=Helm
Annotations:  application-networking.k8s.aws/lattice-assigned-domain-name:
                demo-cluster1-apps-078dc8b191a4202ed.7d67968.vpc-lattice-svcs.ap-northeast-2.on.aws
              meta.helm.sh/release-name: demo-cluster1
              meta.helm.sh/release-namespace: apps
API Version:  gateway.networking.k8s.io/v1beta1
Kind:         HTTPRoute
Metadata:
  Creation Timestamp:  2025-04-26T07:39:38Z
  Finalizers:
    httproute.k8s.aws/resources
  Generation:        1
  Resource Version:  20537
  UID:               b7df038d-ec26-4442-ac91-92ee3f211a77
Spec:
  Hostnames:
    demo-cluster1.example.com
  Parent Refs:
    Group:         gateway.networking.k8s.io
    Kind:          Gateway
    Name:          lattice-gateway
    Namespace:     lattice-gateway
    Section Name:  http-listener
    Group:         gateway.networking.k8s.io
    Kind:          Gateway
    Name:          lattice-gateway
    Namespace:     lattice-gateway
    Section Name:  https-listener-with-custom-domain
  Rules:
    Backend Refs:
      Group:
      Kind:    Service
      Name:    demo-cluster1-v1
      Port:    80
      Weight:  1
    Matches:
      Path:
        Type:   PathPrefix
        Value:  /
Status:
  Parents:
    Conditions:
      Last Transition Time:  2025-04-26T08:33:36Z
      Message:
      Observed Generation:   1
      Reason:                Accepted
      Status:                True
      Type:                  Accepted
      Last Transition Time:  2025-04-26T08:33:36Z
      Message:
      Observed Generation:   1
      Reason:                ResolvedRefs
      Status:                True
      Type:                  ResolvedRefs
    Controller Name:         application-networking.k8s.aws/gateway-api-controller
    Parent Ref:
      Group:         gateway.networking.k8s.io
      Kind:          Gateway
      Name:          lattice-gateway
      Namespace:     lattice-gateway
      Section Name:  http-listener
    Conditions:
      Last Transition Time:  2025-04-26T08:33:36Z
      Message:
      Observed Generation:   1
      Reason:                Accepted
      Status:                True
      Type:                  Accepted
      Last Transition Time:  2025-04-26T08:33:36Z
      Message:
      Observed Generation:   1
      Reason:                ResolvedRefs
      Status:                True
      Type:                  ResolvedRefs
    Controller Name:         application-networking.k8s.aws/gateway-api-controller
    Parent Ref:
      Group:         gateway.networking.k8s.io
      Kind:          Gateway
      Name:          lattice-gateway
      Namespace:     lattice-gateway
      Section Name:  https-listener-with-custom-domain
Events:              <none>

# 해당 Service 명세를 확인
# --> 이 Service를 통해 라우팅 되는 대상이 10.0.26.50:8090 임을 확인

[yu3papa@iworks ~]$ kubectl describe svc demo-cluster1-v1 -n apps
Name:                     demo-cluster1-v1
Namespace:                apps
Labels:                   app.kubernetes.io/managed-by=Helm
Annotations:              meta.helm.sh/release-name: demo-cluster1
                          meta.helm.sh/release-namespace: apps
Selector:                 app=demo-cluster1-v1
Type:                     ClusterIP
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       172.20.181.135
IPs:                      172.20.181.135
Port:                     <unset>  80/TCP
TargetPort:               8090/TCP
Endpoints:                10.0.19.0:8090
Session Affinity:         None
Internal Traffic Policy:  Cluster
Events:                   <none>

# 이어서 VPC 콘솔 > PrivateLink and Lattice > Lattice service 로 이동하여 demo-cluster1-apps의 체크박스를 클릭하고, Routing 탭을 클릭하면 아래와 같이 라우팅 대상 그룹을 확인할 수 있습니다.












결과적으로 이 데모 애플리케이션 Pod가 IAMAuthPolicy의 규칙을 적용받고 있음을 확인할 수 있습니다.
이렇게 IAMAuthPolicy를 사용하면 멀티 클러스터 간에 각 애플리케이션에 대한 액세스 제어를 쉽게 구성할 수 있습니다.

 

Kyverno의 역할

이번 실습에서는 Kyverno의 ClusterPolicy를 사용하여 데모 애플리케이션 Pod에 iptables 규칙과 envoy 사이드카 프록시를 주입했습니다:

  • iptables 규칙은 애플리케이션에서 envoy 프록시로 트래픽을 라우팅합니다(소스 프로세스 gid가 0인 경우 규칙이 적용되지 않으므로 애플리케이션에 다른 gid를 제공합니다: runAsGroup: 1000).
  • envoy 프록시는 시작 시 Private CA 인증서를 검색하여 시작 스크립트를 통해 VPC lattice 서비스를 신뢰하도록 설치합니다.
# envoy 프록시 컨테이너가 뜰 때 실행하는 스크립트를 참조
[yu3papa@iworks ~]$ kubectl --context eks-cluster1 \
        exec -it deploy/demo-cluster1-v1 -c envoy-sigv4 -n apps \
        -- cat /usr/local/bin/launch_envoy.sh
#!/bin/sh

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0

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

 

  • launch_envoy.sh에서 활용한 envoy.yaml.in 스크립트
    • envoy.yaml.in
static_resources:
  listeners:
  - name: http_connect
    address:
      socket_address:
        protocol: TCP
        address: 0.0.0.0
        port_value: 8080
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains:
              - "*"
              routes:
              - match:
                  prefix: '/'
                route:
                  cluster: outbound_proxy
          # Ignore traffic to /health and respond with a 200
          http_filters:
          - name: envoy.filters.http.dynamic_forward_proxy
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.dynamic_forward_proxy.v3.FilterConfig
              dns_cache_config:
                name: dynamic_forward_proxy_cache_config
                dns_lookup_family: V4_ONLY
                typed_dns_resolver_config:
                  name: envoy.network.dns_resolver.cares
                  typed_config:
                    "@type": type.googleapis.com/envoy.extensions.network.dns_resolver.cares.v3.CaresDnsResolverConfig
                    use_resolvers_as_fallback: true
                    resolvers:
                    - socket_address:
                        address: "127.0.0.1"
                        port_value: 53
                    dns_resolver_options:
                      use_tcp_for_dns_lookups: true
                      no_default_search_domain: true
          # SigV4 signing configuration
          - name: envoy.filters.http.aws_request_signing
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.aws_request_signing.v3.AwsRequestSigning
              service_name: vpc-lattice-svcs
              region: ${AWS_REGION}
              use_unsigned_payload: true
              match_excluded_headers:
              - prefix: x-envoy
              - prefix: x-forwarded
              - exact: x-amzn-trace-id
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  clusters:
  - name: outbound_proxy
    lb_policy: CLUSTER_PROVIDED
    cluster_type:
      name: envoy.clusters.dynamic_forward_proxy
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig
        dns_cache_config:
          name: dynamic_forward_proxy_cache_config
          dns_lookup_family: V4_ONLY
          typed_dns_resolver_config:
            name: envoy.network.dns_resolver.cares
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.network.dns_resolver.cares.v3.CaresDnsResolverConfig
              use_resolvers_as_fallback: true
              resolvers:
              - socket_address:
                  address: "127.0.0.1"
                  port_value: 53
              dns_resolver_options:
                use_tcp_for_dns_lookups: true
                no_default_search_domain: true
    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
        common_tls_context:
          validation_context:
            trusted_ca:
              filename: /etc/ssl/certs/ca-bundle.crt

 

  • envoy의 Dockerfile
FROM envoyproxy/envoy:distroless-v1.30.0 as envoy

FROM public.ecr.aws/amazonlinux/amazonlinux:latest
RUN yum -y update && \
    yum clean all && \
    rm -rf /var/cache/yum

COPY --from=envoy /usr/local/bin/envoy /usr/local/bin/envoy
RUN yum install -y python3 jq tar bind-utils procps gettext which unzip tar less util-linux
    unzip awscliv2.zip && \
    ./aws/install

COPY envoy.yaml.in /etc/envoy/envoy.yaml.in
COPY launch_envoy.sh /usr/local/bin/launch_envoy.sh

RUN chmod 755 /usr/local/bin/launch_envoy.sh
ENTRYPOINT ["launch_envoy.sh"]

 

실습 리소스 정리

  • 클러스터 삭제
[yu3papa@iworks ~]$ cd ~/terraform-aws-eks-blueprints/patterns/vpc-lattice/cross-cluster-pod-communication/cluster

# cluster2 삭제
[yu3papa@iworks cluster]$ ./destroy.sh cluster2

# cluster1 삭제
[yu3papa@iworks cluster]$ ./destroy.sh cluster1

 

  • environment 환경 정리
[yu3papa@iworks ~]$ SN=$(aws vpc-lattice list-service-networks --query 'items[?name==`lattice-gateway`].id' --output text)
[yu3papa@iworks ~]$ echo $SN
sn-026818cfe9c62611e

[yu3papa@iworks ~]$ if [ -n "$SN" ]; then
    aws vpc-lattice delete-service-network --service-network-id "$SN"
fi

[yu3papa@iworks ~]$ cd ~/terraform-aws-eks-blueprints/patterns/vpc-lattice/cross-cluster-pod-communication/environment

[yu3papa@iworks environment]$ terraform destroy -auto-approve
...(생략)...
Destroy complete! Resources: 9 destroyed.