쿠버네티스 인그레스(Ingress) & 인그레스 컨트롤러(Ingress Controller) + 모니터링

박도준·2022년 2월 27일
9

Kubernetes & Docker

목록 보기
12/12
post-thumbnail


인그레스(Ingress)

인그레스(ingress)는 클러스터 외부에서 내부로 접근하는 요청들을 어떻게 처리할 지 정의해둔 규칙들의 모음이다.

인그레스는 아래와 같은 기능들을 제공한다.

  • 외부에서 접속가능한 URL 사용
  • 트래픽 로드밸런싱
  • SSL 인증서 처리
  • 도메인 기반 가상 호스팅 제공

인그레스는 위와 같은 기능들에 대해 정의해둔 규칙들을 정의해둔 리소스이고, 이를 실제 동작하기 위해서는 인그레스 컨트롤러가 필요하다.



인그레스 컨트롤러(Ingress Controller)

인그레스 컨트롤러(Ingress Controller)는 클러스터에서 실행되고 수신 리소스에 따라 HTTP 로드 밸런서를 구성하는 응용 프로그램이다.

| ⭐ 인그레스가 동작하기 위해서는 인그레스 컨트롤러가 반드시 필요하다.

인그레스 컨트롤러는 자동으로 실행되지 않고 상황에 맞게 적합한 컨트롤러를 선택하여 설치해야 한다. 쿠버네티스에서는 GCENGINX를 오픈소스로 제공하고 있다.

이외에도 써드파티 솔루션으로 아래와 같은 인그레스 컨트롤러를 쿠버네티스 웹사이트에서 볼 수 있다.

  • AKS Application Gateway Ingress Controller
  • Ambassador
  • BFE Ingress Controller
  • Apache APISIX ingress controller
  • Istio
  • Kong
  • Traefik


인그레스를 통한 통신 흐름

Ingress와 Ingress Controller에 대한 간단한 아키텍쳐를 살펴보면 아래와 같다.

(https://kubetm.github.io/k8s/08-intermediate-controller/ingress/)
외부에서 사용자가 특정 경로로 접속하게 되면 인그레스를 통해 정의해둔 규칙에 따라 인그레스 컨트롤러가 동작하여 서비스에 맞는 파드로 연결해준다.


외부 클라이언트가 인그레스를 통해 클러스터 내부 파드로 접속하는 통신 흐름을 살펴본다.

1. 파드 생성

  • 클러스터 내부에서만 접속

2. 서비스(Cluster Type) 연결

  • 클러스터 내부에서만 접속
  • 동일한 애플리케이션의 다수 파드의 접속을 용이하게 하기 위해 서비스에 접속

3. 서비스(NodePort Type) 연결

  • 외부 클라이언트가 서비스를 통해 내부 파드로 접속

4. 인그레스 컨트롤러 파드 배치

  • 인그레스(정책)이 적용된 인그레스 컨트롤러 파드를 앞단에 배치하여 고급 라우팅 등 기능 제공

5. 인그레스 컨트롤러 이중화 구성

  • Active - Standby 구성으로 Active 파드 장애 대비

6. 인그레스 컨트롤러 파드 외부에 노출

  • 인그레스 컨트롤러 파드를 외부에서 접속하기 위해 노출
  • 인그레스 컨트롤러 노출 시, NodePort 보다는 좀 더 많은 기능을 제공하는 LoadBalancer 타입을 권장(80/443 포트 오픈 시)

7. 인그레스와 파드간 내부 연결의 효율화 방안

  • 인그레스 컨트롤러 파드(Layer7 동작)에서 서비스 파드의 IP로 직접 연결
  • 인그레스 컨트롤러 파드는 K8S API서버로부터 엔드포인트 정보(파드 IP)를 획득 후 바로 파드 IP로 연결 (참고)
  • 지원되는 인그레스 컨트롤러 : Nginx, Traefix 등 대부분의 인그레스 컨트롤러 지원



실습

인그레스와 인그레스 컨트롤러를 직접 설치하여 실습을 해본다.
인그레스 컨트롤러는 쿠버네티스에서 공식적으로 제공하는 Nginx 인그레스 컨트롤러를 사용한다.


실습 환경

  • K8S v1.22.6
  • 노드 OS(Ubuntu 20.04.3)
  • CNI(Calico v3.21, Direct mode)
  • IPTABLES proxy mode

실습 구성도

(스터디장 가시나님 제공 🙏)


Nginx 인그레스 컨트롤러를 설치한다. (공식 문서)

# 설치
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.1/deploy/static/provider/cloud/deploy.yaml

# 설치 확인
$ kubectl get namespaces
$ kubectl get all -n ingress-nginx


디플로이먼트와 서비스를 생성한다.

  • svc1-pod.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
     name: deploy1-websrv
    spec:
     replicas: 1
     selector:
       matchLabels:
         app: websrv
     template:
       metadata:
         labels:
           app: websrv
       spec:
         containers:
         - name: pod-web
           image: nginx
    ---
    apiVersion: v1
    kind: Service
    metadata:
     name: svc1-web
    spec:
     ports:
       - name: web-port
         port: 9001
         targetPort: 80
     selector:
       app: websrv
     type: ClusterIP
  • svc2-pod.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
     name: deploy2-guestsrv
    spec:
     replicas: 2
     selector:
       matchLabels:
         app: guestsrv
     template:
       metadata:
         labels:
           app: guestsrv
       spec:
         containers:
         - name: pod-guest
           image: gcr.io/google-samples/kubernetes-bootcamp:v1
           ports:
           - containerPort: 8080
    ---
    apiVersion: v1
    kind: Service
    metadata:
     name: svc2-guest
    spec:
     ports:
       - name: guest-port
         port: 9002
         targetPort: 8080
     selector:
       app: guestsrv
     type: NodePort
  • svc3-pod.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
     name: deploy3-adminsrv
    spec:
     replicas: 3
     selector:
       matchLabels:
         app: adminsrv
     template:
       metadata:
         labels:
           app: adminsrv
       spec:
         containers:
         - name: pod-admin
           image: k8s.gcr.io/echoserver:1.5
           ports:
           - containerPort: 8080
    ---
    apiVersion: v1
    kind: Service
    metadata:
     name: svc3-admin
    spec:
     ports:
       - name: admin-port
         port: 9003
         targetPort: 8080
     selector:
       app: adminsrv
# 생성 및 확인
$ kubectl apply -f svc1-pod.yaml,svc2-pod.yaml,svc3-pod.yaml
$ kubectl get pod,svc,ep



인그레스를 생성한다.

  • ingress1.yaml

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
     name: ingress-1
     annotations:
       #nginx.ingress.kubernetes.io/upstream-hash-by: "true"
    spec:
     ingressClassName: nginx
     rules:
     #- host: foo.bar.com 
     - http:
         paths:
         - path: /
           pathType: Prefix
           backend:
             service:
               name: svc1-web
               port:
                 number: 80
         - path: /guest
           pathType: Prefix
           backend:
             service:
               name: svc2-guest
               port:
                 number: 8080
         - path: /admin
           pathType: Prefix
           backend:
             service:
               name: svc3-admin
               port:
                 number: 8080

    인그레스 정의 내역을 살펴본다.

  • .spec.ingressClassName : 인그레스 컨트롤러 리소스의 이름으로 어떤 컨트롤러를 구현할 것인지 정의한다.

  • .spec.rules.host: host를 지정할 수 있다. 만약 host를 지정하지 않는다면 ip로 연결된다.

  • .spec.rules.http.paths.path : 각 path는 아래 .spec.rules.http.paths.backend로 연결된다.


# 생성
$ kubectl apply -f ingress1.yaml

# 생성 확인
$ kubectl get ingress
$ kubectl describe ingress ingress-1
$ kubectl describe ingressclasses


외부 클라이언트에서 인그레스를 통한 접속을 확인해본다.

# 외부 클라이언트 환경 접속
## ingress-nginx-controller NodePort(HTTP 접속용) 변수 지정
$ export IngHttp=$(kubectl get service -n ingress-nginx ingress-nginx-controller -o jsonpath='{.spec.ports[0].nodePort}')
$ echo $IngHttp


## 인그레스 접속 테스트
$ curl <마스터노드 IP>:<Nginx Controller 의 Service 의 NodePort 중 HTTP 접속용>
# svc1-web 접속
$ curl -s 192.168.10.10:$IngHttp

svc1-web에 연결된 파드로 접속이 되는 것을 확인할 수 있다.

# svc2-web 접속
$ curl -s 192.168.10.10:$IngHttp/guest

# 부하분산 테스트
$ for i in {1..100}; do curl -s 192.168.10.10:$IngHttp/guest ; done | sort | uniq -c | sort -nr
$ for i in {1..1000}; do curl -s 192.168.10.10:$IngHttp/guest ; done | sort | uniq -c | sort -nr

svc2-web에 연결된 파드로 접속이 잘 되고, 100과 1000번 반복 접속했을 때 대략 50% 확률로 부하분산되는 것을 확인할 수 있다.


# svc3-web 접속
$ curl -s 192.168.10.10:$IngHttp/admin

# 부하분산 테스트
$ for i in {1..100}; do curl -s k8s-m:$IngHttp/admin | grep Hostname ; done | sort | uniq -c | sort -nr

svc3-web에 연결된 파드로 접속이 잘되고, 3개의 파드가 존재할 경우에는 33% 확률로 부하분산이 되는 것을 확인할 수 있다.

그리고 헤더 정보들을 살펴보면 다음과 같다.

  • client_address : src에 접근한 상대측의 출발지 IP (ingress-nginx-controller 파드 IP)
  • x-forwarded-for : 송신지 IP 주소가 변경되는 환경에서, 변환 전 송신지(클라이언트) IP 주소를 저장하는 헤더 (목적지까지 거쳐온 서버의 IP 정보 가지고 있음)
    • 만약 요청이 여러 프록시를 거치는 경우, 가장 왼쪽의 IP 주소는 원래 클라이언트 IP 주소이고 가장 오른쪽 IP 주소가 가장 최근의 프록시 IP 주소이다.
  • x-forwarded-host : 클라이언트가 요청한 원래 서버의 호스트명


❓ Nginx 파드가 endpoint 정보 등을 모니터링 가능한 이유는 무엇일가?

-> clusterrolerole에 endpoints 리소스에 각각 [list, watch], [get, list, watch] 동작을 가능하도록 설정했기 때문이다.

# clusterrole 확인
$ kubectl describe clusterrole ingress-nginx

# role 확인
$ kubectl describe role ingress-nginx -n ingress-nginx

코드에서 살펴보면 svc의 endpoint 정보를 가져오는 것을 알 수 있고, nginx 파드에는 아래와 같이 ingress에서 설정한 path와 서비스 정보가 등록되어 있다.

# nginx 파드 접속
$ kubectl exec -n ingress-nginx -it ingress-nginx-controller-6f4c4d95cb-r6czf -- /bin/bash
$ vi /etc/nginx/nginx.conf



최종 동작 흐름

최종적인 동작 흐름은 아래와 같이 외부 클라이언트에서 들어온 트래픽이 인그레스에 정의된 규칙에 따라 라우팅 되는데, 이때 파드의 서비스를 거치지 않고 엔드포인트 API를 통해 바로 pod로 접속하게 된다.



nginx 인그레스 컨트롤러 모니터링

추가적으로 nginx 인그레스 컨트롤러를 promethuesgrafana를 이용하여 모니터링까지 하는 환경을 구축해본다.

위에서 진행한 실습 환경을 같지만 이번에는 helm을 이용하여 nginx 인그레스 컨트롤러를 설치한다.

# ingress-nginx 저장소 추가 및 업데이트
$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
$ helm repo update

# ingress-nginx 패키지 설치
$ helm install ingress-nginx ingress-nginx/ingress-nginx --version 4.0.17 --create-namespace --namespace ingress-nginx

kustomize를 이용하여 prometheusgrafana를 디플로이한다.

# kustomize를 이용하여 prometheus 디플로이
$ kubectl apply --kustomize github.com/kubernetes/ingress-nginx/deploy/prometheus/

# 설치 확인
$ kubectl get pods,svc -n ingress-nginx

서비스가 정상적으로 생성되었다면 할당받은 포트로 프로메테우스에 접속한다.

# kustomize를 이용하여 grafana 디플로이
$ kubectl apply --kustomize github.com/kubernetes/ingress-nginx/deploy/grafana/


# 설치 확인
$ kubectl get pods,svc -n ingress-nginx

그라파나 서비스가 정상적으로 설치되었다면 할당받은 포트로 그라파나에 접속한다.

그라파나 초기 계정은 admin/admin 이다.

로그인을 한 후, 프로메테우스에서 수집하는 메트릭을 그라파나에서 보기 위해서는 datasource에 등록해야 한다.

[datasource 등록하는 방법]

  • Configuration -> Data source -> Add data source
    [datasource 등록하는 방법]
  • Time series database로 Prometheus 선택
  • URL에 프로메테우스 서버 등록 (http://CLUSTER_IP_PROMETHEUS_SVC:PROMETHUES_NODEPORT)
  • Save & Test 클릭
    • Data source is working 이라는 메세지가 뜨면 성공!

위 과정을 거치면 datasource에 prometheus가 등록된 것을 확인할 수 있다.

이제 prometheus를 통해 메트릭을 받아볼 수 있는데 이때 그라파나에서는 PromQL 이라는 프로메테우스 쿼리 언어를 통해 메트릭을 활용하여 원하는 값을 얻을 수 있다. 그리고 얻어진 값을 바탕으로 그래프나 표 등으로 시각화하여 표현할 수 있다.

요구사항에 맞게 대시보드를 자신이 직접 구성할 수도 있지만, 이번 실습에서는 Grafana Labs에 공유되어진 nginx 인그레스 컨트롤러 대시보드를 활용한다.

[그라파나 대시보드 등록하는 방법]

  • Dashboards -> Browse -> Import

  • Import via grafana.com에 대시보드 ID 적고 Load

  • Prometheus에 등록한 데이터소스 선택하고 Import

정상적으로 Import 된다면 아래와 같이 nginx 인그레스 컨트롤러를 모니터링하는 대시보드를 확인할 수 있다.
(⚠ 9614 ID의 그라파나 대시보드를 사용하면 메트릭 네임이 일치하지 않는 것이 있어 일부 수정이 필요합니다.)





참고자료

쿠버네티스 네트워킹 스터디 자료(by GasidaSeo)

https://arisu1000.tistory.com/27840

https://medium.com/swlh/kubernetes-ingress-controller-overview-81abbaca19ec

https://kubernetes-docsy-staging.netlify.app/ko/docs/concepts/services-networking/ingress-controllers/

https://gruuuuu.github.io/cloud/k8s-service/

https://kubernetes.github.io/ingress-nginx/

https://kubernetes.github.io/ingress-nginx/user-guide/monitoring/

profile
Better late than never

0개의 댓글