개요
트래픽을 외부로부터 받기 위한 전통적인 방법은 Ingress인데, 운영 환경이 거대해지면서 더 정교하고 유연하고 확장성 있는 형태의 네트워크 트래픽 처리가 필요할 때 Gateway API를 사용할 수 있음에 따라 이를 탐색
Ingress에서는 비교적 복잡한 라우팅 시나리오에 제한적이고, HTTP외의 프로토콜에는 지원이 부족한 단점이 있음
개념
Gateway API는 헤더 기반 라우팅, 헤더 변조, 트래픽 미러링 등 Ingress에 비해 조금 더 풍부한 기능을 제공
특징
Improved Resource Model
→ GatewayClass, Gateway 및 Route 같은 CRD를 도입하여 라우팅 규칙을 표현력 있게 정의하는 방법을 추구
Protocol Agnostic
→ HTTP 뿐만 아니라 TCP, UDP, TLS 등의 여러 프로토콜을 지원
Enhanced Security
→ 빌트인 TLS를 지원하고 세분화된 ACL을 지원
Cross-Namespace Support
→ 서로 다른 네임스페이스에 존재하는 Service로 트래픽을 라우팅하여 유연한 아키텍쳐를 구축 가능
Extensibility
→ CRD 및 정책을 통해 API를 쉽게 확장 가능
Role-Oriented
→ 클러스터 운영자, 개발자, 보안 담당자 간의 명확한 분리
→ API도 역할에 따라 동작과 권한을 유연하게 제공할 수 있으며, 각 권한 별로 라우팅 규칙을 지정하여 분리된 네임스페이스 내에서 편리하게 관리 가능
구성 요소
GatewayClass, Gateway, HTTPRoute, TCPRoute, Service
GatewayClass는 공통 설정을 통해 Gateway들을 정의하는 리소스
Gateway는 클라우드 LB처럼 트래픽을 처리하는 인프라 리소스
HTTPRoute는 Service를 엔드포인트로 하며, HTTP 관련 규칙으로 트래픽을 핸들링하는 리스너 리소스
GatewayClass는 인프라 제공자가 담당
Gateway는 클러스터 운영자가 담당
HTTPRoute는 개발자가 담당
통신 흐름
클라이언트 → (HTTP 요청) → Gateway → HTTPRoute → (라우팅 규칙) → Service
종류
Gloo Gateway
Gloo Gateway는 기능이 풍부하고 유연하며, Envoy Proxy 위에 구축된 k8s 네이티브 Ingress Controller
내부 동작 방식
위 컴포넌트들은 Gloo와 k8s Gateway API의 커스텀 리소스를 해석하여 Envoy 설정에 적용하기 위해 상호 동작
1.
Config, Secret을 담당하는 gloo Pod 내부의 컴포넌트가 k8s Gateway API와 Gloo Gateway 리소스 (Gateway, HTTPRoute, RouteOptions) 를 통해 클러스터를 감시
2.
Config와 Secret의 감시자가 새로운 리소스 혹은 수정된 리소스를 발견하게 되면, 리소스의 설정을 Gloo Gateway의 Translation Engine으로 전송
3.
Translation Engine은 k8s Gateway API와 Gloo Gateway 리소스를 Envoy 설정으로 해석하게 되고, 이러한 모든 설정은 xDS 스냅샷 형태로 정리
4.
Reporter는 Translate Engine이 처리한 모든 리소스의 상태를 수신
5.
Reporter는 해당 리소스의 상태를 ETCD에 기록
6.
xDS 스냅샷이 glo Pod 내의 Gloo Gateway의 xDS 서버 컴포넌트로 제공
7.
클러스터 내부의 Gateway 프록시들은 가장 최신의 Envoy 설정을 Gloo Gateway xDS 서버로부터 읽음
8.
사용자가 Gateway 프록시로 노출한 IP 주소 혹은 호스트 이름으로 요청을 전송
9.
Gateway 프록시는 리스너와 라우팅을 결정하고 요청을 클러스터로 전달하기 위한 xDS 스냅샷으로 제공되는 라우팅 설정을 이용
Translate Engine 처리 흐름
1.
Translation는 Envoy 클러스터의 설정을 업스트림과 Service를 통해 생성하면서 시작되는데, 여기서 언급된 Envoy 클러스터란 비슷한 호스트들을 묶은 그룹을 의미 (각 업스트림은 타입에 따라 처리 방법이 정해져 있기 때문에, 올바르게 설정된 업스트림과 Service들은 타입 매칭을 통해 클러스터 메타 데이터를 포함하여 Envoy 클러스터로 적절히 변환되어 생성)
2.
업스트림의 함수와 관련된 클러스터 메타 데이터를 추가하여 나중에 함수 관련 Envoy 필터가 처리할 수 있도록 함
3.
모든 Envoy 경로들이 HTTPRoute와 RouteOption 리소스에 정의한 라우팅 규칙을 토대로 생성하게 되고, 경로 생성 후에 Translation Engine은 VirtualHostOption, ListenerOption, HttpListenerOption 리소스들을 이용하여 Envoy Virtual Host로 집계하고 Envoy HTTP Connection Manager 설정으로 추가하는 처리 과정을 수행
4.
5.
xDS 스냅샷은 유효한 엔드포인트 (EDS), 클러스터 (CDS), 경로 설정 (RDS), 리스너(LDS)로 구성하게 되고, 이러한 스냅샷은 Gloo Gateway xDS 서버로 전달되어 Gateway 프록시가 xDS 서버를 바라보며 새로운 설정을 확인할 수 있도록 도움 (Gateway 프록시는 이 과정에서 새로운 설정을 발견하면 이를 읽어서 이용)
배포 패턴
Single Ingress
Shared Gateway
Shared Gateway with Central Ingress
API Gateway for a Service Mesh
실습 환경 준비
Kind 클러스터를 이용하기 앞 서, Docker Desktop 설정에서 Rosetta 이용 설정을 비활성화
# Kind 클러스터로 이용할 manifest 생성
cat << EOT > kind-1node.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30000
hostPort: 30000
- containerPort: 30001
hostPort: 30001
- containerPort: 30002
hostPort: 30002
EOT
# 클러스터 생성
kind create cluster --image kindest/node:v1.30.0 --config kind-1node.yaml --name myk8s
# 기본 툴을 노드에 설치
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bsdmainutils bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'
Shell
복사
실습
Gateway API 설치
# CRD 설치
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml
# CRD 조회
kubectl get crd
Shell
복사
glooctl 설치
# Control Plane 접속
docker exec -it myk8s-control-plane bash
# glooctl 설치
curl -sL https://run.solo.io/gloo/install | GLOO_VERSION=v1.17.7 sh
# 환경 변수 추가
export PATH=$HOME/.gloo/bin:$PATH
# glooctl 버전 확인
glooctl version
Shell
복사
Gloo Gateway 설치
# Gloo Gateway 차트 설치
helm repo add gloo https://storage.googleapis.com/solo-public-helm
helm repo update
helm install -n gloo-system gloo gloo/gloo \
--create-namespace \
--version 1.17.7 \
--set kubeGateway.enabled=true \
--set gloo.disableLeaderElection=true \
--set discovery.enabled=false
# Gloo Control Plane을 배포
kubectl rollout status deployment/gloo -n gloo-system
Shell
복사
# gloo-system 네임스페이스의 리소스를 확인
# rollout 중인 Pod는 시간이 지나면 삭제되므로 Completed 상태의 0/1이라면 무시
kubectl get po,svc,endpointslices -n gloo-system
Shell
복사
# gatewayclass 리소스 매뉴얼 확인
kubectl explain gatewayclasses
# Gloo Gate 설치 확인
kubectl get gatewayclasses
Shell
복사
HTTP Bin 애플리케이션 배포
# HTTP Bin 애플리케이션 배포
# https://raw.githubusercontent.com/solo-io/solo-blog/main/gateway-api-tutorial/01-httpbin-svc.yaml
kubectl apply -f https://raw.githubusercontent.com/solo-io/solo-blog/main/gateway-api-tutorial/01-httpbin-svc.yaml
Shell
복사
# HTTP Bin 애플리케이션 배포 확인 및 Rollout
kubectl get deploy,po,svc,endpointslices,sa -n httpbin
kubectl rollout status deploy/httpbin -n httpbin
Shell
복사
# Service를 NodePort로 설정
cat << EOT | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
labels:
app: httpbin
service: httpbin
name: httpbin
namespace: httpbin
spec:
type: NodePort
ports:
- name: http
port: 8000
targetPort: 80
nodePort: 30000
selector:
app: httpbin
EOT
# 접속 확인
open http://localhost:30000
Shell
복사
HTTP 리스너 Gateway 생성
# manifest 생성
# https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/02-gateway.yaml
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/02-gateway.yaml
Shell
복사
# Gateway 활성화 확인
kubectl get gateway -n gloo-system
# Gateway 템플릿 확인
kubectl get gateway -n gloo-system -o yaml | k neat
# Gateway 생성으로 gloo-proxy-http라는 Envoy 프록시 역할의 Pod 생성을 확인
kubectl get deployment gloo-proxy-http -n gloo-system
# Envoy 사용 확인
kubectl describe pod -n gloo-system | grep Image:
Shell
복사
# gloo-proxy-http의 Service가 ExternalIP이고, Pending 상태임을 확인
kubectl get svc -n gloo-system gloo-proxy-http
# NodePort를 30001로 고정 설정
cat << EOT | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/instance: http
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: gloo-proxy-http
app.kubernetes.io/version: 1.17.7
gateway.networking.k8s.io/gateway-name: http
gloo: kube-gateway
helm.sh/chart: gloo-gateway-1.17.7
name: gloo-proxy-http
namespace: gloo-system
spec:
ports:
- name: http
nodePort: 30001
port: 8080
selector:
app.kubernetes.io/instance: http
app.kubernetes.io/name: gloo-proxy-http
gateway.networking.k8s.io/gateway-name: http
type: LoadBalancer
EOT
# NodePort 설정 확인
kubectl get svc -n gloo-system gloo-proxy-http
Shell
복사
Gateway 접속을 위한 포트 포워딩
# Envoy의 Data Plane인 gloo-proxy-http에 외부에서 접속할 수 있도록 포트 포워딩 설정
# 라우팅 설정이 아직 없기 때문에 아무런 응답을 받을 수 없음
kubectl port-forward deployment/gloo-proxy-http -n gloo-system 8080:8080 &
Shell
복사
HTTPRoute 생성
•
parentRefs (required) : 어느 Gateway에 설정할 라우팅인지 명시
•
hostnames (optional) : HTTP 헤더에서 매칭될 호스트 이름을 명시
•
rules (required) : HTTP 요청이 어떻게 라우팅할지 결정하기 위한 규칙을 명시
# HTTPRoute 생성
# https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/03-httpbin-route.yaml
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/03-httpbin-route.yaml
Shell
복사
# HTTPRoute 확인
kubectl get httproute -n httpbin
Shell
복사
정적 라우팅 설정
# /etc/hosts 수정
echo "127.0.0.1 api.example.com" | sudo tee -a /etc/hosts
# 설정한 /get 경로 접속
open http://api.example.com:30001/get
# 포트 포워딩으로 접속
# 라우팅 설정이 없어서 응답을 못 받던 것과 달리 응답 확인 가능
curl -is -H "Host: api.example.com" http://localhost:8080/get
Shell
복사
정규 표현식 라우팅 설정
# Gateway에서 사용할 /delay/* 경로는 설정이 되어 있지 않아 포트 포워딩으로는 접속 불가
curl -is -H "Host: api.example.com" http://localhost:8080/delay/1
# HTTP Bin 애플리케이션은 해당 경로를 지원하므로 NodePort로는 접속 가능
curl -is http://api.example.com:30000/delay/11
Shell
복사
# 정규 표현식 적용되어 /delay/*이 접속 가능하도록 manifest 수정
# matches의 Exact를 PathPrefix로 변경
# filter를 이용하여 /api/httpbin/을 /로 치환하도록 적용
# https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/04-httpbin-rewrite.yaml
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/04-httpbin-rewrite.yaml
Shell
복사
# 포트 포워딩으로 /api/httpbin/get 접속
# 8ms 소요
curl -is -H "Host: api.example.com" http://localhost:8080/api/httpbin/get
# 포트 포워딩으로 /api/httpbin/delay/11 접속
# 10016ms 소요
curl -is -H "Host: api.example.com" http://localhost:8080/api/httpbin/delay/11
Shell
복사
# /api/httpbin/ 경로는 HTTPRoute에서 치환하여 접속하는 식이기 때문에 NodePort에선 동작하지 않음
curl http://api.example.com:30000/api/httpbin/get
Shell
복사
Bearer 토큰 주입 설정
라우팅 대상 서버 중 하나가 서버 to 서버에서 인증을 수행해야 하는 제약이 있고, 이를 클라이언트 상에서는 시스템 권한 부여에 사용될 API 키 요구를 노출시키고 싶지 않은 니즈가 있을 때 유용한 규칙
# Bearer 토큰을 주입 가능하도록 manifest 수정
# 프록시 서버 계층에서 간단히 Bearer 토큰을 주입하도록 규칙 설정 가능
# RequestHeaderModfiier라는 타입을 filter에 적용하여 static API 키를 서버 인증에 사용하도록 설정
# https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/05-httpbin-rewrite-xform.yaml
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/05-httpbin-rewrite-xform.yaml
Shell
복사
# /get 경로를 포트 포워딩 경로로 접속 시도 시 Authorization 헤더가 추가된 것을 확인 가능
curl -is -H "Host: api.example.com" http://localhost:8080/api/httpbin/get
Shell
복사
Service 마이그레이션
Gateway API 표준에 따르면 헤더 기반 라우팅과 비율 기반의 카나리 배포를 지원하는데, 이를 이용하여 Service 마이그레이션이 가능 (해당 실습에서는 Dark Launch라는 방법을 이용하여 마이그레이션 실습)
Dark Launch
일부 사용자에게 새로운 기능을 노출하여 피드백을 수집하고 잠재적으로 더 큰 사용자 커뮤니티에 노출되기 전에 개선 사항을 파악하는 클라우드 마이그레이션 기술
# 마이그레이션에 사용할 2가지 버전의 워크로드 생성
# 워크로드는 FakeService를 이용 (HTTP, gRPC 지원)
# v1, v2 my-workload를 생성
# https://github.com/nicholasjackson/fake-service
# https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/06-workload-svcs.yaml
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/06-workload-svcs.yaml
# v1, v2 워크로드 생성 확인
kubectl get deploy,po,svc,endpointslices -n my-workload
Shell
복사
# v1 라우팅을 위한 HTTPRoute 생성
# https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/07-workload-route.yaml
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/07-workload-route.yaml
# 포트 포워딩으로 접속
curl -is -H "Host: api.example.com" http://localhost:8080/api/my-workload
Shell
복사
# v2 라우팅을 위한 HTTPRoute 생성
# matches 규칙에 특정 헤더를 포함한 경우에만 v2로 라우팅되도록 선언적 정책을 정의 (대다수는 v1으로 라우팅)
# https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/08-workload-route-header.yaml
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/08-workload-route-header.yaml
# 헤더가 없이 포트 포워딩 경로로 접속하면 v1
curl -is -H "Host: api.example.com" http://localhost:8080/api/my-workload
# 헤더를 넣고 포트 포워딩 경로로 접속하면 v2
curl -is -H "Host: api.example.com" -H "version: v2" http://localhost:8080/api/my-workload
Shell
복사
# 기본 접속 비율 확인 시 100:0으로 v1, v2에 접속
for i in {1..100}; do curl -s -H "Host: api.example.com" http://localhost:8080/api/my-workload/ | grep body ; done | sort | uniq -c | sort -nr
# HTTPRoute의 backendRefs에서 워크로드의 weight를 수정하여 비율을 50:50로 변경
# https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/09-workload-route-split.yaml
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/09-workload-route-split.yaml
# 변경된 접속 비율 확인 시 50:50으로 v1, v2에 접속
for i in {1..100}; do curl -s -H "Host: api.example.com" http://localhost:8080/api/my-workload/ | grep body ; done | sort | uniq -c | sort -nr
Shell
복사
디버깅
일반적으로 Gloo Gateway를 이용하면서 문제가 발생하는 경우는 업스트림의 참조가 잘못된 경우인 가능성이 높음
복사/붙여넣기 과정에서 존재하지 않는 backendRefs를 입력하거나 누락된 경우가 해당될 수 있는데, 이러한 상황을 가정하고 시뮬레이션으로 어떻게 감지하는지 확인
# 좌측 터미널
# HTTProute 리소스를 감시
kubectl get httproute -n my-workload my-workload -o yaml -w
Shell
복사
# 우측 터미널
# 존재하지 않는 my-bad-workload-v2를 HTTPRoute에 적용하도록 설정
# https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/10-workload-route-split-bad-dest.yaml
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/10-workload-route-split-bad-dest.yaml
# 접속 확인 시 50%는 성공하고, 50%는 존재하지 않는 규칙에 의해 실패
for i in {1..100}; do curl -s -H "Host: api.example.com" http://localhost:8080/api/my-workload/ | grep body; done | sort | uniq -c | sort -nr
Shell
복사
# Control Plane 접속하여 glooctl로 디버깅
docker exec -it myk8s-control-plane bash
# 존재하지 않는 서비스에 대한 위 경우에선 MisConnection을 발견하면서 단서 획득 가능
export PATH=$HOME/.gloo/bin:$PATH
glooctl check
Shell
복사
# Control Plane에서 glooctl을 통해 얻은 단서로 k8s에서 추가 정보 획득
kubectl get httproute my-workload -n my-workload -o yaml
Shell
복사
# 존재하지 않는 backendRefs임을 확실히 했으니 manifest 변경하여 해결
# https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/09-workload-route-split.yaml
kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/gateway-api-tutorial/09-workload-route-split.yaml
# HTTPRoute 확인하여 문제 해결 확인
kubectl get httproute my-workload -n my-workload -o yaml
Shell
복사
모니터링
Envoy는 기본적으로 호스트의 지표들을 확인할 수 있도록 되어 있는데, 해당 실습에서는 3000개 정도의 개별 지표를 확인 가능
# 지표는 19000 포트를 통해 제공되므로 Gateway와 별도인 포트 포워딩을 적용
kubectl -n gloo-system port-forward deployment/gloo-proxy-http 19000 &
Shell
복사
# 간단히 접속 확인하여 HTTP로 제공되는 지표들을 확인
open http://localhost:19000
# 프로메테우스 접속
open http://localhost:19000/stats/prometheus
Shell
복사
# 제공 받는 지표 중에 2XX와 5XX 응답 수를 확인할 수 있는 지표가 존재
curl -s http://localhost:19000/stats | grep -E "(^cluster.httpbin-httpbin-8000_httpbin.upstream.*(2xx|5xx))"
# 2XX 응답 수만 보인다면 HTTP Bin의 API 중에 5XX 응답 수를 늘리는 API를 호출
curl -is -H "Host: api.example.com" http://localhost:8080/api/httpbin/status/500
# 변경된 응답 수를 확인
curl -s http://localhost:19000/stats | grep -E "(^cluster.httpbin-httpbin-8000_httpbin.upstream.*(2xx|5xx))"
Shell
복사
실습 환경 삭제
# 아래 명령어로 편집기를 열고 127.0.0.1 api.example.com에 해당하는 도메인 이름 설정을 삭제
sudo vi /etc/hosts
# 클러스터 삭제
kind delete cluster --name myk8s
Shell
복사