Ingress 는 Service 가 그러하듯이 파드 오브젝트로 생성되어 동작하는 리소스가 아니다. Service 리소스를 오브젝트로 생성한다는 것은, kube-proxy 컴포넌트(파드)가 각 노드의 iptables 를 Service 에 명시된 규칙대로 조작하여 네트워크 라우팅 규칙을 설정하는 것이다. 비슷하게, Ingress 리소스는 rules 를 가지고 있는데 이것만으로는 동작하지 않고, nginx 와 같은 IngressController 오브젝트(파드)를 생성해야 작동한다. nginx 는 Ingress 에 설정한 규칙대로 로드밸런싱을 수행하는 것이다.

 

1. nginx ingress controller 의 구성요소

IngressController 는 여러가지 구현체가 있는데, 그 중에 흔히 쓰이는 것이 nginx ingress 이다. https://secjong.tistory.com/128 를 참고하여 helm 을 통해서 nginx ingress 를 설치하면 된다. 

설치 후 클러스터에는 아래 구성요소들이 생성된다.

  • NameSpace : "ingress-nginx"
  • Service(type : LoadBalancer): "ingress-nginx-controller"
  • Deployment : "ingress-nginx-controller"
  • IngressClass: "nginx"
  • ClusterRole: "ingress-nginx"
  • ClusterRoleBinding: "ingress-nginx"
  • ServiceAccount: "ingress-nginx"

하나씩 살펴보자.

 

2. NameSpace 확인

"ingress-nginx" 네임스페이스가 생성되었음을 알 수 있다.

$ kubectl get ns ingress-nginx
NAME            STATUS   AGE
ingress-nginx   Active   12m

 

3. Service 확인

LoadBalancer 로 생성된 서비스를 조회해본다.

$ kubectl -n ingress-nginx get svc
NAME                                 TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.104.104.52   <pending>     80:31080/TCP,443:31443/TCP   16m
ingress-nginx-controller-admission   ClusterIP      10.102.98.136   <none>        443/TCP                      16m

 

3. Deployment, Pod 확인

$ kubectl -n ingress-nginx get deploy
NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
ingress-nginx-controller   1/1     1            1           18m

$ kubectl -n ingress-nginx get pods
NAME                                        READY   STATUS    RESTARTS   AGE
ingress-nginx-controller-7854678f64-mfcdn   1/1     Running   0          18m

 

4. IngressClass 확인

아래 커맨드처럼 생성된 IngressClass 를 확인한다. "nginx" 라는 이름으로 생성되었음을 알 수 있다. ingressclass.kubernetes.io/is-default-class: "true" 어노테이션으로 지정되어, Ingress 리소스에 ingressClass 미지정시 기본으로 사용되는 IngressClass 임을 알 수 있다.

$ kubectl get ingressclasses.networking.k8s.io nginx -o yaml
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true" # Ingress 리소스에 ingressClass 를 지정하지 않았을 시 본 ingressClass 가 기본으로 지정됨.
    ...
  name: nginx
spec:
  controller: k8s.io/ingress-nginx

 

아래 커맨드처럼 ingress-nginx-controller 파드에 지정한 ingress-class 를 확인하면 "nginx"로 지정된 것을 알 수 있다. 즉, nginx pod 가 "nginx" 라는 IngressClass 를 사용하는 것이다. 이렇게 함으로써 ingressClass: "nginx" 로 지정한 Ingress 의 규칙들이 이 컨트롤러에 적용되게 된다.

$ kubectl -n ingress-nginx get pods ingress-nginx-controller-7854678f64-mfcdn -o yaml | grep -C 10 ingress-class
    uid: baf392a9-6db8-45cb-886b-413720112744
  resourceVersion: "667784"
  uid: 06a2c0b7-6a18-4bc6-b0ac-8ead5a6f61a7
spec:
  containers:
  - args:
    - /nginx-ingress-controller
    - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
    - --election-id=ingress-nginx-leader
    - --controller-class=k8s.io/ingress-nginx
    - --ingress-class=nginx # 여기에서 ingress-class 명 파라미터를 주입
    - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
    - --validating-webhook=:8443
    - --validating-webhook-certificate=/usr/local/certificates/cert
    - --validating-webhook-key=/usr/local/certificates/key
    - --enable-metrics=false
    env:
    - name: POD_NAME
      valueFrom:
        fieldRef:
          apiVersion: v1

 

5. ClusterRole 확인

아래 커맨드처럼 클러스터의 모든 Ingress, EndpointSlice 정보 조회가 포함된 ClusterRole 이 생성되었음을 확인한다.

$ kubectl get clusterrole ingress-nginx -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  ...
  name: ingress-nginx
  ...
rules:
...
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses
  verbs:
  - get
  - list
  - watch
...
- apiGroups:
  - discovery.k8s.io
  resources:
  - endpointslices
  verbs:
  - list
  - watch
  - get

 

6. ClusterRoleBinding 확인

"ingress-nginx" ClusterRole 이 어떤 ServiceAccount 에서 사용되는지 아래 커맨드로 확인한다. "ingress-nginx"라는 이름의 SA 에서 사용됨을 알 수 있다.

$ kubectl get clusterrolebindings.rbac.authorization.k8s.io ingress-nginx -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  ...
  name: ingress-nginx
  ...
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingress-nginx
subjects:
- kind: ServiceAccount
  name: ingress-nginx
  namespace: ingress-nginx

 

7. ServiceAccount 확인

아래 커맨드로 "ingress-nginx" SA 를 확인한다.

$ kubectl -n ingress-nginx get sa ingress-nginx
NAME            SECRETS   AGE
ingress-nginx   0         38m

 

아래 커맨드처럼 ingress-nginx-controller 파드에 지정한 serviceAccount 를 확인하면 "ingress-nginx" 로 지정된 것을 알 수 있다. 즉, nginx pod 는 "ingress-nginx" SA 를 통해 "ingress-nginx" 라는 ClusterRole 의 권한을 가지기 때문에 클러스터의 모든 Ingress, EndpointSlice 를 조회할 수 있다.

$ kubectl -n ingress-nginx get pods ingress-nginx-controller-7854678f64-mfcdn -o yaml
apiVersion: v1
kind: Pod
metadata:
  ...
  name: ingress-nginx-controller-7854678f64-mfcdn
  ...
spec:
  ...
  serviceAccount: ingress-nginx
  ...

 

8. 트래픽 전달 흐름

이렇게 생성된 nginx controller 를 통해 트래픽이 파드까지 전달되는 단계는 아래와 같다.

  1. 사용자 -> Router: 도메인(예: example.com)으로 DNS 에 질의하여 공인 IP에 HTTP/HTTPS 요청을 보냄
  2. Router -> NLB: 공인 IP가 할당된 Router 에서 트래픽을 받아 연결된 Gateway 에서 LoadBalancer type Service 로 생성된 NLB 로 트래픽을 보냄  
  3. NLB -> NodePort: NLB가 트래픽을 받아서 쿠버네티스 워커 노드의 NodePort로 전달
  4. NodePort -> Nginx: 노드로 들어온 패킷을 Nginx Ingress Controller 파드(Pod)로 라우팅
  5. Nginx -> Pod: Ingress 리소스 규칙(Host, Path, Service)을 확인하여 요청을 전달할 타깃 ClusterIP Service 를 확인 후 EndpointSlices 를 조회하여 실제 파드 IP로 직접 트래픽 전달

5번에서 Nginx 가 Pod 의 IP 를 조회하여야 하는데, 먼저 Ingress 정보에서 Service 정보를 취득한 후, Service 정보로부터 EndpointSlice 의 Pod IP 정보를 취득하는 방식이다. 위에서 확인했던 ClusterRole 이 존재하고, Nginx Pod 에서 serviceAccount 를 통해 그 Role 을 사용하기 때문에 가능한 것이다.

 

실제로 Nginx Pod 는 Service IP 를 통해 Pod 로 트래픽을 로드밸런싱하지 "않고", 직접 Pod IP 로 트래픽을 쏜다. Service 는 단지 EndpointSlice 를 조회하는 용도로 사용된다. Service 를 생성함으로 설정되는 iptables 조차 라우팅에 관여하지 않는다.

 

블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,