ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • service에 대한 이야기
    k8s network 2021. 12. 27. 04:39

    해당 글은 ndks 최종과제를 위해 작성되었습니다. (가시다님 잘가세요... 또만나요... 또르르)

     

     

    저는 L2,L3,L4,L7 스위치들을 운영하는 네트워크 엔지니어입니다. k8s 네트워크 부분을 스터디 하면서, 서비스 부분을 이해하고 넘어가기가 쉽지가 않았습니다. 보면 볼수록 아리송 한 부분이 많았었는데요...

    그랬기에 한번 더 정리하고 넘어가는 차원에서 최종과제로 주제를 정했습니다.

    제가 이해한 대로 내용을 쭉 정리할 예정이며, 혹시 보시다가 틀린 점이 보이면 댓글로 알려주세요. 언제든 환영입니다.

     

     

     

     

     

     

     

    1. 서비스의 대략적인 논리 스택

     

     

    개인적으로 서비스를 학습할 때 논리적인 스택을 쌓아올리는 게 정리가 잘 안되었기에, 제가 이해한 만큼 한번 정리해 보겠습니다.

     

    서비스의 종류는 아래와 같습니다.

    - ClusterIP

    - NodePort

    - LoadBalancer

    위의 그림과 같이, 서비스는 특히나 하위 개념을 포함하는 형태로 되어있는데, clusterip를 이해해야 nodeport를 이해할 수 있고, nodeport를 알아야 loadbalancer를 알 수 있기 때문에 처음에는 정신이 없었습니다.

    몇 번 반복해서 보다보면 익숙해지며, 익숙해 진 개념 위에 다른 개념들이 탑처럼 쌓여 올라갑니다.

     

    더욱이 네트워크 엔지니어들은 이러한 부분들을 이해하기가 쉽지 않은데, 네트워크 엔지니어들은 패킷을 바로바로 포워딩해서 성능을 극대화 시키는 게 숙달되어 있는 사람들이기에, loadbalancer가 nodeport로 보내고, nodeport가 clusterip로 보내는 이러한 생각을 하기가 쉽지가 않았습니다. (저러면 성능이 나올까? 성능충 네트워크 엔지니어...)

     

     

     

    서비스는 LB 장비를 떠올리면 이해가 쉽습니다.

    레거시 인프라에서도 고가용성을 높이기 위해 서버를 여러 대 구성하고, 해당 서버들을 적절히 로드발랜싱 해 주기 위해 앞단에 LB 장비를 배치시킵니다. 

    서비스도 이와 비슷하게 생각해 보면, pod 여러 대를 하나의 interface로 묶어서 외부로 노출하여 고가용성을 보장하기 위해 사용한다고 생각하면 됩니다.

     

     

     

     

    + pod가 여러 개 일때, LB를 묶는다는 상황을 가정하고 쉽고 간단하게 풀어 써보겠습니다.

     

     

    1) ClusterIP 사용

    - ClusterIP를 만들면 새로운 VIP가 생성이 되며, 해당 VIP로 접근하면 여러 개의 pod로 분배

    - 하지만 컨테이너 내부에서만 clusterip로 접근이 가능 (외부에서는 접근이 안됨...)

    출처 : https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0

     

     

    2) NodePort 사용

    - clusterip는 외부에서 접근이 불가능하기에, 외부에서도 접근이 가능하게 port하나를 매핑해두는 역할

    - 예를들어 실제 pod에 떠 있는 건 80번 포트의 웹서버이지만, nodeport는 30000으로 오픈

    - node의 30000으로 접근하면 clusterip로 매칭시켜줌 (nodeport는 clusterip를 품고 있음)

    - 기본적으로는 클러스터의 모든 node에 전송해도 해당 pod로 패킷이 라우팅이 됨

    - pod가 있는 node만 port가 오픈되게 설정 가능 (노드 내 불필요한 라우팅 방지)

    출처 : https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0

     

    3) LoadBalancer

    - nodeport만 사용하기에는 몇가지 불편한 제약사항이 존재함

       - 포트당 한 서비스만 할당할 수 있음

       - 30000-32767 사이의 포트만 사용할 수 있음
       - node의 IP 주소가 바뀌면, 이를 반영해야 함

    - 외부 로드발랜서(혹은 metalLB)에서 LB 처리 후 nodeport로 라우팅

    - 이후 nodeport 처리 과정은 동일함

     

    출처 : https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0

     

     

    대략 service가 무엇인지, 그리고 그 서비스인 clusterip, nodeport, loadbalancer가 어떤 건지 적어보았습니다. 

    처음부터 디테일을 욕심내고 들어가기 보다, 전체적으로 어떠한 아이들이 있고, 이러한 아이들이 어떠한 역할을 하며 어떻게 구성되어 있는지 살펴보면 더 이해가 잘 될 거라 생각합니다.

     

     

    디테일을 들어가기에 앞서 알아두면 좋은 개념이 하나가 있는데요,

    LB는 DSR(Direct Server Return)을 제외하고는 LB가 보낸 패킷은 LB를 다시 거쳐서 돌아가야 합니다. DNAT을 해 주었기 때문인데요, 세션이 제대로 맺어지게 하기 위해서는 LB를 거쳐서 나가야 합니다.

    그런데, Inline 구조가 아닌 경우에는 나간 패킷이 다시 LB로 들어오지 않을 수도 있습니다(원암 구성일 때 특히 주의). 이럴 때 사용하는 기법이 SNAT 입니다. source ip를 LB의 ip로 nat 해서 패킷이 나가게 되면, 반드시 다시 LB로 돌아올 수 있기 때문에 자주 사용되는 기법입니다.

    이러한 개념들이 서비스에서도 녹아져 있습니다.

    DNAT되어 나간 패킷을 다시 복원 해 주어야 하기 때문에 패킷이 돌아와야 하는데요, 돌아오게 하기 위해 SNAT을 사용하게 됩니다.

     

     

     

     

     

     

    2. 서비스의 디테일

     

    이제부터 디테일을 따져 들어가 보도록 하겠습니다.

    아래 환경을 만들어 두고, 각 서비스에서 어떻게 처리를 하는지 살펴보도록 하겠습니다.

     

     

     

     

     

     

    1) ClusterIP

     

    clusterip의 동작을 살펴보기 위해 간단하게 팟과 서비스를 구성 해 보았습니다. 해당 구성을 기반으로 패킷이 어떤 형태로 전달되는 지 분석해 볼 예정입니다.

     

     

    앞서 clusterip는 클러스터 내에서만 사용이 가능하다고 하였습니다. master node에서 clusterip로 접근을 해 보도록 하겠습니다.

    접근이 잘 되며, webpod3을 갔다가 1로 가는 등 분배가 적절히 이루어 지는 것으로 볼 수 있습니다.

     

    test를 위한 net pod를 하나 만들어 두었었는데요, 해당 pod에서 테스트를 해보도록 하겠습니다.

     

    worker node 1번에서 clusterip로 tcpdump를 해 보면 패킷이 보이지 않고, pod ip로 dump를 했을 경우에 패킷이 캡쳐되기 시작 하였습니다.

    그렇다는 건, net pod에서 패킷을 전송할 때 clusterip를 도착지로 패킷이 전송된 것이 아니라, pod ip를 도착지 ip로 패킷이 설정되어서 전송되었을 것으로 유추할 수 있습니다.

    그래서 net pod에서 dump를 걸어 보았더니, clusterip를 destination ip로 해서 나간 걸 확인할 수 있었습니다.

    그러면 net pod가 위치해 있는 node에서 dump를 보면 어떻게 될지 궁금하여 dump를 보았습니다. 

    master node에서 보니 clusterip는 쏙 사라지고, endpoint ip (webpod) 로 패킷이 변환되어 전송되는 것을 확인할 수 있었습니다. 

    그렇다면 범인은 결국 master node(net pod가 존재하고 있는 node)가 되는 것으로 생각할 수 있습니다. 

     

     

     

    우선 nat가 어떻게 되는지 보기 위해서 iptables의 nat를 따라가 보기로 하였습니다.

    그래서 저는 master node의 iptables를 따라가 보았습니다. (참고로 cluster 내 모든 node들은 iptables rule이 동일하게 적용되고 있었습니다. )

     

    우선 PREROUTING부터 보았습니다.  2가지 항목이 match 되는걸 볼 수 있는데요, 하나씩 따라 가 보겠습니다.

    cali-PREROUTING은 뭐 특이사항이 보이진 않습니다. 다음으로 넘어 가 보겠습니다.

    제법 많은 것이 보이기 시작했는데요, 

    KUBE-MARK-MASQ는 출발지가 172.16/16이 아니고, 도착지가 clusterip:port(10.101.143.222:9000) 일 때 MASQ를 위해 MARK 찍어주는 것으로 보입니다. 

    net pod의 ip는 출발지에 포함되기에 MARK가 찍히지는 않았습니다. (출발지 ip 보존)

     

    매칭 된 항목인 KUBE-SVC-KBDEBIL6IU6WL7RF 을 살펴보면, 출발지가 any이고, 도착지가 clusterip:port(10.101.143.222:9000) 일 때 포함이 되는데요, 들어가 보도록 하겠습니다.

     

    들어가 보니 KUBE-SEP-XXX 형태로 3개의 항목이 보입니다. SEP는 Service Endpoint로 보여지구요, 지금 clusterip의 endpoint가 3개 있기 때문에 저 항목이 각각의 endpoint를 지칭하는 것으로 보여집니다.

     

    random 0.333, 0.555 를 보면, 확률적으로 하나의 항목이 선택되어 지는 것으로 보이는데요, 한번 더 들어가 보겠습니다.

     

    예상이 맞았습니다! endpoint로 가기 위한 매칭 포인트 였었고, 해당 룰에서 dnat이 이루어 지고 있었습니다.

    위의 MASQ MARK룰은 해당하는 endpoint의 pod에서 올라오는 패킷의 경우 MARK를 찍어주는 것으로 보입니다.

    만약 출발지가 endpoint의 pod였다면, hairpin nat를 시켜주기 위해 source nat이 필요하게 되는데, 여기에서 MASQ MARK를 찍어주는 것으로 생각됩니다.

     

    지금은 해당되지 않았기에 dnat 처리가 된 것을 볼 수 있었습니다.

     

     

    PREROUTING은 확인을 하였구요, POSTROUTING을 추가로 확인 해 보도록 하겠습니다.

     

     

    두개가 매칭된 것으로 보여지는데요, 하나씩 따라가 보면 아래와 같습니다.

     

     

    해당 룰은 특이사항 없었으며, 다음 룰을 확인해보겠습니다.

    네, 특이사항 없이 리턴되는것을 볼 수 있습니다!

     

    clusterip는 결국 iptables의 nat 기능을 사용하여 nat를 수행하는 것이였으며, 별도의 LB역할을 수행하는 서버나 pod가 존재하는 게 아닌것을 확인할 수 있었습니다.

     

     

    LB 장비와 비교해보면 아쉬운 부분이 많기는 합니다.

     

    우선 기능적으로 LB는 Hash, RR, LC 등 여러 Method 를 지원하는데 비해 clusterip는 iptables에 기반하여 확률적 LB를 지원하는 부분도 그렇구요, 스티키 세션같은 경우는 특히나... clusterip같은 경우 sessionAffinity를 설정하면 10800초 스티키가 적용이 되는데요,

    현업에서 사용하다보면 시간을 정밀하게 지정해야 될 경우가 종종 있습니다. 여러 요구 조건에 의해서(특히 was같은 녀석들) 600초,1200초,1800초 등 시간을 맞추어야 하는 경우가 생기는데, clusterip는 그렇게 정밀하게 할 수는 없었습니다.

    -> 내용 정정, time을 설정하는게 가능하네요!  현업에서도 시간을 정밀하게 설정하는 만큼, clusterip의 sessionAffinity 도 가능합니다.

     

    성능적으로는 iptables를 하드하게 쓰고 있다 보니, 이게 제 성능을 발휘할 수 있을까? 에 대한 생각이 들었습니다.

     

     

     

     

     

    2) NodePort

     

    nodeport를 살펴보기 위해 기존의 pod들은 재활용 하고, svc를 clusterip에서 nodeport로 다시 만들었습니다.

     

     

    nodeport는 clusterip를 포함하고 있습니다. 이전의 clusterip 테스트 때와 마찬가지로, clusterip:port로 내부 클러스터에서 접근이 가능합니다. 추가적으로, 외부에서도 내부로 접근이 가능하게 됩니다. 이는 node에 port를 오픈하면서 접속을 허용하게 되는데요.

    한번 사용 해 보도록 하겠습니다.

     

    net pod에서 cluster ip로 curl이 잘 되는 것을 확인할 수 있습니다.

    master node에서 any interface로 tcpdump 패킷캡쳐를 한 것인데,  도착지 ip로 clusterip가 올라오면 endpoint pod ip로 변환하여 나가는 것을 확인할 수 있습니다.

     

     

     

    이제 nodeport를 이용해서 접근을 해보겠습니다.

     

    worker node 1번인 192.168.10.101 의 nodeport인 30074로 curl을 보내 보았습니다. 하나의 특정 node에게만 curl을 지속적으로 보냈는데 세션이 잘 분산되면서 응답을 받아오고 있습니다.

     

    외부에서도 잘 되는지 확인하기 위해 pc에서 node로 curl을 보내보도록 하겠습니다.

     

    해당 스샷을 보면 몇 가지를 정리할 수 가 있습니다.

    우선 클러스터 외부 네트워크에 속해 있는 pc에서도 nodeport를 이용해 webpod 1,2,3으로 접근이 가능하다는 것을 확인할 수 있습니다.

    하지만 특이한 점은 node1로 계속 curl을 보내고 있는데, web pod 1,2,3 이 순차적으로 밸런싱 되면서 응답해 주고 있다는 것을 확인할 수 있습니다.

    추가로 특이한 점은 node1로 curl을 하는데 webpod1이 응답을 했을 경우 remoteaddr이 node1의 interface ip로 nat가 되어서 client의 ip를 알 수가 없었고, webpod1이 아닌 2,3이 응답을 했을 경우는 node1의 interface ip인 192.168.10.101로 nat되어서 역시나 remoteaddreess를 알 수 없었습니다.

     

    패킷이 node1에서 node2의 webpod2로 전달이 되었다면, 해당 패킷의 응답패킷은 다시 node1로 돌아온 뒤에 client로 돌아가야 합니다. webpod2에서 node1로 돌아오게 하기 위해서 source nat 을 사용하여 패킷을 전달한 것입니다.

     

     

    이젠 node1의 iptables를 한번 따라 가 보도록 하겠습니다.

    우선 PREROUTING 부터 보도록 하겠습니다.

    PREROUTING의 첫번째 항목인 cali-XXXXX는 특이사항이 없었고, 두번째 항목인 KUBE-SERVICES를 보도록 하겠습니다.

     

    clusterip와는 조금 다른 부분들이 보입니다.

    clusterip로 접근을 하지 않고 nodeport로 접근을 하였더니, 맨 아래의 KUBE-NODEPORTS 룰에 매칭되었습니다. 마지막 룰이며, 로컬에서 리슨하고 있는 포트의 패킷이라면 매칭되어 들어가는 것으로 확인됩니다.

    조금 더 들어가 보도록 하겠습니다.

     

    익숙한 MASQ MARK 룰이 먼저 보입니다. MASQ를 위해 MARK를 찍었다는 것을 확인할 수 있고요, 그 다음 룰인 KUBE-SVC-XXXXX 룰을 한번 보도록 하겠습니다. dpt가 30074(nodeport의 port)에 매칭된 걸 볼 수 있습니다.

     

     

    clusterip와 매우 비슷한 부분입니다. 결국 endpoint들로 분배되서 갈 수 있도록 dnat 처리가 되는것을 볼 수 있습니다. 

    description만 빼고는 비슷한 것 같네요!

     

     

    이어서 POSTROUTING까지 보도록 하겠습니다.

     

    첫번째 룰은 특이사항이 없었으며, 두번째 KUBE-POSTROUTING을 살펴보도록 하겠습니다.

     

    clusterip의 PORTROUTING과는 다른 부분이 발견되었습니다! clusterip는 MASQ MARK를 하지 않았었는데, nodeport는 MASQ를 MARK 하였었습니다. 그러자 POSTROUTING에서 차이점이 발견되는데요,

    snat되어 나가는 것을 확인할 수 있었습니다.

    추가로 random-fully 라고 적혀 있는 부분에 대해 한번 짚고 넘어가 보겠습니다.

    snat을 하게 되면 source port 까지 바꿀지 말지에 대한 결정이 필요한데요, client의 random port가 우연히 겹치게 되면 source port를 바꾸지 않게 되면 통신에 문제가 발생하게 됩니다. 이를 방지하고자 source port가 중복되지 않게 하는 것이라고 보면 될 것 같습니다.

    상용 LB에서도 snat을 적용하는데 있어서 해당 부분을 옵션으로 선택할 수가 있습니다.

     

     

     

     

     

     

     

     

     

    nodeport를 이용하여 외부에서 내부 pod들을 분배하여 접근할 수 있게 하는것을 보았습니다. iptables를 따라가면서 확인을 해 보니 실제로 현업에서 사용하고 있는 LB와는 많이 다른것을 확인할 수 있었습니다.

     

    상용 LB의 기능을 따라가기 위하여 LoadBalancer 타입과, 추가로 Ingress가 나오게 되었습니다.

    해당 부분은 추가로 포스팅을 할 예정입니다.

     

     

     

    해당 부분을 보면서, 이렇게 iptables를 하드하게 사용하는 부분은 결국 성능을 저하시키는 요소가 될 건데, 하드웨어 LB를 안쓰고 저렇게까지 써야할까? 라는 생각이 강하게 들었습니다. 어떻게 극복할 건지에 대한 궁금증이 추가로 생기기도 하고, 성능 최적화에 대해 스터디를 해 보고 싶다는 생각이 듭니다. 

     

    'k8s network' 카테고리의 다른 글

    calico에 대한 소개 (2)  (1) 2021.12.03
    calico에 대한 소개 (1)  (1) 2021.12.02
Designed by Tistory.