Kubernetes Ingress 및 Egress 트래픽 관리를 단순화하는 방법

Service Mesh가 실제로 Kubernetes 환경 관리를 더 복잡하게 만드는 방법 중 하나는 Ingress Controller와 별도로 구성해야 하는 경우입니다. 별도의 구성에도 시간이 많이 소요되는 것은 아닙니다. 적절한 트래픽 라우팅을 방해하고 보안 취약성(예: 악의적인 사용자가 제한된 앱에 액세스하는 경우)과 좋지 않은 경험(예: 고객이 승인된 앱에 액세스할 수 없는 경우)을 유발할 수 있는 구성 오류의 가능성을 높입니다. 별도의 구성을 수행하는 데 걸리는 시간 외에도 결국 오류를 해결하는 데 더 많은 시간을 소비하게 됩니다.

NGINX Plus 기반 NGINX Ingress Controller를 NGINX Service Mesh와 통합하여 Ingress 및 Egress mTLS 트래픽을 모두 제어하면 이러한 문제를 방지하고 시간을 절약할 수 있습니다.

목차

1. 전제 조건
2. NGINX Service Mesh로 NGINX Ingress Controller 배포
3. 표준 Kubernetes Ingress 리소스를 사용하여 앱 노출
4. NGINX VirtualServer 리소스를 사용하여 앱 노출

5. NGINX Ingress Controller로 보안 Egress Route 구성
6. 지연 시간(Latency)에 “아니오”라고 말하십시오.

1. 전제 조건

실제 데모를 시작하기 전에 다음 전제 조건을 수행했습니다.

1. Kubernetes 클러스터에 NGINX Service Mesh control plane을 설치하고 mTLS 및 Service Mesh에 대한 엄격한 정책을 설정했습니다.

2. NGINX Plus 기반 NGINX Ingress Controller를 Kubernetes 클러스터에 배포(DaemonSet가 아님)로 설치하고 Egress을 활성화하고 LoadBalancer 유형의 Service로 노출했습니다.

3. 지침에 따라 샘플 bookinfo 앱을 다운로드하고 NGINX Service Mesh Sidecar를 삽입하고 앱을 배포했습니다.

참고: 해당 사례는 NGINX OSS 기반 NGINX Ingress Controller에서 작동하지 않습니다. 읽기 쉽도록 이 모범사례의 나머지 부분에서는 NGINX Plus 기반 NGINX Ingress Controller를 간단히 “NGINX Ingress Controller”라고 합니다.

1단계에서 생성된 엄격한 정책의 결과로 Mesh 외부의 클라이언트에서 bookinfo 앱에 대한 요청이 Sidecar에서 거부됩니다. 먼저 다음 명령을 실행하여 포트 전달을 설정하여 데모에서 이를 설명합니다.

> kubectl port-forward svc/product-page 9080
Forwarding from 127.0.0.1:9080 -> 9080
Forwarding from [::1]:9080 -> 9080
Handling connection for 9080

앱에 액세스하려고 하면 로컬 시스템이 Service Mesh의 일부가 아니기 때문에 상태 코드 503이 표시됩니다.

> curl localhost:9080
503

2. NGINX Service Mesh를 사용하여 NGINX Ingress Controller 배포

앱 노출 프로세스의 첫 번째 단계는 NGINX Ingress Controller 인스턴스를 배포하는 것입니다. 해당 지침은 Kubernetes용 NGINX Plus Ingress Controller로 배포 자습서에서 제공됩니다.

NGINX는 이를 위해 Deployment 및 DaemonSet 매니페스트를 모두 제공합니다. 데모에서는 배포 매니페스트인 nginx-plus-ingress.yaml을 사용합니다. 여기에는 동일한 NGINX Ingress Controller 인스턴스를 통해 Ingress 및 Egress 트래픽을 모두 라우팅하는 주석이 포함됩니다.

        nsm.nginx.com/enable-ingress: "true"
        nsm.nginx.com/enable-egress: "true"

매니페스트를 사용하면 NGINX Service Mesh용 인증 기관(CA)인 Spire와 NGINX Ingress Controller를 직접 통합할 수 있으므로 NGINX Service Mesh Sidecar를 NGINX Ingress Controller에 삽입할 필요가 없습니다. 대신 NGINX Ingress Controller는 Spire CA에서 직접 인증서와 키를 가져와 Mesh의 파드(Pod)가 있는 mTLS에 사용합니다. 매니페스트는 Spire 에이전트 주소를 지정합니다.

        args:
          - -spire-agent-address=/run/spire/sockets/agent.sock

Spire 에이전트 UNIX 소켓을 NGINX Ingress Controller 파드(Pod)에 마운트합니다.

      volumes:
      - hostPath:
          path: /run/spire/sockets
          type: DirectoryOrCreate
        name: spire-agent-socket

매니페스트에 대해 마지막으로 주의해야 할 사항은 -enable-internal-routes CLI 인수로, 이 인수를 사용하여 egress 서비스로 라우팅할 수 있습니다.

        args:
          - -enable-internal-routes

kubectl apply -f nginx-plus-ingress.yaml 명령을 실행하여 NGINX Ingress Controller를 설치했으며 이 시점에서 nginx-ingress 네임스페이스의 배포를 검사합니다. 다음 출력의 READY 열에서 볼 수 있듯이 NGINX Service Mesh 사이드카(Sidecar)를 주입하지 않았기 때문에 NGINX Ingress Controller 파드(Pod)에 대한 컨테이너는 하나만 있습니다.

또한 NGINX Ingress Controller(여기서는 35.233.133.188)의 외부 IP 주소를 클러스터 외부에 노출하기 위해 LoadBalancer 유형의 서비스를 배포했습니다. 해당 IP 주소에서 샘플 bookinfo 애플리케이션에 액세스합니다.

> kubectl get pods --namespace=nginx-ingress
NAME                                 READY   STATUS    RESTARTS   AGE
pod/nginx-ingress-867f954b8f0fzdrm   1/1     Running   0          3d3h

NAME                    TYPE           CLUSTER-IP      EXTERNAL-IP      ...
service-nginx-ingress   LoadBalancer   10.31.245.207   35.233.133.188   ...

      ... PORT(S)                      AGE
      ... 80:31469/TCP,443:32481/TCP   4d2h
...

3. 표준 Kubernetes Ingress 리소스를 사용하여 앱 노출

이제 bookinfo-ingress.yaml에 정의된 표준 Kubernetes Ingress 리소스를 사용하여 메시에 bookinfo 앱을 노출합니다.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: bookinfo-ingress
spec:
  ingressClassName: nginx # use only with k8s version >= 1.18.0
  tls:
  - hosts:
    - bookinfo.example.com
    secretName: bookinfo-secret
  rules:
  - host: bookinfo.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: productpage
          servicePort: 9080

리소스는 10행에서 bookinfo 앱에 대한 Kubernetes 시크릿을 참조하고 bookinfo.example.com에 대한 요청이 productpage 서비스(11-18행)로 전송되도록 지정하는 라우팅 규칙을 포함합니다. 비밀은 bookinfo-secret.yaml에 정의되어 있습니다.

apiVersion: v1
kind: Secret
metadata:
  name: bookinfo-secret
type: kubernetes.io/tls
data:
  tls.crt: LS0tLS1CRU...
  tls.key: LS0tLS1CRU...

자체 서명된 키와 인증서를 로드하기 위해 이 명령을 실행합니다.

> kubectl apply -f bookinfo-secret.yaml
secret/bookinfo-secret unchanged

Ingress 리소스를 활성화합니다.

> kubectl apply -f bookinfo-ingress.yaml
ingress.networking.k8s.io/bookinfo-ingress deleted

출력 끝에서 이벤트로 확인된 대로 Ingress Controller가 리소스에 정의된 경로를 추가했는지 확인합니다.

> kubectl describe ingress bookinfo-ingress
...
Events:
  Type    Reason          Age   From                      Message
  ----    ------          ----  ----                      -------
  Normal  AddedOrUpdated  5s    nginx-ingress-controller  Configuration for ...
        ...default/bookinfo-ingress was added or updated

이제 브라우저를 사용하여 https://bookinfo.example.com/에서 bookinfo 앱에 액세스합니다. (위에서 언급한 것처럼 인그레스 컨트롤러 서비스의 IP 주소(데모에서는 35.233.133.188)와 bookinfo.example.com 간에 매핑을 로컬 /etc/hosts 파일에 이전에 추가했습니다. 지침은 설명서를 참조하세요. ) 페이지의 도서 리뷰 섹션에 있는 정보는 bookinfo.yaml(다운로드)에 정의된 세 가지 버전의 리뷰 서비스를 통해 요청이 순환함에 따라 주기적으로 변경됩니다.

다음으로 클러스터에 대한 Ingress 트래픽을 검사합니다. generate-traffic.sh 스크립트를 실행하여 NGINX Ingress Controller의 공용 IP 주소를 통해 productpage 서비스에 요청한 다음 nginx-meshctl top 명령을 실행하여 트래픽을 모니터링합니다.

> nginxmesh-ctl top deploy/productpage-v1
Deployment       Direction  Resource       Success Rate  P99    P90    P50   ...
productpage-v1  
                 To         details-v1     100.00%       3ms    3ms    2ms   
                 To         reviews-v2     100.00%       99ms   90ms   20ms
                 To         reviews-v3     100.00%       99ms   85ms   18ms
                 To         reviews-v1     100.00%       20ms   17ms   9ms
                 From       nginx-ingress  100.00%       192ms  120ms  38ms

      ... NumRequests 
      ... 14
      ... 5
      ... 5
      ... 12

4. NGINX VirtualServer 리소스를 사용하여 앱 노출

다음으로 NGINX VirtualServer 리소스를 사용하여 앱을 노출하는 다른 방법을 보여줍니다. 트래픽 분할(traffic splitting) 및 content-based 라우팅과 같은 보다 복잡한 트래픽 처리를 지원하는 사용자 지정 NGINX Ingress Controller 리소스입니다.

먼저 표준 Ingress 리소스를 삭제합니다.

> kubectl delete -f bookinfo-ingress.yaml
ingress.networking.k8s.io "bookinfo-ingress" deleted

bookinfo-vs.yaml 파일은 bookinfo-ingress.yaml(7-8행)과 동일한 비밀로 mTLS를 구성합니다. 9-12행은 productpage 서비스를 업스트림으로 정의하고 13-24행은 bookinfo.example.com에서 만들어진 모든 GET 요청을 해당 업스트림으로 보내는 경로입니다. GET 이외의 HTTP 메서드의 경우 상태 코드 405를 반환합니다.


apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: bookinfo-vs
spec:
  host: bookinfo.example.com
  tls:
    secret: bookinfo-secret
  upstreams:
  - name: product
    service: productpage
    port: 9080
  routes:
  - path: /
    matches:
    - conditions:    
      - variable: $request_method
        value: GET
      action:
        pass: product
    action:
     return:
       code: 405
       body: "Method not allowed\n"

리소스를 적용합니다.

> kubectl apply -f bookinfo-vs.yaml
virtualserver.kubernetes.nginx.org/bookinfo-vs created

그런 다음 Ingress 리소스와 동일한 단계를 수행합니다. kubectl describe 명령을 실행하여 올바른 배포를 확인하고 브라우저에서 앱에 액세스합니다. 앱이 올바르게 작동하고 있다는 또 다른 확인은 POST 메서드를 거부한다는 것입니다.

> curl -k -X POST https://bookinfo.example.com/
Method not allowed

5. NGINX Ingress Controller로 보안 Egress Route 구성

이제 NGINX Ingress Controller를 통해 Egress 트래픽을 라우팅하는 방법을 보여줍니다.

우리는 이미 bash.yaml에 간단한 bash 파드(Pod)를 정의하고 요청을 보내는 기본 네임스페이스에 배포했습니다. 이 출력의 READY 열에 표시된 것처럼 NGINX Service Mesh 사이드카(Sidecar)가 삽입되었습니다.

> kubectl get all
NAME                        READY  STATUS    RESTARTS   AGE
pod/bash-6ccb678958-zsgm7   2/2    Running   0          77s

NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.31.240.1   <none>        443/TCP   4d2h
...

파드(Pod) 내에서 NGINX Service Mesh의 일부가 아닌 엔터티인 Egress 서비스로의 요청을 활성화하려는 몇 가지 사용 사례가 있습니다. 예는 배포된 서비스입니다.

  • 클러스터 외부
  • 다른 클러스터에서
  • 동일한 클러스터에 있지만 NGINX Service Mesh 사이드카(Sidecar)가 삽입되지 않음

데모에서는 최종 사용 사례를 고려하고 있습니다. NGINX Service Mesh에 의해 제어되지 않고 NGINX Service Mesh 사이드카(Sidecar)의 자동 삽입(Automatic Injection)이 비활성화된 레거시 네임스페이스에 애플리케이션이 배포되어 있습니다. 앱에 대해 실행 중인 파드(Pod)는 하나만 있습니다.

> kubectl get all --namespaces=legacy
NAME                          READY  STATUS    RESTARTS   AGE
pod/target-5f7bcb96c6-km9lz   1/1    Running   0          27m

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/target-svc   ClusterIP   10.31.245.213   <none>        80/TCP,443/TCP   27m
...

NGINX Service Mesh에 대해 엄격한 mTLS 정책을 구성했음을 기억하십시오. 결과적으로 서로 인증할 수 없기 때문에 bash 파드(Pod)에서 대상 서비스로 직접 요청을 보낼 수 없습니다. 시도하면 다음과 같이 상태 코드 503이 표시됩니다.

> kubectl exec -it bash-6ccb678958-zsgm7 -c bash -- curl target-svc.legacy
curl: (56) Recv failure: connection reset by peer
503command terminated with exit code 56

솔루션은 bash 파드(Pod)가 NGINX Ingress Controller를 통해 Egress 트래픽을 보낼 수 있도록 하는 것입니다. bash.yaml의 14-15행에서 주석을 제거합니다.


      #annotations:
        #config.nsm.nginx.com/default-egress-allowed: "true" # uncomment to route egress traffic to NGINX Ingress Controller

그런 다음 새 구성을 적용합니다.

> kubectl apply -f bash.yaml
deployment.apps/bash configured

새 bash 파드(Pod)가 실행되었는지 확인합니다.

> kubectl get pods
NAME                    READY  STATUS        RESTARTS   AGE
bash-678c8b4579-7sfml   2/2    Running       0          6s
bash-6ccb678958-zsgm7   2/2    Terminating   0          3m28s

이제 이전과 동일한 kubectl exec 명령을 실행하여 bash 파드(Pod)에서 대상 서비스로 요청을 보내면 503 대신 상태 코드 404가 표시됩니다. 이는 bash 파드(Pod)가 NGINX Ingress Controller에 요청을 성공적으로 보냈음을 나타냅니다. 그러나 후자는 경로가 정의되지 않았기 때문에 어디로 전달할지 모릅니다.

legacy-route.yaml에서 다음 Ingress 리소스 정의를 사용하여 필요한 경로를 생성합니다. 7행의 internal-route 주석은 대상 서비스가 인터넷에 노출되지 않고 NGINX Service Mesh 내의 워크로드에만 노출된다는 것을 의미합니다.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: target-internal-route
  namespace: legacy
  annotations: 
    nsm.nginx.com/internal-route: "true"
spec:
  ingressClassName: nginx # use only with k8s version >= 1.18.0
  tls:
  rules:
  - host: target-svc.legacy
    https:
      paths:
      - path: /
        backend:
          serviceName: target-svc
          servicePort: 80

새 리소스를 활성화하고 NGINX Ingress Controller가 리소스에 정의된 경로를 추가했는지 확인합니다.

> kubectl apply -f legacy-route.yaml
ingress.networking.k8s.io/target-internal-route created
> kubectl describe ingress target-internal-route -n legacy
...
Events:
  Type    Reason          Age   From                      Message
  ----    ------          ----  ----                      -------
  Normal  AddedOrUpdated  6s    nginx-ingress-controller  Configuration for ...
        ...legacy/target-internal-route was added or updated

이제 kubectl exec 명령을 실행하면 대상 서비스에 도달합니다.

{"req": {"method": "GET"
                "url": "/",
                "host": "target-svc.legacy",
                "remoteAddr": "10.28.2.76:56086"}}

NGINX Ingress Controller를 통해 Egress 트래픽을 라우팅할 때의 이점은 클러스터 내부에서 도달할 수 있는 외부 서비스를 정확히 제어할 수 있다는 것입니다.

데모에서 마지막으로 보여줄 것은 Egress 트래픽을 모니터링하는 방법입니다. kubectl exec 명령을 실행하여 여러 요청을 보낸 후 다음 명령을 실행합니다.

> nginxmesh-ctl top deploy/nginx-ingress -n nginx-ingress
Deployment      Direction  Resource  Success Rate  P99  P90  P50  NumRequests
nginx-ingress  
                To         target    100.00%       1ms  1ms  1ms  9
                From       bash      100.00%       0ms  0ms  0ms  9

6. 지연 시간(Latency)에 “아니오”라고 말하십시오.

많은 Service Mesh가 Ingress 및 Egress Gateway 옵션을 제공하지만 NGINX 통합의 추가 이점인 짧은 지연 시간을 높이 평가할 것이라고 생각합니다. 대부분의 Mesh는 Ingress Controller에 Sidecar를 주입해야 하며, 앱으로 이동하는 도중 추가 홉을 만들기 위해 트래픽이 필요합니다. 몇 초가 중요하며 디지털 경험을 느리게 하는 추가 홉으로 인해 고객이 다른 곳으로 눈을 돌릴 수 있습니다.

NGINX Service Mesh는 NGINX Ingress Controller에 사이드카(Sidecar)를 삽입하지 않기 때문에 불필요한 대기 시간을 추가하지 않습니다. 대신 Mesh의 CA인 Spire와 직접 통합하여 NGINX Ingress Controller가 NGINX Service Mesh의 일부가 됩니다. NGINX Ingress Controller는 단순히 Spire 에이전트에서 인증서와 키를 가져와 메쉬된 파드(Pod)와의 mTLS 인증서 교환에 참여하는 데 사용합니다.

Kubernetes용 NGINX Ingress Controller에는 NGINX OSS 및 NGINX Plus의 두 가지 버전이 있습니다. 이 모범사례에 설명된 대로 NGINX Service Mesh와 함께 NGINX Ingress Controller를 배포하려면 30일 무료 평가판으로 제공되는 NGINX Plus 버전을 사용해야 합니다.