Kubernetes

kubernetes 스터디 2주차

쿠버네티스 스터디 2주차에서 공부한 주제는 NETWORK 와 STORAGE 이다.

과거 온프렘 기반으로 쿠버네티스를 사용해서 , cloud에서는 쿠버네티스 네트워크가 어떻게 구성되어 있는지 궁금했는데 잘 알수 있는 시간이였다. 특히 아래 기술하겠지만 node 와 pod가 같은 대역의 IP를 사용함으로써, 효율적인 통신이 가능해졌다고 생각했다. 

 

Kubernetes network

쿠버네티스에서 사용되는 네트워크 통신중 아래 두 형태에 대해서 알아보자

1) 서로 결합된 컨테이너와 컨테이너 간 통신

2) pod와 pod간의 통신 (CNI 가 필요)

 

1) 서로 결합된 컨테이너와 컨테이너 간 통신

도커에서의 통신

  • Docker에서 같은 노드내의 컨테이너 통신은 docker0라는 가상 인터페이스를 통해 지원한다. 
  • 도커는 컨테이너가 생성되면 veth pair를 생성해 각각 컨테이너와 docker0에 attach한다. 
    각 컨테이너는 veth라는 가상 네트워크 인터페이스를 가지며 고유한 IP주소 값으로 통신할 수 있다.

쿠버네티스 POD내부에서 컨테이너간 통신

  • 하나의 veth0 에 동시에 여러 컨테이너가 할당되어 있다. (즉, 두 개의 컨테이너 모두 veth0라는 동일한 네트워크를 사용하는 것이다.
  • veth0 안의 컨테이너는 고유한 port 번호로 서로를 구분한다.
  • k8s pod가 실행되고 있는 워커 노드에서 docker ps를 입력하면 pause 라는 명령으로 실행된 컨테이너를 볼 수 있다. 이 특별한 컨테이너는 각 POD마다 존재하며 다른 컨테이너들에게 네트워크 인터페이스를 제공하는 역할만 담당한다.

 

2) pod와 pod간의 통신

  • kubernetes는 기본적으로 kubenet이라는 아주 기본적인 네트워크 플러그인을 제공하지만, 이 자체로는 크로스 노드 네트워킹이나 네트워크 정책 설정과 같은 고급 기능은 구현되어 있지 않다.
    => 따라서 k8s에서는 pod네트워킹 인터페이스로 cni스펙을 준수한 플러그인 사용을 권장한다.(kubeadm은 기본적으로 CNI 기반 네트워크 플러그인만 사용할 수 있도록 되있다)
  • POD의 특징 중 하나로 각 POD는 고유한 IP주소를 가진다. (위의 veth0) 따라서 각 POD는 kubenet 혹은 여러개의 워커노드 사이에 각각 다른 노드에 존재하는 pod가 서로 통신하려면 router를 경유하고 CNI로 구성된 네트워크 인터페이스를 통하여 고유한 IP주소로 통신한다
  • 같은노드내(같은 subnet을 공유)에서 pod간의 통신에는 쿠버네트워크 없이도 가능하지만 크로스 노드 네트워킹엔 쿠버네트워크가 필요하다.

 

CNI (Container Network Interface)

쿠버네티스에서 클러스터로 구성된 멀티 노드간의 통신을 위해 CNI 가 필요하다

 

CNI 란?

컨테이너 네트워크 구현을 위해서는 일련의 절차가 필요합니다. 도커 뿐 아니라 쿠버네티스 , rkt , mesos등 다양한 플랫폼에서도 네트워크 인터페이스 구성이 필요하다. 반복되는 CNI 개발작업을 덜기위해 통일된 표준을 만들었는데 이것이 바로 CNI (Container Network Interface) 다. (flannel, weave, cilium, calico... 모두 cni 표준을 준수하여 구현한 컨테이너 네트워크다)

 

CNI 의 역할

1) k8s의 virtual network (L2 , L3 , overlay) 구성

2) pod interface 생성 및 IP/subnet/routing table 설정

  - virtual interface pair (veth pair 또는 tap)을 만들어 pod와 virtual network device 연결

  - L2 or L3 or overlay 통신 등의 정책에 따라 pod routing table 설정

3) proxy ARP

 

 

AWS는 AWS VPC CNI를 제공하고 있는데 기존 CNI 와 가장큰 차이점은 파드가 사용하는 IP 대역이 노드 IP 대역과 같다는 것이다 ! 

아래는 EKS 에서 확인한 NODE IP와 POD IP 이다.

아래 보이는 바와 같이 NODE 와 POD가 사용중인 IP 대역이 10.80.X.X 대역으로 동일한 것을 확인할 수 있다.

NODE IP
POD IP

 

그렇다면 POD가 사용하는 IP대역이 NODE 와 동일할 때 얻을수 있는 이점이 무엇이 있을까 ?

=> 답은 바로 별도의 오버레이(Overlay) 통신 기술 없이, VPC Native 하게 파드간 직접 통신이 가능하다는 것입니다 

 

파드간 통신에 별도의 NAT 동작없이 통신이 가능한지 테스트를 해보겠습니다.

 

테스트를 위해 network 관련 커맨드가 설치된 netshoot 이라는 파드를 배포합니다.

# 테스트용 파드 netshoot-pod 생성
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: netshoot-pod
spec:
  replicas: 2
  selector:
    matchLabels:
      app: netshoot-pod
  template:
    metadata:
      labels:
        app: netshoot-pod
    spec:
      containers:
      - name: netshoot-pod
        image: nicolaka/netshoot
        command: ["tail"]
        args: ["-f", "/dev/null"]
      terminationGracePeriodSeconds: 0
EOF

생성된 파드의 IP를 각각 변수로 지정합니다.

PODNAME1=netshoot-pod-66899db78b-dw26z
PODNAME2=netshoot-pod-66899db78b-m779q
PODIP1=10.80.1.77
PODIP2=10.80.2.186

파드1 에서 파드2로 ping을 보냅니다.

마찬가지로 파드2에서 파드1로 ping을 보냅니다.

 

그리고 아래 명령어로 노드에서 tcpdump 명령어로 패킷을 덤프하면 NAT 변환없이 POD IP 로 노드간의 통신을 하는것을 확인할 수 있습니다. 

sudo tcpdump -i any -nn icmp

 

 

Kubernetes Storage

컨테이너 내의 디스크에 있는 파일은 임시적이다. 컨테이너가 크래시될 때 파일이 손실되는 문제가 발생한다. 이를 해결하기 위해 볼륨이 도입되었다.

 

Persistent Volumes
PV는 관리자가 프로비저닝하거나 스토리지 클래스를 사용해 동적으로 프로비저닝한 클러스터의 스토리지이다.

PV는 요청에 따라 rwo, rwx, rox 와 같은 accessmode를 제공한다.

 

Persistent Volume Claims

사용자는 PVC를 통해 원하는 사양의 PV를 binding 할 수 있다.

- persistentVolumeReclaimPolicy : Retain , Delete , Recycle

 

Storageclass
파드가 생성될 때 자동으로 볼륨을 마운트하여 파드에 연결하는 기능을 동적 프로비저닝 이라고 하는데, 사용자가 요청한 볼륨을 동적으로 생성해주는 것을 스토리지 클래스가 담당한다. 

 

 

AWS 는 기본적으로  EBS CSI DRIVER 를 통해 ebs 볼륨을 파드에 동적으로 바인딩할 수 있는 기능을 제공한다. 대신 ebs 볼륨은 다수의 파드가 공유해서 사용할 수 있는 볼륨이 아니다. 다수 파드가 공유할 볼륨을 생성하기 위해서는 efs 볼륨을 사용해야 한다.

 

 

이번 학습과정에서 kubestr 이라는 아주 유용한 storage 성능 측정 툴을 알게 되었다. 마침 이번에 새롭게 도입한 NAS storage가 있어서 성능 측정을 해봤다.

 

kubestr의 측정 과정은 아래 그림과 같다.

 

먼저 kubestr 툴을 다운받아 설치한다.

https://github.com/kastenhq/kubestr/releases/download/v0.4.37/kubestr_0.4.37_Linux_amd64.tar.gz
tar xvfz kubestr_0.4.37_Linux_amd64.tar.gz && mv kubestr /usr/local/bin/ && chmod +x /usr/local/bin/kubestr

테스트에 사용할 데이터셋을 다운받는다. (read, write)

curl -s -O https://raw.githubusercontent.com/wikibook/kubepractice/main/ch10/fio-read.fio
curl -s -O https://raw.githubusercontent.com/wikibook/kubepractice/main/ch10/fio-write.fio

테스트 실행

# 읽기 테스트
kubestr fio -f fio-read.fio -s local-path --size 10G

# 쓰기 테스트
kubestr fio -f fio-write.fio -s local-path --size 10G

측정결과

PVC created kubestr-fio-pvc-56q6s
Pod created kubestr-fio-pod-pfprh
Running FIO test (fio-read.fio) on StorageClass (mysc) with a PVC of Size (10G)
Elapsed time- 2m5.302319122s
FIO test results:

FIO version - fio-3.30
Global options - ioengine=libaio verify= direct=1 gtod_reduce=

JobName:
  blocksize= filesize= iodepth= rw=
read:
  IOPS=197756.734375 BW(KiB/s)=791026
  iops: min=115822 max=228608 avg=197804.796875
  bw(KiB/s): min=463288 max=914432 avg=791219.250000