Ingress 는 L7 네트워크 레이어에서 작동하는 로드 밸런서이다. 인그레스는 Service 종류의 하나가 아니라, 정해진 규칙에 따라 여러 Service들로 라우팅을 해주는 독립적인 리소스이다.

 

인그레스는 인그레스 리소스만을 가리키는 말이 아니라, Ingress 리소스 + Ingress 컨트롤러 + Ingress 클래스를 포괄하는 용어이다. 각각은 아래와 같은 역할을 한다.

  • Ingress 리소스: 매니페스트에 라우팅 규칙을 등록하여 API 를 분산
  • Ingress 컨트롤러: Ingress 리소스를 기반으로 실제 로드 밸런서를 생성하며, Ingress 리소스를 감시하여 변경된 사항을 로드 밸런서에 반영
  • Ingress 클래스: Ingress 컨트롤러가 어떤 Ingress 리소스를 감시/처리할지의 기준을 명시하는 리소스

 

인그레스에는 크게 두가지 타입이 존재하는데, 하나는 클라우드 프로바이더에서 제공하는 인그레스로, k8s 클러스터 외부 로드 밸런서를 사용하는 인그레스이고, 또 하나는 k8s 클러스터 내부에 자체적으로 인그레스용 파드를 띄워서 사용하는 인그레스이다.

종류 전달순서 예시
클러스터 외부 로드 밸런서를 사용한 인그레스 클라이언트 -> 외부 L7 로드 밸런서(인그레스) -> NodePort 서비스 -> 목적지 파드 GKE 인그레스
클러스터 내부 파드를 사용한 인그레스 클라이언트 -> 내부 L4 LoadBalancer 서비스 -> 인그레스 파드(L7) -> 목적지 파드 Nginx 인그레스

 

Public Cloud Provider Ingress

 

 

Nginx Pod Ingress

 

우리는 ingress-nginx 를 사용하는 방법을 알아본다.

 

1. nginx 인그레스 컨트롤러 설치

https://github.com/kubernetes/ingress-nginx/blob/main/docs/deploy/index.md#bare-metal-clusters 을 참고하여 설치

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.14.1/deploy/static/provider/baremetal/deploy.yaml

 

단, 기본적으로 NodePort 서비스가 생성되는데, 자동으로 30000-32767 사이의 포트를 할당받고, Node IP 의 해당 포트로 호출하여야 한다. 아래 커맨드로 생성된 NodePort 서비스를 확인한다.

$ kubectl -n ingress-nginx get svc
NAME                                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             NodePort    10.110.81.89     <none>        80:32321/TCP,443:32165/TCP   23s
ingress-nginx-controller-admission   ClusterIP   10.110.134.192   <none>        443/TCP                      23s

아래 커맨드로 Ingress 디플로이먼트, 레플리카셋, 파드를 확인한다.

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

$ kubectl -n ingress-nginx get rs
NAME                                  DESIRED   CURRENT   READY   AGE
ingress-nginx-controller-68f58fbbcc   1         1         1       3m17s

$ kubectl -n ingress-nginx get pods
NAME                                        READY   STATUS    RESTARTS   AGE
ingress-nginx-controller-68f58fbbcc-2tqhj   1/1     Running   0          4m27s

 

2. LoadBalancer 를 사용하기 위해 metallb 설치

https://github.com/kubernetes/ingress-nginx/blob/main/docs/deploy/baremetal.md#a-pure-software-solution-metallb 을 참고하여 metallb를 설치한다.

# 1) kube-proxy IPVS 모드를 사용하는 경우 strick ARP 모드 활성화
$ kubectl edit configmap -n kube-system kube-proxy
# mode: "ipvs" 로 수정
# ipvs.strictARP: true 로 수정
# 아래 커맨드로 바로 수정도 가능
# see what changes would be made, returns nonzero returncode if different
$ kubectl get configmap kube-proxy -n kube-system -o yaml | \
  sed -e "s/strictARP: false/strictARP: true/" | \
  kubectl diff -f - -n kube-system

# actually apply the changes, returns nonzero returncode on errors only
$ kubectl get configmap kube-proxy -n kube-system -o yaml | \
  sed -e "s/strictARP: false/strictARP: true/" | \
  kubectl apply -f - -n kube-system

# 2) manifest 를 사용하여 metallb 설치
$ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.15.3/config/manifests/metallb-native.yaml

 

3. IPAddressPool, L2Advertisement 리소스 생성

아래 매니페스트로 LoadBalancer 생성에 필요한 IPAddressPool, L2Advertisement 를 생성한다.

---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: default
  namespace: metallb-system
spec:
  addresses:
  - 203.0.113.10-203.0.113.15
  autoAssign: true
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: default
  namespace: metallb-system
spec:
  ipAddressPools:
  - default

 

단, IPAddressPool 의 spec.addresses 의 IP 대역은 실제 사용하지 않고있고, 앞으로도 사용할 가능성이 없는 대역으로 지정해야 한다.

아래 커맨드를 참고하여 사용하지 않는 IP 를 확인한다.

# 1) 현재 노드가 속한 네트워크 대역 확인
$ ip -4 addr
...
inet 172.30.1.3/24
...

# 2) 현재 네트워크에서 이미 사용중인 IP 확인
$ sudo apt install -y arp-scan
$ sudo arp-scan 172.30.1.0/24

# 3) DHCP가 사용하는 범위 확인
# 공유기/라우터 관리 페이지 DHCP 설정 대역 확인

 

 

3. LoadBalancer 생성

아래 커맨드를 실행해서 기본 서비스 타입인 NodePort 가 아닌 LoadBalancer 타입으로 서비스를 띄워줘야 함.(공식 제공되는 ingress-nginx-controller 초기 yaml 에는 NodePort 로 지정되어있기 때문)

$ kubectl -n ingress-nginx patch svc ingress-nginx-controller -p '{"spec":{"type":"LoadBalancer"}}'

 

4. Backend 파드와 서비스 생성

아래 매니페스트로 파드와 서비스를 생성한다.

---
apiVersion: v1
kind: Service
metadata:
  name: sample-ingress-svc-1
spec:
  type: NodePort
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8888
      targetPort: 80
  selector:
    ingress-app: sample1
---
apiVersion: v1
kind: Pod
metadata:
  name: sample-ingress-apps-1
  labels:
    ingress-app: sample1
spec:
  containers:
    - name: nginx-container
      image: nginx:1.27
      ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: sample-ingress-svc-2
spec:
  type: NodePort
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8888
      targetPort: 80
  selector:
    ingress-app: sample2
---
apiVersion: v1
kind: Pod
metadata:
  name: sample-ingress-apps-2
  labels:
    ingress-app: sample2
spec:
  containers:
    - name: nginx-container
      image: nginx:1.27
      ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: sample-ingress-default
spec:
  type: NodePort
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8888
      targetPort: 80
  selector:
    ingress-app: default
---
apiVersion: v1
kind: Pod
metadata:
  name: sample-ingress-default
  labels:
    ingress-app: default
spec:
  containers:
    - name: nginx-container
      image: nginx:1.27
      ports:
        - containerPort: 80

 

5.  ingress 리소스 생성

아래 매니페스트로 인그레스 리소스를 생성한다. 리소스가 생성되면 인그레스 컨트롤러가 자동으로 읽어 인그레스 디플로이먼트 파드에서 라우팅이 된다.

Ingress 는 기본적으로 http 요청을 https 로 리다이렉트 해주는데 nginx.ingress.kubernetes.io/ssl-redirect: "false" 어노테이션을 주어 비활성화 한다. spec.ingressClassName 항목은 ingressClass 를 지정하는 항목인데, 기본적으로 생성되는 ingressClass인 nginx 를 지정하면 된다. spec.defaultBackend 에 지정한 서비스는 매칭되는 경로가 없을 때 라우팅되는 서비스이다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: sample-ingress-by-nginx
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
  ingressClassName: nginx
  rules:
    - host: sample.example.com
      http:
        paths:
          - path: /path1/
            pathType: Prefix
            backend:
              service:
                name: sample-ingress-svc-1
                port:
                  number: 8888
          - path: /path2/
            pathType: Prefix
            backend:
              service:
                name: sample-ingress-svc-2
                port:
                  number: 8888
  defaultBackend:
    service:
      name: sample-ingress-default
      port:
        number: 8888
  tls:
    - hosts:
        - sample.example.com
      secretName: tls-sample

 

spec.rules[].http.paths[].pathType 항목은 요청 URL 의 path 를 어떤 방식으로 매칭할 것인지를 정의한다. Ingress Controller 는 이 설정을 기반으로 어느 backend service 로 라우팅할지 결정한다.

값별 설명은 아래와 같다.

pathType 매칭 방식 정밀도 실무 사용 특이사항
Exact 정확히 일치 가장 엄격 드물게 사용
health check 엔드포인트 등 특수한 경우
trailing slash 도 엄격히 비교
Prefix URL prefix 일반적 권장 "/" 를 구분자로 path segment 를 분리하여 전방 일치 판단.
즉, "/api" 인 경우 "/apis" 는 매칭되지 않음.
여러 prefix 가 중복된 경로를 가진 경우, path segment 가 긴 path 일수록 우선순위 높음
ImplementationSpecific 컨트롤러에 따라 다름 불명확 비권장 컨트롤러 교체시 동작 변경 위험

 

6. 확인

# 각 노드의 IP 확인
$ kubectl get nodes -o wide
NAME       STATUS   ROLES           AGE    VERSION    INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME
master01   Ready    control-plane   172m   v1.31.3    192.168.64.5   <none>        Ubuntu 24.04.3 LTS   6.8.0-90-generic   containerd://1.7.28
worker01   Ready    <none>          144m   v1.31.14   192.168.64.6   <none>        Ubuntu 24.04.3 LTS   6.8.0-90-generic   containerd://1.7.28
worker02   Ready    <none>          136m   v1.31.14   192.168.64.7   <none>        Ubuntu 24.04.3 LTS   6.8.0-90-generic   containerd://1.7.28

# ingress 컨트롤러로 접근할 수 있는 IP 확인(worker02 의 IP 가 확인되는데, 이는 진입점이 worker02 에 떠있는 NodePort 서비스가 된다는 소리임)
$ kubectl get ingresses
NAME                      CLASS   HOSTS                ADDRESS        PORTS     AGE
sample-ingress-by-nginx   nginx   sample.example.com   192.168.64.7   80, 443   3m41s

# (NodePort 로 사용하는 경우)NodePort 서비스이기 때문에 NodePort 의 Port 를 확인하여야 함
$ kubectl -n ingress-nginx get services
NAME                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             NodePort    10.108.27.225   <none>        80:30329/TCP,443:32630/TCP   115m
ingress-nginx-controller-admission   ClusterIP   10.97.149.209   <none>        443/TCP                      115m

# LoadBalancer 서비스로 바꾼 후에는 EXTERNAL-IP 로 바로 접속하여야 함
$ kubectl -n ingress-nginx get svc ingress-nginx-controller
NAME                       TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)                      AGE
ingress-nginx-controller   LoadBalancer   10.108.27.225   203.0.113.10   80:30329/TCP,443:32630/TCP   3h59m

# LoadBalancer IP를 변수로 저장
INGRESS_LB_IP=`kubectl -n ingress-nginx get svc ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}'`

# 경로별 라우팅 확인
curl http://${INGRESS_LB_IP}/path1/ -H "Host: sample.example.com"
curl http://${INGRESS_LB_IP}/path2/ -H "Host: sample.example.com"
curl http://${INGRESS_LB_IP}/ -H "Host: sample.example.com"

 

IngressClass 란?

Ingress Controller 가 Ingress 리소스를 식별하는 기준이 된다. 아래와 같은 매니페스트로 생성한다.

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx-external
spec:
  controller: example.com/ingress-nginx-external

 

항목명 의미
metadata.name 인그레스 클래스명. 인스레스 리소스에 spec.ingressClassName 을 지정하고, 인그레스 컨트롤러 파드에서 --ingress-class 옵션으로 추가하여 컨트롤러(파드)가 어떤 인그레스 리소스를 감시/처리할지 판단하는 기준이 됨
spec.controller 어떤 인그레스 컨트롤러가 사용할지 명시하는 고유 문자열

 

이렇게 생성한 IngressClass명을 Ingress 리소스의 spec.ingressClassName 에 지정해주고, Ingress Controller 에 어떤 IngressClass 를 감시/처리 실행 옵션으로 아래와 같이 지정한다. Ingress Controller 는 보통 Deployment 로 관리되는 Pod 이므로, Pod Template 의 spec.containers[].args 에 아래와 같이 지정하면 된다.

# ingress-nginx 기준 예시
...
args:
  - /nginx-ingress-controller
  - --controller-class=example.com/ingress-nginx-external
  - --ingress-class=nginx-external
  - --watch-ingress-without-class=false

 

옵션의 의미

옵션명 의미
/nginx-ingress-controller 컨테이너 내부 실행 바이너리
--controller-class IngressClass 리소스의 spec.controller 와 매칭
--ingress-class Ingress 리소스의 spec.ingressClassName 와 매칭
--watch-ingress-without-class=false class 없는 ingress 무시(운영 권장)

 

 

 

 

블로그 이미지

망원동똑똑이

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

,