[네떡스터디🔥kans] Ingress

CloudNet@ 가시다님이 진행하는 쿠버네티스 네트워크 스터디 KANS 3기 내용을 정리한 글입니다.

 

 

Ingress

기존의 쿠버네티스에 존재하던 NodePort 및 LoadBalancer의 경우 단순한 로드밸런서 외에는 아무런 기능을 하지 못했다. 이러한 한계점을 보완하고자 나온 것이 Ingress이다. 하지만 이 Ingress에는 단점이 많이 존재함으로 만약 처음 공부하고 계신 분이라면 Gateway API를 먼저 알아보는 것을 추천한다. Ingress도 한계점이 많아 새롭게 만들어진것이 Gateway API이다. 그래서 심지어 Ingress에는 더 이상 기능이 추가 되지 않는다.

 

 

 

 

기본적으로 쿠버네티스에서 만들어진 서비스라는 객체는 L4레이어에서 동작하는 로드밸런서라고 생각하면 좋을 것 같다. 하지만 Ingress는 L7레이어에서 동작하며 HTTP, HTTPS에 대한 통신을 처리하는 것으로 알고있다. Path기반의 라우팅이나 Host기반의 라우팅을 지원하며 카나리 배포 또한 가능하다. 

 

 

Ingress Nginx

nginx가 F5라는 회사에 인수가 되었는데 그 이후에 Ingress Nginx도 F5에서 운영이 되다가 Kubernetes로 넘어오게 되었다. 그래서 helm차트를 찾다보면 Nginx가 만들었다고나오는 Ingress들이 보이는데 이것은 F5가 관리하던것으로 아마 일반적으로 알려진 Ingress Nginx와는 조금 다르다고 한다. 그러니 주의

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

 

GitHub - kubernetes/ingress-nginx: Ingress NGINX Controller for Kubernetes

Ingress NGINX Controller for Kubernetes. Contribute to kubernetes/ingress-nginx development by creating an account on GitHub.

github.com

 

설치하기

<참고> 테스트 환경은 orbstack의 기본 쿠버네티스를 사용했습니다.

 

설치는 Helm차트를 이용해서 진행하고자 합니다.

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

 

레포를 추가 완료했다면 Ingress를 설치할 네임스페이스도 생성합니다.

kubectl create ns ingress

 

설치하는 버전은 글 작성 기준으로 최신의 헬름차트 버전인 4.11.2를 사용했습니다.

helm install ingress-nginx ingress-nginx/ingress-nginx --namespace ingress --version 4.11.2

 

이렇게 설치하고 나면 컨트롤러와 서비스가 생성이 된다. 

 

그리고 Ingress에 연결할 default ingress class도 생성이 된다.

 

Ingress Nginx에도 상태를 확인할수있는 Health Check 경로가 존재합니다.

curl -s -o /dev/null -w "%{http_code}" http://198.19.249.2/healthz

 

200 정상응답으로 설치에 성공했습니다.

 

 

Ingress Nginx의 기능

더보기

테스트를 위한 환경 구축 방법입니다.

총 3개의 서비스를 구성할 예정입니다. 

 

첫번째

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

 

두번째

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

 

세번 째

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

 

Host 기반 라우팅

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-1
  annotations:
  	# 기본은 라우팅이 RR방식인데 IP-Hash 방식으로도 변경가능합니다. 
    nginx.ingress.kubernetes.io/upstream-hash-by: "true" 
spec:
  ingressClassName: nginx
  rules:
  - 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

 

Canary 업그레이드

나중에 Canary 업그레이드 방법에 대해 문서를 작성해 보고 싶지만, 여기서는 간단하게 설명해 보겠습니다. Canary 업그레이드는 일부 트래픽을 먼저 새로 업그레이드된 버전으로 보내어 이상 여부를 확인한 뒤, 문제가 없으면 전체 시스템에 적용하는 방식입니다. 만약 문제가 생기면, 즉시 롤백하여 서비스의 안정성을 확보할 수 있습니다. 

 

 

Canary라는 단어가 붙은 이유는 광부들이 광산 내 유해 가스(일산화탄소 등)를 탐지하기 위해 민감한 생명체인 카나리아를 먼저 내려보내서 가스가 있는지 확인했던 것처럼, Canary 배포 방식도 시스템 전체에 영향을 주기 전에 소규모 트래픽이나 일부 사용자에게만 먼저 새로운 버전을 배포해 안전성을 확인하는 것을 의미합니다. 

 

더보기

테스트 용 환경 구축하기

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dp-v1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: svc-v1
  template:
    metadata:
      labels:
        app: svc-v1
    spec:
      containers:
      - name: pod-v1
        image: k8s.gcr.io/echoserver:1.5
        ports:
        - containerPort: 8080
      terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Service
metadata:
  name: svc-v1
spec:
  ports:
    - name: web-port
      port: 9001
      targetPort: 8080
  selector:
    app: svc-v1

 

업그레이드 용 설정

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dp-v2
spec:
  replicas: 3
  selector:
    matchLabels:
      app: svc-v2
  template:
    metadata:
      labels:
        app: svc-v2
    spec:
      containers:
      - name: pod-v2
        image: k8s.gcr.io/echoserver:1.6
        ports:
        - containerPort: 8080
      terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Service
metadata:
  name: svc-v2
spec:
  ports:
    - name: web-port
      port: 9001
      targetPort: 8080
  selector:
    app: svc-v2

기존에 운영중이던 Ingress가 이렇게 구성되어있다고 합시다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-canary-v1
spec:
  ingressClassName: nginx
  rules:
  - host: kans.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: svc-v1
            port:
              number: 8080

 

신규 v2 버전에 트래픽을 일부 보내고 싶다면 새로운 Ingress를 생성해야합니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-canary-v2
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"       # Canary 트래픽을 설정하는 주석. 트래픽의 일부만 이 리소스로 라우팅.
    nginx.ingress.kubernetes.io/canary-weight: "10"  # Canary 트래픽의 비율을 설정. 10%의 트래픽을 이 Ingress로 라우팅
spec:
  ingressClassName: nginx
  rules:
  - host: kans.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: svc-v2
            port:
              number: 8080

 

HTTPS 처리 (TLS 종료, TLS PassThorugh, SSL Redirect)

TLS 터미네이션은 클라이언트와 서버 간 암호화된 TLS 연결을 네트워크 경계 지점(예: 로드 밸런서, 프록시)에서 해제하는 방식입니다. 클라이언트에서 보낸 암호화된 요청을 해당 지점에서 복호화한 뒤, 서버로는 평문으로 전달하며, 서버 응답 시에도 다시 암호화하여 클라이언트로 전송됩니다.

 

Ingress Nginx에서는 TLS설정으로 TLS종료 처리를 합니다. TLS 종료와는 반대로 오히려그냥 넘어가는 설정도 있는데

이 설정의 경우 annotations에 nginx.ingress.kubernetes.io/ssl-passthrough 설정을 하면 됩니다. Nginx에서 TLS 통신을 복호화 할 수 없기 때문에 헤더를 삽입하거나 그외의 기능들을 사용할 수 없습니다. 

 

그 외에도 http로 들어온 요청을 https로 강제로 리다이렉트도 시킬 수 있습니다. nginx.ingress.kubernetes.io/ssl-redirect: 'true' 값을 동일하게 annotations에 넣어줄 경우 http로 들어온 요청을 https로 다시 요청하게 합니다. 

 

XFF(X-Forwarded-For)와 XFP(X-Forwarded-Proto) 헤더

이전 포스팅(2024.09.26 - [K8S/network] - Proxy Protocol: Client IP를 유지하는 방법)에서 Proxy Protocol을 통해 L4 레이어에서 클라이언트 IP를 유지하는 방법을 다뤘습니다. 그렇다면 L4가 아닌 L7 레이어에서 클라이언트 IP를 유지하려면 어떻게 해야 할까요? 이 경우, 헤더를 사용하면 가능합니다.

 

X-Forwarded-For 헤더는 네트워크 장비나 서버 등에서 송신지 IP 주소가 변경되는 상황에서, 원래 클라이언트의 IP 주소를 기록하는 역할을 합니다. 즉, 원본 송신지 IP를 유지하는 방법입니다. 여러 장비를 거치게 되면 X-Forwarded-For 값에는 여러 IP가 추가됩니다. 이 헤더의 값은 새로운 장비를 지날 때마다 추가되므로, 맨 첫 번째 IP가 사용자의 IP를 나타냅니다. 예를 들어, "X-Forwarded-For: 10.140.1.4, 40.1.22.9"라는 IP 정보가 들어온다면 10.140.1.4가 클라이언트의 IP입니다.

 

클라이언트 IP 유지 외에도, 요청 시 사용된 프로토콜 정보를 저장할 수 있습니다. 이를 위한 헤더가 X-Forwarded-Proto입니다. 이 헤더는 원래의 프로토콜 정보를 기록합니다. 예를 들어, SSL 오프로딩 환경에서 서버는 클라이언트가 처음 요청할 때 사용한 프로토콜(HTTP/HTTPS)을 이 헤더를 통해 확인할 수 있습니다.