TOPIC/Infra

Kubernetes Namespace 간 Ingress 트래픽 라우팅 하기

H-Y-E-N 2024. 1. 3. 21:49

안녕하세요. HYEN입니다.

이번에는 Kubernetes Namespace 간 Ingress 트래픽을 라우팅하는 방법에 대해 알아보도록 하겠습니다. 🤗


Contents

    1. 궁금증의 시작

    저는 최근 Terraform으로 Azure Infra를 구성하는 테스트를 진행하고 있습니다. 
    모듈화는 처음이라 굉장히 고난을 겪었지만😢 멋진 동기 덕분에 무사히 테스트를 완료하였습니다. 👏🏻👏🏻👏🏻

    그 다음 목표는 배포된 Azure Kubernetes Service 내부에 세부 구성을 진행하는 것이었는데요. 
    도커 이미지를 Build해서 Azure Container Registry로 Push하고 Azure Kubernetes Service에서 그 이미지를 가지고 Web용 Deployment를 배포하기로 했습니다. 

    동기가 만든 Deployment와 제가 만든 Deployment를 경로에 따라 분기하기 위해서 Nginx Ingress를 생성하는 작업을 진행하던 도중 궁금증이 생겼습니다. 

    Deployment가 서로 다른 namespace에 있을 때,
    어떤 namespace에 Ingress 리소스를 지정해 줘야 하는 걸까?


    이 궁금증을 해결하기에 앞서, 먼저 Ingress 리소스에 대해 알아보도록 하겠습니다. 

     

     

    2. Ingress란?

    • L7 로드 밸런싱을 제공하는 리소스로 Service들을 묶는 Service의 상위 Object입니다. 
      • TLS/SSL 인증서 처리, 도메인 기반 가상 호스팅 제공, 특정 HTTP 경로의 라우팅 등의 규칙을 정의합니다.
      • 외부에서 Kubernetes 내부로 들어오는 네트워크 요청을 어떻게 처리할 지 결정하는 역할을 합니다. 
    • 추가적으로, Ingress Controller는 Ingress가 어떻게 동작하는 지를 결정하는 Controller이며 이번 테스트에서는 Nginx Ingress Controller를 사용하였습니다.
      그러나 Azure의 Application Gateway의 AGIC 기능을 활성화 하여 Ingress Controller로 사용할 수 있습니다. 

     

    정리해 보면,

    Ingress Object와 같은 Namespace에 있는 Service의 경우 Ingress가 해당 Service를 잘 찾아갈 수 있지만
    Ingress Object와 다른 Namespace에 있는 Service는 Ingress가 인지할 수 없다는 것을 알 수 있습니다. 

     

    이때 사용할 수 있는 방법은 바로 ExternalName Type의 Service를 추가로 배포하는 것입니다.

     

    그렇다면 ExternalName Type의 Service는 뭘까요?

     

     

    3. ExternalName

    ExternalName type의 Service는 Selector를 기반으로 하는 것이 아니라 DNS 이름으로 매핑하여 외부의 특정 FQDN에 대한 CNAME 매핑을 제공합니다.

    즉, Cluster IP 주소와 같은 ip 주소를 기반으로 액세스하는 것이 아니라 Service를 생성할 때 manifest file의 externalName field에 정의된 값을 포함하는 CNAME 레코드를 반환하게 됩니다.

     

     

    4. 아키텍처

    최종 테스트 아키텍처는 다음과 같습니다.

    • Ingress를 namespace-1에 배포하고 namespace-1에 있는 Service를 백엔드로 지정

    • namespace-1에 ExternalName type의 Service 배포 - namespace-2에 있는 Service를 찾아가기 위한 bridge 역할

    • 해당 Service에 externalName: service-2. namespace-2(Service Name.Namespace Name) 지정

     

     

    5. 테스트 내용

    5.1 hyein-ns

    1. Load Balancer type의 Service 생성 및 Deployment 생성

    1) lb.yaml

    apiVersion: v1
    kind: Service
    metadata:
      name: first-svc
      annotations:
        service.beta.kubernetes.io/azure-load-balancer-internal: "true"
        service.beta.kubernetes.io/azure-load-balancer-internal-subnet: "snet-lb"
    spec:
      selector:
        app: hyein
      ports:
        - protocol: TCP
          port: 80
      type: LoadBalancer
    • internal LB로 생성하기 위해 annotation을 추가하였습니다. (service.beta.kubernetes.io/azure-load-balancer-internal: "true")

    • 실제로 생성을 한 후에 kubectl get svc first-svc -o wide 명령어를 입력하여 확인해 보면 하기와 같이 External IP가 Private IP인 것을 확인할 수 있습니다.

     

    • 또한 lb가 생성될 subnet을 지정하기 위해 annotation을 추가하였습니다. (service.beta.kubernetes.io/azure-load-balancer-internal-subnet: "snet-lb")

    • Azure Portal 상에서 Load Balancer가 존재하는 VNet을 선택하면 [설정] 블레이드의 [연결된 디바이스] 탭에서 kubernetes-internal이라는 이름의 Load Balancer가 snet-lb라는 subnet 대역에 배포되어 있는 것을 확인할 수 있습니다. 

     

     

    2) deploy.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: first-deploy
      labels:
        app: hyein
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: hyein
      template:
        metadata:
          labels:
            app: hyein
        spec:
          containers:
          - name: first-container
            image: # Azure Container Registry Server/Repository/Image:Tag
            ports:
            - containerPort: 5000
    • Azure Container Registry에 있는 image를 참조하기 위해 image field에 대한 값에 registry 주소/repository/Image:version 값을 기입하였습니다. 

     

     

    2. ExternalName type의 Service 생성

    apiVersion: v1
    kind: Service
    metadata:
      name: lily-svc-bridge
      namespace: hyein-ns
    spec:
      type: ExternalName
      externalName: lily-svc.lily-ns
    • lily-svc-bridge라는 이름은 pod에서 DNS를 호출하는 이름으로 사용됩니다.

    • externalName 값은 lily-svc.lily-ns로 지정합니다. 이는 lily-ns에 있는 lily-svc를 가리킵니다. 또한 이는 pod에서 lily-svc-bridge를 호출하면 실제로 변경되는 external 주소입니다. 

    • ExternalName type의 Service는 클러스터 내부에서 클러스터 외부의 특정 서비스에 접속하기 위해 DNS CNAME을 설정하는 방식이라는 것 ! 잊지 않으셨죠? 

    • 실제로 ExternalName type의 Service를 생성해 보았습니다. 하기와 같이 External IP Field에 ip가 아닌 lily-ns에 배포된 lily-svc를 가리키는 도메인이 지정된 것을 확인할 수 있습니다. 

     

    3. Ingress 생성

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: hyein-ingress
      namespace: hyein-ns
      annotations:
        nginx.ingress.kubernetes.io/ssl-redirect: “false”
    spec:
      ingressClassName: nginx
      rules:
      - host: hyein.example.com # domain address 01 
        http:
          paths:
          - path: /
            backend:
              service:
                name: first-svc
                port:
                  number: 80
            pathType: Prefix
      - host: lily.example.com # domain address 02
        http:
          paths:
          - path: /
            backend:
              service:
                name: lily-svc-bridge
                port:
                  number: 80
            pathType: Prefix
    • 기본적으로 Ingress Controller는 해당 ingress에 대해 TLS 설정이 되어 있는 경우 HTTPS로 강제 redirection 합니다.

    • 이를 비활성화 하기 위해서는 nginx.ingress.kubernetes.io/ssl-redirect: "false" 이라는 annotation을 사용해야 합니다.

    • 규칙에 2개의 host를 추가해 보았습니다. 각 host별 각기 다른 backend가 지정됩니다. 규칙에 정의한 내용 대로 traffic이 분기된다고 보시면 됩니다. 

    주의할 점은,
    Ingress Object와 같은 namespace에 있는 Service의 경우, 그대로 Service를 backend로 지정하면 되지만
    Ingress Object와 다른 namespace에 있는 Service의 경우, Ingress가 ExternalName type의 Service를 참조하여
    해당 Service를 찾아갈 수 있도록 backend로 ExternalName type의 Service를 지정해 주어야 합니다. 

     

    • 상기 Yaml file로 Ingress Object를 생성해 보았습니다.
      • Helm을 통해 Ingress Controller를 생성하였을 때 --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-internal"=true 라는 파라미터를 통해 Azure Internal Load Balancer를 사용하도록 지정하였습니다. 따라서 Address가 Private IP인 것을 확인할 수 있습니다. 

     

    5.2 lily-ns

    1. Load Balancer type의 Service 생성 및 Deployment 생성

     

    5.3 Azure Application Gateway 설정 및 DNS Zone 등록

    지금부터는 Azure Portal로 이동하여 Azure Application Gateway에 대해 몇 가지 설정을 진행하고 DNS Zone에 Record를 등록해 보겠습니다. 

     

    1. 백 엔드 풀에 Ingress Object의 External IP 등록 

    • Azure Application Gateway의 백 엔드 풀 대상으로 Ingress의 Address를 입력해 줍니다.

    • 이로서 Azure Application Gateway의 뒷단에 Ingress가 연결되게 되고 이러한 Ingress는 Service의 상위 Object로서 각 Service로의 Traffic 분기를 담당하게 됩니다. 

     

     

    2. 상태 프로브 생성

    프로브는 하나 이상의 HTTP 설정과 연결된 경우에만 백엔드 상태를 모니터링하며,
    프로브가 연결된 HTTP 설정과 연결과 backend pool의 백엔드 리소스를 모니터링합니다. 
    또한 프로브 요청은 <protocol>://<hostName>:<port>/(urlPath)로 전송됩니다.
    ex. http://10.10.144.93:80/healthz

    • 호스트 : Ingress Object의 External IP를 입력합니다.

    • 경로 : helm으로 nginx ingress controller 생성 시 지정해 준 경로 입력
      ex.) --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz

     

     

     

    3. 백 엔드 설정 수정

    • Terraform으로 생성한 백 엔드 설정 중 일부를 Azure Portal 상에서 수정했습니다. 
      • 백 엔드 경로 재정의 : 재정의할 필요가 없기 때문에 공란으로 둡니다. 
      • 사용자 지정 프로브 사용 : [예] 선택 후 2번에서 생성한 상태 프로브를 선택합니다.

     

     

     

    4. 규칙 및 수신기 설정

    • Azure Application Gateway는 Frontend IP 주소로 traffic이 들어오면 Azure Application Gateway 내 구성된 규칙을 확인하여 지정된 백 엔드 대상으로 트래픽을 전송합니다. 

    • 리스너는 지정된 포트(ex. 80), 프로토콜, 호스트, IP 주소 등을 사용하여 들어오는 연결 요청을 확인하는 역할을 합니다. 
    • 규칙 - [수신기] 탭

     

    • 규칙 - [백 엔드 대상] 탭 

     

     

     

    5. DNS Zone에 Record 등록

    • Ingress Object 생성 시 지정한 host 이름(도메인)과 일치하도록 Record를 등록합니다. 

    • 예를 들어 hyein.example.com이 Ingress의 Manifest file에 정의되어 있다면 example.com이라는 Azure DNS Zone을 배포한 후 hyein이라는 A 레코드를 등록해 주어야 합니다. 

    • 또한 Azure Application Gateway의 Frontend IP 주소를 각 레코드와 매핑해 주어야 합니다. 

     

    5.4 접속 테스트

    실제로 생성한 페이지에 접근해 보겠습니다. (도메인은 테스트용으로 실제 사용하고 있어 블러 처리 하였습니다.)

    • hyein-ns에 생성한 Deployment 

     

    • lily-ns에 생성한 Deployment

    이상으로 Kubernetes Namespace 간 Ingress 트래픽을 라우팅하는 방법에 대해 알아 보았습니다! 

    728x90
    320x100
    SMALL