출처: https://github.com/kubernetes/community/tree/master/icons

 

 


Kubernetes 클러스터는 보안을 위해 외부 네트워크와 격리되어 있습니다. 클러스터 내부 서비스에 외부에서 직접 접근하는 것은 기본적으로 허용되지 않습니다. 그럼에도 불구하고 현실적인 서비스 운영을 위해 외부에서 클러스터 내부 서비스에 접근해야 하는 상황이 필요합니다. 단적인 예시로 Pod로 띄운 API 서버에 대한 요청도 외부에서 내부로 접근하는 상황이 그러할 것입니다.

 

 

쿠버네티스에서는 이런 상황에서 즉, 클러스터 외부에서 내부 서비스에 접근할 수 몇 가지 방법을 제안합니다. 그 전에 서비스 타입에 대해  간단하게 알아보겠습니다.


서비스 타입의 이해

쿠버네티스에서는 워크로드를 일련의 Pod 집합 내에서 실행합니다. 모든 파드는 클러스터 안에서 IP를 부여받게 되며, 이 가상 IP는 쿠버네티스 클러스터 안에서 유일합니다. 그럼 이 IP를 통해 통신이 가능한가요? 클러스터 내에서는 해당 IP를 통해 통신이 가능합니다.

예시: nginx Pod가 할당받은 IP

그렇다면 마음놓고 이 IP로 통신해도 될까요? 당연하게도 답은 어지간하면 NO입니다!

문제는 파드들은 중단되고 재시작할 수 있다는 점입니다. 그리고 죽었다 살아난 Pod가 어떤 노드에서 생성될 지는 아무도 알 수 없다는 점입니다. 또한 같은 노드에서 생성된다고 할 지라도 다른 IP를 부여받게 됩니다. 즉, 정적 라우팅으로는 가용성 있는 통신을 할 수 없게 됩니다. 이를 해결하기 위해 우리는 Kubernetes의 Service라는 오브젝트를 사용하게 됩니다.

이를 위해서 선행되어야 할 것이 Service 타입에 대한 간단한 이해입니다. 쿠버네티스 상에서 Service는 단순히 Pod를 묶어서 통신하는 것을 뜻하지 않습니다. Service는 Pod들이 생성되고 삭제되더라도 항상 안정적인 단일 엔드포인트를 제공합니다. 애플리케이션은 Service를 통해 Pod의 세부 정보를 숨기고, 단순히 Service 이름만으로 통신할 수 있습니다. 이런 추상화를 통해 정적 라우팅에서 마주쳤던 문제는 다시 겪지 않을 수 있습니다.


Service의 종류

쿠버네티스 상에서 Service 타입은 크게 세 가지로 나뉩니다.

(ExternalName 타입은 특정한 외부 서비스와의 DNS 매핑에만 사용되기에 배제하였습니다.)

  • ClusterIP: 클러스터 내부에서만 접근 가능한 서비스입니다. 일반적으로 클러스터 내부 서비스 간 통신에 사용됩니다.
  • NodePort: 클러스터 노드의 특정 포트로 트래픽을 라우팅하여 외부에서 접근할 수 있도록 합니다.
  • LoadBalancer: 클러스터 외부의 로드 밸런서를 통해 서비스를 노출시킵니다. 클라우드 환경에서 외부에 서비스를 노출하는 가장 일반적인 방법입니다.

이 중에서 외부 서비스와 통신을 위한 서비스 타입은 NodePort와 LoadBalaner입니다.

 


NodePort

출처: https://medium.com/google-cloud/

 

NodePort는 Kubernetes 클러스터 내의 서비스에 외부에서 접근할 수 있도록 하는 가장 간단한 방법입니다. 클러스터의 모든 노드에 동일한 포트 번호를 할당하여, 외부에서 이 포트 번호와 노드의 IP 주소를 통해 서비스에 접근할 수 있도록 합니다. 서비스를 노출시키기 위해 별도의 복잡한 설정이 필요하지 않아 아래와 같이 간편하게 설정할 수 있습니다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - port: 80
      # By default and for convenience, the `targetPort` is set to
      # the same value as the `port` field.
      targetPort: 80
      # will allocate a port from a range (default: 30000-32767)
      nodePort: 30007

 

 

하지만, 단점도 분명히 존재합니다. NordPort는 모든 노드에서 동일한 포트를 열어두기 때문에 보안에 취약할 수 있습니다. 또한 많은 서비스를 운영할 경우 각 서비스의 노드 포트를 관리하는 것이 번거롭습니다. 이는 사용자 관점에서도 서비스마다 포트 번호를 기억해야 한다는 단점이 있습니다.

포트 번호 관련 이슈는 proxy-server를 중간에 두어 적절한 라우팅을 통해 굳이 개별 포트 번호를 알 필요는 없지만 이는 또 proxy-server 자체의 관리, 성능, 보안 등의 여러 복잡성을 가져옵니다.

그럼에도 간편한 설정 덕에 개발 환경에서는 자주 애용되고는 합니다. (필자의 짧은 경험과 구글링을 통한 카더라통신에 의하면..)


LoadBalancer

출처: https://medium.com/google-cloud/

 

LoadBalancer 타입은 클라우드 환경의 외부 로드 밸런서를 생성하여 이를 클러스터 내부 서비스(ClusterIP)와 연결하는 방법입니다. 이를 통해 클러스터 내부의 서비스에 대해 외부적으로 IP 주소를 할당받고, 트래픽을 분산할 수 있습니다.

 

하지만 로드밸런서는 Layer4에서 동작하는 서비스이며, Layer4의 장단점을 모두 가져갑니다. 이에 따라 각각의 서비스에 대해 독립적인 로드밸런서를 만들어주어야 한다는 단점이 있습니다. 


Ingress 

드디어 Ingress에 도달했습니다. 본격적으로 들어가기 앞서 NetworkPolicy를 설정해보신 분이라면 Ingress라는 단어가 익숙한 사람도 있을 것입니다. 이처럼 Ingress는 이름에서부터 트래픽에 관련된 용어이며 그 중에서도 안으로 들어오는 트래픽에 관련된 리소스라는 것을 알 수 있습니다.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

 

 

출처: 네이버 단어사전

 

위에서 말했듯이 Ingress라는 단어는 기본적으로 들어간다는 의미를 내포합니다. Ingress는 클러스터로 들어오는 트래픽을 의미합니다. 잡설이 길었으니 Ingress 타입에 대해 알아보겠습니다.

 

 

 

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

 

NodePort와 LoadBalancer는 각각의 장단점을 가지고 있지만, 어느정도 위에서 떡밥을 뿌렸던 것처럼 Ingress가 필요한 이유가 있습니다. Ingress는 7계층에서 동작하며, 트래픽 컨트롤러 역할을 하여 HTTP와 HTTPS 트래픽을 다양한 기준에 따라 다른 서비스로 라우팅할 수 있게 해줍니다. 즉, URL 경로 또는 호스트 이름에 기반하여 요청을 적절한 서비스로 전달합니다. 이 외에도 SSL/TLS 사용 및 부하 분산을 할 수 있습니다.

이렇게 다양한 애플리케이션을 하나의 도메인 내에서 효율적으로 운영하는 것은 많은 장점이 있습니다. 

  1. 중앙 집중식 관리: Ingress를 사용하면 모든 트래픽 라우팅과 SSL 설정을 Kubernetes 클러스터 내에서 하나의 지점에서 관리할 수 있습니다. 이를 통해 여러 애플리케이션에 대한 설정을 통합하고, 유지 관리의 복잡성을 줄일 수 있습니다.
  2. 비용 절감: 여러 서비스가 단일 IP와 도메인 내에서 운영되므로, LoadBalancer 서비스를 개별적으로 사용하는 것보다 비용을 절감할 수 있습니다. Ingress를 사용하면 하나의 로드 밸런서에서 여러 서비스를 관리할 수 있습니다.
  3. 유연한 트래픽 라우팅: URL 경로나 도메인 이름에 따라 트래픽을 효율적으로 라우팅할 수 있습니다. 예를 들어, myonlinestore.com/wear로 들어오는 트래픽을 Wear 애플리케이션으로, myonlinestore.com/watch로 들어오는 트래픽을 Video 애플리케이션으로 라우팅할 수 있습니다.
  4. SSL 보안: Ingress를 통해 HTTPS를 설정하고, 클러스터 내의 서비스에 대한 보안을 강화할 수 있습니다.

 

반면, Ingress를 사용하지 않을 경우, 다음과 같은 단점들이 있을 수 있습니다:

  • 복잡한 관리: 여러 애플리케이션에 대해 각각의 LoadBalancer를 설정하고 관리해야 합니다.
  • 비용 증가: 각 서비스에 대해 별도의 로드 밸런서를 사용하는 경우, 클라우드 환경에서 높은 비용이 발생할 수 있습니다.
  • 보안 설정의 분산: SSL 설정을 각 서비스별로 별도로 관리해야 하며, 이는 보안 유지보수를 복잡하게 만듭니다.

Ingress 구성요소

Ingress는 Ingress Controller와 Ingress 정의 파일로 나뉩니다. 각각의 역할은 다음과 같습니다.

  1. Ingress Controller: Ingress 리소스를 관리하고, 트래픽을 적절히 라우팅하는 로드 밸런서 역할을 합니다. NGINX, HAProxy, Traefik, Istio 등이 대표적인 Ingress Controller입니다.
  2. Ingress Resource: Kubernetes의 정의 파일을 사용하여 설정합니다. 이 파일에는 URL 경로나 도메인 이름에 따라 트래픽을 어떤 서비스로 라우팅할지를 정의하는 규칙이 포함됩니다.

 

 

 

현재까지 설명한 내용을 전체적인 그림으로 그린다면 위와 같습니다. 이론을 정립했으니 실제로 실습을 진행해보겠습니다.


Ingress 구축 과정

제가 진행한 Ingress는 다음과 같은 단계로 진행됩니다.

1. Ingress-controller 설치
2. Ingress 리소스 정의
3. Route53 서브도메인 설정

 

1. Ingress-controller 설치
Ingress-controller는 사실 여러가지가 있습니다. Ingress Controller는 사실 Nginx, Apache 심지어 관리자용 UI를 제공해주는 Traefik 등등 여러가지가 있습니다. 저는 가장 자료가 많은 듯 보이며, 교육 과정 중에도 학습했던 Nginx Controller를 사용하겠습니다.

Ingress-nginx.yaml을 생성하고 ingress-nginx 네임스페이스에 apply 해줍니다.

(ingress-nginx.yaml 소스는 공식 깃허브에 있습니다. 본인 환경과 버전에 맞도록 yaml 파일을 다운받아 사용하시면 될 것 같습니다.)

$ kubectl apply -f ingress-nginx.yaml -n ingress-nginx

 

서비스가 제대로 배포되었는 지 확인하고 ingress-nginx namespace에 올라온 Pods들을 확인합니다.

$ kubectl get deployments -n ingress-nginx
NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
ingress-nginx-controller   1/1     1            1           3m55s


$ kubectl get pods -n ingress-nginx
NAME                                        READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-r6p2f        0/1     Completed   0          4m18s
ingress-nginx-admission-patch-x4v25         0/1     Completed   0          4m18s
ingress-nginx-controller-6d858b598b-v2x5q   1/1     Running     0

 

각 Pod들의 역할을 간단하게 설명하자면 다음과 같습니다.

ingress-nginx-admission-create : 컨트롤러의 admission webhook을 설치하기 위한 초기화 작업 담당
-> 일회성 작업으로 수행 후 종료
ingress-nginx-admission-patch : 웹훅이 제대로 동작할 수 있도록 패치 작업 수행
-> 일회성 작업으로 수행 후 종료
ingress-nginx-controller : 실제로 Ingress NGINX 컨트롤러를 실행하는 Pod. 클러스터 내에서 외부 트래픽을 관리하고 라우팅하는 역할 담당
-> Running 상태로 존재

 

ingress-nginx-controller와 연결된 ALB의 DNS이름을 확인합니다.

ingress controller와 연결된 ALB의 DNS 이름

 

2. Ingress 리소스 정의

Ingress Controller를 설치하였기 때문에 Ingress 리소스를 정의하여 Ingress Controller가 해당 서비스에 대한 트래픽을 관리할 수 있도록 정의해줍니다. 예시에서는 기존에 올라와있던 Springapp에 대하여 진행해보겠습니다.

기존에는 아래에서 볼 수 있듯이 springapp의 서비스 타입을 LoadBalancer로 배포하여 로드밸런서의 dns로 접근했습니다. 이를 역할의 분리를 위해 Ingress로 통신하도록 수정해보겠습니다.

 

기존 Spring App Pod의 배포 스크립트에서 서비스 타입 부분을 ClusterIP로 수정하여 내부통신만 가능하게 배포합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: springapp-deploy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: springapp-pod
  template:
    metadata:
      labels:
        app: springapp-pod
    spec:
      containers:
        - name: springapp-container
          imagePullPolicy: Always
          image: { YOUR_IMAGE }
          resources:
            limits:
              memory: "512Mi"
              cpu: "1000m"
          ports:
            - containerPort: 8080
      imagePullSecrets:
        - name: ecr-registry-secret

---
apiVersion: v1
kind: Service
metadata:
  name: springapp-svc
spec:
  selector:
    app: springapp-pod
  ports:
    - port: 80
      targetPort: 8080
  type: ClusterIP

 

ClusterIP 타입으로 배포된 springapp-svc

 

 

3. Route53 서브도메인 설정

aea344e~~로 정의

서브도메인과 Ingress Controller의 DNS를 연결합니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: springapp-ingress
  annotations:
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: # { Your subdomain }
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: springapp-svc
            port:
              number: 80

 

이처럼 설정한 후 해당 ingress 리소스를 적용하면 우리가 정의한 서브도메인으로 원하는 서비스에 접근할 수 있습니다.

 


참고자료

https://kubernetes.io/ko/docs/concepts/overview/ 

https://medium.com/google-cloud/deploying-service-or-ingress-on-gke-59a49b134e3b

훈