서비스 메시와 Kubenetes gateway API

김현우 · 원프레딕트 백엔드 엔지니어
February 27, 2024

Kubenetes gateway API v1 릴리즈!

KGA   쿠버네티스에서 ingress와 로드밸런서 구성의 늪에 빠진 수많은 개발자가 있다. 필자도 쿠버네티스의 로드밸런서를 구성하면서 날먹(?)할 수 있는 방법을 갈구하게 됐다. 그러던 도중 알게 된 사실. Gateway API의 v1이 23년 10월에 정식 출시 되었다는 사실!!! 근데 Kubenetes Gateway API가 뭔데?

Kubenetes gateway API를 알기 위해서

  아.. 이름이 너무 길다. 이제부터 필자는 이 글에서 Kubenetes gateway API를 KGA로 줄여서 부를 것이다. 공식 문서의 설명을 인용하면

  "Gateway API는 Kubernetes의 L4 및 L7 라우팅에 초점을 맞춘 공식 Kubernetes 프로젝트입니다. 이 프로젝트는 차세대 Kubernetes 수신, 로드 밸런싱 및 서비스 메시 API를 나타냅니다. 처음부터 일반적이고 표현력이 풍부하며 역할 지향적으로 설계되었습니다."

  이라 한다. 오... 차세대 서비스 메시 API라니... 듣기만 해도 군침이 싸악 돈다. 우리는 쿠버네티스를 운영해 보면서 무엇인가 아름답게 정리되었단 느낌을 받은 적이 있었을까? 필자는 드디어 그 아름다움을 표현해 줄 수 있는 것이 모습을 드러내고 있다고 생각한다.

  KGA는 Service mesh, Istio, Envoy Proxy, Sidecar 등 사전에 알아야 할 것이 많다. 이 글을 보는 독자들의 시간은 소중하기에 큼직한 개념들을 간단하게 짚어보고 넘어가려 한다. 사실 필자의 글을 읽는 수준 높은 독자들은 득달같이 구현해 보고자 하는 욕구를 참기 어려울 것이다. 게다가 아래의 글을 읽으며 따라 해보기만 해도 구현된 하찮고 아름다운 무언가를 볼 수 있기까지 할 것이다. (아마도..?) 하지만 필자는 클라우드라는 환경을 한 도화지에 그려보고자 하는 쿠버네티스라는 성의 철학과 그 안의 마이크로서비스들이 평화로운 가정을 꾸릴 수 있도록 노력해 온 수많은 사람들의 노고를 독자들이 이해했으면 하기에 독자들이 꾹 참고 천천히 읽어줬으면 하는 바램을 갖고 있다.

서비스 메시(Service mesh)란?

레포트 상세 이미지

  서비스 메시(Service mesh)는 서비스가 애플리케이션 수명 주기 전반에 걸쳐 데이터와 일관성을 공유하며 서로 통신할 수 있도록 지원하는 사전 구성된 애플리케이션 서비스이다. 서비스 메시는 컨테이너의 쓰기 가능한 얇은 계층을 사용하여 마이크로서비스를 관리하는 데 사용된다.

  쉽게 설정하고 배포할 수 있도록 구축된 서비스 메시는 마이크로서비스의 가치를 실현하여 기업이 새로운 서비스를 쉽게 발견하고 API 제품으로 관리할 수 있도록 한다. 즉 MSA에서 각각의 마이크로서비스가 서로 사이좋게 지낼 수 있게 해준다는 의미다. 그렇다면 대표적인 서비스 메시에는 무엇이 있을까?

Istio란?

Istio 추상화

  Istio는 애플리케이션 네트워크 기능을 유연하고 쉽게 자동화할 수 있는 투명한 언어 독립적 방법을 제공하는 현대화된 서비스 네트워킹 레이어인 서비스 메시이다. 클라우드 기반 애플리케이션을 구성하는 다양한 마이크로서비스를 관리하기 위해 널리 사용되는 솔루션이다. Istio는 이러한 마이크로서비스가 서로 통신하고 데이터를 공유하는 방법을 지원한다.

  각 마이크로서비스가 사이좋게 지내기 위한 언어와 언어의 조작을 제공하는 실제화된 서비스 메시인 것이다! Istio는 마이크로 서비스 간의 모든 네트워크 통신을 담당할 수 있는 프록시인 Envoy Proxy를 이용한다. Envoy Proxy는 C++로 개발된 고성능 프록시로써 사이드카 패턴을 이용해서 서비스 메시의 모든 서비스에 대한 inbound/outbound 트래픽을 처리한다. 그렇다면 사이드카는 무엇일까?

사이드카 패턴이란?

사이드카 패턴

  사이드카 패턴은 클라우드 네이티브 애플리케이션 설계와 구현을 위한 쿠버네티스 패턴 24개 중 하나이다. 원래 사용하려고 했던 기본 컨테이너의 기능을 확장하거나 보조하는 용도의 컨테이너를 추가하는 패턴으로써 기본 컨테이너와 독립적으로 동작하는 별도의 컨테이너를 붙이는 패턴이기 때문에 애플리케이션 컨테이너의 변경이나 수정 없이 독립적으로 동작하는 컨테이너를 붙였다 뗐다 할 수 있다. 사이드카 패턴을 이용하면 Istio는 각 서비스에 쉽게 프록시를 부착하여 네트워크를 포착하고 이를 감시 및 통제할 수 있게 된다.

Istio와 Envoy proxy, 그리고 사이드카

Istio 아키텍처   아아.. 알아야 될 것이 뭐 이리도 많은 것일까? 하지만 개발을 개발세발 공부하다 보면 결국 앞에서부터 다시 보는 브루트포스 참사가 발생하니 꾹 참고 조금만 더 들여다 봐주길 바란다. Istio 아키텍처를 조금만 더 자세히 알아보자.

  위 사진은 Istio 아키텍처를 나타낸다. Istio는 크게 두가지 Plain으로 나뉜다. Data Plane은 Envoy를 사이드카 패턴으로 배포하여 Envoy를 통해 돌아다니는 트래픽을 통제한다. Control Plane은 Envoy의 트래픽 관리 및 설정을 전달하는 역할을 한다. Control Plane의 구성요소로는 Pilot, Citadel, Galley가 있다.

  • Pilot: Envoy 설정 관리를 수행하는 모듈이다. Envoy가 호출하는 서비스의 주소를 얻을 수 있는 서비스 디스커버리 기능, 서비스에서 서비스로 호출하는 경로를 컨트롤하는 서비스 트래픽 라우팅 기능 제공. 서비스 안정성을 위해 서비스 간 호출 시 재시도와 서킷브레이커 및 타임아웃 등의 기능을 제공한다. 또한 Pilot 에서 Envoy의 설정을 추상화시켜두어 Istio는 하나의 설정으로 Kubernetes, consol, nomad 등의 다양한 환경에서 사용 할 수 있게 된다.
  • Citadel: 보안 관련 기능을 수행하는 모듈이다. 내장된 인증 방식을 이용하여 서비스와 서비스, 엔드포인트유저와 엔드포인트유저 사이의 인증을 강화하거나, 서비스에 접근 가능한 권한을 제어 할 수 있게 된다.
  • Galley: Istio의 구성 관리 서비스를 제공한다.

  정리하면 Istio는 Envoy를 사이드카 형식으로 배포해 트래픽을 통제하고 내부의 Control Plane으로 설정 관리 및 보안 관련 기능을 수행하는 서비스 메시이다.

  자, 우리는 KGA를 알기 위해서 많은 개념을 훑어보고 왔다. 물론 이를 더 자세히 알기 위해선 L4/L7 라우팅과 로드밸런서 등 네트워크 지식이 더 필요하다. 하지만 이러한 계층적인 지식까지 다 알아보는 것은 독자들이 원하지 않을뿐더러 훌륭한 우리 블로그의 독자들은 너무도 궁금해서 유심히 알아볼 것이라 필자는 굳게 믿고 있으니 걱정하지 않는다.

Istio에 대해서 알아본 이유

  KGA를 적용하기 위해선 KGA와 같이 사용하기 위한 서비스 메시를 골라야 한다. 필자는 Istio를 해당 서비스 메시로 골랐기 때문에 Istio에 대해서 심층적으로 더 알아보았다. 그렇다면 다른 서비스 메시는 무엇이 있을까?

  • Linkerd: 가볍고 사용하기 쉽도록 설계된 오픈소스이다. Istio와 달리 linkerd2-proxy라는 마이크로 프록시위에 구축되었으며 WebSocket 프록시, 자동 TLS, 자동 대기 시간 인식, L7 LB 등을 포함한 여러 주요 기능을 제공함.
  • Consul: 인프라 자동화 소프트웨어 분야의 선도적인 제공업체인 HashiCorp가 개발한 서비스 메시 플랫폼이다. Consul 서비스 검색, 트래픽 관리, 보안을 포함한 여러 주요 기능을 제공한다.
  • AWS 앱 메시: AWS App Mesh는 AWS 클라우드 서비스와 함께 사용하도록 명시적으로 설계된 서비스 메시 플랫폼이다. 또한 트래픽 관리, 보안, 관찰 가능성을 포함한 여러 주요 기능을 제공한다.
  • Google Anthos: Google은 여러 클라우드 환경에서 마이크로서비스 아키텍처를 관리하는 통합 방법을 제공하도록 설계된 서비스 메시 플랫폼이다. 이 또한 여러 주요 기능을 제공한다.

  위와 같이 많은 서비스 메시는 이미 상용화된 서비스들로써 존재하고 있으며 널리 사용되고 있다. 하지만 필자가 Istio를 선택한 이유는 Istio는 아래와 같이 빅테크들의 후원을 받고 있는 서비스 메시이며 이미 수많은 프로덕션에서 채용했기 때문에 Istio를 선택하였다.

providers   위 사진은 대표적인 예시들이다. 실제론 더 많은 기업들이 있으며 구글, IBM, Red Hat, HUAWEI 등 클라우드 빅테크 기업은 거의 다 후원하고 있다. 그러면 적어도 나의 프로덕션이 갑자기 시한부 판정을 받는 일은 없을 것이다!

Ingress-nginx가 아닌 Gateway API를 사용하는 이유

  Nginx는 2023년 기준 세계 1위 프록시 서비스를 개발 및 배포하고 있는 기업이다. 독자들은 이전까진 Nginx사의 Ingress를 사용해서 앱으로의 접근을 구현했을 것이다. 그렇다면 왜 그 거대한 Nginx의 Ingress가 아닌 Gateway API를 써야 할까? 게다가 Gateway API도 L4/L7 라우팅 기능을 제공하는 Ingress와 같은 개념의 진입점이기도 한데 말이다. 하지만 그럼에도 우리가 새로운 도전을 해야 하는 이유는 크게 5가지로 본다.

  • 지원 기능 수: 카나리아 릴리즈, A/B 테스트, 요청 및 응답 조작, 리디렉션 요청, 트래픽 미러링, 네임스페이스 간 트래픽 라우팅 등을 추가로 지원한다. 특히 카나리아 릴리즈, A/B 테스트는 로드밸런서의 구성이 연관된 문제인 만큼 이것들의 구성이 가능하고 또한 쉬워졌다는 것은 전체적인 테스트 환경 구성 및 프로덕션의 네트워크 구성이 용이해졌다는 것을 의미한다.
  • 강력한 확장성: Gateway API에는 여러 확장 지점, 사용자 정의 필터, 정책 첨부 및 대상이 포함된 강력한 어노테이션이 없는 확장성 모델이 포함되어 있다. 이 모델을 사용하면 게이트웨이 API가 유효성 검사를 제공하는 사용자 지정 리소스를 통해 고급 Data Plane 기능을 제공할 수 있다.. 또한 사용자는 라우팅 규칙과 같은 리소스 부분별로 확장을 적용하여 세부성을 더욱 높일 수 있다.
  • 역할 분리: 게이트웨이 API는 인프라 제공자, 클러스터 운영자, 애플리케이션 개발자 라는 세 가지 역할 간에 인프라 프로비저닝 및 구성에 대한 책임을 나눈다. 위의 역할은 RBAC에 의한 책임 분리를 가능하게 한다.
  • 이식성: 많은 기능을 사용하면 게이트웨이 API 구현별 확장 API에 의존할 필요성이 줄어든다. 즉, 사용자가 해당 API에 덜 묶여 있게 됩니다. 이와 대조적으로 Ingress 사용자는 Ingress 컨트롤러와 관련된 확장 프로그램에 크게 의존한다. 또한 Gateway API에는 구현에서 API 기능이 지원되는 방식의 일관성을 보장하기 위한 테스트가 함께 제공된다.
  • 커뮤니티: Gateway API는 커뮤니티 중심 프로젝트다. 프로젝트에 기여하고 싶다면 누구나 역량을 발휘하여 기여할 수 있는 기회를 여기서 가질 수 있다.

  사유를 제외하고도 Experimental한 기능들도 지속적으로 추가되고 있다. 예를 들어 v0.8.0에서 추가된 GAMMA initiative 같은 기능은 South-North, East-west traffic을 효과적으로 관리할 수 있는 서비스 메시 기능이 있다. 게다가 KGA는 Kubernetes의 서비스 네트워킹을 개선하고 표준화하기 위해 구축되고 있는 Sig-Network의 프로젝트로써 쿠버네티스의 차기 표준 API가 될 가능성이 매우 높다는 의미이다. 이 정도면 KGA를 해보고자 하는 의지가 이미 독자들을 아래 예제로 이끌고 있을 것이다. 우리가 알아볼 것은 충분히 알아봤으니 드디어 정말로 아주 간단하지만 명확한 예제를 손으로 직접 해볼 차례다.

KGA 적용해보기

설치 환경

OS Input method
CPU Intel i3 13100f
RAM 16GB RAM
DISK 1TB SSD
OS Windows 11 with wsl2
VM 3 Each Ubuntu22.02 in virtualbox were made by vagrant
NODE 1 Master Node and 2 Worker Node

  본 글은 KGA에 대해서 다루는 글이기 때문에 쿠버네티스는 이미 구축되어 있다는 것을 전제로 진행한다. 쿠버네티스는 노드당 최소 1개의 프로세서 할당 및 16GB RAM, VM당 20GB 이상의 스토리지 용량을 권장하고 있으니 참고하자. 필자는 접근성을 높이기 위해 비교적 낮은 사양으로 준비했다.

사전 준비

Istio를 다운받자.

# istio 다운
curl -L https://istio.io/downloadIstio | sh -
cd istio-1.20.3

그리고 istio를 실행하기 위해 환경 변수를 잡아주자.

# istio 권한 부여
export PATH=$PWD/bin:$PATH
sudo chmod 777 $PWD/bin
sudo cp -f $PWD/bin/istioctl /usr/bin

이제 우리가 본격적으로 다뤄볼 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.0.0" | kubectl apply -f -; }

  CRD는 쿠버네티스의 커스텀 리소스로서 쿠버네티스 개념이므로 넘어간다. 이제 istio를 minimal 모드로 설치한다. istio는 여러 설치 모드를 지원하며 우리에겐 minimal이면 충분하다. 설치가 완료된 모습은 아래와 같다. istio 설치

istio-system namespace에 생긴 istiod pod는 아래와 같다.

k8s istio resources

  우리는 gcr(Google Container Registry)의 훌륭한 예제를 가지고 실습을 진행할 것이다. 실습은 아주 간단하고 쉬운 예제를 들고 왔으니 걱정할 필요는 없다.

앱 서비스 구축

  일단 프론트, 백엔드 파드와 서비스를 배포해 우리의 서비스를 구축해보자.

프론트엔드

frontend-deploy.yaml 디플로이는 아래와 같다.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  selector:
    matchLabels:
      app: hello
      tier: frontend
      track: stable
  replicas: 1
  template:
    metadata:
      labels:
        app: hello
        tier: frontend
        track: stable
    spec:
      containers:
        - name: nginx
          image: "gcr.io/google-samples/hello-frontend:1.0"
          lifecycle:
            preStop:
              exec:
                command: ["/usr/sbin/nginx","-s","quit"]
...

아래 명령어로 리소스를 적용해주자.

kubectl apply -f ./frontend-deploy.yaml

모든 리소스는 위의 명령에서 파일 이름만 바꿔서 적용한다.

frontend-scv.yaml 서비스는 아래와 같다.

---
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  selector:
    app: hello
    tier: frontend
  ports:
  - protocol: "TCP"
    port: 80
    targetPort: 80
  type: LoadBalancer
...

백엔드

backend-deploy.yaml 디플로이는 아래와 같다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
spec:
  selector:
    matchLabels:
      app: hello
      tier: backend
      track: stable
  replicas: 3
  template:
    metadata:
      labels:
        app: hello
        tier: backend
        track: stable
    spec:
      containers:
        - name: hello
          image: "gcr.io/google-samples/hello-go-gke:1.0"
          ports:
            - name: http
              containerPort: 80

backend-scv.yaml 서비스는 아래와 같다.

apiVersion: v1
kind: Service
metadata:
  name: hello
spec:
  selector:
    app: hello
    tier: backend
  ports:
  - protocol: TCP
    port: 80
    targetPort: http

Gateway

  게이트웨이는 논리적 로드밸런서의 인스턴스화를 나타낸다. 이는 가상의 istio 템플릿으로 만들어졌으며 게이트웨이는 포트 80에서 HTTP 트래픽을 수신한다. 게이트웨이는 EXTERNAL-IP 주소를 자동으로 할당한다.

Gateway 구축

gateway.yaml 게이트웨이는 아래와 같다.

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: hello-gateway
  annotations:
    service.beta.kubernetes.io/port_80_no_probe_rule: "true" # FOR AZURE
spec:
  gatewayClassName: istio
  listeners:
  - name: default
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: All

HttpRoute

  HTTpRoute는 ParentRefs를 사용하여 연결하려는 게이트웨이를 지정한다. 게이트웨이가 이 연결을 허용하는 한 (기본적으로 동일한 네임스페이스의 경로는 신뢰 됨) 이를 통해 경로가 상위 게이트웨이로부터 트래픽을 수신할 수 있다. BackendRefs는 트래픽이 전송될 백엔드를 정의한다. 아래에서는 게이트웨이 수신기의 트래픽이 백엔드로 라우팅 되는 방식을 정의한다. 지정된 호스트 경로가 없기 때문에 이 HTTPRoute는 로드 밸런서의 포트 80에 도착하는 모든 HTTP 트래픽을 일치시켜 서비스 포드로 보낸다.

  HTTPRoute는 다양한 백엔드(잠재적으로 소유자가 다를 수 있음)에 대한 트래픽을 필터링하는 데 사용되는 경우가 많지만, 이번에는 단일 서비스 백엔드를 사용하여 가능한 가장 간단한 경로를 보여준다. 이 예시는 서비스 소유자가 게이트웨이와 HTTPRoute를 모두 단독으로 배포하여 가장 쉽고 간단하게 서비스를 제공하는 방법을 보여준다.

hello-httproute.yaml 라우트는 아래와 같다.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: hello-route
spec:
  parentRefs:
    - name: hello-gateway
  rules:
    - filters:
        - type: ResponseHeaderModifier
          responseHeaderModifier:
            add:
              - name: Controller
                value: ISTIO
      backendRefs:
        - name: frontend
          port: 80

  이제 게이트웨이와 HTTPRoute까지 리소스를 적용했으므로 전체 리소스를 확인한다. 확인은 아래 명령어로 한다.

kubectl get all

뒤에 -n 옵션을 붙이면 네임스페이스 별로 조회할 수 있다. 붙이지 않으면 기본 네임스페이스인 default 네임스페이스의 리소스를 조회한다. 조회한 모습은 아래와 같다.

k8s 모든 리소스

  hello-gateway-istio의 EXTERNAL-IP가 실제 쿠버네티스의 외부에서 접속할 수 있는 IP이므로 기억해 두자. frontend의 경우엔 컨테이너 내부의 nginx를 부팅하기 때문에 6번 정도 restart한 후에 Running으로 변경된다.

  아래 명령어로 위의 hello-gateway-istio 의 EXTERNAL-IP가 실제로 네트워크 어뎁터에 추가되었는지 확인하자.

ip a

조회한 모습은 아래와 같다. ip a

  curl로 hello-gateway-istio 의 EXTERNAL-IP 로 http 요청을 날려 연결을 확인해 보자. 만약 가상 어댑터가 보이는데 연결이 안된다면 각 가상머신 혹은 디바이스에서 ipv4 포워드가 설정되어 있는지 확인해 보자.

helloworld! 위와 같이 {"message":"Hello"} 메세지를 확인할 수 있다. 드디어 우리는 쿠버네티스가 지향하는 아름다운 방법으로 우리의 작은 세상의 첫 문을 열었다

맺음말

  실제로 구축을 해본 독자들은 깨달을 것이다. Gateway API의 리소스 yaml 파일들은 굉장히 간소하고 직관적이다. 외부 세상과 첫 접점이 되는 Gateway를 만들어주며 해당 Gateway에 서비스가 직접 연결되는 것처럼 보인다. 이는 서비스의 개요를 파악하는 데 있어서 굉장히 큰 이점을 가져다준다. 그렇다면 이제 무엇을 더 할 수 있을까?

  아쉽게도 본 글에서 준비한 것은 여기까지이다. 이미 많은 개념을 늘어놓은 데다가 각각의 개념이 쉽지 않기에, 여기서 더 하진 않기로 했다. 하지만 KGA는 HttpRoute,TLSRoute, TCPRoute를 넘어 IoT의 메세지 큐 플랫폼을 위한 gRPCRoute도 실험적 기능으로 지원한다. 그리고 경로 규칙을 통해 로드밸런싱을 지원하며 가중 라우팅을 이용해 카나리아 테스트, A/B 테스트, 스트랭글러 패턴 개발, 트래픽 할당량 조절 등 수많은 기능들을 손쉽게 구현해 볼 수 있다. 또한 엠비언트 메쉬를 이용하면 사이드카 패턴이 가지는 서비스와 1대1 매칭에서 가지는 리소스 핸들링의 어려움 등의 단점을 극복할 수도 있다.

  필자는 다음 글에선 독자들의 숨길 수 없는 빛나는 눈빛을 만족시켜 주기 위해서 심화 예제를 가져와 독자들에게 소개해 볼 예정이다. 다음 글을 즐겁게 쓸 수 있도록 이 글의 독자는 아래 채용 블로그를 참고하여 원프레딕트에 합류해 더 넓은 세상을 같이 고민해 볼 수 있는 기회를 가져보도록 하자.

원프레딕트는 더 나은 제품을 고민하며 기술적인 문제를 함께 풀어낼 동료를 찾고 있습니다.
자세한 내용은 채용 사이트를 참고해 주세요.