본문 바로가기
카테고리 없음

쿠버네티스 3장 발표 대본 정리

by 흰색남자 2022. 1. 22.

 

[2022_01_25]chap3_네트워킹_김종훈.pptx
2.25MB

안녕하세요. 쿠버네티스 3장 발표를 진행하게 된 김종훈 입니다.

순서는 도커 네트워킹과 쿠버네티스 네트워킹의 차이, 노드포트, 로드밸런서, 멀티포트, 클러스터ip/headless, endpoint, 인그레스, 멀티 테넌시 순으로 진행하겠습니다.

도커는 설치 시 docker0라는 브릿지가 생성이 됩니다. 이 브릿지를 통해 각 네트워크는 서로 통신하는 구조입니다.

도커는 기본적으로 앞의 그림에서 나타내듯, eth0라는 랜카드에 ip를 부여받게 됩니다. 이 eth0라는 랜카드와

docker0의 veth가 서로 1:1로 매핑되면서 통신하는 구조입니다.

도커0의 정보는 ifconfig를 통해 확인할 수 있습니다. 또한 eth0와 veth역시 확인이 가능한데,

eth0의 경우 컨테이너 안에 존재하다보니, 컨테이너안에 접속해서 확인이 가능합니다.

docker0의 자세한 정보는 docker network inspect bridge를 통해 확인이 가능합니다.

 

자 이제 쿠버네티스 네트워킹을 설명드리겠습니다. 일단 작은 부분부터 하나하나 들어가면서 설명하겠습니다.

쿠버네티스는 앞의 그림처럼 도커와는 다르게 pod수준에서 ip가 부여됩니다.

그림에 나타나듯이 하나이상의 컨테이너가 하나의 랜카드를 부여받은 것을 볼 수 있습니다.

그러면 이 pod안의 컨테이너들은 어떻게 통신할 수 있을까요?

바로 pause라는 컨테이너 덕분입니다. 이 pause컨테이너는 pod안의 인프라를 담당하는 핵심 컨테이너 입니다.

이 컨테이너를 통해 pod안의 컨테이너들이 서로 통신하고, 자원을 공유하고, localhost를 통해 통신이 가능합니다.

 

그러면 이제 한단계 더 큰 범위에서 네트워킹을 설명드리겠습니다.

자 앞에 그림은 도커와 쿠버네티스를 설치하고 바로 실행을하면 가장 최악의 경우를 나타내었습니다.

docker0의 ip가 같고, veth0의 주소가 같습니다. 그러면 이 문제르 어떻게 쿠버네티스는 해결할 수 있을까요?

 

쿠버네티스는 이 문제를 두가지 방법으로 해결하였습니다.

1. cni라는 컨테이너 네트워크 인터페이스를 설치해, cbr0라는 커스텀 브릿지의 subnet을 다르게 할당하여

아예 cbr0 밑의 파드들이 다른 대역의 ip를 할당받게 하는 방식입니다.

2. 두번째는 10.100.0.1의 게이트웨이,라우터에 외부에서 들어오는 패킷이 어떠한 경로로 라우팅할지 iptables를 정의합니다.

이 두가지 방법으로 쿠버네티스는 파드 간 ip할당 문제를 해결하였고, 이 두가지 방법을 일컬어 오버레이 네트워크라고 합니다.

 

그럼 이 오버레이 네트워크의 정의는 무엇일까요?

이 오버레이 네트워크의 정의는 물리 네트워크 위에 성립되는 가상의 네트워크입니다.

쉽게 말해, 자동차로 부산을 갈 때, 많은 신호등과 고속도로, 국도 등을 지나지만, 비행기를 타고 간다고 생각하면

이 많은 요소들을 무시하고 출발과 도착만을 고려한다는 의미입니다.

 

저번주에 설명을 이현병 박사님이 설명해주셨지만, 이번 파트가 네트워킹 파트인만큼 다시 한번 설명드리겠습니다.

자 일단 파드를 생성을 해야합니다.

1. 관리자가 kubectl 명령으로 파드를 생성합니다.

2. api서버가 이를 받고 etcd 기록을 합니다. etcd에 기록을 하는 이유는, 만약 쿠버네티스에 문제가 발생할 경우

이 etcd를 보면서 오류를 찾아 백업, 복구를 하기 위함입니다.

3. 이제 컨트롤러 매니저가 api서버를 보고 있으면서 파드를 생성합니다. 여기서 api서버는 파드를 생성하는지 감시만하고 아무 일도 하지 않습니다. 컨트롤러 매니저는 파드를 생성하고 api서버에도 바뀐 정보를 업데이트 합니다.

4. 이제 스케줄러 차례입니다. 스케줄러는 api서버를 보고 있다가, 밸런스에 맞게 각 워커 노드에 pod를 스케쥴합니다.

여기서도 api서버는 아무 일도 하지 않고 스케줄러가 일을 잘 하는지 감시만 합니다.

5. 이제 각 워커노드의 kubelet이 api서버를 보고 있다가  컨테이너 런타임에 파드를 동작하도록 지시합니다.

그러면 컨테이너 런타임이 컨테이너를 생성하면서 파드가 사용자랑 통신하도록 합니다.

 

여기서 가장 중요한 것이, 모든 것은 다른 구성요소에 관여를 하지 않고, 감시만 한다는 것 입니다.

이것을 보면 쿠버네티스의 철학인 마이크로서비스 아키텍처가 잘 들어납니다.

그리고 모든 것은 apiserver를 중심으로 한다는 것 입니다.

이제 다른 그림을 보면서 cni에 대해 설명드리겠습니다.

 

앞의 그림은 전의 그림과 동일하지만, cni라는 컨테이너 네트워크 인터페이스의 존재를 잘 들어냅니다.

쿠버네티스에서도 kubenet이라는 컨테이너 네트워크 인터페이스가 존재합니다. 하지만 이 kubenet에는

고급기능이 존재하지 않습니다. 고급 기능이라 하면, 크로스 노드 네트워크, 네트워크 정책 선언과 같은 기능을 의미합니다.

 

이러한 이유로 cni를 설정을 해 주어야 앞에서 설명한 오버레이 네트워크를 구성할 수 있습니다.

 

그러면 이 cni는 어떤 것 들이 있을까요??

1. 위브: 도커 컨테이너를 위한 오버레이 네트워크를 제공함.

2. 플란넬: CoreOs에서 만들어졌으며, etcd를 사용하는 오버레이 네트워크.

3. 캘리코: 리눅스의 내장 라우팅 기능을 사용하는 네트워킹 모델.

4. 커낼(Canal): 캘리코의 네트워크 정책과 플란넬의 오버레이를 단일 솔루션으로 합침.

5. Kube-router : 사용하기 쉬운 고성능 네트워킹을 제공하기 위해 특별히 제작된 네트워킹 솔루션.

이런 5개가 존재합니다.

 

그러면 이제 쿠버네티스의 균형잡힌 설계를 설명드리겠습니다.

자 가장 크게 호스트(node)에서 ip를 부여한다면, 컨테이너가 늘어 날 때 마다, 포트의 관리가 너무 복잡해 질 것입니다.

그렇다고 컨테이너 수준에서 ip를 부여한다면, 변화하는 네트워크에서 nat 프로토콜에 과부하가 걸릴 것 입니다.

이러한 이유를 바탕으로 쿠버네티스에서는 호스트와 컨테이너 중간 수준인 pod수준에서 ip를 부여함에 따라

균형잡힌 설계를 구현할 수 있습니다.

 

이제 kube-proxy 3가지 모드에 대해 말씀드리겠습니다.

1. 첫번째로 iptables 모드입니다.

iptables모드는 클라이언트로부터 오는 모든 요청을 iptables을 거쳐서 직접 pod로 전달됩니다. 여기서

kube-proxy는 iptables를 관리하는 역할 만 하고, 직접 클라이언트로부터 트래픽을 받지 않습니다.

만약 첫번째 파드로 가서 연결이 실패하면 요청은 그냥 실패로 끝나므로, readiness probe를 잘 설정해야 합니다.

 

2. 두번째 모드는 userspace모드입니다.

클라이언트로부터 오는 요청을 iptables를 거쳐서 kube-proxy가 요청을 받은 다음 그 서비스ip가 연결되어야 하는 적절한 pod로 연결해줍니다. 여기서 요청을 pod들에 나눠주는 방식은 round robin알고리즘을 사용합니다.

 

3. ipvs모드입니다. 

책에는 나와있지 않지만, kubernetes docs에는 나와있길래 소개해 드리겠습니다.

앞서 설명드렸던 iptables모드랑 그림이 매우 흡사합니다. 차이점은 이 모드를 사용하려면 IPVS 커널 모듈이 설치되어 있어야합니다. 이 IPVS는 커널에서 작동하고 데이터 구조를 해시테이블로 저장해서 가지고 있기 때문에 iptables모드보다 빠르고 좋은 성능을 낸다고 합니다.

 

이제 서비스 영역으로 넘어가겠습니다.

 첫번째로 노드포트입니다.

앞의 그림에 나와있듯, 사용자가 직접 노드의 ip:port 로 접속합니다. 그러면 이 서비스가 NodePort 타입으로 만들어져 있으니까 nodePort서비스로 연결되게 됩니다. 그러면 이 nodeport서비스가 기록되어 있는 pod를 찾아서 연결해주는 구조입니다.

일단 서비스가 존재하려면 pod가 존재해야합니다. 앞에 그림에서 볼 수 있듯이 파드를 선언하고

service의 selector가 pod에서 선언한 app을 연결하면서 들어온 포트에 따라 서비스로 연결해주는 구조입니다.

 

아까 직접적인 노드의 ip:port로 접근한다고 말씀드렸습니다.

실제로도 그렇게 동작하는 것을 볼 수 있습니다.

그러면 이러한 방법에는 어떠한 문제가 있을까요? ip를 직접적으로 노출한다는 점에서 DDOS공격의 일종인 스머프,스푸핑 등 다양한 보안 위험이 존재합니다. 이를 보완하려고 Ingress라는 서비스를 사용합니다.

 

다음은 로드밸런서입니다.

로드밸런서는 선언함에 따라 config맵이라는 컴포넌트에서 ip를 할당해서 사용합니다. 

앞에 노트포트에서는 직접적으로 노드의 ip를 노출하였지만 로드밸런서 타입에서는 그런 걱정이 없습니다.

로드밸런서로 선언한 서비스는 해당 ip로 들어가면 로드밸런서 서비스로 연결됩니다. 이 로드밸런서 서비스에서

해당 pod를 찾아서 연결해주는 구조입니다.

 

netfilter란 Rule-based 패킷 처리 엔진입니다. kernel space에 위치하며 모든 오고 가는 패킷의 생명주기를 관찰합니다. 그리고 규칙에 매칭되는 패킷을 발견하면 미리 정의된 action을 수행합니다. 

 

앞에 NodePort에서 본 구조와 매우 흡사한 구조를 가집니다.

로드밸런서로 노출한 정보를 보고 넘어가겠습니다.

k get po명령어를 검색하면 각 pod의 ip를 볼 수 있습니다.

그리고 loadbalancer의 자세한 정보를 보면, 각 endpoint에 pod의 ip가 연결 된 것을 볼 수 있습니다.

그리고 해당 ip로 접속하면 노트포트와 마찬가지로 서비스가 잘 동작하는 것을 볼 수 있습니다.

 

다음 커스텀 포트입니다. 

커스텀 포트는 한 서비스에 관리자가 마음대로 포트를 설정 할 수 있습니다. 제 생각으로는 앞에 노드포트와 로드밸런서 타입의 서비스와 별 차이가 없는 것 같습니다. 단지 포트만 여러개 열어 준 것으로 차이가 별로 없습니다.

이 또한 잘 동작하는 것을 볼 수 있습니다.

 

클러스터 ip입니다.

클러스터 ip는 실제로는 존재하지 않지만 kube-proxy에 의해 관리되어지는 ip주소입니다.

배포한 서비스가 사용하는 ip주소이기도 합니다.

 

이 클러스터 ip는 서비스 선언시 자동으로 할당합니다. 하지만 clusterIp를 None로 설정하면 headless가 됩니다.

그러면 이 클러스터 ip도 없는것을 대체 왜 사용할까요?

바로 이 headless는 내부통신용이기 때문입니다.

아직 배우지는 않았지만 statefulSet이라는 pod의 타입이 존재합니다.

이 타입은 다른 타입의 pod와는 무슨 차이일까요?

앞의 사진에서 볼 수 있듯이, 다른 파드에는 파드이름 끝에 hash값이 붙습니다.

하시만 flask-sts파드를 보면 그런 해시값이 없습니다.

다른 파드는 다시 선언하면 pod의 이름이 바뀌어서 도메인이 달라진다는 점입니다.

하지만 statepulSet을 선언하면 변하지 않습니다. 이를 바탕으로 내부 통신용으로 사용한다는 점이 특징입니다.

또한 도메인이 항상 같으므로, 다른 namespace에서 접속할 수 있습니다.

 

그러면 yaml파일을 보면서 이해해보겠습니다.

statepulSet의 특징으로는 servicename이 꼭 필요합니다. 그리고 앞에 말씀드렸다싶이 내부통신용임을 확인하기 위해

pod내부로 접속해서 nslookup명령을 실행해보면 통신이 가능하다는 점을 볼 수 있습니다.

 

다음은 ingress입니다. 

아까 노드포트에서 말씀드렸듯, 직접적인 ip보다 ingress만의 ip로 접속해서 각 url로 들어온 바탕으로 각 다른 서비스로

로드밸런싱하는 기능을 지원합니다.

ingress yaml파일을 보고 넘어가겠습니다.

파일을 보면 못보던 annotations, backend등 다양한 것이 보입니다. 일단 label은 사람이 알아보기 위한 것 입니다.

그리고 annotation은 컴퓨터가 알아보기 위한 것 입니다.

딥러닝 cnn 학습 이미지를 만들 때 annotation과 labeling을 하는 것과 비슷하다고 생각합니다.

 

backend 부분을 보면, service에서 선언했던 metadata의 name: 이름 을 지정해주는 것을 볼 수 있습니다.

ingress를 보면 ip주소가 보이고 각 주소별로 다른 backend를 지정해주는 것을 볼 수 있습니다.

 

endpoint도 지금 껏 많이 보았듯이 커스텀으로 지정할 수 있습니다. 각 파드를 endpoint로 지정하여

자신이 직접 경로를 지정해줍니다.

 

멀티 테넌시는 클러스터 수준에서 리소스의 격리를 의미합니다. 리눅스에서도 존재하는 개념이며,

클러스터 수준에서 네임스페이스를 지정하여 격리한다는 것을 의미합니다.

다른 네임스페이스는 기본적으로 자원을 간섭할 수 없으며, 사용자별로 리소스를 격리시켜 사용하기 위해 존재합니다.

이상으로 발표를 맡은 충북대학교 김종훈입니다.