Kubernetes Nginx Ingress Controller

Kubernetes Nginx Ingress Controller

Kubernetes에서 Nginx Ingress를 제어하는 Nginx Ingress Controller를 분석한다. 분석한 Nginx Ingress Controller의 Version은 0.26.1이다.

1. Kubernetes Nginx Ingress Controller

[Figure 1] Nginx Ingress Controller

[Figure 1] Nginx Ingress Controller

Nginx Ingress Controller는 Kubernetes의 Ingress 및 관련 Ojbect들에 따라서 Nginx를 제어하고, Nginx 관련 Metric 정보를 수집하여 외부로 전달하는 역할을 수행한다. [Figure 1]은 Nginx Ingress Controller를 나타내고 있다. Nginx Ingress Controller는 Nginx Ingress Controller Pod에 Nginx와 같이 존재한다. Nginx Ingress Controller는 Leader (Active)/Non-leader (Standby) 방식으로 동작하지만 Leader/Non-leader 둘다 자신과 같은 Pod안에서 구동중인 Nginx를 제어하고, 관련 Metric 정보를 수집하는 것은 동일하다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
http {
        lua-shared-dict certificate-data 20M;
        lua-shared-dict certificate-servers 5M;
        lua-shared-dict configuration-data 20M;  
...
        upstream upstream-balancer {
                balancer-by-lua-block {
                        balancer.balance()
                }
        }
...
        server {
                ssl-certificate-by-lua-block {
                        certificate.call()
                }                     
...
                location /app {
                        log-by-lua-block {
                                balancer.log()
                                monitor.call()
                                plugins.run()
                        }
...                       
[File 1] Lua Module을 이용하는 Nginx의 nginx.conf

Nginx는 Lua Module을 이용하여 Nginx Config를 Reload를 최소화 하여 Packet 손실을 최소화 하도록 구현되어 있다. 또한 Metric 정보도 Lua Module을 이용하여 수집하도록 구성되어 있다. [File 1]은 Lua Module을 이용하는 Nginx의 nginx.conf 파일을 나타내고 있다. [File 1]의 3 ~ 5줄은 [Figure 1]에도 표현된 Nginx의 Backend 정보와 Certificate가 저장되는 Dictionary 기반 Shared Memory를 나타내고 있다. nginx.conf의 upstream 부분에는 일반적으로 Load Balancing의 대상이 되는 Server의 정보가 저장되어 있는데, [File 1]의 8~10줄에는 Server 정보 대신 balancer Lua Module을 호출하는 것을 확인할 수 있다.

nginx.conf의 server 부분에는 일반적으로 Certificate Path 정보가 저장되는데, [File 1]의 14~16줄은 Certificate Path 정보 대신 certificate Lua Module을 호출하는 것을 확인할 수 있다. [File 1]의 18 ~ 23줄은 /app URL이 호출될 경우 balancer를 통해서 Packet이 어느 Pod으로 전달되는지 Log를 남기고, monitor를 통해서 관련 Metric 정보를 남기는 부분을 나타낸다.

1.1. Configuration

Nginx Ingress Controller의 Store는 Kubernetes Client인 client-go를 이용하여 Ingress Object 및 Ingress와 관련된 Endpoint, Secret, ConfigMap, Service Object들을 Watch한다. Watch하고 있는 Object가 Update된다면 Store는 Update된 Object를 받아 Ingress Sync에게 전달한다. Ingress Sync는 Update된 Object를 바탕으로 Nginx Config를 구성하고 새롭게 구성한 Nginx Config와 기존에 적용된 Nginx Config를 비교한다. 두 Nginx Config가 동일하면 Nginx Config를 변경하지 않지만, 다르다면 변경된 Nginx Config를 Nginx에 적용한다.

Nginx Config 중에서 Backend 부분이 변경되었다면 변경된 내용은 nginx.conf 파일과 Nginx의 /configuration/backends URL을 통해서 Nginx의 Shared Memory에 저장된다. Nginx Config 중에서 Ceritificate가 변경되었다면 변경 내용은 /configuration/servers URL을 통해서 Nginx의 Shared Memory에 저장된다. Kubernetes Cluster의 Ingress Object의 변경으로 인해서 Nginx의 Backend가 변경되는 경우, nginx.conf 파일의 내용도 변경되어야 하기 때문에 Nginx는 nginx.conf Reload 해야한다. 하지만 단순히 Ingress Object에 Mapping 되어 있는 Service의 Pod의 개수가 변경되는 경우에는 nginx.conf의 변경이 필요없고 Shared Memory에 저장되어 있는 Backend의 Endpoint만 변경하면 되기 때문에, Nginx는 nginx.conf Reload를 수행하지 않는다.

이와 유사하게 Ingress Object의 변경으로 인해서 Nginx의 Ceritificate만 변경되야하는 경우에도 Shared Memory에 저장되어 있는 Certificate만 변경하면 되기 때문에, Nginx는 nginx.conf Reload를 수행하지 않는다. 이처럼 Nginx는 Lua Module을 이용하여 Nginx의 nginx.conf Reload를 최소화 하도록 구현되어 있다. Nginx Ingress Controller의 Configuration changes detected, backend reload required. Log는 Ingress Sync가 기존에 적용된 Nginx Config와 새롭게 구성한 Nginx Config를 비교한 다음 Reload가 필요하다고 판단한걸 의미하고, Backend successfully reloaded. Log는 Nginx Reload 성공을 나타낸다.

1.2. Metric Collector

Nginx Ingress Controller의 Metric Collector는 Metric 정보를 수집하여 Prometheus에게 전송하는 역할을 수행한다. [Figure 1]에는 Metric Collector로 전송되는 Metric의 경로도 포함하고 있다. Metric Collector는 3가지 경로를 통해서 Metric 정보를 수집한다. 첫번째로 Nginx 내부의 HTTP Stub Status Module이 제공하는 Metric 정보를 Nginx의 /nginx-status URL을 통해서 얻어온다. 두번째로 Nginx의 Monitor Lua Module을 통해서 Metric 정보를 얻어온다. Client가 Nginx를 통해서 App에게 Packet을 전송할때 마다 관련 Metric 정보는 Monitor Lua Module로 전송된다. Monitor Lua Module은 받은 Metirc 정보를 모아 한꺼번에 주기적으로 Domain Socket을 이용하여 Metric Collector로 전송한다.

마지막으로 Nginx Ingress Controller Pod의 procfs를 통해서 Nginx Process의 Metric 정보를 얻는다. 얻은 Metric 정보는 Nginx Ingress Controller의 /metrics URL를 통해서 Prometheus에게 전달된다. 따라서 각 Nginx Ingress Controller는 Prometheus의 Exporter 역할을 수행하게된다.

1.3. Load Balancing, TLS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  tls:
  - secretName: tls-secret
  rules:
  - host: ssup2.com
    http:
      paths:
      - path: /app
        backend:
          serviceName: app
          servicePort: 443
---
apiVersion: v1
kind: Service
metadata:
  name: app
spec:
  ports:
  - port: 443
    targetPort: 443
    protocol: TCP
    name: http
  selector:
    app: app
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      containers:
      - name: app
        image: ssup2/demo:latest
        ports:
        - containerPort: 443
[File 2] Ingress, Service, Deployment

Nginx는 Lua Module을 이용하여 Client의 Packet을 Load Balancing하고 필요에 따라서 TLS 암호화/복호화도 수행한다. [File 2]은 Kubernetes의 Ingress, Service, Deployment의 예제를 나타내고 있다. [File 2]의 내용처럼 Ingress는 Service에 Mapping이되고 Service는 Pod(Deployment)에 Mapping이 된다. 따라서 [File 2]의 내용을 보면 Client의 Packet은 Nginx에서 Service IP로 DNAT되고 다시 Service IP에서 Pod IP로 2번 DNAT 및 Load Balancing 되어 전송되는것 처럼 보인다. 하지만 실제로 Nginx는 Client가 전송한 Packet을 Configuration Lua Module이 Shared Memory에 저장한 Backend의 Service 및 Endpoint(Pod IP/Port) 정보를 바탕으로 한번만 DNAT를 수행하여 Load Balancing 및 Packet을 Pod으로 바로 전송한다.

Load Balancing 알고리즘은 기본적으로 Round Robin을 이용하고 configmap을 이용하여 설정 할 수 있다. Protocol은 HTTP/HTTPS 뿐만 아니라 TCP/UDP도 지원한다. Ingress 설정시 TLS를 이용하도록 설정되어 있다면 Configuration Lua Module이 Shared Memory에 저장한 Certificate 정보를 바탕으로 TLS 암호화/복호화를 수행한다.

1.4. Health Check

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
...
[File 3] Nginx Ingress Controller Pod의 Liveness, Readiness Probe

Nginx Ingress Controller는 Nginx의 /healthz URL로 Packet을 Redirect하는 /healthz URL을 제공한다. 따라서 Nginx Ingress Controller의 /healthz로 전송한 요청의 응답을 받을 수 없다면 Nginx Ingress Controller 또는 Nginx에 문제가 생겼다는걸 의미한다. 일반적으로 Nginx Ingress Controller Pod의 Liveness, Readiness Probe를 Nginx Ingress Controller의 /healthz로 지정하여 Nginx Ingress Controller 및 Nginx의 Health를 검사한다. [File 3]은 Nginx Ingress Controller Pod의 Liveness, Readiness Probe의 설정 Example을 나타내고 있고, [Figure 1]에는 Nginx Ingress Controller의 /healthz로 전송된 요청이 다시 Nginx의 /healthz로 Redirect 하는 내용을 포함하고 있다.

2. 참조