Kubernetes

Ingress(인그레스)

jih0ssang 2024. 2. 17. 14:00

서비스 API 카테고리

컨테이너를 외부에 공개하는 엔드포인트를 제공하는 리소스

- 서비스

   - ClusterIP

 - ExternalIP  (ClusterIP의 한 종류)

   - NodePort

   - LoadBalancer

   - 그 외 서비스 기능

   - Headless (None)

   - ExternalName

   - None-Selector

- 인그레스

 

 

인그레스

지금까지 설명한 서비스는 L4 로드밸런싱이지만 Ingress는 L7 로드밸런싱을 제공하는 서비스이다.

서비스들을 묶는 서비스들의 상위 객체로, 서비스 종류의 하나로서가 아닌 독립된 리소스로 구현되어 있다.

Network Policy 리소스에 Ingress/Egress라는 설정 항목이 있지만 이 인그레스와 관련은 없다.

 

1. 리소스와 컨트롤러

쿠버네티스에서 서비스를 생성할 때, 매니페스트로 정의한 리소스를 쿠버네티스에 등록한다.

하지만 등록만으로는 아무런 처리가 이루어지지 않으며, 실제 처리는 컨트롤러라는 시스템 구성 요소가 필요하다.

예를 들어, 디플로이먼트에서는 레플리카셋을 생성하거나 레플리카 수를 변경해가며 롤링 업데이트를 하는 디플로이먼트 컨트롤러 라고 하는 시스템 구성 요소가 쿠버네티스 클러스터에서 동작한다.

 

2. 인그레스 리소스와 인그레스 컨트롤러

'인그레스 리소스'란 매니페스트에 등록된 API 리소스를 의미하고, '인그레스 컨트롤러'는 인그레스 리소스가 쿠버네티스에 등록되었을 때 어떤 처리를 한다.

 

3. 인그레스 종류

인그레스의 구현 방법은 실제로 많이 사용되는 GKE 인그레스 컨트롤러와 Nginx 인그레스 컨트롤러를 소개한다.

이 두 가지 컨트롤러는 인그레스 리소스를 생성했을 때 처리를 담당하는 컨트롤러이다.

어떤 L7 로드 밸런서를 생성할지는 이 인그레스 컨트롤러에 따라 달라진다.

 

  • 클러스터 외부 로드 밸런서를 사용한 인그레스
    • GKE 인그레스
  • 클러스터 내부 인그레스용 파드를 배포하는 인그레스
    • Nginx 인그레스

 

클러스터 외부 로드밸런서를 사용한 인그레스

GKE처럼 클러스터 외부 로드밸런서를 사용한 인그레스의 경우, 인그레스 리소스 생성만으로 로드 밸런서의 가상 IP가 할당되어 사용할 수 있다.

 

따라서 인그레스의 트래픽은 GCP의 GCLB가 트래픽을 수신한 후 GCLB에서 SSL 터미네이션이나 경로 기반 라우팅을 통해 NodePort에 트래픽을 전송함으로써 대상 파드까지 도달한다.

 

트래픽 경로

1. 클라이언트

2. 인그레스(GKE 인그레스)

3. NodePort 서비스(L4)

4.   목적지 파드

 

클러스터 내부 로드밸런서를 사용한 인그레스

인그레스 리소스에서 정의한 L7 수준의 로드 밸런싱 처리를 하기 위해 인그레스용 파드를 클러스터 내부에 생성해야 한다.

또 생성한 인그레스용 파드에 대해 클러스터 외부에서 접속할 수 있도록 별도로 인그레스용 파드에 LoadBalancer 서비스(L4)를 생성하는 등의 준비가 필요하다. 

인그레스용 파드가 L7수준의 처리를 하기 위해 부하에 따른 파드 레플리카 수의 오토 스케일링도 고려해야 한다.

 

인그레스용 파드에 Nginx를 사용한 Nginx 인그레스의 경우 로드 밸런서가 Nginx 파드까지 전송하고, 그 다음에는 Nginx가 대상 파드에 전송한다. 이때, Nginx 파드에서 대상 파드까지는 NodePort를 통과하지 않고 직접 파드 IP주소로 전송된다.

 

트래픽 경로

1. 클라이언트

2. → LoadBalancer(L4)

3.  인그레스(Nginx 파드)

4. 목적지 파드 

 

 

인그레스 컨트롤러 배포

인그레스를 사용하려면 인그레스 컨트롤러를 배포해야 한다.

 

GKE 인그레스 컨트롤러 배포

GKE의 경우 클러스터를 생성할 때 HttpLoadBalancing 애드온을 활성화하면 배포된다.

또 이 애드온은 기본값으로 활성화되어 있기 때문에 4장에서 설명한 순서대로 클러스터를 구축했다면 이미 이 인그레스를 사용할 수 있다.

 

 

Nginx 인그레스 컨트롤러 배포

Ngnix 인그레스를 사용하는 경우에는 Nginx 인그레스 컨트롤러를 배포해야 한다.

Nginx 인그레스에는 인그레스 컨트롤러 자체가 L7 수준의 처리를 하는 파드이기도 하고, 이름은 컨트롤러이지만 실제 처리도 한다.

 

정의된 규칙에 일치하지 않을 경우, 기본 목적지용 디플로이먼트도 생성해둬야 한다.

혹은 404 Not Found 페이지를 준비해둬도 문제 없다.

 

클러스터 외부와의 통신 확보를 위해 Nginx 인그레스 컨트롤러의 파드에 로드밸런서 서비스(LoadBalancer, NodePort 등)를 생성해야한다.

 

 

GKE용 인그레스 리소스 생성

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: external-ingress
spec:
  rules: # 규칙
  - host: sample.example.com
    http:
      paths:
      - path: /path1/
        backend:
          serviceName: sample-ingress-svc-1
          servicePort: 8888

      - path: /path2/
        backend:
          serviceName: sample-ingress-svc-2
          servicePort: 8888

  backend: # 규칙에 적용되지 않을 때 default
    serviceName: sample-ingress-default
    servicePort: 8888
  
  tls:     # 인증서 적용
  - hosts:
    - sample.example.com
    secretName: tls-sample

 

# 인그레스 리소스 확인
$ kbuectl get services
NAME		         HOSTS		        ADDRESS	           PORT(S)	 AGE
external-ingress	 sample.example.com	XX.XXX.XX.XXX      80, 443	4m14s

 

# L7 로드 밸런서의 가상 IP를 환경 변수에 저장
$ INGRESS_IP=`kubectl get ingresses sample-ingress \
-o jsonpath='{.status.loadbalancer.ingress[0].ip}'`

# 인그레스 리소스 경유의 HTTP 요청(/path1/* > sample-ingress-svc-1)
$ curl http://${INGRESS_IP}/path1/ -H "Host: sample.example.com"
Host=sample.example.com Path=/path1/ From=sample-ingress-apps-1 ClientIP=10.178.0.61 
XFF= xxx.xxx.xxx.xxx, xxx.xxx.xxx

# 인그레스 리소스 경유의 HTTP 요청(/path2/* > sample-ingress-svc-2)
$ curl http://${INGRESS_IP}/path2/ -H "Host: sample.example.com"
Host=sample.example.com Path=/path2/ From=sample-ingress-apps-2 ClientIP=10.178.0.63 
XFF= xxx.xxx.xxx.xxx, xxx.xxx.xxx

# 인그레스 리소스 경유의 HTTP 요청(/* > sample-ingress-default)
$ curl http://${INGRESS_IP}/ -H "Host: sample.example.com"
Host=sample.example.com Path=/ From=sample-ingress-apps-default ClientIP=10.178.0.61 
XFF= xxx.xxx.xxx.xxx, xxx.xxx.xxx

# 인그레스 리소스 경유의 HTTPS 요청
$ curl https://${INGRESS_IP}/path1/ -H "Host: sample.example.com" --insecure
Host=sample.example.com Path=/path1/ From=sample-ingress-apps-1 ClientIP=10.0.2.54
XFF=xx.xxx.xxx.xxx, xx.xxx.xxx.xxx

 

Nginx 인그레스용 인그레스 리소스 생성

GKE 환경에서 Nginx 인그레스를 사용하는 경우에는 Nginx 인그레스를 사용하도록

kubernetes.io/ingress.class: nginx의 어노테이션을 지정해야 한다.

또한, Nginx 인그레스가 GKE 인그레스용으로 생성한 인그레스 리소스 (external-ingress)를 보고 있어서 동시에 동작하는 경우에는 external-ingress에도 kubernetes.io/ingress.class: gce의 어노테이션을 지정해야한다.

 

추가로, Nginx 인그레스는 HTTP로부터의 요청을 HTTPS로 리다이렉트하도록 설정되어 있기 때문에 이번에는 비활성화한다.

# gke ingress
apiVersion: networking.k8s.io.v1beta1
kind: Ingress
metadata:
  name: Nginx-ingress
  annotations:
    kubernetes.io/ingress.class: nginx    			   # nginx 명시
    nginx.ingress.kubernetes.io/ssl-redirect: "false"  # https 리다이렉트 비활성화
spec:
  rules:
  - host: sample.example.com
    http: 
      paths:
      - path: /path1/
        backend:
          serviceName: sample-ingress-svc-1
          servicePort: 8888
      - path: /path2/
        backend:
          serviceName: sample-ingress-svc-2
          servicePort: 8888
    
  backend:
    serviceName: default
    servicePort: 8888
  
  tls:
  - hosts:
    - sample.example.com
    secretName: tls-sample

 

# 인그레스 리소스 목록 확인
$ kbuectl get ingresses
NAME		CLASS    HOSTS	             ADDRESS	     PORTS  AGE
Nginx-ingress   <none>	 sample.example.com  xxx.xxx.xxx.xxx 80,443 47s

# 서비스에서 해당 IP 주소를 확인해야 함
$ kubectl get services -n ingress-nginx 
NAME		                    TYPE         CLUSTER-IP    EXTERNAL-IP      PORT(S)                      AGE
ingress-nginx-controller            LoadBalancer 10.3.240.68   xxx.xxx.xxx.xxx  80:30372/TCP,443:31141/TCP   82m
ingress-nginx-controller-admission  ClusterIP    10.3.240.30   <none>           443/TCP                      82m

인그레스 컨트롤러 디플로이먼트에 대한 LoadBalancer 서비스 IP 주소가 인그레스에도 등록되도록 되어 있다.

여러 인그레스 리소스를 생성해도 같은 IP 주소를 사용한다.

 

XFF 헤더에 의한 클라이언트 IP 주소 참조

인그레스 경유로 들어오는 트래픽에는 기본적으로 X-Forwarded-For(XFF) 헤더가 지정되어 있으므로 클라이언트 IP(발신측 IP)를 참조할 수 있게 되어 있다. 환경이나 사용하는 구현에 따라 지정되지 않거나 NAT 되어 정확한 값을 참조할 수 없는 경우도 있으므로 주의하자.

 

인그레스 클래스에 의한 인그레스 분리

배포한 인그레스 컨트롤러는 클러스터에 등록된 모든 인그레스 리소스를 볼 수 있어 충돌이 발생할 가능성이 있다.

이런 경우에는 인그레스 클래스를 사용하여 인그레스 리소스를 분리할 수 있다.

 

GKE 인그레스 컨트롤러

서비스 A와 서비스 B가 각각 존재하고 GKE 인그레스 컨트롤러에서 인그레스 리소스를 두 개 생성한 경우, 두 개의 GCLB가 생성되고 엔드포인트는 두 개를 할당받게 된다.

 

Nginx 인그레스 컨트롤러

Nginx 인그레스 컨트롤러에서는 위의 예상되는 동작을 하지 않는다.

인그레스 컨트롤러 자체가 L7 로드 밸런서인 것처럼 구현되어 있어서 아무 설정하지 않는 경우에는 전체 인그레스 리소스를 watch하여 Nginx 파드 설정을 업데이트하므로 분리가 되지 않기 때문이다.

 

분리성을 확보하기 위해 인그레스 리소스에 인그레스 클래스의 어노테이션을 지정하고 Nginx 인그레스 컨트롤러에 해당 하는 인그레스 클래스를 설정하여 대상을 분리할 수 있다

  • Nginx 인그레스 컨트롤러 기동 시 --ingress-class 옵션을 전달
    • /nginx-ingress-controller --ingress-class=system_a...
  • 인그레스 리소스에 어노테이션을 지정
    • kubernetes.io/ingress.class: "system_a"

 

'Kubernetes' 카테고리의 다른 글

컨피그 & 스토리지 API 카테고리  (0) 2024.02.18
서비스 API 카테고리 정리  (0) 2024.02.18
None-Selector 서비스  (0) 2024.02.17
스케줄링(taint&tolerations/NodeSelector/NodeAffinity)  (0) 2024.02.13
Resource Requests/Limits  (0) 2024.02.13