k8s에서 HTTP/2 (gRPC 서버)를로드 밸런싱하고 싶습니다.

kubernetes에서 HTTP / 2를로드 밸런싱했습니다.



소개



배경



현재 학생 프로젝트에서 인프라로 GKE (Google Kubernetes Engine)를 이용하고 있습니다.
REST API 서버를 gRPC로 환장하려고 할 때 어떤 문제에 부딪혔습니다.

전제지식으로



Kubernetes의 로드 밸런서


Services are a “layer 4” (TCP/UDP over IP) construct, the proxy was purely in userspace현재의 Kubernetes Ingress는 OSI 참조 모델의 전송 계층(L4) 로드 밸런서를 제공합니다. (베타 버전은 그러하지 않습니다!)

gRPC 통신 방법



gRPC 통신은 하나의 TCP 연결을 사용합니다.
즉, 연결이 설정되는 동안 서버와 클라이언트는 계속 연결됩니다.

문제란?


gRPC is a modern RPC protocol implemented on top of HTTP/2. HTTP/2 is a Layer 7 (Application layer) protocol (참조: 공식 사이트에서)
그렇듯이 gRPC는 애플리케이션 계층 (L7)에서 작동합니다.
즉, L4 로드 밸런서는 제대로 부하를 분산하지 못할 수 있습니다!
Ingrees 사용할 수 없잖아!

Envoy를 만났습니다.



L7 LoadBalancer라고 조사하고 있으면 기사를 찾아왔습니다.
ref : kubernetes로 gRPC 할 때 envoy를 끼워 보았습니다.
Envoy는 클라우드 네이티브용 마이크로서비스용 프록시로 설계된 오픈 소스 소프트웨어입니다.
L7 프록시도 물론 대응입니다!


설정



그럼 조속히 도입해 갑시다!
흐름은 이런 느낌입니다.
1. gRPC 서버의 서비스를 Load Balancer에서 Headless Service로 설정
2. Envoy를 클러스터에 배포하고 외부 게시

gRPC 서버의 서비스를 Load Balancer에서 Headless Service로 설정



Envoy의 구성 파일은 이런 느낌입니다.


apiVersion: v1
kind: ConfigMap
metadata:
  name: "envoy-config"
data:
  envoy.json: |
    {
      "listeners": [
        {
          "address": "tcp://0.0.0.0:15001",
          "filters": [
            {
              "type": "read",
              "name": "http_connection_manager",
              "config": {
                "codec_type": "auto",
                "stat_prefix": "ingress_http",
                "route_config": {
                  "virtual_hosts": [
                    {
                      "name": "service",
                      "domains": ["*"],
                      "routes": [
                        {
                          "timeout_ms": 0,
                          "prefix": "/",
                          "cluster": ""
                        }
                      ]
                    }
                  ]
                },
                "filters": [
                  {
                    "type": "decoder",
                    "name": "router",
                    "config": {}
                  }
                ]
              }
            }
          ]
        }
      ],
      "admin": {
       "access_log_path": "/dev/stdout",
       "address": "tcp://127.0.0.1:8001"
      },
      "cluster_manager": {
        "clusters": [
          {
            "name": "rakusale-grpc",
            "features": "http2",
            "connect_timeout_ms": 250,
            "type": "strict_dns",
            "lb_type": "round_robin",
            "hosts": [{"url": "tcp://Host名:Port番号"}]
         }
        ]
      }
    }

주목하고 싶은 곳은 여기입니다!


"cluster_manager": {
        "clusters": [
          {
            "name": "rakusale-grpc",
            "features": "http2",
            "connect_timeout_ms": 250,
            "type": "strict_dns",
            "lb_type": "round_robin",
            "hosts": [{"url": "tcp://Host名:Port番号"}]
         }
        ]
      }

clusters["hosts"]에서 서버 측 IP와 포트를 지정합니다.
즉, Envoy 측이 부하 분산을 위해 Pod의 IP를 알아야 합니다.

여기서 Headless Service



헤드리스 서비스는 서비스에 DNS 요청을 보내면 포드의 IP를 반환합니다.
움직이고 싶은 서버의 서비스는 이렇게 설정하고 있습니다.
apiVersion: v1
kind: Service
metadata:
  labels: 
    name: ""
  name: ""
spec:
  clusterIP: None
  ports:
    - name: ""
      port: 3001
      protocol: TCP
      targetPort: 3001
  selector:
    app: ""

ClusterIP: None으로만 하면 Headless Service가 됩니다!

Envoy를 클러스터에 배포하고 외부 게시



필요한 것은 세 가지입니다.
- envoy_configmap.yaml : 아까 소개하고 있습니다!
- envoy_deployment.yaml
- envoy_service.yaml

envoy_deployment.yaml


apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: "envoy"
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: "envoy"
    spec:
      volumes:
        - name: envoy
          configMap:
            name: "envoy-config"
        # - name: tls
        #   secret:
        #     secretName: tlssecret

      containers:
        - name: envoy
          image: envoyproxy/envoy:6e3633496f5a9412abdca8bac7db6b701ae8ce14
          command:
            - "/usr/local/bin/envoy"
          args:
            - "--config-path /etc/envoy/envoy.json"
          resources:
            limits:
              memory: 512Mi
          ports:
            - containerPort: 15001
              name: app
            - containerPort: 8001
              name: envoy-admin
          volumeMounts:
            - name: envoy
              mountPath: /etc/envoy
            # - name: tls
            #   mountPath: /etc/tlssecret
            #   readOnly: true

envoy_service.yaml


apiVersion: v1
kind: Service
metadata:
  labels:
    name: "envoy"
  name: "envoy-service"
spec:
  type: LoadBalancer
  selector:
    app: "envoy"
  ports:
    - name: tcp
      port: 15001
      targetPort: 15001

이상을 준비해 kubectl apply 하고 GIP에 접속하면, 부하 분산이 완성되어 있을 것입니다!

끝에



Kubernetes 클러스터 내의 패킷이나 네트워크 감시하는 툴로 좋은 것이 있으면 가르쳐 주었으면 합니다!
그리고, gRPC서버 쓸 때에 로그를 제대로 쓰는 구현으로 하지 않으면 마음 불편하다고 생각했습니다,,,
항상 프레임워크를 해주었기 때문에,,,
앞으로도 정진하겠습니다.

좋은 웹페이지 즐겨찾기