후니의 IT인프라 사전
[KANS-7주차] Istio에서 Kubernetes Ingress Gateway 활용하기 본문
Istio의 자체 Ingress Gateway를 사용할 수도 있지만 이번 실습에서는 Kubernetes Ingress Gateway를 활용해 보겠습니다.
Istio의 공식문서에는 다음과 같이 2개의 gateway yaml을 모두 제공하고 있습니다.
역사
https://istio.io/latest/blog/2022/gateway-api-beta/
2022년 Kubernetes SIG가 Gateway API의 베타릴리즈를 하면서 istio도 Kubernetes의 Gateway API를 베타로 승격하고 추후에는 이것을 기본 Gateway로 사용한다고 밝혔습니다.
2017년 Istio를 출시 했을 때에는 Kubernetes에서의 Ingress API를 지원했으나 한계가 많이 있었습니다. 당시 Ingress API는 HTTP 라우팅 정도만 지원했고 다른 기능들은 공급업체들의 annotation을 통해서 기능을 확장했는데 이를 위해서 각 공급업체별로 각기 다른 annotation과 지원으로 인해 통일성이 부족한 문제가 있었습니다.
따라서 2018년 초 Istio에서는 Gateway와 VirtualService, DestinationRule과 같은 라우팅 등의 기능을 하는 Istio traffic API들을 만들었습니다.
이렇게 사용하던 중 2020년에 GatewayAPI 지원에 대한 필요성이 계속 대두되어 지원을 추가하였으며 기존에 사용하는 Istio API도 당분간은 지속적으로 공존하도록 하고 있습니다.
https://istio.io/latest/blog/2024/gateway-mesh-ga/
2024년 5월에 쓰인 글로 Gateway API가 Istio에서 안정적으로 동작하고 있으며, 사용법에 대해서 기술하였습니다.
다만 해당 글은 GatewayAPI에 대해서 자세하게 서술하고 있지 않기 때문에 Istio의 공식문서에서 Kubernetes Gateway API를 통해서 알아보도록 합시다.
https://istio.io/latest/docs/tasks/traffic-management/ingress/gateway-api/
따라서 지금부터 진행될 튜토리얼은 Kubernetes Gateway API에 대한 기술입니다.
Istio API와 Kubernetes Gateway API의 차이점
배포 방식의 차이
Istio API는 기존에 배포된 게이트웨이(서비스, 디플로이먼트)를 설정하는 방식입니다. 이미 만들어진 것에 설정값을 넣습니다.
Gateway API는 설정하고 배포하는 역할으 동시에 합니다. 즉 Gateway 리소스를 정의하면 필요한 게이트웨이가 자동으로 생성됩니다.
따라서 Gateway API가 더 자동화되고 유연한 방식을 제공합니다.
프로토콜 규정
Istio의 VirtualService에서는 모든 프로토콜이 하나의 리소스에 규정됩니다.
Gateway API에서는 각 프로토콜이 그 리소스에 맞게 규정됩니다.
이 외에도 Gateway API는 수 많은 라우팅 기능을 제공합니다.
개요
Istio에서는 Kubernetes Gateway API를 안정적으로 지원합니다. 서비스메시 클러스터를 외부에 노출시켜 Ingress 트래픽을 관리하고 각 서비스에 접근할 수 있도록 하며 Istio를 이용하므로 서비스 트래픽 매니징과 모니터링이 가능하게 됩니다.
사전 준비
Gateway API CRD 생성
먼저 kubernetes Gateway API CRD를 설치합니다.
kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
{ kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.1.0" | kubectl apply -f -; }
프로필 생성
istioctl을 사용해 minimal이라는 profile을 만듭니다.
istioctl install --set profile=minimal -y
Gateway 설정
이 번 튜토리얼에서는 simple application을 배포하며 Gateway를 통해서 외부로 노출하겠습니다.
1. httpbin 테스트 애플리케이션 배포
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.23/samples/httpbin/httpbin.yaml
다만 최신버전의 httpbin에 문제가 있어서 나는 0.1.0버전을 사용했다.
apiVersion: v1
kind: ServiceAccount
metadata:
name: httpbin
---
apiVersion: v1
kind: Service
metadata:
name: httpbin
labels:
app: httpbin
service: httpbin
spec:
ports:
- name: http
port: 8000
targetPort: 8080
selector:
app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v1
template:
metadata:
labels:
app: httpbin
version: v1
spec:
serviceAccountName: httpbin
containers:
- image: docker.io/kong/httpbin:0.1.0
imagePullPolicy: IfNotPresent
name: httpbin
# Same as found in Dockerfile's CMD but using an unprivileged port
command:
- pipenv
- run
- gunicorn
- -b
- 0.0.0.0:8080
- httpbin:app
- -k
- gevent
env:
# Tells pipenv to use a writable directory instead of $HOME
- name: WORKON_HOME
value: /tmp
ports:
- containerPort: 8080
2. Gateway API의 라우팅(/get) 규정 및 배포
# 네임스페이스 (istio-ingress) 생성
kubectl create namespace istio-ingress
# ingress gateway로 80 포트 오픈
# HTTPRoute를 통해 /get 요청을 httpbin 8080으로 전달
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway
namespace: istio-ingress
spec:
gatewayClassName: istio
listeners:
- name: default
hostname: "*.example.com"
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: All
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http
namespace: default
spec:
parentRefs:
- name: gateway
namespace: istio-ingress
hostnames: ["httpbin.example.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /get
backendRefs:
- name: httpbin
port: 8000
EOF
3. Ingress Host 환경 변수 설정
kubectl wait -n istio-ingress --for=condition=programmed gateways.gateway.networking.k8s.io gateway
export INGRESS_HOST=$(kubectl get gateways.gateway.networking.k8s.io gateway -n istio-ingress -ojsonpath='{.status.addresses[0].value}')
4. curl을 통해 httpbin의 서비스에 접근
기존 HTTPRoute설정의 hostnames을 httpbin.example.com으로 지정하여 -H 옵션으로 호스트 값을 주입했습니다.
curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST/get"
5. httpbin의 노출되지 않은 url path 호출
다른 url path를 사용했을 경우 노출되는 값이 없기 때문에 HTTP 404 error 가 발생합니다.
curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST/headers"
6. 라우트 규칙에 /headers 추가
기존에 404 error 가 발생했던 경로를 라우트 규칙에 추가하고 똑같이 호스트 네임은 유지합니다. 이 때 기존의 spec.rules.matches에 추가합니다.
또한 filters.type에 RequestHeaderModifier를 통해서 my-added-header라는 키에 added-value라는 값을 add 하도록 주입하였습니다.
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http
namespace: default
spec:
parentRefs:
- name: gateway
namespace: istio-ingress
hostnames: ["httpbin.example.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /get
- path:
type: PathPrefix
value: /headers
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: my-added-header
value: added-value
backendRefs:
- name: httpbin
port: 8000
EOF
7. /headers 경로 다시 호출
정상적으로 값이 반환되면서 filter에 추가한 request header 키와 값을 볼 수 있습니다.
curl -s -HHost:httpbin.example.com "http://$INGRESS_HOST/headers"
배포 방법
수동으로 배포하는 것도 가능합니다만, Gateway API의 경우 자동화된 배포가 가능합니다. 위의 예시에서도 확인했지만 별도로 ingress Gateway 디플로이먼트 리소스를 생성하지 않아도 Gateway에 대한 설정을 통해 자동으로 Gateway 디플로이먼트 및 서비스가 자동으로 배포되는 것을 확인할 수 있습니다.
자동화된 배포
기본적으로 Gateway API에서는 자동으로 서비스와 디플로이먼트 리소스를 동일한 이름으로 생성합니다. 만약에 포트 변경과 같이 Gateway 설정이 변하면 자동으로 업데이트 됩니다.
이런 설정들을 이용해 사용자가 커스텀 할 수 있습니다.
- Gateway의 annotation과 labels 설정은 서비스와 디플로이먼트에 그대로 주입됩니다. 따라서 해당 설정을 통해서 Internal 로드밸런서 등을 통제할 수 있습니다.
- Istio 자체에서 추가적인 annotation 설정을 지원하고 있습니다.
- networking.istio.io/service-type 해당 설정은 Service.spec.type 이 부분을 다룰 수 있는데 외부 노출을 하지 않는 서비스의 경우 ClusterIP로 지정할 수 있습니다. (기본 값은 LoadBalancer 타입이기 때문에)
- Service.spec.loadBalancerIP 필드로 addresses 부분에 IP를 직접 규정할 수 있습니다.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway
spec:
addresses:
- value: 172.18.255.202
type: IPAddress
...
변경전 (172.18.255.200)
변경 후 (172.18.255.202)
리소스 연결 및 확장 (실험적)
아직은 실험적인 기능이지만 Gateway에 리소스를 연결해서 사용자가 수정할 수 있습니다.
현재 대부분 Kubernetes 리소스는 Gateway에 직접 연결할 수 없지만 생성된 디플로이먼트와 서비스는 연결할 수 있습니다.
이렇게 생성된 리소스 이름은 <gateway 이름>-<gateway 클래스 이름> 형식이고, label로 gateway.networking.k8s.io/gateway-name: <gateway-name>이 붙습니다.
예를 들어 Gateway 배포시에 HorizontalPodAutoscaler와 PodDisruptionBudget을 같이 배포하는 것입니다.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway
spec:
gatewayClassName: istio
listeners:
- name: default
hostname: "*.example.com"
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: All
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: gateway
spec:
# 생성된 디플로이먼트를 gateway를 연결할 수 있다.
# `kind: Gateway`을 쓰면안된다.
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: gateway-istio
minReplicas: 2
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: gateway
spec:
minAvailable: 1
selector:
# label을 통해서 gateway와 연결시킨다.
matchLabels:
gateway.networking.k8s.io/gateway-name: gateway
수동 배포
자동 배포를 원하지 않을 때 수동으로 서비스와 디플로이먼트를 배포하도록 할 수 있습니다. 수동으로 생성하는 것이기 때문에 서비스를 Gateway에 잘 연결해야하며 포트도 잘 맞춰줘야합니다.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway
spec:
addresses:
- value: ingress.istio-gateways.svc.cluster.local
type: Hostname
...
메시 트래픽
Gateway API는 mesh traffic을 규정할 수 있습니다. parentRef를 통해서 규정하고 여기에서 gateway 대신 대상 service를 연결합니다.
아래 예시는 클러스터 내에 example이라는 서비스에 대한 모든 요청에 header를 add 하는 내용입니다.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: mesh
spec:
parentRefs:
- group: ""
kind: Service
name: example
rules:
- filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: my-added-header
value: added-value
backendRefs:
- name: example
port: 80
정리
실습 후 리소스를 삭제합니다.
1. httpbin 애플리케이션 및 gateway 삭제
kubectl delete -f samples/httpbin/httpbin.yaml
kubectl delete httproute http
kubectl delete gateways.gateway.networking.k8s.io gateway -n istio-ingress
kubectl delete ns istio-ingress
2. istio 삭제
istioctl uninstall -y --purge
kubectl delete ns istio-system
3. Gateway API CRD 삭제
kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.1.0" | kubectl delete -f -
이상으로 Ingress Gateway 실습을 마칩니다.