본문 바로가기

VMware/구축(TAS)

[Tanzu] Cloud Foundry와 K8s 비교 (컨테이너)

최근들어 쿠버네티스와 SRE 영역에 대해서내가 부족한 점이 많다는것을 느꼈고 그중에서도 쿠버네티스 네트워크에 대해서 

전혀 정리되지 않은 상태라는것을 깨달았다. 그래서 Cloud Foundry와 쿠버네티스의 비교를 통해 쿠버네티스 네트워크부터 

Container Orchestration까지 어떤 차이가 있는지 알아보도록 하겠다.

 

최근에는 메가존 클라우드에서 Cloud Foundry와 Kubernetes 두가지 플랫폼을 주로 운영 및 유지보수 하고 있고,

생소하실것 같아서 Cloud Foundry가 무엇인지 간략하게 소개 해드리겠다.

 

아마 Netflix의 Spring Cloud는 들어보셨을수도 있는데, 현재 현대화된 어플리케이션을 사용하고 생명주기가 단축되고 있는 현 시장에서 많이 사용하고 있다.

VMware 기반의 Cloud Foundry는 Pivotal에서 만든 MSA 특화 플랫폼이다. 물론 쿠버네티스에 시장 점유율을 많이 뺏겼지만 

현재 외국에서는 그래도 꽤 큰 기업에서 많이 사용하고 있다.

 

쿠버네티스보다 조금 더 개발자 친화적인 플랫폼이라, build 부터 배포까지 정말 단조롭고 쉽게 개발되어 있다.

OCI 규격을 지키는 Garden 컨테이너를 사용하고 low level의 컨테이너 런타임은 runC로 구성되어 있기 때문에 k8s에서도 동작한다.

그리고 이를 관리하기 위해서 디에고라는 녀석이 Garden 컨테이너에 대한 생명주기를 관리하고 앱 컨테이너의 스케쥴링, self healing등을 지원한다.

 

네트워킹 부분도 일반적으로 Client - 물리 LB - Gorouter - Container을 통해 통신하며, Kubernetes 네트워킹과 같이 

같은 노드에서의 Pod와 Pod간의 통신은 Linux bridge 기술을 활용하며 다른 노드에서의 Pod와 Pod간의 통신은 노드의 인터페이스를 통해 통신한다. 노드는 실크라는 CNI를 통해서 라우팅 rule을 관리하며, 터널링 인터페이스를 통해 패킷을 인캡슐레이션 하여 

VXLAN 또는 IP IP 모드를 지원한다.

 

이 플랫폼 역시 MSA 아키텍처를 지원하기 위해 개발된 플랫폼으로 어플리케이션 생산성을 확보할수 있게 설계 되었고 

플랫폼은 Spring Cloud 에 대한 아키텍처를 적극적으로 받아들였다.

따라서 공장 자동화 어플리케이션을 2000개 넘는 어플리케이션으로 분리하여 그 관리를 Spring Cloud 기반의 API Gateway와 Config Server / 서비스 디스커버리 서버를 GUI로 관리 할수 있으며 서비스를 노출하는 DNS와 내부 internal DNS를 두어서 내외부 통신을 관리합니다. 이러한 환경에서 1년 가까이 무중단 서비스를 운영하였습니다.

 

쿠버네티스 쪽 유지보수와 구축경험도 조금 이야기 해보려고 합니다. 

제가 쿠버네티스를 처음 접한것은 2년정도 된것 같습니다. 고객사의 GPU 플랫폼을 운영을 하는것으로 시작했는데..

 

그때 당시 이미 Application Platform을 1년간 경험했기 때문에, 

쿠버네티스에서는 다른 방법으로 제공하고 있고 그렇게 비교하면서 공부하는게 재밌었다.

유지보수 하면서 인상깊은 경험을 꼽으려면 etcd 단편화에 대한 이야기를 하고 싶다.

 

# etcd 디스크 full이 클러스터 전체 장애로 확산되다.

S사 유지보수 시 가장 인상깊은 경험은 쿠버네티스 클러스터가 전체 서비스 장애로 확산된 경험입니다.
이때 상황은 인프라 모니터링 중 kube-api서버 1개가 2주 전부터 메모리 사용률이 굉장히 높아지고 있었고 
장애 발생 시점 3시간 전, kubectl 명령어가 동작하지 않고 kube-api-server가 죽어버렸습니다.
그런 상황에서 해당 VM을 재부팅 하고 docker / kubelet을 재기동 하였습니다.

# 상황 해결을 위한 로그 확인

도착해서 바로 확인 곳은 kube-api server의 노드에 저장되어 있는 로그들이였습니다. 그 이유는 
kubectl 명령어가 듣지 않았기 때문입니다.
그리고 더불어서  etcd, kubelet 등의 구성 요소가 정상적으로 동작하고 있는지 확인 했습니다.

# 로그 확인 중 실마리 발견

kube-api server의 로그와 syslog에 etcd disk full 이라는 로그가 지속적으로 기록되어 있는것을 확인
하였고 etcd 상태를 확인 했지만 정상적으로 동작 하고 있었습니다.
etcdctl을 설치하여 member list와 endpoint status를 확인 하였더니 디스크가 Full 차있는것을 확인

# revision 삭제 -> defragment 진행

etcd는 db에 데이터를 저장할때 key value값뿐만 아니라 변경사항의 이력사항(history)까지 
revision과 함께 저장을 해놓기 때문에 revision에 대해 관리가 없이 운영 시 호스트 OS의 디스크 공간 부족으로 
etcd 프로세스가 언젠가 중단될 가능성이 있다.
불필요한 revision의 과도한 리소스 사용을 피하고자 etcd는 compaction 기능을 제공합니다.
compaction을 통해 Revision을 삭제로 인해 발생한 fragment를 정리 해주어야 디스크 공간이 확보되기 때문에
크론잡(CronJob)을 개발하여 defragmentation을 주기적으로 수행해 주거나, etcd_quota_backend_bytes 옵션을 넉넉하게 부여하여
(max 8G) etcd 클러스터의 가용성에 문제가 생기는 일이 없도록 했습니다.

 

그외에도 네트워킹 트래킹에 대한 트러블슈팅도 다수 경험 했습니다.

이때는 pod의 통신과 CNI에 대해서 자세히 공부하게 되었습니다.

 

통상적으로 잘 되던 pod이 안되는 경우 네트워킹 쪽 진단이 필요한 경우가 자주 있었습니다.

우선 pod 네트워킹은 bridge 네트워크를 알아야 합니다.

같은 노드에서의 pod끼리의 통신은 리눅스의 bridge 네트워크를 이용합니다.



veth라는 인터페이스는 리눅스의 가상인터페이스입니다. 노드의 네트워크 안에 또 가상화된 네트워크를 만드는것이죠. 

이 veth 인터페이스는 node의 네트워크 인터페이스를 조회하면 나오지 않습니다.



다만 calixxx 라는 IP는 없지만 mac주소는 가지고 있는 녀석과 tunl0라는 터널링 인터페이스만 존재하죠. 오?! 

통신할 인터페이스는 있지만 이녀석은 routing table과 ARP table이 없습니다. 하지만 calixxx는 MAC주소를 알고 있으니 

뭔가 잘 조합하면 통신할수 있을것 같아 보입니다.



pod에 접속해서 routing table을 보면 default gateway로 되어 있는 ip가 있을겁니다. pod의 인터페이스는 이쪽으로 밖에 트래픽을 보낼수밖에 없는거죠. 하지만 특이합니다. default gateway의 IP 주소를 보면 node의 subnet도 아니고, pod의 subnet도 아닙니다.



이 default gateway의 IP는 Proxy ARP 기능을 해주는 device입니다.

커널 space에 존재하는 device입니다.

Calico의 default ip는 169.254.1.1 입니다.



ARP는 MAC주소를 IP로 변경해주고 IP를 MAC주소로 변경해주는 프로토콜입니다. 

본인의 MAC주소를 대신 전달해주어 ARP 요청자로 하여금 자신에게 패킷이 전달되도록 응답합니다.



Proxy로 전달된 트래픽은 Proxy에 의해 실제 목적지로 전달됩니다. 이때 사용되는 인터페이스가 tunnel입니다.

이렇게 프록싱을 목적으로 ARP 요청에 대해 자신의 MAC 주소를 대신 응답하는 프로세스를 퍼블리싱(publishing)이라고도 부릅니다

 

  • 각 노드의 routing table을 살펴보겠습니다.

 

그렇다면? CNI가 없는 상태에서는 어떨까? 

다른 노드에 존재하는 pod와는 통신이 안될것이다.

 

ubuntu@ip-10-0-0-192:~$ kubectl get pods -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP           NODE                                       NOMINATED NODE   READINESS GATES
pod1   1/1     Running   0          18s   10.0.3.170   ip-10-0-3-10.us-east-2.compute.internal    <none>           <none>
pod2   1/1     Running   0          18s   10.0.1.86    ip-10-0-1-138.us-east-2.compute.internal   <none>           <none>
pod3   1/1     Running   0          17s   10.0.3.62    ip-10-0-3-10.us-east-2.compute.internal    <none>           <none>
ubuntu@ip-10-0-0-192:~$ kubectl exec -it pod1 -- /bin/sh

~ # ping 10.0.1.86 ( 다른 노드에 있는 pod는 통신이 안된다.)
PING 10.0.1.86 (10.0.1.86) 56(84) bytes of data.
^C
--- 10.0.1.86 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3056ms

~ # ping 10.0.3.62 ( 같은 노드에 있는 pod는 통신이 된다.)
PING 10.0.3.62 (10.0.3.62) 56(84) bytes of data.
64 bytes from 10.0.3.62: icmp_seq=1 ttl=63 time=0.082 ms
64 bytes from 10.0.3.62: icmp_seq=2 ttl=63 time=0.054 ms
64 bytes from 10.0.3.62: icmp_seq=3 ttl=63 time=0.056 ms

 

K8s에서는 pod간의 통신을 확장하는 규약을 CNI(Container Network Interface)로 정의합니다.

CNI는 다양한 방식으로 k8s의 네트워크 환경을 구성하며, 

네트워크 관련 제조사나 커뮤니티별로 다양한 플러그인을 제공하고 있기 때문에,

사용자는 K8s의 CNI 관련 페이지에서 각자의 환경에 맞는 네트워크를 선택할 수 있습니다.

 

일반적인 K8s 네트워크에서는 Node의 IP대역과 pod의 IP대역을 서로 다르게 나누어 구성합니다.

Node 내부에서 pod간의 통신이 일어나는 경우, Node 내부의 bridge를 통해서 바로 통신하지만,

Node를 넘어선 pod간의 통신은, Overlay 네트워크를 통해서 최종 목적지 pod를 찾아가는 구조입니다.

 

이 경우 Pod 간의 통신을 위해 VxLAN, IPIP, GRE 등 다양한 Overlay 네트워크의 활용이 필요합니다.

출발지 node에서 패킷에 Overlay 헤더를 덧붙히고, 도착지 Node에서 헤더를 해제하는 두번의 capsulation 과정을 거치며,

이를 위해 Node의 컴퓨팅 리소스를 사용하게 되고, 결과적으로 해당 구간의 네트워크 Latency를 증가시키게 됩니다.

또한, Overlay 구간에서는 패킷의 세부사항을 쉽게 확인할 수 없으며, Overlay 네트워크와 Underlay 네트워크의 구성이 달라 관리의 복잡성이 증가합니다.

 

다음 시간에는 EKS의 컨테이너간 통신에 대해서 비교해보도록 하겠다.