Skip to content

Kubernetes를 온프레미스 인프라 아키텍처에 구축하면서 겪은 네트워크 트러블슈팅 회고

Notifications You must be signed in to change notification settings

kimjuls/k8s-onpremise-network-troubleshooting

Repository files navigation

k8s-onpremise-network-troubleshooting

이 글은 Kubernetes를 온프레미스 인프라 아키텍처에 구축하면서 겪은 네트워크 트러블슈팅에 대한 회고이다.

Summary

  • 온프레미스 인프라 환경에서 k8s를 구축할 계획이며, 설계는 다음과 같다.

    k8s-structure

  • Ingress 관련하여 네트워크 충돌 및 외부 요청 실패 문제가 발생한 것에 대해 Ingress 클래스 설치 시 다음과 같이 해결했다.

    helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
    helm repo update
    helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
      --namespace ingress-nginx \
      --set controller.hostNetwork=false \  # 호스트와는 네트워크를 격리시키는 것이 충돌 우회에 유리
      --set controller.dnsPolicy=ClusterFirst \
      --set controller.admissionWebhooks.enabled=true \  # Ingress 검증을 위해 사용
      --set controller.service.type=LoadBalancer \  # MetalLB 관련 설정
      --set controller.service.externalTrafficPolicy=Local \
      --set controller.service.nodePorts.http=30080 \  # 외부 트래픽의 라우터 포트포워딩을 위한 고정 포트
      --set controller.service.nodePorts.https=30443  # 외부 트래픽의 라우터 포트포워딩을 위한 고정 포트

상황

  1. 테스트 배포 시 admission Webhooks에서의 인그레스 검증이 되지 않아 enabled=false 상태로 배포하여 성공하였다.
  2. 외부 HTTP 트래픽을 받아야하므로, 인바운드 80, 443 포트를 라우터 포트포워딩을 통해 해당 노드에 연결해주었다.
  3. 연결이 되지 않아 확인해보니, 호스트에서 80, 443으로 작동 중인 프로세스가 없었다. 그래서 hostNetwork=true로 인그레스 클래스를 재배포하였다.
  4. 외부 트래픽 요청을 성공적으로 받고, TLS 인증까지 잘 받아 테스트가 완료되었다.

다만, Production 배포를 준비하는 과정에서 문제가 발생하였다.

문제 발생과 해결 과정

Production 배포에 앞서, controller.admissionWebhooks.enabled=true로 전환하려고 하니, 인그레스 배포 시 실패가 발생하였다. 이 문제를 해결하지 못하면 운영 환경에서 검증 없이 네트워크를 열어야한다.

인그레스 배포 시 AdmissionWebhook 검증 실패

일단, controller.admissionWebhooks.enabled=true로 인그레스 클래스 및 컨트롤러를 재배포하였다. 적용은 문제없지만 인그레스 배포 시 네트워크 검증에 실패할 것이다.

helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --set controller.hostNetwork=true \
  --set controller.dnsPolicy=ClusterFirstWithHostNet \
  --set controller.admissionWebhooks.enabled=true \  # Ingress 검증을 위해 사용
  --set controller.service.type=LoadBalancer

포트 충돌 인지

AdmissionWebhook의 동작을 찾아보니, Ingress에 TLS 인증서 설정이 있을 경우 해당 인증 정보 검증 시 에러가 발생한다고 한다.

다시 말해, 443 포트를 웹훅 포트로 사용하고 있다는 뜻이다. 하지만 우리는 서비스로 호스트에 80과 443 포트를 열었다. 충돌이 발생할 수 밖에 없는 상황이다.

호스트 네트워크 격리 조치

일단, hostNetwork=false로 바꾸어, k8s 클러스터 네트워크를 호스트와 격리해야한다. 그럼 호스트와 충돌하지 않을 것이다.

helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
    --namespace ingress-nginx \
    --set controller.hostNetwork=false \
    --set controller.dnsPolicy=ClusterFirst \
    ...

마찬가지로, dnsPolicy=ClusterFirst로 적용 또한 해준다. 호스트 네트워크를 사용하지 않으니 클러스터 내 DNS 서버를 조회해야한다.

재배포 시도 성공, 그리고 연결 실패

위와 같이 수정한 후, 인그레스를 배포해본다. 배포는 성공했지만, DNS네임으로 접속이 되지 않는다. 브라우저에서 Connection refused 에러가 발생한다.

우리는 위에서 k8s와 호스트의 네트워크를 끊었다. 라우터 포트포워딩은 80, 443 포트가 열려있고, hostNetwork=false를 적용하는 순간 해당 포트로 Listening하고 있는 프로세스는 사라진다. 이제 호스트의 80, 443 포트를 통해 인그레스에 접근할 수 없다.

답은 NodePort다.

라우터 포트포워딩 수정 및 NodePort 고정

라우터 포트포워딩으로 NodePort를 등록하려고 해도 이 인그레스 컨트롤러 서비스의 NodePort는 배포마다 바뀐다. 이렇게 되면 배포하려는 폐쇄망 기관마다 설정을 손봐줘야한다. 다행스럽게도, Helm 배포 시 이 포트를 고정할 수 있는 옵션이 있다.

helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  ...
  --set controller.service.externalTrafficPolicy=Local \ # 해당 Node내 Pod로만 트래픽 전달
  --set controller.service.nodePorts.http=30080 \  # 외부 트래픽의 라우터 포트포워딩을 위한 고정 포트
  --set controller.service.nodePorts.https=30443  # 외부 트래픽의 라우터 포트포워딩을 위한 고정 포트
  • externalTrafficPolicy? 클러스터 외부에서 들어오는 트래픽을 어떻게 처리할지 결정하는 정책인데, 두 가지 모드가 존재한다.

    모드 의미 장점 단점
    Cluster (Default) 외부 요청 시 클러스터 내부에서 kube-proxy가 해당 트래픽을 아무 노드의 Pod로 라운드 로빈 노드 간 부하 분산 클라이언트 Source IP가 사라짐 (SNAT; 출발지를 변경해버림)
    Local 외부 요청은 요청 받은 노드에 있는 Pod로만 전달됨 클라이언트 실제 IP 유지 (Ingress/SSL/Firewall Rule 등에서 유리) 해당 노드에 Pod 없으면 접속 불가 (트래픽 드롭)
  • 클라우드라면 외부 트래픽을 (AWS의 경우) ELB를 통해 잘 전달해주겠지만, 우리는 온프레미스로서 물리적 Node를 다룬다. 특정 Node에 바인딩되는 로드밸런서 IP를 위해 Local 모드로 소스IP를 유지해준다는데, 이 소스IP는 인그레스 클래스를 설치할 때 함께 배포되는 로드밸런서 타입의 컨트롤러에 바인딩되는 외부IP를 말한다. 이 외부IP가 나의 경우에서는, 라우터 포트포워딩한 <Public IP>:<Port>의 그 Public IP이다. 그리고 IP를 MetalLB로 할당하였다. 포트포워딩을 했으니 해당 Node(<내부IP>:<NodePort>)로만 트래픽이 갈 것이고, 그렇다면 로드밸런싱을 할 수 없는 구조이다. 그래서 Local 타입 설정을 사용해야한다.

  • 최종적으로 고정한 NodePort를 라우터 포트포워딩하였다.

결과

결론적으로 검증에 성공하여 접속 테스트까지 잘 되었다. 참고할 k8s 템플릿은 별도로 첨부해두었다.

최종 핵심은 다음과 같다.

MetalLB 설치 및 설정 배포

helm repo add metallb https://metallb.github.io/metallb
helm repo update
helm install metallb metallb/metallb --namespace metallb-system --create-namespace

kubectl apply -f metallb-config.yaml

metallb-config.yaml 참고

Ingress class 배포 시 최종 설정

helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --set controller.hostNetwork=false \
  --set controller.dnsPolicy=ClusterFirst \
  --set controller.admissionWebhooks.enabled=true \
  --set controller.service.type=LoadBalancer \
  --set controller.service.externalTrafficPolicy=Local \
  --set controller.service.nodePorts.http=30080 \
  --set controller.service.nodePorts.https=30443

이후 Ingress 배포

kubectl apply -f app-ingress.yaml

app-ingress.yaml 참고

*위를 포함한 setup 전체 과정에 대해 k8s-install.md에 업로드해두었다

회고

  • 위 온프레미스 k8s 네트워크 설정은 사실 단일 노드로 테스트되었고, 멀티노드 환경이 될 경우, externalTrafficPolicy=Cluster 설정을 사용해야한다. 그렇지 않다면 인그레스가 특정 노드에서만 작동하도록 노드를 고정해야한다. 하지만 현재 구조로는 라우터 포트포워딩을 통해 한 곳으로 트래픽을 보내야만하고, 이러면 Local 정책을 사용할 수밖에 없다. 아니면 모든곳에 컨트롤러를 띄워야한다. 추후 확장성을 고려한 설계가 어떠할지, 잘 고려해야겠다.
  • 현재 네트워크가 Public IP를 이미 자체 관리하고 있는 SOHO 라우터 기반 네트워크인데, MetalLB로 오버엔지니어링해버렸다. MetalLB는 온프레미스 노드가 Public IP를 통한 직접 트래픽을 받을 경우 필요한 컴포넌트이다. 나의 경우는 필요가 없었던 것이다. 기술체크 중 찾아본 케이스들이 나의 케이스와 맞지 않아 발생한 문제이다.
  • 추후 설계 수정을 통해 필요한 컴포넌트만 설치할 계획을 하고 있다. MetalLB를 제거 후 인그레스 컨트롤러를 NodePort 기반으로 배포하고, NodePort를 고정한 다음 해당 포트로 라우터 포트포워딩하면 된다. 그리고 해당 노드에서만 컨트롤러가 작동하도록 nodeSelector 등으로 제한해야한다.

About

Kubernetes를 온프레미스 인프라 아키텍처에 구축하면서 겪은 네트워크 트러블슈팅 회고

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published