Kind
그림 출처: https://kind.sigs.k8s.io/
- kubernetes in docker의 약자
- 로컬환경에서 테스트용도로 사용하며 docker 기반으로 Kubernetes를 설치함
- docker 컨테이너 안에서 docker를 사용해서 Kubernetes 실행에 필요한 각 컴포넌트들을 컨테이너로 실행
- 멀티 클러스터에 대한 테스트를 해보기 좋음
아래 명령으로 설치 및 실행
# Install Kind
brew install kind
kind --version
# Install kubectl
brew install kubernetes-cli
kubectl version --client=true
# Install Helm
brew install helm
helm version
# Install Wireshark : 캡처된 패킷 확인
brew install --cask wireshark
# 노드 정보 확인
kubectl get node -o wide
# 파드 정보 확인
kubectl get pod -A
kind 생성로 클러스터 생성 시 yaml 파일을 사용할 수도 있음
cat << EOT > kind-2node.yaml
# two node (one workers) cluster config
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
EOT
kind create cluster --config kind-2node.yaml --name myk8s
Kind로 워커 노드를 실행할 때 NodePort로 호스트의 특정 Port로 들어온 트래픽을 대상 Pod로 전달하기 위해 extraPortMapping을 사용함. 아래 yaml 파일에서 containerPort와 hostPort 정보를 추가해주면 Kind가 컨테이너 생성 시 해당 정보를 반영해서 생성함
# '컨트롤플레인, 워커 노드 1대' 클러스터 배포 : 파드에 접속하기 위한 포트 맵핑 설정
cat <<EOT> kind-2node.yaml
# two node (one workers) cluster config
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
extraPortMappings:
- containerPort: 31000
hostPort: 31000
listenAddress: "0.0.0.0" # Optional, defaults to "0.0.0.0"
protocol: tcp # Optional, defaults to tcp
- containerPort: 31001
hostPort: 31001
EOT
CLUSTERNAME=myk8s
kind create cluster --config kind-2node.yaml --name $CLUSTERNAME
- extraPortMappings은 kind 노드 컨테이너와 호스트 시스템 간의 포트 매핑을 정의하고, 클러스터 생성 시점에 적용되기 때문에 실행 중인 클러스터의 노드에 직접 반영할 수 있는 방법이 없음
- 클러스터를 제거하고 새로 생성해야함
- 미리 Port 여러개를 점유해놓는 것도 방법
Tip) kind로 Control Plane만 설치가 됐는데 왜 Pod를 생성할 수 있을까?
- Amazon EKS와 같이 관리형 Kubernetes 서비스를 사용할 때는 데이터플레인 영역에만 Pod를 실행할 수 있음
- 기본적으로 컨트롤 플레인의 노드들에는 사용자가 생성하는 Pod를 해당 노드에 배포되지 않도록 Taint 설정이 default로 되어 있음
- kind로 설치한 클러스터는 Taints 설정이 없기 때문에 컨트롤플레인 노드 조차 Pod가 배포할 대상이 될 수 있게됨
- 만약 클러스터에 워커 노드가 별도로 추가되면 Kind가 자동으로 컨트롤 플레인의 Taints에 다른 Pod가 실행되지 않도록 설정을 추가함
Docker in Docker
- Docker in Docker는 컨테이너 내부에 컨테이너가 실행되는 구조이며, 컨테이너 안에서 실행된 컨테이너 정보는 호스트의 docker ps 명령으로 확인할 수 없음
- 가상화된 프로세스 안에서 다시 가상화된 프로세스를 생성하는 구조
- Kind는 kubeadm을 사용해서 클러스터를 생성
- Kind로 생성된 docker 컨테이너 안에서 컨테이너 리스트를 볼 때는 docker 명령이 아닌 crictl 명령 사용
- crictl은 CRI(Container Runtime Interface) 표준을 준수하는 모든 컨테이너 런타임(ex: containerd, CRI-O 등)에서 사용 가능
- crictl은 쿠버네티스 클러스터 관리자가 노드 레벨에서 컨테이너를 직접 관리하고 문제를 해결할 때 유용한 도구
- Kind로 생성된 클러스터 내부의 노드는 모두 컨테이너로 되어 있음. 따라서 모든 노드가 Docker 네트워크의 IP Range에서 IP를 할당 받음
- 쿠버네티스 클러스터 안에서는 기본적으로 kindnet이라는 경량의 CNI를 사용함
- docker ps 명령을 실행해보면 호스트의 58321 포트로 트래픽이 컨트롤플레인 컨테이너의 6443 포트로 전달되고 있는 것을 확인할 수 있음
- 참고로 호스트의 58321 포트 번호는 Kind로 Kubernetes를 설치할 때마다 랜덤으로 바뀜
- 컨트롤 플레인의 6443 포트를 사용하는 컴포넌트는 kube-apiserver 임. 즉, kubectl 명령을 실행하면 config 파일에 적혀있는 API 서버로 트래픽을 보내는데 해당 트래픽은 Docker에 의해 DNAT 돼서 컨트롤 플레인에 있는 kube-apiserver로 전달됨
Tip) kube-apiserver, etcd, kube-scheduler 등 컨트롤 플레인을 구성하는 Pod는 누가 생성하는걸까?
- 클러스터에 join 한 노드가 실행될 때 수동으로 실행하지 않아도 특정 디렉토리(일반적으로 /etc/kubernetes/manifests)에 매니페스트 파일을 작성해놓으면 kubelet이 자동으로 Pod를 실행함
- 이를 Static Pod라고 하며, API 서버 없이 특정 노드의 kubelet 데몬에 의해 직접 관리되는 Pod를 의미
- kubelet은 해당 디렉토리를 주기적으로 스캔하여 변경사항을 감지하고 Pod를 생성, 수정, 삭제함
- 노드 레벨에서 항상 실행되어야 하는 중요한 시스템 컴포넌트를 배포하는 데 사용됨
- 단, Pod만 생성 가능하고 다른 리소스(Deployment, Service 등)는 생성할 수 없음
CRI(Container Runtime Interface)
- Kubernetes와 Kubelet, 컨테이너 런타임 사이의 통신을 위한 표준화된 API
- kubelet과 CRI와는 unix domain socket으로 통신함
- 쿠버네티스 초기에는 Docker를 컨테이너 런타임으로 사용했었고, Docker에 대한 의존성이 쿠버네티스 코드베이스에도 포함되어 있었음
- 커뮤니티에서 다양한 컨테이너 런타임 사용에 대한 요구사항이 증가하면서 확장성과 유연성 개선이 필요해졌고, 1.5버전부터 CRI가 도입되었음
- 컨테이너 런타임에 직접적인 의존성을 갖지 않고 표준화된 인터페이스를 추가함으로써 kubelet이 더이상 컨테이너 런타임 변경에 재빌드해야하는 의존성이 사라짐
- CRI 구현체로 containerd와 CRI-O와 같은 새로운 컨테이너 런타임이 개발되었고, Docker도 내부적으로 containerd를 사용하는 걸로 변경됨
- 쿠버네티스의 기본 런타임이었던 Docker 자체는 CRI 표준을 준수하지 않아서 쿠버네티스 내부에서는 Dockershim으로 호환성을 유지했었지만 의존성으로 인한 유지보수 문제로 인해 1.24부터는 Dockershim을 완전히 제거하고 containerd, CRI-O 등이 주요 컨테이너 런타임으로 자리 잡음
- Pod는 리소스 제약이 있는 격리된 환경의 애플리케이션 컨테이너 그룹으로 구성되며, CRI에서 이 환경을 PodSandbox라고 함
- PodSandbox의 목적은 Pod 내 컨테이너들을 위한 공유 네임스페이스와 리소스 제약을 제공하기 위함
- 이렇게 되면 컨테이너 간 격리를 유지하면서도 리소스 공유를 가능하게 할 수 있음
- kubelet이 Pod를 시작하기 전에 RuntimeService.RunPodSandbox를 호출해서 PodSandbox 환경을 생성하고, 이 과정에서 IP 할당과 같은 Pod를 위한 네트워크 설정도 이루어짐
- pause 컨테이너를 사용해서 바로 이 PodSandbox를 구현함
Pause 컨테이너
- pause 컨테이너가 Pod 내 모든 컨테이너들의 부모역할을 한다고 볼 수 있음
- 만약 Pod 내 컨테이너들의 프로세스 네임스페이스를 공유하는 설정을 한 경우 PID 1번으로 실행되고 애플리케이션 컨테이너가 자식 프로세스로 생성됨
- 그렇지 않으면 같은 레벨에 위치한 두 컨테이너 중 하나는 PID 1번이 아니게 될테니 좀비 프로세스가 생겨날 수 있음
- Pod가 실행되면 그 안에서는 애플리케이션 컨테이너와 함께 네임스페이스 공유를 위한 Pause 컨테이너가 생성되고, 호스트에서는 프로세스 리스트 중 containerd-shim 프로세스의 자식 프로세스로 확인할 수 있음
아래 명령을 실행하면 호스트와 다른 네임스페이스를 사용하는 리스트를 볼 수 있음
pstree -aclnpsS
아래와 같이 실제로 pause 컨테이너에 대해 호스트와 ipc, mnt, net, pid, uts 네임스페이스의 아이노드 값이 다른 것을 확인할 수 있음
추가로 애플리케이션의 네임스페이스 정보를 보면 아래와 같이 net, uts, ipc는 Pause 컨테이너의 네임스페이스를 공유하는 것을 확인할 수 있음
Flannel CNI
쿠버네티스 네트워크 모델의 4가지 요구사항
- 파드와 파드 간 통신 시 NAT 없이 통신이 가능해야함
- 노드의 에이전트(예. kubelet, 시스템 데몬)는 파드와 통신이 가능해야함
- 호스트 네트워크를 사용하는 파드는 NAT 없이 파드와 통신이 가능해야함
- 서비스 클러스터 IP 대역과 파드가 사용하는 IP 대역은 중복되지 않아야함
쿠버네티스 네트워크의 4가지 해결 과제
- 파드 내 컨테이너는 루프백을 통한 통신을 할 수 있어야함
- 파드 간 통신을 할 수 있어야함
- 클러스터 내부에서 서비스를 통한 통신을 할 수 있어야함
- 클러스터 외부에서 서비스를 통한 통신을 할 수 있어야함
- kubelet을 통해 파드가 신규로 생성될 때 네트워크 관련 설정 추가가 필요한데 CNI 플러그인이 하는 역할은 전달되는 설정 정의서를 보고 실제 파드가 통신하기 위한 네트워크 설정들을 실행하는 것
- CNI 플러그인은 IP 할당 관리를 해야하기 때문에 IPAM을 통해 파드 간 통신을 위한 라우팅 설정을 처리함
각 노드에 배치된 파드 간 통신을 위한 3가지 네트워크 환경
- 네트워크 오버레이 기술을 구현해주는 대표적인 방법인 VXLAN을 지원
- 물리적인 네트워크 환경 위에서 가상의 네트워크 환경을 만들어 줌
- 터널링 기법을 사용하며 파드의 패킷을 감싸서 노드를 빠져나가고 목적지 노드에 도착해서 해당 패킷에 감싸진 부분을 제거하여 목적지 파드에 전달하게 됨
- UDP 네트워크 오버레이 기법을 지원
- VXLAN을 지원하지 않는 오래된 리눅스 커널 버전 운영 시 사용
- 권장하지 않음
- host-gw 모드 지원
- 네트워크 오버레이 기법을 사용하지 않고 각 노드의 파드 네트워크 대역을 라우팅 테이블에 업데이트하여 직접 라우팅 수행
- 네트워크 오버레이를 사용하지 않기 때문에 빠르지만 모든 노드가 동일 네트워크 대역에 배포되어 있어야 함
- 실제 운영 환경에서는 사용하기 쉽지 않음
통신 흐름 이해
- 동일 노드에서 파드 간 통신을 할 때는 flannel.1까지 트래픽이 가지 않고 cni0 브리지 네트워크에서 통신이 수행됨
- 다른 노드에 있는 파드로 트래픽을 보낼 때는 flannel.1 인터페이스를 거쳐서 패킷을 한번 wrapping함
- wrapping 시 출발지, 목적지에 해당하는 노드의 IP가 추가됨
'Work > 개발 노트' 카테고리의 다른 글
[KANS] 4주차 - Service (ClusterIP, NodePort) (1) | 2024.09.28 |
---|---|
[KANS] 3주차 - Calico CNI & Mode 정리 (0) | 2024.09.21 |
[KANS] 1주차 - 컨테이너 격리 & 네트워크 및 보안 (2) | 2024.08.31 |
[Kubernetes] Overlay 파일시스템으로 이미지 중복 제거 (1) | 2024.08.30 |
[Kubernetes] Airflow on EKS의 DAG 파일 관리 (0) | 2023.11.26 |
댓글