본문 바로가기
사이드 프로젝트

[CI/CD 프로젝트] Ingress 를 통한 외부 트래픽 관리

by 간장공장공차장 2024. 11. 20.
반응형

Ingress란?

Ingress는 클러스터 외부의 트래픽을 내부의 특정 서비스로 라우팅하는 Kubernetes 리소스이다. Ingress는 URI, hostname, path 등과 같은 protocol을 사용할 수 있기 때문에 HTTP(S) 네트워크를 가능하게 해준다. Ingress가 지원하는 기능은 아래와 같다.

  • 경로 기반 라우팅: HTTP(S) path에 따라 트래픽을 서로 다른 서비스로 라우팅합니다.
  • 도메인 기반 라우팅: 여러 도메인에 대해 별도의 트래픽 라우팅 규칙을 설정합니다.
  • TLS 지원: TLS 인증서를 통해 HTTPS 보안을 설정할 수 있습니다.

Ingress, NodePort, LoadBalancer

세가지 모두 외부에서 접근할 수 있도록 도와주는 툴이다.

이 세 가지에서, 가장 큰 차이점은 Ingress는 Layer7에서 동작한다. 그러나 NodePort와 Loadbalancer는 Layer4에서 동작한다. 추가로, Ingress는 기본적으로 라우팅 기능이 있다. path 별로 다른 서비스에 라우팅을 진행한다. NodePort는 Pod를 외부에서 접근할 수 있도록 하는 목적이 있고, Loadbalancer는 서비스에 대한 부하 분산이 목적이다.

 

  • Ingress의 동작
    출처 : https://kubernetes.io/docs/concepts/services-networking/ingress/

Ingress는 외부에서 도달 가능한 URL을 제공한다. 또한 Layer7에서의 보안 프로토콜인 SSL, TLS을 지원한다.

아래와 같이 ingress는 임의의 포트나, 프로토콜을 노출하지 않고, 이름을 기반한 가상호스팅 (path를 통한 service discovery)만 가능하다. 따라서 인터넷/외부로 서비스를 노출시키고자 하면, Ingress Controller가 설치되어야한다.

Ingress Controller가 서비스를 expose할 때는 일반적으로 Service.Type=NodePort 또는 Service.Type=LoadBalancer 유형의 서비스를 사용한다.

ex) nodeport로 controller를 사용할 경우

helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --set controller.service.type=NodePort \
  --set controller.service.nodePorts.http=30080 \
  --set controller.service.nodePorts.https=30443

Controller는 일반적으로 Loadbalancer 서비스 타입으로 배포되어, 클러스터 외부에서 트래픽을 받을 수 있도록 합니다. 이때 클라우드 환경에서는 외부 Load Balancer가 자동 생성됩니다.

순서대로 정리하면, 아래와 같다.

  1. Ingress Controller의 Ingress 리소스 감시
    • Ingress Controller는 클러스터 내에서 생성된 모든 Ingress 리소스를 지속적으로 감시하면서 새롭게 추가되거나 변경된 규칙이 있는지 확인한다. 리소스가 추가되면 규칙에 맞게 트래픽을 라우팅할 수 있도록 한다.
  2. 클라이언트의 요청 수신
    • 외부 클라이언트가 HTTP/HTTPS 요청을 보낼 때, Ingress Controller는 클러스터 외부에서 이 트래픽을 수신하고, Ingress 리소스에서 정의된 규칙에 따라 이를 적절한 서비스로 전달합니다.
  3. 트래픽 라우팅
    • Ingress Controller는 요청의 URL 경로 또는 호스트 이름을 기반으로 트래픽을 Ingress 리소스에서 지정한 서비스로 라우팅합니다.
    • 예를 들어, /fronted 경로가 /frontend 서비스로 매핑되었다면, Controller는 해당 트래픽을 /frontend 서비스로 전달합니다.
  4. 서비스와 파드로의 트래픽 전달
    • Controller가 서비스로 트래픽을 전달하면, 서비스는 이를 내부적으로 labels가 동일한 pod로 전달합니다.
    • 이를 통해 외부 클라이언트의 요청이 클러스터 내의 특정 파드로 도달하게 된다.
  5. 응답 반환
    • 파드는 요청을 처리하고 응답을 반환합니다.
    • Ingress Controller는 파드에서 받은 응답을 클라이언트에게 다시 전달하여 외부 트래픽이 정상적으로 처리됩니다.
  6. TLS

Ingress Controller는 클러스터에 SSL 인증서를 추가하여 HTTPS 트래픽을 처리할 수도 있다. Ingress 리소스에 TLS 설정을 추가하고, 인증서를 연결한다. Ingress Controller는 HTTPS 연결을 종료하고, 내부적으로는 HTTP로 트래픽을 전달하여 성능을 최적화합니다.

  • Ingress.yaml template
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hellospring-ingress
spec:
  tls:  # tls 설정 
    - hosts:
        - ssh-hellospring.com
      secretName: hellospring-tls
  rules:
    - host: ssh-hellospring.com
      http:
        paths:
          - path: /hello
            pathType: Prefix
            backend:
              service:
                name: hellospring-service
                port:
                  number: 3000

Loadbalancer, NodePort

  • NodePort 의 동작

Node에 Port와 서비스를 연결하여 외부에서 접속을 허용시키는 옵션이다. 쿠버네티스 컨트롤 플레인은 지정된 범위에서 포트를 할당한다 (기본값 : 30000-32767).

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30007
  • Loadbalancer의 동작

Loadbalancer type은 클라우드 공급자 상에서 지원을 한다. AWS는 ELB와 연결되어 지원된다.

또한 loadbalancer type는 cloud-controller-manager 컴포넌트는 할당된 해당 NodePort로 트래픽을 전달하도록 외부 로드 밸런서를 구성한다. 다만, 알파 기능으로서, 로드 밸런스된 서비스가 NodePort 할당을 생략하도록 구성할 수 있는데, 이는 클라우드 공급자의 구현이 이를 지원할 때에만 가능하다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  clusterIP: 10.0.171.239
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 192.0.2.127

Ingress Controller

위에 언급 되었듯이, Ingress 가 실제로 동작하려면 Ingress Controller라는 추가 컴포넌트가 설치되어야한다.

가장 대표적인 Ingress Controller는 Nginx ingress controller와, AWS Load Balancer Controller가 있다.

NGINX Ingress Controller:

  • 가장 널리 사용되는 Ingress 컨트롤러로, NGINX를 기반으로 한다.
  • 다양한 설정 옵션을 제공하며, TLS 종료, 로드 밸런싱, 리다이렉션 등의 기능을 지원한다.
  • 간단한 설치와 사용법 덕분에 많은 Kubernetes 배포에서 기본 선택으로 사용됩니다.

AWS Load Balancer Controller

  • Ingress 리소스 지원: Ingress 리소스에 명시된 규칙에 따라 ALB를 생성하여 외부 트래픽을 Kubernetes 서비스로 라우팅한다.
  • Service 리소스 지원: Service 리소스에 loadbalancer type를 설정하면 NLB를 생성하여 외부 트래픽을 Kubernetes 서비스로 라우팅한다.

앞서 ingress는 로드밸런서와 배우 밀접한 리소스이다. 따라서, 클라우드공급자와 함께 사용할 때 원활하게 배포되는 리소스이다. 그렇다면, 본 실습의 절차와 같이 EC2나 베어메탈로 접근할 때에는 어떻게 사용할까?

  • MetalLB
    • 작동 방식: 클러스터 외부에서 접근할 수 있는 고정 IP 주소를 할당하여, Kubernetes 서비스에 대한 외부 트래픽을 분산시킵니다.
    • 장점: 클라우드 서비스 없이도 로드 밸런서 기능을 사용할 수 있으며, IP 주소 관리가 용이합니다.
  • NodePort
    • 클러스터의 각 노드에서 고정 포트를 열어, 외부 사용자가 해당 포트를 통해 서비스에 접근합니다.
    • 작동 방식: 클러스터의 각 노드에서 고정 포트를 열어, 외부 사용자가 해당 포트를 통해 서비스에 접근합니다.

실습

  • nginx ingress controller helm repo 추가
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
  • 외부에서 접근 가능하도록 Service 설정

EC2 환경에서는 LoadBalancer 타입 대신 NodePort로 설정해야 합니다. 설치 시 set 옵션을 통해 NodePort로 설정할 수 있다

kubectl create namespace ingress-nginx

helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --set controller.service.type=NodePort \
  --set controller.service.nodePorts.http=30080 \
  --set controller.service.nodePorts.https=31443 \
  --set controller.admissionWebhooks.patch.enabled=true \
  --set controller.admissionWebhooks.patch.failurePolicy=Ignore

argocd에서 30443 사용하고 있어 31443으로 사용한다.

  • 나중에 추가할 시 helm upgrade 명령어로 가능하다.
helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --set controller.service.type=NodePort \
  --set controller.service.nodePorts.http=30080 \
  --set controller.service.nodePorts.https=31443 \
  --set controller.service.nodePorts.<new-port-name>=<new-port-number>

이렇게 설정하면 HTTP는 30080 포트, HTTPS는 30443 포트로 접근할 수 있개된다.

설치가 완료되었아. 이제 안내되어진 template을 변형하여 이전 helm으로 배포한 hellospring에대한 접근을 확인해본다.

3. Ingress 리소스 생성

Spring Boot 애플리케이션을 위한 Ingress 리소스를 설정한다.

먼저, hellospring서비스의 이름부터 찾는다.

_helpers.tpl에서 release name과 chart name이 조합된 것을 볼 수 있다.

{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" -}}

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hellospring-ingress
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - http:  
      paths:
        - path: /hello
          pathType: Prefix
          backend:
            service:
              name: hellospring-hellospring
              port:
                number: 3000
  • 에러 발생

webhook과의 timeout 이 발생하는데, pod에서 telnet 다 되고, 서비스도 정상적으로 띄워져있어 문제되는 부분 찾지 못함.. 인터넷 찾아봐도 명확한 원인 아는사람 없는 것 같음..

Error from server (InternalError): error when creating "ingress.yaml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": failed to call webhook: Post "https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s": context deadline exceeded

kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission

일단 webhook을 삭제한다. 잘못 배포해도 검증할 수단이 없어지지만,, 나중에 AWS Loadbalancer Controller를 쓰는것으로..

삭제한 후 ingress를 apply 한다.

k apply -f ingress.yaml
  • 접속이 잘 되고 있다.
반응형