본문 바로가기
스터디 이야기/Kubernetes Advanced Networking Study

[KANS] Ingress

by lakescript 2024. 10. 9.
더보기

이 스터디는 CloudNet@에서 진행하는 KANS 스터디를 참여하면서 공부하는 내용을 기록하는 블로그 포스팅입니다.

CloudNet@에서 제공해주는 자료들을 바탕으로 작성되었습니다.

 

Ingress

 

Ingress란?

https://kubernetes.io/ko/docs/concepts/services-networking/ingress/

Ingress는 Kubernetes에서 외부 트래픽을 클러스터 내의 서비스로 라우팅하는 역할을 하는 리소스입니다. Service는 Layer4 계층에서 동작하지만 Ingress는 Layer 7계층에서 동작하기에 HTTP/HTTPS 통신에 다양한 기능들이 제공합니다. 

 

즉, 클러스터 내부의 서비스(ClusterIP, NodePort, Loadbalancer)를 외부로 노출(HTTP/HTTPS)합니다. (Web Proxy 역할)

Ingress Controller

Ingress의 실제 동작 구현은 Ingress Controller가 처리합니다. Kubernetes는 Ingress API만 정의하고, 실제 구현은 Add-on이 담당합니다.

 

nginx-ingress vs ingress-nginx
ingress-nginx :  Kubernetes의 공식 NGINX Ingress Controller입니다. (공식문서)
nginx-ingress : NGINX, Inc.(현재 F5)에서 제공하는 NGINX Plus Ingress Controller입니다. (공식문서)

 

Ingress를 통한 Kubernetes 내부 통신 방법 (IP 기반)

nginx ingress controller

(Nginx Ingress Controller일 경우) Ingress Controller Pod에 인입된 트래픽은 ingress에 설정한 규칙을 기반으로 동작을 처리하게 됩니다. 이때, Service에 연동된 Pod1, Pod2로 전달하게 되는데 Pod에 연동된 ClusterIP나 NodePort로 전달하지 않고, 바로 목적지 Pod IP로 통신하게 됩니다. 하지만 이러한 동작을 하기 위해서는 Pod의 정보를 알아야 합니다. 이를 위해 Ingress Controller Pod는 Kubernetes API에 해당 정보를 요청하고 취득할 수 있는 권한이 설정되어 있습니다.

 

Ingress를 통한 Kubernetes 내부 통신 방법(Host 기반)

 

위 그림처럼 http://k8s.book/32080의 도메인 주소로 외부에서 Ingress에 접속을 합니다. 그러면 Ingress Controller Pod는 현재 설정되어 있는 Ingress에 HOST가 k8s.book으로 매칭되고, Path가 매칭되었을 시 해당 서비스에 연동된 Pod로 트래픽을 전달하게 됩니다. 만약, 외부에서 http://test.k8s.book:32080으로 접속을 한다면,  *.k8s.book으로 매칭되고 경로 확인 후 해당 Service에 연동된 Pod로 직접 전달합니다. 

 

Canary 배포

 

위 그림처럼 클라이언트의 요청 중 90%는 기존에 운영되고 있는 Srvice1에 연동된 Pod v1.0으로 전달하고, 나머지 10%는 Service2에 연동된 Pod v1.1로 전달합니다. 이 후 신규 버전인 v1.1의 Pod가 문제가 없다면 점점 비중을 높여 배포합니다. 결국 최종적으로 v1.1의 Pod로 100% 요청이 전달되면, 안정적으로 신규 버전의 Pod로 이전할 수 있습니다.

...
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
 ...

이를 위해선 Nginx Ingress Controller에 annotation 설정이 필요합니다. 

 

 

Ingress 실습

실습 환경 구성

EC2 배포

VPC 1개(퍼블릭 서브넷 2개), EC2 인스턴스 4대 (Ubuntu 22.04 LTS, t3.medium - vCPU 2 , Mem 4)으로 EC2를 생성합니다.

 

Deployment 배포 (1)

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

 

 

Deployment 배포 (2)

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

 

Deployment 배포 (3)

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 get pod,svc,ep

 

ingress 배포

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-1
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

 

배포 확인

kubectl get ingress

 

kubectl describe ingress ingress-1

 

 

Ingress-Nginx 컨트롤러 생성

controller:
  service:
    type: NodePort
    nodePorts:
      http: 30080
      https: 30443
  nodeSelector:
    kubernetes.io/hostname: "k3s-s"
  metrics:
    enabled: true
  serviceMonitor:
      enabled: true

 

 

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

kubectl create ns ingress
helm install ingress-nginx ingress-nginx/ingress-nginx -f ingress-nginx-values.yaml --namespace ingress --version 4.11.2

 

배포 확인

kubectl get all -n ingress

 

externalTrafficPolicy 설정

kubectl patch svc -n ingress ingress-nginx-controller -p '{"spec":{"externalTrafficPolicy": "Local"}}'

externalTrafficPolicy가 Local로 설정되면, 외부에서 들어오는 트래픽은 서비스가 실행 중인 노드에서만 처리됩니다. 즉, 외부 트래픽이 무조건 Ingress 컨트롤러가 실행 중인 노드로 전달되고, 그 노드에서만 처리가 됩니다.

 

기본 nginx conf 파일 확인

kubectl exec deploy/ingress-nginx-controller -n ingress -it -- cat /etc/nginx/nginx.conf

 

설정이 반영된 nginx conf 파일 확인

kubectl exec deploy/ingress-nginx-controller -n ingress -it -- cat /etc/nginx/nginx.conf | grep 'location /' -A5

 

 

인그레스(Nginx 인그레스 컨트롤러)를 통한 접속(HTTP 인입) 확인

ingress 정보 확인

kubectl get ingress

 

 

kubectl describe ingress ingress-1 | sed -n "5, \$p"

 

PC에서 인그레스를 통한 접속

# / 
echo -e "Ingress1 sv1-web URL = http://$(curl -s ipinfo.io/ip):30080"

 

# /quest
echo -e "Ingress1 sv2-guest URL = http://$(curl -s ipinfo.io/ip):30080/guest"

 

# /admin
echo -e "Ingress1 sv3-admin URL = http://$(curl -s ipinfo.io/ip):30080/admin"

 

부하 분산 확인

for i in {1..100}; do curl -s $MYIP:30080/admin | grep Hostname ; done | sort | uniq -c | sort -nr

기본적으로 Nginx 는 라운드 로빈 부하분산 알고리즘을 사용하기에 부하 분산 테스트를 해보겠습니다. ($MYIP에는 해당 EC2 IP를 넣어주시면 됩니다.)

 

분산 알고리즘 변경

IP-Hash 설정

sed -i 's/#nginx.ingress/nginx.ingress/g' ingress1.yaml


...
	...
	metadata:
  		name: ingress-1
  		annotations:
    	nginx.ingress.kubernetes.io/upstream-hash-by: "true"
	spec:
    ...
...

 

접속 확인

for i in {1..100}; do curl -s 43.203.224.12:30080/admin | grep Hostname ; done | sort | uniq -c | sort -nr

 

Nginx 파드가 endpoint 정보 등을 모니터링 가능한 이유

clusterrole 확인

kubectl describe clusterrole ingress -n ingress | egrep '(Verbs|endpoints)'

 

role 확인

kubectl describe roles ingress-nginx -n ingress | egrep '(Verbs|endpoints)'

 

clusterRole과 Role이 endpoints, endpointslices를 list, watch,get 등이 바인딩된 ServiceAccount를 사용하기에 Nginx Pod가 endpoint정보 등을 모니터링 가능합니다.

 

 

Host 기반 라우팅 실습

ingress 배포

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-2
spec:
  ingressClassName: nginx
  rules:
  - host: lakescript.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: svc3-admin
            port:
              number: 8080
  - host: "*.lakescript.com"
    http:
      paths:
      - path: /echo
        pathType: Prefix
        backend:
          service:
            name: svc3-admin
            port:
              number: 8080

 

ingress 확인

kubectl get ingress

kubectl describe ingress ingress-2 | sed -n "5, \$p"

접속 결과 확인

curl -H "host: $MYDOMAIN1" $MYIP:30080

 

$MYDOMAIN1에는 위에서 ingress 설정에 입력한 HOST URL을 넣어주면되고, $MYIP에는 EC2의 IP를 넣어주시면 됩니다.

 

curl -H "host: test.lakescript.com" 43.203.224.12:30080 -v

 

 

test.lakescript.com으로 하게되면 ingress에서 *.lakescript.com으로 규칙이 매칭되어 / 경로로 접근하려고 하면 404 Not Found 라는 에러 메시지가 보여집니다.

 curl -H "host: test.lakescript.com" 43.203.224.12:30080/echo -v

 

경로에 /echo를 넣어서 올바르게 경로를 매칭해주면 트래픽이 전달되는 것을 확인하실 수 있습니다.

 

Canary 배포 실습

Deploy 배포 (1)

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

이전 버전의 Pod를 의미하는 Deployment를 배포합니다.

 

Deployment 배포 (2) 

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

신규 버전의 Pod로 사용할 Deployment를 배포합니다.

 

배포 확인

kubectl get svc,ep,pod

 

ingress 배포 (1)

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

 

ingress 배포 (2)

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-canary-v2
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  ingressClassName: nginx
  rules:
  - host: lakescript.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: svc-v2
            port:
              number: 8080

 

접속 테스트

for i in {1..1000}; do curl -s -H "host: lakescript.com" 43.203.224.12:30080 | grep nginx; done | sort | uniq -c | sort -nr

100번의 접속 테스트를 진행했을 때 10%에 해당하는 10개의 트래픽 만이 v1.31.1의 svc에 유입된 것을 확인하실 수 있습니다.

 

kubectl annotate --overwrite ingress ingress-canary-v2 nginx.ingress.kubernetes.io/canary-weight=50

v1.31.1의 pod의 비중을 50%로 설정한 후 다시 접속해보겠습니다.

52:48의 배율로 50%정도의 트래픽이 v1.31.1의 svc로 전달됩니다.

 

kubectl annotate --overwrite ingress ingress-canary-v2 nginx.ingress.kubernetes.io/canary-weight=100

마지막으로 v1.31.1의 svc에 100% 비중으로 설정해보겠습니다.

모든 트래픽이 v1.31.1로 전달되는 것을 확인하실 수 있습니다.

 

728x90