08. Week12 쿠버네티스 네트워킹(2) - chuirang/DevOps GitHub Wiki

쿠버네티스 네트워킹(2)

목차

  1. 쿠버네티스 네트워킹 Overview

  2. 쿠버네티스 서비스

  3. 쿠버네티스 인그레스

1. 쿠버네티스 네트워킹 Overview

1.1 쿠버네티스 네트워킹(1) recap

1.2 서비스(Service)는 어떤 문제를 해결하려 하는가?

앞서 쿠버네티스는 애플리케이션은 파드의 형태로 실행되어 서로 통신을 한다고 했다. 다만 파드는 아래와 같은 특성을 가지기 때문에 pod-to-pod 단위로 애플리케이션을 연계시키기 어렵다.

  • 파드는 일시적(Ephemeral)이다.
  • 클라이언트가 실행된 시점에 파드 IP를 알 수 없다.
  • 파드는 스케일링 된다.

이러한 이유로 여러 개의 파드의 집합에 대한 단일진입점을 제공해주기 위해서 서비스(Service) 오브젝트가 사용된다. 그보다 더 넓게 생각을 해보면 쿠버네티스 환경의 내부 통신 뿐 아니라 외부에서 접근하기 위한 연결점을 제공해주는 역할까지 확장한다.

출처: [도서] 쿠버네티스 인 액션

2. 쿠버네티스 서비스

쿠버네티스는 서비스 오브젝트를 통해 단일 진입점을 생성해주고, 내/외부의 요청을 받을 수 있도록 한다.

이러한 역할을 하는 서비스의 종류에는 ClusterIP, NodePort, LoadBalancer 가 있다. 세가지 서비스를 이해하는데는 몇 가지 포인트를 살펴보면 아래와 같다.

  1. Cluster IP는 클러스터 내부의 통신을 위한 것이고, NodePort와 LoadBalancer는 외부에서 클러스터의 서비스를 호출하기 위한 오브젝트이다. 단, LoadBalancer 는 클러스터 외부에서 이를 구현해주는 서비스가 있어야 가능하므로, 보통 퍼블릭클라우드에서 Load Balancer 서비스를 통해 연계된다. 온프레미스에서는 metal-lb 와 같은 솔루션을 사용할 수 있다.

  2. 아래 그림과 같이 결국 하위 서비스를 바탕으로 구현된다. 그래서 이 그림이 개념을 이해하는데 도움이 된다.

https://goglides.io/clusterip-nodeport-and-loadbalancer-service-types-in-kubernetes/98/

즉 NodePort를 생성해도 ClusterIP가 생성되고, 이를 외부로 연결해주는 접점이 NodePort가 된다. 마찬가지로 LoadBalancer를 생성하면 NodePort, ClusterIP가 생성된다. 이는 아래의 kubectl get svc 의 결과를 보면 이해할 수 있다. (LoadBalancer 타입이지만 Cluster IP와 Nodeport가 지정되어 있다)

user@u2004-master:~$ kubectl get svc
NAME                      TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)           AGE
sampleapp-container-svc   LoadBalancer   10.110.35.229   172.16.3.242   18186:30663/TCP   81d
  1. 서비스 오브젝트는 Layer 4 기반이다. TCP/UDP 패킷을 처리하되 페이로드(Payload)는 신경쓰지 않는다. 이를 두가지 관점에서 얘기하지면 아래와 같다.
  • 서비스 오브젝트는 L7 수준의 동작은 처리해줄 수 없다. (이것이 Ingress 가 필요한 이유이기도 하다)
  • ClusterIP에 세션 어피니티 옵션이 있지만 단순히 None/ClientIP 로만 지정이 가능하다.

2.1 ClusterIP

서비스의 가장 기본 타입은 Cluster IP 로, 서비스를 생성할 때 관련된 파드를 레이블 셀렉터를 사용하여 지정한다.

출처: [도서] 쿠버네티스 인 액션

ClusterIP 실습

user@u2004-master:~/10_network$ kubectl apply -f clusterIP.yaml
deployment.apps/kubia created
service/kubia unchanged

user@u2004-master:~/10_network$ kubectl get svc
NAME    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
kubia   ClusterIP   10.103.18.22   <none>        80/TCP    8m15s
user@u2004-master:~/10_network$ curl 10.103.18.22
This is v1 running in pod kubia-5d584d58bc-rxw4q
user@u2004-master:~/10_network$ curl 10.103.18.22
This is v1 running in pod kubia-5d584d58bc-vxl4p
user@u2004-master:~/10_network$ curl 10.103.18.22
This is v1 running in pod kubia-5d584d58bc-rxw4q
user@u2004-master:~/10_network$ curl 10.103.18.22
This is v1 running in pod kubia-5d584d58bc-vxl4p

서비스를 kubectl describe 로 살펴보면 endpoint 라는 부분이 보이는데, 파드 리스트와 대응하는 IP이다.

user@u2004-master:~/10_network$ kubectl describe svc kubia
Name:              kubia
Namespace:         userns
Labels:            <none>
Annotations:       <none>
Selector:          app=kubia
Type:              ClusterIP
IP Families:       <none>
IP:                10.103.18.22
IPs:               10.103.18.22
Port:              <unset>  80/TCP
TargetPort:        8080/TCP
Endpoints:         192.168.146.182:8080,192.168.95.166:8080,192.168.95.173:8080
Session Affinity:  None
Events:            <none>

user@u2004-master:~/10_network$ kubectl get po -owide
NAME                     READY   STATUS    RESTARTS   AGE   IP                NODE           NOMINATED NODE   
kubia-5d584d58bc-2cw6n   1/1     Running   0          18m   192.168.95.166    u2004-third    <none>           
kubia-5d584d58bc-rxw4q   1/1     Running   0          18m   192.168.146.182   u2004-fourth   <none>           
kubia-5d584d58bc-vxl4p   1/1     Running   0          18m   192.168.95.173    u2004-third    <none>           

실제로 파드가 실행될 때 바로 endpoint 로 등록되지는 않는다. 상식적으로도 파드가 실행된다고 애플리케이션이 준비되지 않기 때문에 파드가 실행되고 요청을 받을 수 있는 단계가 되었을 때 endpoint에 등록되어야 한다.

user@u2004-master:~/10_network$ kubectl get po
NAME                     READY   STATUS              RESTARTS   AGE
kubia-5d584d58bc-2cw6n   1/1     Running             0          36s
kubia-5d584d58bc-rxw4q   0/1     ContainerCreating   0          36s
kubia-5d584d58bc-vxl4p   1/1     Running             0          36s
user@u2004-master:~/10_network$ kubectl get ep
NAME    ENDPOINTS                                 AGE
kubia   192.168.95.166:8080,192.168.95.173:8080   6m16s

이러한 '준비' 를 위한 체크가 Readiness Probe 이다. 즉 Readiness probe 에 의해 상태가 확인되어야 endpoint 로 등록된다. Ready가 되지 않은 파드는 아래와 같이 0/1 의 상태로 확인된다.

kubectl get pod 로 확인된 결과의 READY 열은 '준비된컨테이너수/총컨테이너수' 의 의미이다.

user@u2004-master:~/10_network$ kubectl get po
NAME                     READY   STATUS              RESTARTS   AGE
kubia-5d584d58bc-2cw6n   1/1     Running             0          36s
kubia-5d584d58bc-rxw4q   0/1     ContainerCreating   0          36s
kubia-5d584d58bc-vxl4p   1/1     Running             0          36s

Readiness Probe vs. Liveness Probe

Liveness Probe 는 컨테이너의 건전성을 체크하는 개념으로, Liveness Probe의 실패는 기존 컨테이너를 삭제하고 새로운 컨테이너를 생성한다. 반면 Readiness Probe 는 컨테이너 자체를 삭제하거나 재실행하지는 않지만, 대신 준비가 되지 않았다고 판단하면 서비스에 노출하지 않는다. (즉 endpoint 에 등록되지 않는다)

그렇다면 다른 서비스(파드)가 상대 서비스(Cluster IP)를 어떻게 알고 찾아갈 것인가?

2.2 쿠버네티스 내부 DNS 의 의미

쿠버네티스 내부에서는 서비스 디스커버리(Service Discovery)를 위하여 DNS를 활용한다.

쿠버네티스 클러스터가 생성되면 두 개의 built-in 서비스를 가지는데, kubernetes 와 kube-dns 이다.

user@u2004-master:/etc/netplan$ kubectl get svc -n kube-system
NAME                                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                        AGE
kube-dns                                             ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP,9153/TCP         111d
...
<생략>

user@u2004-master:/etc/netplan$ kubectl get svc -n default
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   111d

쿠버네티스에서 실행되는 모든 파드는 자동으로 kube-dns를 사용하도록 구성된다. 쿠버네티스는 각 컨테이너의 /etc/resolv.conf 를 수정해 이를 수행하는데, 아래와 같이 파드의 /etc/resolv.conf 를 보면 10.96.0.10 (kube-dns의 Cluster IP) 를 nameserver 로 바라보고 있다

user@u2004-master:~$ kubectl exec -it sampleapp-745dc64bd4-dgcgd -- cat /etc/resolv.conf
nameserver 10.96.0.10
search userns.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

또한 쿠버네티스에서 서비스와 파드가 생성되면 그에 해당하는 DNS 레코드가 생성된다.

Service: service-name.namespace-name.svc.cluster.local

Pod: pod-ip-address.namespace-name.pod.cluster.local

이러한 방식으로 예측가능한 이름을 가지게 되므로, 이를 통해 클라이언트 파드는 FQDN으로 서비스를 엑세스 할 수 있다.

아래와 같이 pod로 접근하여 curl을 해보면 서비스에 해당하는 FQDN으로 호출된다.

$ kubectl exec -it kubia-3inly -- bash
root@kubia-3inly:/#
root@kubia-3inly:/# curl http://kubia.default.svc.cluster.local
You’ve hit kubia-5asi2
root@kubia-3inly:/# curl http://kubia.default
You’ve hit kubia-3inly
root@kubia-3inly:/# curl http://kubia
You’ve hit kubia-8awf3

컨테이너 내부의 DNS resolver 가 구성되어 있고, svc.cluster.local 과 같은 접미사를 생략할 수 있는 것을 알 수 있다. (마지막 search 열 참고)

user@u2004-master:~$ kubectl exec -it sampleapp-745dc64bd4-dgcgd -- cat /etc/resolv.conf
nameserver 10.96.0.10
search namespace.svc.cluster.local svc.cluster.local cluster.local

2.3 Nodeport, LoadBalancer

쿠버네티스에서는 외부 클라이언트에 서비스를 노출하기 위해 Nodeport와 Load Balancer 를 사용할 수 있다. 아래는 Nodeport를 설명한 그림이다.

출처: [도서] 쿠버네티스 인 액션

Nodeport를 사용하게 되면 어느 노드에서나 오픈된 포트를 통해서 서비스를 노출시켜 줄 수 있지만, 한편으로 어느 워커노드를 지정해서 서비스를 오픈 시켜줄 것인가가 애매하다. 정책적으로 특정 노드(proxy 노드 등)에 대해서만 Nodeport 를 사용하도록 지정할 수도 있지만, 궁극적으로는 nodeport를 하나의 단일진입점으로 만들어주는 서비스가 필요하다.

이를 지원해주는 것이 LoadBalancer 이다. 단 이는 앞서 이야기한바와 같이 LB 자원을 동적으로 구성해주는 인프라 서비스가 제공된 경우 가능한 옵션이라는 단점이 있다. (CTEK 환경에서는 metal-lb 를 구성하였기 때문에 Loadbalancer 타입의 서비스도 테스트가 가능하다)

출처: [도서] 쿠버네티스 인 액션

2.4 서비스의 내부 매커니즘

앞서 살펴본 서비스 오브젝트와 Endpoint 그리고 kube-proxy의 역할을 하나의 그림으로 살펴보면 아래와 같다.

  1. 쿠버네티스의 대부분의 컴포넌트가 그러하듯 kube-proxy 는 apiserver 의 Service 오브젝트를 watch 하고, (ADDED 나 MODIFIED 이벤트가 발생하면) 그에 상응하는 iptables 정책을 생성/수정한다.

  2. 또한 kube-proxy 는 Nodeport를 각 서버에 바인딩시켜 LISTEN 상태로 만들어주는 역할을 한다.

  3. 그 이후 서비스는 iptables 로 각 파드에 적절히 로드밸런싱 된다.

https://www.youtube.com/watch?v=VR8ep5Wf_MA

서비스의 Cluster IP로 요청이 오면 iptables 의 룰에 따라 알맞은 파드로 요청이 전달된다. 노드에서 확인해보면 kubia라는 이름으로 등록된 정책이 보인다.

root@u2004-second:~# iptables -S -t nat |grep kubia
-A KUBE-SEP-35BAGYPF4GBQHJY4 -s 192.168.146.182/32 -m comment --comment "userns/kubia" -j KUBE-MARK-MASQ
-A KUBE-SEP-35BAGYPF4GBQHJY4 -p tcp -m comment --comment "userns/kubia" -m tcp -j DNAT --to-destination 192.168.146.182:8080
-A KUBE-SEP-FC6ZILG5GDUYUQ27 -s 192.168.95.166/32 -m comment --comment "userns/kubia" -j KUBE-MARK-MASQ
-A KUBE-SEP-FC6ZILG5GDUYUQ27 -p tcp -m comment --comment "userns/kubia" -m tcp -j DNAT --to-destination 192.168.95.166:8080
-A KUBE-SEP-PNPU5G2TZZYHPC2C -s 192.168.95.173/32 -m comment --comment "userns/kubia" -j KUBE-MARK-MASQ
-A KUBE-SEP-PNPU5G2TZZYHPC2C -p tcp -m comment --comment "userns/kubia" -m tcp -j DNAT --to-destination 192.168.95.173:8080
-A KUBE-SERVICES ! -s 192.168.0.0/16 -d 10.103.18.22/32 -p tcp -m comment --comment "userns/kubia cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.103.18.22/32 -p tcp -m comment --comment "userns/kubia cluster IP" -m tcp --dport 80 -j KUBE-SVC-YWRBL7YBACTRITYR
-A KUBE-SVC-YWRBL7YBACTRITYR -m comment --comment "userns/kubia" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-35BAGYPF4GBQHJY4
-A KUBE-SVC-YWRBL7YBACTRITYR -m comment --comment "userns/kubia" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-FC6ZILG5GDUYUQ27
-A KUBE-SVC-YWRBL7YBACTRITYR -m comment --comment "userns/kubia" -j KUBE-SEP-PNPU5G2TZZYHPC2C

다시 조금 세부적으로 살펴보면 아래와 같은 흐름을 알수 있다.

## 생성된 리소스
user@u2004-master:~/10_network$ kubectl get svc
NAME    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
kubia   ClusterIP   10.103.18.22   <none>        80/TCP    22m
user@u2004-master:~/10_network$ kubectl get po -owide
NAME                     READY   STATUS    RESTARTS   AGE   IP                NODE           NOMINATED NODE   READINESS GATES
kubia-5d584d58bc-2cw6n   1/1     Running   0          18m   192.168.95.166    u2004-third    <none>           <none>
kubia-5d584d58bc-rxw4q   1/1     Running   0          18m   192.168.146.182   u2004-fourth   <none>           <none>
kubia-5d584d58bc-vxl4p   1/1     Running   0          18m   192.168.95.173    u2004-third    <none>           <none>

노드에서 iptables를 살펴본다.

## 서비스IP를 기준으로 정책을 살펴본다.
root@u2004-second:~# iptables -S -t nat |grep 10.103.18.22
-A KUBE-SERVICES ! -s 192.168.0.0/16 -d 10.103.18.22/32 -p tcp -m comment --comment "userns/kubia cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.103.18.22/32 -p tcp -m comment --comment "userns/kubia cluster IP" -m tcp --dport 80 -j KUBE-SVC-YWRBL7YBACTRITYR

## KUBE-SVC-YWRBL7YBACTRITYR 를 찾아본다.
root@u2004-second:~# iptables -S -t nat |grep KUBE-SVC-YWRBL7YBACTRITYR
-N KUBE-SVC-YWRBL7YBACTRITYR
-A KUBE-SERVICES -d 10.103.18.22/32 -p tcp -m comment --comment "userns/kubia cluster IP" -m tcp --dport 80 -j KUBE-SVC-YWRBL7YBACTRITYR
-A KUBE-SVC-YWRBL7YBACTRITYR -m comment --comment "userns/kubia" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-35BAGYPF4GBQHJY4
-A KUBE-SVC-YWRBL7YBACTRITYR -m comment --comment "userns/kubia" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-FC6ZILG5GDUYUQ27
-A KUBE-SVC-YWRBL7YBACTRITYR -m comment --comment "userns/kubia" -j KUBE-SEP-PNPU5G2TZZYHPC2C

## random 모드로 전달된 KUBE-SEP-35BAGYPF4GBQHJY4 를 살펴본다.
root@u2004-second:~# iptables -S -t nat |grep KUBE-SEP-35BAGYPF4GBQHJY4
-N KUBE-SEP-35BAGYPF4GBQHJY4
-A KUBE-SEP-35BAGYPF4GBQHJY4 -s 192.168.146.182/32 -m comment --comment "userns/kubia" -j KUBE-MARK-MASQ
-A KUBE-SEP-35BAGYPF4GBQHJY4 -p tcp -m comment --comment "userns/kubia" -m tcp -j DNAT --to-destination 192.168.146.182:8080

## pod IP인 192.168.146.182/32 가 확인된다.

서비스의 트러블슈팅 관점

서비스 오브젝트는 iptables 로 구현되기 때문에 IP가 노출되지 않고 iptables에 의해서 처리된다. (서비스의 Cluster IP는 가상 IP로 서비스 포트와 결합된 경우에만 의미가 있다) 보통 네트워크 문제를 디버깅을 하기 위해 흔히 ping을 확인하는데 Cluster IP에 대한 ping 체크는 동작하지 않는다.

흔히 서비스 문제는 EndPoint 가 enable 되었는지와 이를 위해 레이블이 일치하는지, readiness probe가 적절히 통과하는지와 같은 수준에서 접근해야 한다. 추가로 Network Policy 가 사용되는 경우도 CNI를 통해 패킷이 차단되므로 이 경우 Network policy의 사용 여부를 확인해야할 수도 있다.

마지막으로 노드포트는 kube-proxy에 의하여 포트바인딩이 되므로 각 노드에서 LISTEN 여부가 확인된다.

2.4 ExternalName, Headless Service

External Name과 Headless Service 는 DNS 를 활용하는 서비스 유형이다. 이는 간단히 개념만 살펴보고 넘어간다.

ExternalName

ExternalName은 외부 서비스에 대한 별칭을 만들어 준다. ExternalName은 DNS 레벨에서 구현이 되는데, 리소스를 만들면 내부의 DNS 레코드가 생성되어 FQDN이 아닌 external-name.namespace-name.svc.cluster.local 과 같은 형식으로 호출 할 수 있다.

externalName.yaml

apiVersion: v1
kind: Service
metadata:
  name: external-service
spec:
  type: ExternalName
  externalName: www.naver.com
  ports:
  - port: 80

Headless Service

단일진입점을 제공하는 서비스의 연결은 임의의 파드로 전달되지만, 클라이언트가 각각의 파드를 직접 연결해야 할때 사용할 수 있는 오브젝트가 Headless Service이다. Headless Service는 ClusterIP 필드를 None으로 지정하면 생성된다.

headlessService.yaml

apiVersion: v1
kind: Service
metadata:
  name: kubia-headless
spec:
  clusterIP: None
  ports:
  - port: 80
   targetPort: 8080
  selector:
  app: kubia

Headless Service가 생성되면 DNS로 호출할 때 서비스 IP 대신 파드 IP들을 반환해 준다.

$ kubectl run dnsutils --image=tutum/dnsutils --generator=run-pod/v1  --command -- sleep infinity
$ kubectl exec dnsutils nslookup kubia-headless
...
Name: kubia-headless.default.svc.cluster.local
Address: 10.108.1.4
Name: kubia-headless.default.svc.cluster.local
Address: 10.108.2.5

쿠버네티스에서 상태를 가지는 애플리케이션을 위한 Statefulset 리속스가 있는데, Statefulset 으로 만들어진 파드는 예측가능한 이름을 가진다. (ex. app-0, app-1, app-2..) 이를 접근할 때 headless service를 만들면 pod 이름을 붙인 FQDN 으로 각 파드에 접근이 가능한 예시도 있다. 이렇게 하면 파드 간 피어 디스커버리가 가능해진다.

3. 쿠버네티스 인그레스

앞서 서비스 오브젝트를 살펴보며 외부 클라이언트에 서비스를 노출하기 위한 NodePort와 Loadbalancer 서비스를 살펴보았다. 인그레스 또한 외부 클라이언트 요청을 받을 수 있다.

3.1 인그레스(Ingress)

L4 기반의 서비스와 다르게 인그레스는 애플리케이션 계층에서 작동하므로 HTTP, HTTPS와 같은 서비스를 외부에 노출해주는 역할을 한다.

참고: 인그레스 쿠버네티스에서는 인그레스 라는 용어가 다른 곳에서도 사용된다. 여기서 다루는 인그레스는 쿠버네티스의 오브젝트를 의미하지만, 한편으로 Network Policy 에서 Ingress, Egress 라는 용어를 사용하는 트래픽의 방향을 나타낼 때도 사용된다. 즉 Ingress 는 트래픽의 방향성을 나타내는 일반적인 용어와 쿠버네티스 오브젝트로서 인그레스를 구분할 필요가 있다

왜 인그레스가 필요한가?

앞서 Nodeport 앞에 Loadbalancer 를 통해서 서비스를 노출할 수 있다고 했지만 L4 기반의 Loadbalancer 가 가지는 IP에 두 개의 웹서비스를 연결할 수 있는 방법이 없다. 그에 반해 인그레스는 하나의 단일 진입점에 여러 개의 호스트(virtual host)나 경로(아래의 /kubia, /foo 등)를 기준으로 서비스를 분배해줄 수 있다.

출처: [도서] 쿠버네티스 인 액션

흔히 인그레스 리소스에 대해서 헷갈려하는 부분은 인그레스 자체가 완전히 독립적인 리소스라고 생각하는 경우가 있는데, 인그레스 리소스 자체는 서비스 리소스를 노출하는 개념이다. 아래 ingress 명세와 service 명세를 보면 관계를 알 수 있다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kubia
spec:
  rules:
  - host: kubia.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: kubia
            port:
              number: 80

이때 인그레스로 만든 리소스를 어떻게 호출되는지를 헷갈려 하는 사람도 있다. 인그레스는 리소스이고, 인그레스를 생성하면 이를 구현해주는 것이 인그레스 컨트롤러이다. 아래 그림에서 보듯이 인그레스를 생성하면 인그레스 컨트롤러가 실행된 노드의 80, 443 포트를 통해 서비스가 노출된다.

출처: [도서] 쿠버네티스 인 액션

ingress controller 자체는 hostNetwork 옵션을 통해 Proxy 역할을 하는 노드의 레이블을 지정해주고 daemonset 으로 실행하면 호스트의 80, 443 으로 LISTEN 시켜줄 수 있다. 혹은 Loadbalancer 를 통해 서비스를 오픈해주면 된다.

user@u2004-master:~/$ kubectl get po -n ingress-nginx
NAME                             READY   STATUS    RESTARTS   AGE
ingress-nginx-controller-jzfpf   1/1     Running   3          106d

user@u2004-master:~/$ kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.111.41.122    172.16.3.240   80:30339/TCP,443:31832/TCP   106d
ingress-nginx-controller-admission   ClusterIP      10.106.244.214   <none>         443/TCP                      106d

인그레스 실습

user@u2004-master:~/10_network$ kubectl apply -f ingress.yaml
ingress.networking.k8s.io/kubia created
user@u2004-master:~/10_network$ kubectl get ingress
NAME    CLASS    HOSTS               ADDRESS   PORTS   AGE
kubia   <none>   kubia.example.com             80      8s
user@u2004-master:~/10_network$ kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.111.41.122    172.16.3.240   80:30339/TCP,443:31832/TCP   121d

마지막으로 헷갈려하는 부분은 ingress 와 내부 DNS(kube-dns)와 정식 DNS의 관계이다. ingress에서 정의한 HOST: 필드는 kube-dns에 등록되지 않는다. 그리고 외부 DNS 역시 kubernetes 클러스터에서 관여하는 부분이 아니다. 그렇기 때문에 DNS 등록 절차는 별도로 필요하다.

테스트 환경에서는 DNS 서비스를 제공하지 않고 ingress controller는 metal-lb를 통해 LoadBalancer를 구성하였으므로, hosts 파일에 172.16.3.240으로 ingress 로 등록된 host 필드의 값을 등록하면 테스트가 가능하다. (Windows 의 경우 C:\Windows\System32\drivers\etc\hosts)

예시)

172.16.3.240 kubia.example.com

3.2 인그레스 컨트롤러

마지막으로 인그레스 컨트롤러 자체를 자세히 살펴본다. Nginx 로 구현된 nginx ingres controller는 한마디로 nginx 기반의 웹서버를 내장한 컨트롤러(controller)이다.

https://ssup2.github.io/theory_analysis/Kubernetes_Nginx_Ingress_Controller/

*본 내용은 상기 블로그의 내용을 요약함

위 그림과 같이 Nginx Ingress Controller는 Nginx Ingress Controller 파드에 Nginx와 같이 존재한다.

동작과정을 간략히 살펴보면,

  1. 인그레스 컨트롤러는 apiserver 의 Ingress 및 Ingress와 관련된 Endpoint, Secret, ConfigMap, Service Object을 watch 한다.

  2. Watch 하고 있는 오브젝트에 업데이트가 발생하면 Ingress sync에게 전달하여, 업데이트된 오브젝트를 바탕으로 Nginx config를 구성하고, 새롭게 변경한 config와 기존의 config를 비교하여 다르다면 Nginx에 적용한다.

  3. 이때 Nginx는 rua 모듈을 이용하여 Nginx의 nginx.conf 리로드를 최소화 하도록 구현되어 있다. (예를 들어, 파드의 개수가 변경되는 경우에는 nginx.conf의 변경이 필요 없고 shared memory에 저장되어 있는 backend endpoint 만 변경한다. certificate 변경의 경우에도 shared memory만 변경한다)

  4. Nginx는 Lua 모듈을 이용하여 Client의 패킷을 로드밸런싱 하고, 필요한 경우 TLS 암/복호화도 수행한다.

(추가 상세 동작과정은 위의 블로그를 참고하시기 바랍니다)

⚠️ **GitHub.com Fallback** ⚠️