K8s Gateway-api 사용하기

기존에 Https 라우팅을 위해 사용하던 Ingress 리소스를 Gateway-Api 의 리소스로 싹 고쳤다. 이 글은 마이그레이션 방법 및 Gateway-api 에 대한 기록이다.

Gateway-API 란?

Gateway api 는 k8s 새롭게 제공하는 네트워크 표준을 말한다. 이를 이용하면 Ingress 에서는 할 수 없었던 복잡한 규칙( 라우팅 규칙, 가중치 기반 트래픽 처리.. 등 )을 처리할 수 있다.
사실 기존에도 이 한계를 극복하기 위해 나온 다양한 솔루션들이 이미 존재한다. (ex, istio, Linkerd ) 하지만 이것들은 각자 독자적인 CRD를 사용했고, 그 벤더사에 lock-in 되는 문제도 있었다.

이에, gateway-api 를 이용해 공통된 표준을 만들고, 이를 istio 나 linkerd 같은 구현체들이 따를 수 있게 함으로써 이러한 문제들을 해결하려는 것이다. 더 다양한 내용은 k8s 공식 설명 을 참고하자.

Ingress Migration

본격적으로, Migration 해보자. k8s gateway api 를 지원하는 구현체를 골라야 하는데, 필자는 istio를 사용했다. istio docs 에 따르면, 계속해서 지원할 예정이고, 점차 표준으로 채택한다고 한다. 또한, 기존에 Service mesh 로 강력한 기능들을 많이 제공해 왔기 때문에 선택했다.
일단 istio 를 설치해보자. 스크립트는 여기서 참고했다. 작성일 기준으로 istio 버전은 1.27.1 이다.

#!/bin/bash

# repo 추가
helm repo add istio https://istio-release.storage.googleapis.com/charts
helm repo update

# Gateway-api CRDS 추가. experimental 버전을 써도 되긴 하나, 그냥 standard 를 사용했다.
wget https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml
kubectl apply -f standard-install.yaml

# istio-base 설치.
helm install istio-base istio/base -n istio-system --create-namespace
# istiod 설치. values.yaml 은 resource 부분만 변경했다. 필요하면 고치자.
helm install istiod istio/istiod -n istio-system -f ./istiod/values.yaml --wait

여기서 ingress-gateway 를 배포해야 하는데, 여기서 두가지 선택지가 있다.

  1. gateway.networking.k8s.io/v1
  2. networking.istio.io/v1

전자가 k8s gateway-api 표준으로 GatewayClass, HttpRoute, ReferenceGrant 등을 지원하는것이고, 후자는 기존에 사용하던 istio 의 custom crd 이다. Virtual Service, DestinationRule.. 등등이 있다.
Istio는 이제 양쪽 모두를 지원하기 때문에, 기왕이면 표준 API 를 이용해서 설치한다.

# gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: api-gateway
  namespace: istio-system
spec:
  gatewayClassName: istio
  listeners:
  - name: http #for aceme-challenge http.
    hostname: "your-domain"
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: All
  ...

Gateway 에 보면, 아래쪽에 listeners 를 이용해서 규칙을 적용할 수 있는데, 여기서 cert-manager 를 사용하고 있다면( 필자도 마찬가지였다 ) http 로 라우팅되는 저 규칙을 반드시 추가해 주어야 한다. acme-challenge 를 받아줄 http 통신이기 때문에 없으면 갱신도, 인증서도 발급되지 않는다.
이 yaml 을 적용하면 gateway가 만들어진다. ( svc type 은 LoadBalancer 이다. )

추가적으로, cert-manager 의 경우도 Gateway Api 를 사용해야 하기 때문에 함께 업데이트 한다.

#!/bin/bash

helm install \
  cert-manager oci://quay.io/jetstack/charts/cert-manager \
  --version v1.18.2 \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true \
  --set config.apiVersion="controller.config.cert-manager.io/v1alpha1" \
  --set config.kind="ControllerConfiguration" \
  --set config.enableGatewayAPI=true

기존에 사용하던 ClusterIssuer 를 다음과 같이 변경해야 한다.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-issuer
spec:
  acme:
    # The ACME server URL
    server: https://acme-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: your-email
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-priv-key-name
    # Enable gateway-api
    solvers:
      - http01:
          gatewayHTTPRoute:
            parentRefs:
            - name: api-gateway
              namespace: istio-system
              kind: Gateway

solver 를 ingress 에서 http route 로 변경해주고, parentRefs 에 위쪽에 만든 Gateway-api 를 사용하는 게이트웨이를 추가한다.

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: letsencrypt-cert
  namespace: istio-system
spec:
  secretName: letsencrypt-priv-key-name
  issuerRef:
    name: letsencrypt-issuer
    kind: ClusterIssuer
  dnsNames:
  - your.dns.here
  commonName: your.dns.here

그리고 certificate 를 만들어서, 챌린지가 정상적으로 성공하는지 확인한다. 기본 Gateway 로 라우팅 된 후, certificate 까지 발급되었는지 확인하자.

여기까지 만들어졌으면 이제 Https 로 통신하기 위한 라우팅 규칙을 추가한다.

# gateway.yaml 에 https 라우팅 추가하고 rollout 한다.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: api-gateway
  namespace: istio-system
spec:
  gatewayClassName: istio
  listeners:
  ... # http 삭제하면 자동갱신 안됨.
  - name: https-blog
    hostname: "notypie.dev" # 위쪽에서 작성한 도메인을 쓰면 된다.
    protocol: HTTPS
    port: 443
    allowedRoutes:
      namespaces:
        from: All
    tls:
      mode: Terminate
      certificateRefs:
      - name: notypie-dev-blog-tls # 위쪽에서 만든 tls 이름을 작성하면 된다.
        kind: Secret
...
---
# blog_route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: blog-route
  namespace: notypie-dev
spec:
  parentRefs:
  - name: api-gateway
    namespace: istio-system
    sectionName: https-blog # gateway listeners 이름을 맞춰준다.
  hostnames:
  - "notypie.dev"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: kubernetes.service.name
      port: PORT

이 블로그 라우팅을 예로 들면, 위쪽에서 만든 tls 를 설정값으로 추가하고, Ingress 를 대체하기 위해 HTTP Route 리소스를 생성한다.
여기까지 작성해서, domain 을 이용해서 접근했을 때 정상적으로 https 로 페이지가 보인다면 성공한 것이다.

마치며

istio 를 구현체로 이용해서 Gateway api 를 사용해봤다. 변경해야 할 부분이 많아서, 고생을 좀 했다.
이번에 표준으로 다 맞춰두었으니 다음번엔 istio 리소스들을 활용해서 다양한 규칙, 서킷브레이커 등을 적용해보자.

Leave a Comment