WellSpring

2주차 : K8S Flannel CNI & PAUSE 본문

KANS3 - k8s Advanced Networking Study

2주차 : K8S Flannel CNI & PAUSE

daniel00324 2024. 9. 2. 21:55

※ 본 게재 글은 gasida님의 KANS(Kubernetes Advanced Networking Study) 강의내용과 실습예제 및 kubernetes 공식 가이드 문서, "쿠버네티스입문" 서적 등을 참고하여 작성하였습니다. 

 

 

2주차 주제를 본격적으로 다루기에 앞서, k8s에 대해 간단히 짚어보고 넘어가겠습니다.

 

1. Kubernetes 란?

 - 배의 조타수란 그리스 단어에서 유래했으며, 2014년 구글에서 사용하던 컨테이너 오케스트레이션 시스템 인

   'borg' 를 오픈 소스로 공개한 것 ( 'k'와 's' 사이에 글자 갯수가 8개이므로 'k8s' 로 줄여서 표기하기도 함 )

  •  기능적으로는,컨테이너화된 애플리케이션의 자동 디플로이, 스케일링 등을 제공하는 관리시스템 이고,
  •  개념적으로는, 컨테이너를 쉽고 빠르게 배포/확장하고 관리를 자동화해주는 오픈소스 플랫폼 이다.

 

1-1. docker VS Kubernetes - Link1, Link2

 - docker와 kubernetes는 모두 컨테이너 서비스라는 공통분모를 가지고 있습니다.


[ docker ]

S/W를 컨테이너라는 표준화 된 유닛으로 패키징하는 기술을 지칭함

 - 컨테이너는 소프트웨어 실행을 위한 모든 것을 포함함 ( 컨테이너 = 라이브러리+시스템 도구+코드 etc )

 

[ kubernetes ]

컨테이너 시스템을 확장하여 대규모로 스케쥴링 및 예약할 수 있는 Orchestration Tool


[ More ... ]

더보기

[ Docker 와 Kubernetes 의 개념도 ]

[ 관리대상 및 범위를 비교해 보라!! ]

 

[ K8s vs. Docker ]

 

1-2. Kubernetes Components : master 와 node로 구성 - link

 

[ More ... ]

더보기

[  kubectl 명령어 수행 경로 ]

[ https://blog.naver.com/love_tolty/222167051615 ]

 

Control Plane(마스터 노드) 핵심 컴포넌트 : 마스터는 단일 서버 혹은 고가용성을 위한 클러스터 마스터로 구축

  • kube-apiserver : 마스터로 전달되는 모든 요청을 받아 드리는 API 서버 - (심화) 링크1 링크2 링크3
  • etcd : 클러스터내 모든 메타 정보를 저장하는 서비스
  • kube-scheduler : 사용자의 요청에 따라 적절하게 컨테이너를 워커 노드에 배치하는 스케줄러
  • kube-controller-manager : 현재 상태와 바라는 상태를 지속적으로 확인하며 특정 이벤트에 따라 특정 동작을 수행하는 컨트롤러 - 링크
  • cloud-controller-manager : 클라우드 플랫폼(AWS, GCP, Azure 등)에 특화된 리소스를 제어하는 클라우드 컨트롤러 - 링크

워커노드 

  • kubelet : 마스터의 명령에 따라 컨테이너의 라이프 사이클을 관리하는 노드 관리자
  • kube-proxy : 컨테이너의 네트워킹을 책임지는 프록시, 네트워크 규칙을 유지 관리
  • Container Runtime : 실제 컨테이너를 실행하는 컨테이너 실행 환경 ( containerd & CRI-O )
https://semaphoreci.com/blog/kubernetes-vs-docker

 ADD-ON 

  • CNI : Container Network Interface 는 k8s 네트워크 환경을 구성해준다 - link
              다양한 플러그인 제공함
  • DNS : 쿠버네티스 서비스를 위해 DNS 레코드를 제공해주며, Service Discovery 기능을 제공을 한다.
               대표적으로 CoreDNS 가 있다

1-3. 쿠버네티스 핵심개념

 1) 애완동물 vs 가축 의 차이?!

더보기

쿠버네티스는 서버를 애완동물(Pet) 보다는 가축(cattle)에 가깝다고 비유합니다.

애완동물은 세심한 관리가 필요하고 가축은 떼로 방목하여 키웁니다.

애완동물은 개체마다 정해진 이름이 있고 배고프지 말라고 끼니를 챙겨주며, 아프지 말라고 예방 접종을 합니다.

반면, 가축은 개별적으로 정해진 이름 없이 무리로 관리합니다. 때로 무리에서 낙오된 개체는 죽기도 합니다.

애완동물과는 다르게 가축 한두 마리 죽는 것에 크게 슬퍼하지 않습니다. 워낙 개체수가 많으니까요.

 

[그림 출처:책] 핵심만 콕 쿠버네티스

 

쿠버네티스도 서버를 유사한 관점으로 바라봅니다.

첫째, 서버마다 특별한 이름을 부여하지 않음

  •   예를 들어, 특정 서버 역할을 빌드 서버, 웹 서버, 모니터링 서버 등으로 구분하지 않음
  •   모든 서버는 워커노드로 동작함

둘째, 가축과 마찬가지로 한두 개의 서버(워커노드)가 망가져도 문제 없습니다.

  • 쿠버네티스 안의 모든 서버들은 마스터와 워커로만 구분
  • 워커 별 특별한 역할을 맡기지 않기 때문에 - 마스터가 문제 없는 이상 - 다른 워커에게
    특정 워커 수행하던 업무를 맡겨도 문제없음
  • 컨테이너라는 고립화 기술로 이식성을 높였기에 가능한 이야기임

 2) 바라는 상태(Desired State)

   - 컨트롤러 (특정 리소스를 지속적으로 바라보며 리소스의 생명주기에 따라 미리 정해진 작업을 수행함) 를

     통해 자원을 원하는 상태로 유지함

더보기

에어콘을 사용할 때, 현재 상태가 존재하고(=현재 온도), 사용자가 바라는 상태(=희망 온도)가 있습니다.

에어컨 시스템이 사용자의 희망온도에 따라 현재 상태를 변경시키기 위해 에어컨 시스템을 작동시키듯,

쿠버네티스는 사용자의 요청에 따라 현재 상태가 바라는 상태와 동일해지도록 사전이 미리 정의된 특정 작업을 수행합니다.

쿠버네티스에서 바라는 상태사용자가 생각하는 최종 애플리케이션 배포 상태를 말합니다.

사용자는 자신이 원하는 애플리케이션 배포 상태를 쿠버네티스에 알려주면 쿠버네티스가 자동으로 현재 상태를 바라는 상태로 변경합니다.

바라는 상태가 가지는 것의 장점으로는 쿠버네티스가 장애가 발생하여 애플리케이션이 죽더라도,

바라는 상태를 알기 때문에 손쉽게 원래의 바라는 상태로 배포 상태를 되살릴 수가 있습니다(=자가 치유)

 

[그림 출처:책] 핵심만 콕 쿠버네티스

 3) 네임스페이스(Namespace)

  - docker 에서 말하는 namespace와는 다른개념이며, 클러스터를 논리적으로 분리하는 개념임

더보기

네임스페이스 마다 서로 다른 권한 설정을 할 수 있으며, 네트워크 정책 등을 설정할 수 있다.

쿠버네티스의 모든 리소스를 크게 두 가지로 구분하자면 네임스페이스 레벨 리소스클러스터 레벨 리소스

구분할 수 있다.

[그림 출처:책] 핵심만 콕 쿠버네티스

 

. 네임스페이스 리소스는 특정 네임스페이스 안에 속하여 존재함

. Pod, Deployment, Service와 같이 대부분의 쿠버네티스 리소스가 네임스페이스 안에 포함됨

. 반면, Node, PersistentVolume, StorageClass 와 같이 네임스페이스 영역에 상관없이 클러스터 레벨에서 존재하는 리소스도 있음

 

 4) 쿠버네티스 리소스(Resource)

  - 쿠버네티스는 모든 것이 리소스(Resource)로 표현된다.

더보기

- 다양한 리소스가 존재하며, 각자의 역할이 있다. (예, Pod, ReplicaSet, Deployment 등)

- 모든 리소스는 오브젝트로 표현될 수 있다. (예, pods, Replica Set, Service Account, Node etc)

- 쿠버네티스의 가장 기본적인 리소스 = Pod ( k8s의 최소 실행단위 )

- 쿠버네티스는 '프로세스 (컨테이너)를 실행한다'는 의미는 'Pod 리소스를 생성하는 것과 같다'고 볼 수 있습니다.

 

# K8s 사용할 수 있는 오브젝트 확인
root@manager:~# kubectl api-resources
NAME                              SHORTNAMES   APIVERSION                             NAMESPACED   KIND
bindings                                       v1                                     true         Binding
componentstatuses                 cs           v1                                     false        ComponentStatus
configmaps                        cm           v1                                     true         ConfigMap
endpoints                         ep           v1                                     true         Endpoints
events                            ev           v1                                     true         Event
limitranges                       limits       v1                                     true         LimitRange
namespaces                        ns           v1                                     false        Namespace
nodes                             no           v1                                     false        Node
persistentvolumeclaims            pvc          v1                                     true         PersistentVolumeClaim
persistentvolumes                 pv           v1                                     false        PersistentVolume
pods                              po           v1                                     true         Pod
...

# 특정 오브젝트 설명
root@manager:~# kubectl explain pod
KIND:     Pod
VERSION:  v1

DESCRIPTION:
     Pod is a collection of containers that can run on a host. This resource is
     created by clients and scheduled onto hosts.

FIELDS:
   apiVersion	<string>
     APIVersion defines the versioned schema of this representation of an
     object. Servers should convert recognized schemas to the latest internal
     value, and may reject unrecognized values. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
...

# 특정 오브젝트의 하위 필드의 정보 확인
root@k8s-m:~# kubectl explain pod.spec | more
KIND:     Pod
VERSION:  v1

RESOURCE: spec <Object>

DESCRIPTION:
     Specification of the desired behavior of the pod. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
...

# 특정 오브젝트의 모든 하위 필드 정보 확인
root@k8s-m:~# kubectl explain pod --recursive | more
KIND:     Pod
VERSION:  v1

DESCRIPTION:
     Pod is a collection of containers that can run on a host. This resource is
     created by clients and scheduled onto hosts.
...

 

 5) 선언형 커멘드(Declarative Command)

  - 컨테이너가 어떤 상태이기를 원하는지만 쿠버네티스에 설정하면 지속해서 컨테이너의 상태를 확인하고,

  - 원하는 상태가 아니면 지속적으로 그에 맞게 맞춘다는 개념

더보기

> 선언형 커맨드란 ? 

 - 사용자가 직접 시스템의 상태를 바꾸지 않고 사용자가 바라는 상태를 선언적으로 기술하여 명령을 내리는 방법
    (예, HTML - 어떻게 명령을 수행해야 할지는 없고, 무엇을 해야 하는지에 대한 정의만 있다. )

 - 명령형(Imperative) 커맨드는 이와 반대의미, 일반적 명령형식 (예, SQL 쿼리 - 어떻게 케이블의 데이터를 질의할 것인가?)

 

> 쿠버네티스는 'YAML' 형식을 통해 선언형 명령을 내린다.

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: nginx
    image: nginx:latest

이러한 YAML 형식을  'YAML 정의서(YAML description)' 라고 부른다.

사용자는 쿠버네티스에 어떤 명령을 전달하거나 옵션값을 수정할 때 YAML 정의서의 YAML property를 추가하거나 수정합니다.

사용자는 각 리소스마다의 모든 설정값을 외울 필요가 없습니다. 최소한의 필수값을 채워서 리소스를 생성하면, 나머지는 쿠버네티스가 기본값으로 리소스를 생성합니다.

 

참고) YAML (YAML Ain't Markup Language) - link

 


2. Kind 소개 및 설치

> 소개 : ‘도커 IN 도커 docker in docker’로 쿠버네티스 클러스터 환경을 구성 - Link

o kind or kubernetes in docker is a suite of tooling for local Kubernetes “clusters” where each “node” is a Docker container

o kind is targeted at testing Kubernetes , kind supports multi-node (including HA) clusters

o kind uses kubeadm to configure cluster nodes.

[ 그림 출처: https://kind.sigs.k8s.io/ ]

 

. KIND는 별도의 host VM 없이도 docker 컨테이너를 k8s node로 사용하여 k8s 클러스터를 구성할 수 있도록 하는 도구

. Only Test 목적, Quick 하게 자원 구성 가능

 

[ 실습 - Windows 사용자 기준 ]

 > 환경준비 ( VirtualBox + Vagrant )

1. VirtualBox 설치 : 6.x 버전 이상 설치(현재 최신 버전 7.0.20) - https://www.virtualbox.org/wiki/Downloads

더보기
D:\Vagrant-Lab\kind> "C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" --version
7.0.20r163906

 

2. Vagrant 설치 : 윈도우용 설치 - https://developer.hashicorp.com/vagrant/install?product_intent=vagrant

더보기
D:\Vagrant-Lab\kind>vagrant version
Installed Version: 2.4.1
Latest Version: 2.4.1

 3. Vagrant 로 ubuntu(kind 등 실습 툴 설치) 가상 머신 배포 : 윈도우 터미널창 신규 열기(관리자 권한으로 열기)

  - vagrant 배포 시 여러 파일들이 생성되므로 관리하기 편하게 (아무) 폴더 생성 후 해당 폴더에서 아래 명령어를 실행하자

 > (참고) Vagrantfile : ubuntu 22.04 , vCPU 4 , Mem 8 GiB ⇒ vCPU는 8로 수정 사용 권장, 단 호스트 PC에 PC Spec 고려

더보기
# Base Image : https://portal.cloud.hashicorp.com/vagrant/discover?query=ubuntu%2Fjammy64

Vagrant.configure("2") do |config|
    config.vm.box = "ubuntu/jammy64"
    config.vm.box_version = "20240823.0.1"
    config.vm.hostname = "kind"
    config.vm.synced_folder "./", "/vagrant", disabled: true
    config.vm.provision "shell", path: "init_cfg.sh"
    config.vm.provider "virtualbox" do |vb1|
      vb1.customize ["modifyvm", :id, "--nested-hw-virt", "on"]
      vb1.memory = 8192
      vb1.cpus = 4
      vb1.name = "kind"
      vb1.linked_clone = true
    end
    config.vm.network "private_network", ip: "192.168.50.10"
    config.vm.network "forwarded_port", guest: 22, host: 60000, auto_correct: true, id: "ssh"
  end

> (참고) init_cfg.sh : kind, kubectl 등 툴 설치

더보기
#!/usr/bin/env bash

echo "[TASK 0] Setting SSH with Root"
printf "qwe123\nqwe123\n" | passwd >/dev/null 2>&1
sed -i "s/^#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config
sed -i "s/^PasswordAuthentication no/PasswordAuthentication yes/g" /etc/ssh/sshd_config.d/60-cloudimg-settings.conf
systemctl restart sshd  >/dev/null 2>&1

echo "[TASK 1] Profile & Bashrc"
echo 'alias vi=vim' >> /etc/profile
echo "sudo su -" >> .bashrc
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

echo "[TASK 2] Disable AppArmor"
systemctl stop ufw && systemctl disable ufw >/dev/null 2>&1
systemctl stop apparmor && systemctl disable apparmor >/dev/null 2>&1

echo "[TASK 3] Install Packages"
apt update -qq >/dev/null 2>&1
apt-get install bridge-utils net-tools jq tree unzip kubecolor -y -qq >/dev/null 2>&1

echo "[TASK 4] Install Kind"
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.24.0/kind-linux-amd64 >/dev/null 2>&1
chmod +x ./kind
mv ./kind /usr/bin

echo "[TASK 5] Install Docker Engine"
curl -fsSL https://get.docker.com | sh >/dev/null 2>&1

echo "[TASK 6] Install kubectl"
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" >/dev/null 2>&1
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

echo "[TASK 7] Install Helm"
curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash >/dev/null 2>&1

echo "[TASK 8] Source the completion"
source <(kubectl completion bash)
echo 'source <(kubectl completion bash)' >> /etc/profile

echo "[TASK 9] Alias kubectl to k"
echo 'alias k=kubectl' >> /etc/profile
echo 'complete -F __start_kubectl k' >> /etc/profile
echo 'alias kubectl=kubecolor' >> /etc/profile
echo 'compdef kubecolor=kubectl' >> /etc/profile

echo "[TASK 10] Install Kubectx & Kubens"
git clone https://github.com/ahmetb/kubectx /opt/kubectx >/dev/null 2>&1
ln -s /opt/kubectx/kubens /usr/local/bin/kubens
ln -s /opt/kubectx/kubectx /usr/local/bin/kubectx

echo "[TASK 11] Install Kubeps & Setting PS1"
git clone https://github.com/jonmosco/kube-ps1.git /root/kube-ps1 >/dev/null 2>&1
cat <<"EOT" >> ~/.bash_profile
source /root/kube-ps1/kube-ps1.sh
KUBE_PS1_SYMBOL_ENABLE=true
function get_cluster_short() {
  echo "$1" | cut -d . -f1
}
KUBE_PS1_CLUSTER_FUNCTION=get_cluster_short
KUBE_PS1_SUFFIX=') '
PS1='$(kube_ps1)'$PS1
EOT

echo "[TASK 12] To increase Resource limits"
# cat /proc/sys/fs/inotify/max_user_watches >> 8192
# cat /proc/sys/fs/inotify/max_user_instances >> 128
sysctl fs.inotify.max_user_watches=524288 >/dev/null 2>&1
sysctl fs.inotify.max_user_instances=512 >/dev/null 2>&1
echo 'fs.inotify.max_user_watches=524288' > /etc/sysctl.d/99-kind.conf
echo 'fs.inotify.max_user_instances=512'  > /etc/sysctl.d/99-kind.conf
sysctl -p >/dev/null 2>&1
sysctl --system >/dev/null 2>&1

 

> (참고) virtualbox Network 구성정보

더보기

 

☞  vagrant ssh 접속 시 기본적으로 호스트에 127.0.0.1(2222)를 목적지로 접속 → 이후 포트포워딩(SNAT + DNAT)을 통해서 내부에 VM로 SSH 연결됨

☞  참고 : NAT Mode 시 10.0.2.2(GateWay), 10.0.2.3(DNS Server), 10.0.2.4(TFTP Server) 용도로 VBOX 에서 예약됨

[ Ubuntu에서 외부 네트웍 접속 시 경로 ]

 

# Ubuntu 배포 관련 Vagrantfile, init_cfg.sh 파일 다운로드
curl -O https://raw.githubusercontent.com/gasida/KANS/main/kind/Vagrantfile
https://raw.githubusercontent.com/gasida/KANS/main/kind/init_cfg.sh

# vagrant 로 ubuntu 가상머신 1대 프로비저닝
vagrant up
... # 설치 스크립트 init_cfg.sh 실행되어 다소 시간 소요

# vagrant 상태 확인
vagrant status

# Ubuntu SSH 접속
vagrant ssh
-----------
# 기본 정보 확인
whoami
hostnamectl
htop
ip -c a

# 툴 설치 확인
docker info
kind --version
kubectl version --client=true
helm version

# 외부 통신 확인
ping -c 3 8.8.8.8
curl ipinfo.io

# 실습 완료 후 전원 off
poweroff
-----------

# vagrant 상태 확인
vagrant status

# 추후 다시 실습 시 Ubuntu (재)시작 후 Ubuntu SSH 접속
vagrant reload
vagrant ssh

[ vagrant 명령어는 "관리자 모드" 에서 수행 !! ]

 

 

[ Tip & Tips ]

☞ Virtaual Box 설치 후, VM 기동에러 (rc=-5637) 발생 시, 

  - hypervisor 기능 enable 체크!!

☞ vagrant 실행은 관리자 모드 command 창에서 실행

 

 

 

 

 

(참고) 모든 실습 완료 후 vagrant 로 생성한 ubuntu 가상머신 제거 : vagrant destroy -f

 

4. kind 기본 사용환경 구성 및 점검 - 클러스터 배포 및 확인

더보기
# 클러스터 배포 전 확인
docker ps

# Create a cluster with kind
kind create cluster

# 클러스터 배포 확인
kind get clusters
kind get nodes
kubectl cluster-info

# 노드 정보 확인
kubectl get node -o wide

# 파드 정보 확인
kubectl get pod -A
kubectl get componentstatuses

# 컨트롤플레인 (컨테이너) 노드 1대가 실행
docker ps
docker images

# kube config 파일 확인
cat ~/.kube/config

# nginx 파드 배포 및 확인 : 컨트롤플레인 노드인데 파드가 배포 될까요?
kubectl run nginx --image=nginx:alpine
kubectl get pod -owide

# 노드에 Taints 정보 확인
kubectl describe node | grep Taints
Taints:             <none>

# 클러스터 삭제
kind delete cluster

# kube config 삭제 확인
cat ~/.kube/config

 

5. wireshark 설치 : https://www.wireshark.org/download.html

 


[ Tip & Tips - Trouble shoot ]

=> 문제 발생 케이스 

   - 실습 중,  vagrant ssh 상태에서 'power off' 를 하지 않고 나온 후, 'vagrant halt' 명령어로 VM 중지

   - 다음 실습을 위해 'vargrant up' 을 수행 했지만, CODE  Error 메세지와 함께 kind 기동 불가한 상태가 발생 .. ㅠㅠ

몇 번의 시행 착오 끝에 상태 clear 후, vagrant up 으로 해결함

      (windows User는 반드시 '관리자 권한 CMD' 에서 수행할 것!!) 

[ More details ... ]

더보기

방법1) 윈도우즈 hypervisor 기능 off 후, 기동시도

 

 

방법 2) 재설치  ( 4번 정리내용 따라하시면 됩니다 ~ )


[ 심화 실습 1 - KIND 이용한 Cluster 구성 ]

 - 구성 실습을 통해 kind 를 이용한 자원 생성 및 동작여부를 확인 할 수 있다.

https://kccnceu2024.sched.com/event/1YhhY

 

[ 주요 실습 Step ]

Step 1. kind 로 클러스터 생성 ( control-plane + 2-nodes ) -> 생성된 자원에 대한 확인

 

Step 2. kubernetes 관련 정보조사

 

Step 3. Pod 생성 및 확인

 

Step 4. control-plane 컨테이너 정보조사

 

 

 

o “Node” Container 은 ‘myk8s-control-plane’ 컨테이너 (그림에 빨간색) 으로 표기

 

o 해당 “Node” 컨테이너 내부에 쿠버네티스 관련 파드(컨테이너)가 기동되는 구조 → Docker in Docker (DinD)

 

 

 

 

 

Step 1) 클러스터 생성 및 조회

더보기
# 클러스터 배포 전 확인
docker ps

# kind 는 별도 도커 네트워크 생성 후 사용 : 기본값 172.18.0.0/16
docker network ls
docker inspect kind | jq

# Create a cluster with kind
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 get nodes --name myk8s

# k8s api 주소 확인 : 어떻게 로컬에서 접속이 되는 걸까?
kubectl cluster-info
docker ps # 포트 포워딩 정보 확인
docker exec -it myk8s-control-plane ss -tnlp | grep 6443
kubectl get pod -n kube-system -l component=kube-apiserver -owide # 파드 IP 확인
kubectl describe  pod -n kube-system -l component=kube-apiserver
docker exec -it myk8s-control-plane curl -k https://localhost:6443/livez ;echo
docker exec -it myk8s-control-plane curl -k https://localhost:6443/readyz ;echo

# 노드 정보 확인 : CRI 는 containerd 사용
kubectl get node -o wide

# 파드 정보 확인 : CNI 는 kindnet 사용
kubectl get pod -A -owide

# 네임스페이스 확인 >> 도커 컨테이너에서 배운 네임스페이스와 다릅니다!
kubectl get namespaces

# 컨트롤플레인, 워커 컨테이너 각각 1대씩 실행 : 도커 컨테이너 이름은 myk8s-control-plane , myk8s-worker 임을 확인
docker ps
docker images

# 디버그용 내용 출력에 ~/.kube/config 권한 인증 로드
kubectl get pod -v6

# kube config 파일 확인
cat ~/.kube/config
혹은
cat $KUBECONFIG

# local-path 라는 StorageClass 가 설치, local-path 는 노드의 로컬 저장소를 활용함
# 로컬 호스트의 path 를 지정할 필요 없이 local-path provisioner 이 볼륨을 관리
kubectl get sc
kubectl get deploy -n local-path-storage

# 툴 설치
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree jq psmisc lsof wget bridge-utils tcpdump htop git nano -y'
docker exec -it myk8s-worker sh -c 'apt update && apt install tree jq psmisc lsof wget bridge-utils tcpdump htop git nano -y'

 [ 실행 결과 ]

> 기본 클러스터 노드 생성 및 구성정보 확인

  

Step 2) 구성된 kubernetes 관련 정보확인

더보기
# static pod manifest 위치 찾기
docker exec -it myk8s-control-plane grep staticPodPath /var/lib/kubelet/config.yaml
staticPodPath: /etc/kubernetes/manifests

# static pod 정보 확인 : kubectl 및 control plane 에서 관리되지 않고 kubelet 을 통해 지정한 컨테이너를 배포
docker exec -it myk8s-control-plane tree /etc/kubernetes/manifests/
	/etc/kubernetes/manifests/
	├── etcd.yaml
	├── kube-apiserver.yaml
	├── kube-controller-manager.yaml
	└── kube-scheduler.yaml

docker exec -it myk8s-worker tree /etc/kubernetes/manifests/
...

# 워커 노드(컨테이너) bash 진입
docker exec -it myk8s-worker bash
---------------------------------
whoami

# kubelet 상태 확인
systemctl status kubelet

# 컨테이너 확인
docker ps
crictl ps

# kube-proxy 확인
pstree
pstree -p
ps afxuwww |grep proxy
iptables -t filter -S
iptables -t nat -S
iptables -t mangle -S
iptables -t raw -S
iptables -t security -S

# tcp listen 포트 정보 확인
ss -tnlp

# 빠져나오기
exit
---------------------------------

[ 실행결과 ]

  •  소켓 분석 명령어 'ss' 사용법 - link 

 

Step 3) Pod 생성 및 정보조회

더보기
# 파드 생성
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: netpod
spec:
  containers:
  - name: netshoot-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx-pod
    image: nginx:alpine
  terminationGracePeriodSeconds: 0
EOF

# 파드 확인
kubectl get pod -owide

# netpod 파드에서 nginx 웹 접속
kubectl exec -it netpod -- curl -s $(kubectl get pod nginx -o jsonpath={.status.podIP}) | grep -o "<title>.*</title>"
<title>Welcome to nginx!</title>

[ 실행 결과 ]

  

Step 4) Control-Plane 정보조회

더보기
# 도커 컨테이너 확인
docker ps
docker inspect myk8s-control-plane | jq
...
      "Entrypoint": [
        "/usr/local/bin/entrypoint",
        "/sbin/init"
      ],
...

# 컨트롤플레인 컨테이너 bash 접속 후 확인
docker exec -it myk8s-control-plane bash
-------------------------------------------
# CPU 정보 확인
arch
aarch64  # mac m1~m3
혹은
x86_64   # intel/호환 cpu

# 기본 사용자 확인
whoami
root

# 네트워크 정보 확인
ip -br -c -4 addr
ip -c route
cat /etc/resolv.conf

# Entrypoint 정보 확인
cat /usr/local/bin/entrypoint

# 프로세스 확인 : PID 1 은 /sbin/init
ps -ef

# 컨테이터 런타임 정보 확인
systemctl status containerd

# DinD 컨테이너 확인 : crictl 사용
crictl version
crictl info
crictl ps -o json | jq -r '.containers[] | {NAME: .metadata.name, POD: .labels["io.kubernetes.pod.name"]}'
crictl ps
CONTAINER           IMAGE               CREATED             STATE               NAME                      ATTEMPT             POD ID              POD
ff3d3a53905fd       9d6767b714bf1       12 minutes ago      Running             nginx-pod                 0                   20328fe63d512       nginx
bebe6b14d1ab3       eead9e442471d       13 minutes ago      Running             netshoot-pod              0                   28cd918f0561a       netpod
...

# 파드 이미지 확인
crictl images
IMAGE                                           TAG                  IMAGE ID            SIZE
docker.io/library/nginx                         alpine               9d6767b714bf1       20.2MB
docker.io/nicolaka/netshoot                     latest               eead9e442471d       178MB
...

# kubectl 확인
kubectl get node -v6
cat /etc/kubernetes/admin.conf

exit
-------------------------------------------

# 도커 컨테이너 확인 : 다시 한번 자신의 호스트PC에서 도커 컨테이너 확인, DinD 컨테이너가 호스트에서 보이는지 확인
docker ps
docker port myk8s-control-plane

# kubectl 확인 : k8s api 호출 주소 확인
kubectl get node -v6

 

Step 5) 자원 정리

더보기
# 클러스터 삭제
kind delete cluster --name myk8s
docker ps

[ 심화실습 2 - Multi-Node Cluster (Control-plane, Nodes) with kube-ops-view & Mapping ports ] - Link

더보기

> Port Mapping

# '컨트롤플레인, 워커 노드 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: 30000
    hostPort: 30000
    listenAddress: "0.0.0.0" # Optional, defaults to "0.0.0.0"
    protocol: tcp # Optional, defaults to tcp
  - containerPort: 30001
    hostPort: 30001
EOT

CLUSTERNAME=myk8s
kind create cluster --config kind-2node.yaml --name $CLUSTERNAME

# 배포 확인
kind get clusters
kind get nodes --name $CLUSTERNAME

# 노드 확인
kubectl get nodes -o wide

# 노드에 Taints 정보 확인
kubectl describe node $CLUSTERNAME-control-plane | grep Taints
Taints:             node-role.kubernetes.io/control-plane:NoSchedule

kubectl describe node $CLUSTERNAME-worker | grep Taints
Taints:             <none>

# 컨테이너 확인 : 컨테이너 갯수, 컨테이너 이름 확인
# kind yaml 에 포트 맵핑 정보 처럼, 자신의 PC 호스트에 30000 포트 접속 시, 워커노드(실제로는 컨테이너)에 TCP 30000 포트로 연결
# 즉, 워커노드에 NodePort TCP 31000 설정 시 자신의 PC 호스트에서 접속 가능!
docker ps
docker port $CLUSTERNAME-worker
30000/tcp -> 0.0.0.0:30000
30001/tcp -> 0.0.0.0:30001

# 컨테이너 내부 정보 확인 : 필요 시 각각의 노드(?)들에 bash로 접속하여 사용 가능
docker exec -it $CLUSTERNAME-control-plane ip -br -c -4 addr
docker exec -it $CLUSTERNAME-worker  ip -br -c -4 addr

 

 

> Mapping ports to the host machine - 링크

 [ 실습 요약 ]

 - kind inner docker 에 구성 된 cluster worker-node 에 kube-ops-view 와 deploy 형태로 webserver 구성하고,

   node-port 를  이용하여 local 호스트에서 접속하여 서비스 여부 확인

[ https://www.eksworkshop.com/beginner/080_scaling/install_kube_ops_view/ ]

. kube-ops-view : NodePort 3000

# kube-ops-view
# helm show values geek-cookbook/kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30000 --set env.TZ="Asia/Seoul" --namespace kube-system

# 설치 확인
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view

# kube-ops-view 접속 URL 확인 (1.5 , 2 배율)
echo -e "KUBE-OPS-VIEW URL = http://192.168.50.10:30000/#scale=1.5"
echo -e "KUBE-OPS-VIEW URL = http://192.168.50.10:30000/#scale=2"

 

 http://192.168.50.10:30000/#scale=2 : ubuntu 의 enp0s8(192.168.50.10) vNIC에 TCP 30000(nodeport)로 웹 접속


 . nginx : NodePort 30001

   - 하기 코드로 구성 후, 점검

# 디플로이먼트와 서비스 배포
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-websrv
spec:
  replicas: 2
  selector:
    matchLabels:
      app: deploy-websrv
  template:
    metadata:
      labels:
        app: deploy-websrv
    spec:
      terminationGracePeriodSeconds: 0
      containers:
      - name: deploy-websrv
        image: nginx:alpine
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: deploy-websrv
spec:
  ports:
    - name: svc-webport
      port: 80
      targetPort: 80
      nodePort: 30001
  selector:
    app: deploy-websrv
  type: NodePort
EOF

# 확인
docker ps
CONTAINER ID   IMAGE                  COMMAND                   CREATED         STATUS         PORTS                                  NAMES
117a1145a676   kindest/node:v1.29.2   "/usr/local/bin/entr…"   7 minutes ago   Up 7 minutes   0.0.0.0:30000-30001->30000-30001/tcp   myk8s-worker
...

kubectl get deploy,svc,ep deploy-websrv
...
NAME                    TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/deploy-websrv   NodePort   10.96.204.112   <none>        80:30001/TCP   55s
...

# 자신의 PC에 192.168.50.10:30001 접속 시 쿠버네티스 서비스에 접속 확인
curl -s 192.168.50.10:30001
<title>Welcome to nginx!</title>
...

# 디플로이먼트와 서비스 삭제
kubectl delete deploy,svc deploy-websrv

 ☞ NodePort <포트넘버> 고정하고, kind 배포 시 사용할 포트들은 추가하여 사용할 것!!

  http://192.168.50.10:30001/ : ubuntu 의 enp0s8(192.168.50.10) vNIC에 TCP 30001(nodeport)로 웹 접속

 

[ 실행결과 ]


  > kind 삭제 : kind delete cluster --name $CLUSTERNAME


3. 파드 & PAUSE 컨테이너

요약 : 파드는 1개 이상의 컨테이너로 구성된 컨테이너의 집합이며, PAUSE 컨테이너가 Network/IPC/UTS 네임스페이스를 생성하고 유지/공유합니다.

 

3-1. CRI 에 대하여

 - Container Runtime : kubelet → CRI High Level Runtime (containerd) ← OCI → Low Level Runtime (Runc)

 

♣ kubelet 을 통해 전달된 명령어는 grpc(Google Remote Procedure Call)에 의해 high-level 과 low-level Runtime layer 를 거쳐, POD로 전달된다.

 

♣ CRI 의 배경 - Ref.Link

 

 

 - kubernetes 노드에서 가장 낮은 계층에 Container Run Time이 존재하며, 이를 통해 container를 중지/기동 하는 등 자원 관리를 하는데,

   초창기 docker RunTime이 이 역활을 해 왔고, 추후 개선된 CRI 계층을 통해 k8s는 더 효율적으로 자원을 관리 할 수 있게 되었다.

 

 - CRI란 무엇이고 쿠버네티스에 왜 필요한가요? 표준 인터페이스를 통해서 kubelet 재컴파일 없이 다양한 컨테이너 런타임 사용

더보기

[ containerd 의 버전 업그레이드에 따른 변경사항 ] - Ref. Link

- containerd 1.0의 구조와 특성

 . containerd 1.0의 경우 Kubelet과 containerd 사이에서 작동하려면 cri-containerd라는 데몬이 필요함

 . cri-containerd는 Kubelet의 컨테이너 런타임 인터페이스(CRI) 서비스 요청을 처리하고

 . containerd를 사용하여 컨테이너와 컨테이너 이미지에 대한 관리를 하는 구조임

 . Docker CRI 구현( dockershim )과 비교했을 때, 스택에서 하나의 추가 홉이 제거됨

- containerd v1.0 의 한계

 . cri-containerd와 containerd 1.0은 여전히 grpc를 통해 상호 작용하는 2개의 다른 데몬이 존재함

 . 루프에 데몬이 추가되어 사용자가 이해하고 배포하기가 더 복잡해졌고 불필요한 통신 오버헤드가 발생함

- containerd 1.1의 구조와 개선사항

 . containerd 1.1에서 cri-containerd 데몬은 이제 containerd CRI 플러그인으로 리팩토링 됨

 . CRI 플러그인은 containerd 1.1에 내장되어 있으며 기본적으로 활성화되어 있음

 . cri-containerd와 달리 CRI 플러그인은 직접 함수 호출을 통해 containerd와 상호 작용함

 . 이 새로운 아키텍처는 통합을 보다 안정적이고 효율적으로 만들고 스택에서 또 다른 grpc 홉을 제거함

  (사용자는 Kubernetes를 containerd 1.1과 함께 직접 사용할 수 있으며,  cri-containerd 데몬은 더 이상 필요하지 않음)

 

- CRI의 구조와 동작방식

kubelet클라이언트 역할을 하고 CRI shim서버 역할을 함

ImageService : 리포지토리에서 이미지를 가져오고, 이미지를 검사하고, 제거하는 RPC를 제공

RuntimeService : POD/Container Life-cycle 관리하며, RPC와 컨테이너와 상호 작용하기 위한 호출(exec/attach/port-forward)이 포함

 

1) KubeletgRPC 프레임워크를 사용하여 Unix 소켓을 통해 컨테이너 런타임(또는 런타임용 CRI shim)과 통신

2) 프로토콜 버퍼API에는 두 가지 gRPC 서비스인ImageServiceRuntimeService가 포함되어 있음

3) Pod는 리소스 제약이 있는 격리된 환경애플리케이션 컨테이너 그룹으로 구성되며, CRI에서 이 환경을 PodSandbox라고 합니다.

 - pod를 시작하기 전, kubelet RuntimeService.RunPodSandbox호출하여 환경을 만듬

 - PodSandbox 가 기동되면, 독립적으로 container 를 생성/기동/중지/삭제 가 가능해 짐

   ( 자원의 종속성에 따른 관리순서 : container가 먼저 삭제되어야 PodSandbox 삭제가능)

 - Kubelet은 RPC를 통해 컨테이너의 수명 주기를 관리하고, 컨테이너 수명 주기 후크와 활성/준비 확인을 실행하며,

   Pod의 재시작 정책을 준수함

 

- Pod 란 ?  ( Ref.Link / Code )

 : 컨테이너 어플리케이션의 기본 단위이며, 1개의 Pod 안에는 N개의 Container가 존재할 수 있다.

 

♣ 1개 POD - N개의 containers

♣ 1개의 POD내 containers는 1개의 Node에 종속됨

   ( POD 삭제 시, 내부 containers 모두 삭제됨 )

♣ POD IP는 노드 IP와 별개 (CNI 통해 POD 간 통신가능)

♣ POD 내 containers는 POD IP 공유/ Node port를 통해 개별 접근허용

Pause Container = 모든 container의 부모이며, container 들은 net ns 공유함 (주요역할: 1.POD에서 net ns 공유기반, 2. PID ns 활성화 후, PID1 역할을 하며, zombie-process를 거둠)

 

♣ volume 공유 : Pod 안의 컨테이너들은 동일한 볼륨과 연결이 가능하여 파일 시스템을 기반으로 서로 파일을 주고받을 수 있음

PodSandbox 의 특성 3가지 적용 ( 상기 CRI 관련 언급된 내용 동일 )

 

3-2. 실습 (따라하기)

 - Pause container 의 역할에 대해 이해할 수 있다.

 - pause.c - 코드

 - 실습 환경 구성 : Multi-Node Cluster (Control-plane, Nodes) with kube-ops-view

더보기

1) 환경설정

# '컨트롤플레인, 워커 노드 1대' 클러스터 배포 : 파드에 접속하기 위한 포트 맵핑 설정
cat <<EOT> kind-2node.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
  - containerPort: 30001
    hostPort: 30001
EOT
kind create cluster --config kind-2node.yaml --name myk8s

# 툴 설치
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree jq psmisc lsof wget bridge-utils tcpdump htop git nano -y'
docker exec -it myk8s-worker        sh -c 'apt update && apt install tree jq psmisc lsof wget bridge-utils tcpdump htop -y'

# 확인
kubectl get nodes -o wide
docker ps
docker port myk8s-worker
docker exec -it myk8s-control-plane ip -br -c -4 addr
docker exec -it myk8s-worker  ip -br -c -4 addr

# kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30000 --set env.TZ="Asia/Seoul" --namespace kube-system

# 설치 확인
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view

# kube-ops-view 접속 URL 확인 (1.5 , 2 배율) : macOS 사용자
echo -e "KUBE-OPS-VIEW URL = http://localhost:30000/#scale=1.5"
echo -e "KUBE-OPS-VIEW URL = http://localhost:30000/#scale=2"

# kube-ops-view 접속 URL 확인 (1.5 , 2 배율) : Windows 사용자
echo -e "KUBE-OPS-VIEW URL = http://192.168.50.10:30000/#scale=1.5"
echo -e "KUBE-OPS-VIEW URL = http://192.168.50.10:30000/#scale=2"

   

2) POD 배포 및 격리 확인

# [터미널1] myk8s-worker bash 진입 후 실행 및 확인
docker exec -it myk8s-worker bash
----------------------------------
systemctl list-unit-files | grep 'enabled         enabled'
containerd.service                                                                    enabled         enabled
kubelet.service                                                                       enabled         enabled
...

#
crictl ps

# 확인 : kubelet에 --container-runtime-endpoint=unix:///run/containerd/containerd.sock
pstree -aln
systemd
  |-systemd-journal
  |-containerd
  |   \-12*[{containerd}]
  |-kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --container-runtime-endpoint=unix:///run/containerd/containerd.sock --node-ip=172.18.0.4 --node-labels=mynode=worker2 --pod-infra-container-image=registry.k8s.io/pause:3.9 --provider-id=kind://docker/myk8s/myk8s-worker2 --runtime-cgroups=/system.slice/containerd.service
  |   \-14*[{kubelet}]
  |-containerd-shim -namespace k8s.io -id e41d2d62c1bb44a955fe13ddef0dbb006c44352fda493e8d76c489138756d2fa -address /run/containerd/containerd.sock
  |   |-12*[{containerd-shim}]
  |   |-pause
  |   \-kube-proxy --config=/var/lib/kube-proxy/config.conf --hostname-override=myk8s-worker2
  |       \-9*[{kube-proxy}]
  |-containerd-shim -namespace k8s.io -id 9768cd57beeee9b0d1dc38e46dce44697113c3e3924d098e7b8c776909852f63 -address /run/containerd/containerd.sock
  |   |-11*[{containerd-shim}]
  |   |-pause
  |   \-flanneld --ip-masq --kube-subnet-mgr
  |       \-10*[{flanneld}]
  \-containerd-shim -namespace k8s.io -id 6bd147995b3a6c17384459eb4d3ceab4369329e6b57c009bdc6257b72254e1fb -address /run/containerd/containerd.sock
      |-11*[{containerd-shim}]
      |-pause
      \-metrics-server --cert-dir=/tmp --secure-port=10250 --kubelet-insecure-tls --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --kubelet-use-node-status-port --metric-resolution=15s
          \-12*[{metrics-server}]

# 확인 : 파드내에 pause 컨테이너와 metrics-server 컨테이너, 네임스페이스 정보
pstree -aclnpsS
...
	\-containerd-shim,1776 -namespace k8s.io -id 6bd147995b3a6c17384459eb4d3ceab4369329e6b57c009bdc6257b72254e1fb -address /run/containerd/containerd.sock
	|-{containerd-shim},1777
  ...
	|-pause,1797,ipc,mnt,net,pid,uts
	|-metrics-server,1896,cgroup,ipc,mnt,net,pid,uts --cert-dir=/tmp --secure-port=10250 --kubelet-insecure-tls --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --kubelet-use-node-status-port --metric-resolution=15s
	|   |-{metrics-server},1912
  ...

# 네임스페이스 확인 : lsns - List system namespaces
lsns -p 1
lsns -p $$
        NS TYPE   NPROCS PID USER COMMAND
4026531834 time       15   1 root /sbin/init
4026531837 user       15   1 root /sbin/init
4026532525 mnt         9   1 root /sbin/init
4026532550 uts        13   1 root /sbin/init
4026532551 ipc         9   1 root /sbin/init
4026532577 pid         9   1 root /sbin/init
4026532579 net        13   1 root /sbin/init
4026532891 cgroup     13   1 root /sbin/init

# 해당 파드에 pause 컨테이너는 호스트NS와 다른 5개의 NS를 가짐 : mnt/pid 는 pasue 자신만 사용, net/uts/ipc는 app 컨테이너를 위해서 먼저 생성해둠
lsns -p 1797
        NS TYPE   NPROCS   PID USER  COMMAND
4026531834 time       15     1 root  /sbin/init
4026531837 user       15     1 root  /sbin/init
4026532891 cgroup     13     1 root  /sbin/init
4026533496 net         2  1797 65535 /pause
4026533625 uts         2  1797 65535 /pause
4026533626 ipc         2  1797 65535 /pause
4026533624 mnt         1  1797 65535 /pause
4026533627 pid         1  1797 65535 /pause

# app 컨테이너(metrics-server)는 호스트NS와 다른 6개의 NS를 가짐 : mnt/pid/cgroup 는 자신만 사용, net/uts/ipc는 pause 컨테이너가 생성한 것을 공유 사용함
pgrep python3
lsns -p $(pgrep python3)
pgrep metrics-server
1896
lsns -p $(pgrep metrics-server)
        NS TYPE   NPROCS   PID USER  COMMAND
4026531834 time       15     1 root  /sbin/init
4026531837 user       15     1 root  /sbin/init
4026533496 net         2  1797 65535 /pause
4026533625 uts         2  1797 65535 /pause
4026533626 ipc         2  1797 65535 /pause
4026533628 mnt         1  1896 1000  /metrics-server --cert-dir=/tmp --secure-port=10250 --kubelet-insecure-tls --kubelet-preferred-address-types=Inte
4026533629 pid         1  1896 1000  /metrics-server --cert-dir=/tmp --secure-port=10250 --kubelet-insecure-tls --kubelet-preferred-address-types=Inte
4026533630 cgroup      1  1896 1000  /metrics-server --cert-dir=/tmp --secure-port=10250 --kubelet-insecure-tls --kubelet-preferred-address-types=Inte

#
ls -l /run/containerd/containerd.sock

# 특정 소켓 파일을 사용하는 프로세스 확인
lsof /run/containerd/containerd.sock

#
ss -xl | egrep 'Netid|containerd'

#
findmnt -A
TARGET                                                  SOURCE                 FSTYPE    OPTIONS
/                                                       overlay                overlay   rw,relatime,lowerdir=/var/lib/docker/overlay2/l/HW4BGGJ4LV6M5
...
|-/sys                                                  sysfs                  sysfs     ro,nosuid,nodev,noexec,relatime
| |-/sys/kernel/debug                                   debugfs                debugfs   rw,nosuid,nodev,noexec,relatime
| |-/sys/kernel/tracing                                 tracefs                tracefs   rw,nosuid,nodev,noexec,relatime
| |-/sys/fs/fuse/connections                            fusectl                fusectl   rw,nosuid,nodev,noexec,relatime
| |-/sys/kernel/config                                  configfs               configfs  rw,nosuid,nodev,noexec,relatime
| \-/sys/fs/cgroup                                      cgroup                 cgroup2   rw,nosuid,nodev,noexec,relatime

findmnt -t cgroup2
grep cgroup /proc/filesystems
stat -fc %T /sys/fs/cgroup/

----------------------------------
[ pause container 가 관리하는 namespace ]

 

[ 특정 socket file을 사용하는 process ]
[ pause container와 app container에서 사용하는 namespace의 구성 상세보기 ]

 

3) 신규 POD 를 배포하고 확인

# [터미널2] kubectl 명령 실행 및 확인

# Pod 생성 : YAML 파일에 컨테이너가 사용할 포트(TCP 80)을 설정
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: myweb
spec:
  containers:
  - image: nginx:alpine
    name: myweb-container
    ports:
    - containerPort: 80
      protocol: TCP
  terminationGracePeriodSeconds: 0
EOF

# Pod 정보 확인 : pause 컨테이너 정보가 보이는가?
kubectl get pod -o wide
kubectl describe pod myweb
kubectl get pod myweb -o json # status.conditions 에 Type 정보 확인 : 시간 정렬은 안되어 있음..

---

# [터미널1] myk8s-worker bash 진입 후 실행 및 확인
docker exec -it myk8s-worker bash
----------------------------------
crictl ps
pstree -aln
pstree -aclnpsS # 파드내에 pause 컨테이너와 app 컨테이너, 네임스페이스 정보

# 네임스페이스 확인 : lsns - List system namespaces
lsns -p 1
lsns -p $$
lsns -p <pstree -aclnpsS에서 출력된 pause 컨테이너 PID> 
lsns -p $(pgrep nginx) # app 컨테이너(metrics-server)

----------------------------------

# [터미널2] kubectl 명령 실행 및 확인
kubectl delete pod myweb

 

4)  멀티 컨테이너 POD 배포 후, NET IPC UTS 공유 확인

. myweb2.yaml ( 2개 container 포함한 POD )

☞ network ns 공유로 각 컨테이너는 동일 IP 보유 (확인!!)
apiVersion: v1
kind: Pod
metadata:
  name: myweb2
spec:
  containers:
  - name: myweb2-nginx
    image: nginx
    ports:
    - containerPort: 80
      protocol: TCP

  - name: myweb2-netshoot
    image: nicolaka/netshoot
    command: ["/bin/bash"]
    args: ["-c", "while true; do sleep 5; curl localhost; done"] # 포드가 종료되지 않도록 유지합니다

  terminationGracePeriodSeconds: 0

 

# [터미널1] 파드 생성
kubectl apply -f https://raw.githubusercontent.com/gasida/NDKS/main/3/myweb2.yaml

# 확인
# pod 정보 READY 에 2/2 를 확인 : pod 내 모든 컨테이너가 정상이여야지 status 가 Running 가 됨
kubectl get pod -owide

# Pod 상세 정보에 컨테이너 2개 정보가 보인다
kubectl describe pod myweb2
root@k8s-m:~# kubectl describe pod myweb2
Name:         myweb2
...
Containers:
  myweb2-nginx:
    Container ID:   docker://2717dd093ee5c69a918c6c52461f47cf5f0c0330378730ce717d1fcabb0fc748
    Image:          nginx
...
  myweb2-netshoot:
    Container ID:  docker://e3e3aef9ee53ef805336d4b6e0986f63e23c767b1648d18ff09948815c5f06a9
    Image:         nicolaka/netshoot
...

# 파드의 각각 컨테이너 IP 확인 >> IP가 같다!
kubectl exec myweb2 -c myweb2-netshoot -- ip addr
kubectl exec myweb2 -c myweb2-nginx -- apt update
kubectl exec myweb2 -c myweb2-nginx -- apt install -y net-tools
kubectl exec myweb2 -c myweb2-nginx -- ifconfig

# myweb2-netshoot 컨테이너 zsh 진입
kubectl exec myweb2 -c myweb2-netshoot -it -- zsh
----------------------------------
ifconfig
ss -tnlp
curl localhost # nginx 컨테이너가 아닌데, 로컬 접속 되고 tcp 80 listen 이다. 왜그럴까?
ps -ef # nginx 프로세스 정보가 안보이는데... 
exit
----------------------------------

# 터미널3 : nginx 컨테이너 웹 접속 로그 출력 : 접속자(myweb2-netshoot)의 IP 가 ::1(ipv6) 혹은 127.0.0.1(ipv4) 이닷!
kubectl logs -f myweb2 -c myweb2-nginx
::1 - - [01/Sep/2024:06:33:26 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.7.1" "-"
혹은
127.0.0.1 - - [16/Jun/2021:06:22:24 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.77.0" "-"


# [터미널2] 
docker exec -it myk8s-worker bash
----------------------------------
# 컨테이너 정보 확인 : POD 와 POD ID가 같음을 확인
crictl ps
CONTAINER           IMAGE               CREATED              STATE               NAME                ATTEMPT             POD ID              POD
f0401ef30af36       e286c635d1232       About a minute ago   Running             myweb2-netshoot     0                   40f9a3021011a       myweb2
1f83b080de52d       a9dfdba8b7190       About a minute ago   Running             myweb2-nginx        0                   40f9a3021011a       myweb2
...

# 워커 노드에서 컨테이너 프로세스 정보 확인
ps -ef | grep 'nginx -g' | grep -v grep
root       14508   14483  0 09:23 ?        00:00:00 nginx: master process nginx -g daemon off;

ps -ef | grep 'curl' | grep -v grep
root       14596   14574  0 09:23 ?        00:00:00 /bin/bash -c while true; do sleep 5; curl localhost; done

# 각각 프로세스를 변수에 지정
NGINXPID=$(ps -ef | grep 'nginx -g' | grep -v grep | awk '{print $2}')
echo $NGINXPID

NETSHPID=$(ps -ef | grep 'curl' | grep -v grep | awk '{print $2}')
echo $NETSHPID

# 한 파드 내의 각 컨테이너의 네임스페이스 정보 확인
## time, user 네임스페이스는 호스트와 같음, 격리하지 않음
## mnt, uts, pid 네임스페이스는 컨테이너별로 격리
## ipc, uts, net 네임스페이스는 파드 내의 컨테이너 간 공유 (IPC : 컨테이너 프로세스간 공유 - signal, socket, pipe 등)
## Pause 컨테이너는 IPC, Network, UTS 네임스페이스를 생성하고 유지 -> 나머지 컨테이너들은 해당 네임스페이스를 공유하여 사용
## 유저가 실행한 특정 컨테이너가 비정상 종료되어 컨터이너 전체에서 공유되는 네임스페이스에 문제가 발생하는 것을 방지

lsns -p $NGINXPID
        NS TYPE   NPROCS   PID USER  COMMAND
4026531834 time       28     1 root  /sbin/init
4026531837 user       28     1 root  /sbin/init
4026533482 net        12  2112 65535 /pause
4026533611 uts        12  2112 65535 /pause
4026533612 ipc        12  2112 65535 /pause
4026533614 mnt         9  2172 root  nginx: master process nginx -g daemon off;
4026533615 pid         9  2172 root  nginx: master process nginx -g daemon off;
4026533616 cgroup      9  2172 root  nginx: master process nginx -g daemon off;

lsns -p $NETSHPID
        NS TYPE   NPROCS   PID USER  COMMAND
4026531834 time       28     1 root  /sbin/init
4026531837 user       28     1 root  /sbin/init
4026533482 net        12  2112 65535 /pause
4026533611 uts        12  2112 65535 /pause
4026533612 ipc        12  2112 65535 /pause
4026533617 mnt         2  2296 root  /bin/bash -c while true; do sleep 5; curl localhost; done
4026533618 pid         2  2296 root  /bin/bash -c while true; do sleep 5; curl localhost; done
4026533619 cgroup      2  2296 root  /bin/bash -c while true; do sleep 5; curl localhost; done

# pause 정보 확인 : 
PAUSEPID=<각자 자신의 pause PID>
PAUSEPID=2112
lsns -p $PAUSEPID
        NS TYPE   NPROCS   PID USER  COMMAND
4026531834 time       28     1 root  /sbin/init
4026531837 user       28     1 root  /sbin/init
4026532760 cgroup     15     1 root  /sbin/init # cgroup 호스트와 같은 이유는?
4026533482 net        12  2112 65535 /pause
4026533610 mnt         1  2112 65535 /pause     # app 컨테이너와 다른 이유는?
4026533611 uts        12  2112 65535 /pause
4026533612 ipc        12  2112 65535 /pause
4026533613 pid         1  2112 65535 /pause     # app 컨테이너와 다른 이유는?

# 개별 컨테이너에 명령 실행 : IP 동일 확인
crictl ps
crictl ps -q
crictl exec -its 904e43d8fca65 ifconfig
crictl exec -its 63f82edc9caa6 ifconfig


# PAUSE 의 NET 네임스페이스 PID 확인 및 IP 정보 확인
lsns -t net
nsenter -t $PAUSEPID -n ip -c addr
nsenter -t $NGINXPID -n ip -c addr
nsenter -t $NETSHPID -n ip -c addr

# 2개의 네임스페이스 비교 , 아래 2112 프로세스의 정제는?
crictl inspect <myweb2-nginx    컨테이너ID> | jq
crictl inspect <myweb2-netshoot 컨테이너ID> | jq
crictl inspect 904e43d8fca65 | jq
crictl inspect 63f82edc9caa6 | jq
...
        "namespaces": [
          {
            "type": "pid"
          },
          {
            "type": "ipc",
            "path": "/proc/2112/ns/ipc"
          },
          {
            "type": "uts",
            "path": "/proc/2112/ns/uts"
          },
          {
            "type": "mount"
          },
          {
            "type": "network",
            "path": "/proc/2112/ns/net"
          },
          {
            "type": "cgroup"
          }
        ],
...

[ 실행 결과 ]

 

 

[ container 별 IP 비교 ]

 

 


4. Flannel CNI

요약 : 쿠버네티스는 네트워크 모델의 요건을 만족하는 CNI 플러그인이 있고, 대표적으로 'Calico, Cilium 등'이 있습니다.

 - K8S 버전은 1.30.4 : CNIflannel(v0.25.6)

 

4-1. kubernetes 네트워킹

더보기

> 네트웍 모델의 4가지 요구사항

 1. 파드와 파드 간 통신 시 NAT 없이 통신 가능해야 함

 2. 노드의 에이전트는 파드와 통신이 가능해야 함

 3. 호스트 네트워크를 사용하는 파드는 NAT 없이 파드와 통신이 가능해야 함

 4. 서비스 클러스터 IP 대역파드가 사용하는 IP 대역은 중복되지 않아야 함

 

> 4가지 문제(취약점) 해결필요

 1. 파드 내 컨테이너 loopback 통한 통신 가능

 2. 파드 간 통신을 할 수 있음

 3. 클러스터 내부에서 서비스를 통한 통신 가능

 4. 클러스터 외부에서 서비스를 통한 통신 가능

[ 쿠버네티스 네트워킹 통신 동작 ]

 

쿠버네티스에서는 CNI(Container Network Interface) 를 정의하였고, CNI는 CNCF (Cloud Network Computing Foundation)에서 컨테이너의 네트워크 인터페이스 설정을 기술한 명세와 라이브러리로 구성되어 있음

[ CNI 플러그인 동작 ]

 kubelet 을 통해 파드가 신규 생성 도리 때 네트워크 관련 설정을 추가한다. CNI 플러그인은 전달되는 설정 정의서를 보고 실제 파드 통신을 위한 네트워크를 구성하게 된다.

또한 CNI 플러그인은 IPAM(IP Address Management), 즉 IP 할당 관리를 수행해야 하며, 파드 간 통신을 위한 라우팅 설정을 처리해야 한다.

 

[ CNI 플러그인 대표 3종 비교 요약 ]

. 플라넬 - layer-3 네트워크를 간단히 구성가능, vxlan 지원 (Port - UDP 8472, DirectRouting 기능 지원)

. 칼리코 - kubernetes network 구성 default 선택지, BGP 기반의 layer-3 network 구성가능

               보유한 장비들이 BGP 기능 지원할 경우, 오버레이 타입 네트웍 구성 없이 native cluster network 구성 가능

               ( 클러스터 외부에서 직접 파트 IP 이용해 접근 가능하도록 구성 가능, IP-in-IP 터널 네트웍 이용)

. 실리엄 - 리눅스 커널에 포함된 BPF(Berkley Packet Filter) 기능 이용

               ( BPF는 시스템을 실행 중에 재기동 필요없이 커널에 추가하는 기술이다.) 

                 BPF 기술 중 하나인 XDP(eXpress Data Path)를 이용해 시스템 실행 중 네트웍 경로 조정이 가능함 

[ 참고 - Flannel 통신관리 경로 ]

[ 내부 pod 간 통신경로 / 노드 간 통신 경로 ]

[ https://ikcoo.tistory.com/101 ]

 

 

4-2. 실습 ( Kind & Flannel 배포 및 통신흐름의 이해 )

 

4-2-1. kind & Flannel 배포 - Docs Blog Github

더보기
#
cat <<EOF> kind-cni.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  labels:
    mynode: control-plane
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
  - containerPort: 30001
    hostPort: 30001
  - containerPort: 30002
    hostPort: 30002
  kubeadmConfigPatches:
  - |
    kind: ClusterConfiguration
    controllerManager:
      extraArgs:
        bind-address: 0.0.0.0
    etcd:
      local:
        extraArgs:
          listen-metrics-urls: http://0.0.0.0:2381
    scheduler:
      extraArgs:
        bind-address: 0.0.0.0
  - |
    kind: KubeProxyConfiguration
    metricsBindAddress: 0.0.0.0
- role: worker
  labels:
    mynode: worker
- role: worker
  labels:
    mynode: worker2
networking:
  disableDefaultCNI: true
EOF
kind create cluster --config kind-cni.yaml --name myk8s --image kindest/node:v1.30.4

# 배포 확인
kind get clusters
kind get nodes --name myk8s
kubectl cluster-info

# 네트워크 확인
kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"

# 노드 확인 : CRI
kubectl get nodes -o wide

# 노드 라벨 확인
kubectl get nodes myk8s-control-plane -o jsonpath={.metadata.labels} | jq
...
"mynode": "control-plane",
...

kubectl get nodes myk8s-worker -o jsonpath={.metadata.labels} | jq
kubectl get nodes myk8s-worker2 -o jsonpath={.metadata.labels} | jq

# 컨테이너 확인 : 컨테이너 갯수, 컨테이너 이름 확인
docker ps
docker port myk8s-control-plane
docker port myk8s-worker
docker port myk8s-worker2

# 컨테이너 내부 정보 확인
docker exec -it myk8s-control-plane ip -br -c -4 addr
docker exec -it myk8s-worker  ip -br -c -4 addr
docker exec -it myk8s-worker2  ip -br -c -4 addr

#
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree jq psmisc lsof wget bridge-utils tcpdump iputils-ping htop git nano -y'
docker exec -it myk8s-worker  sh -c 'apt update && apt install tree jq psmisc lsof wget bridge-utils tcpdump iputils-ping -y'
docker exec -it myk8s-worker2 sh -c 'apt update && apt install tree jq psmisc lsof wget bridge-utils tcpdump iputils-ping -y'
#
watch -d kubectl get pod -A -owide

#
kubectl describe pod -n kube-system -l k8s-app=kube-dns
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  57s   default-scheduler  0/1 nodes are available: 1 node(s) had untolerated taint {node.kubernetes.io/not-ready: }. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling.

# Flannel cni 설치
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

# namespace 에 pod-security.kubernetes.io/enforce=privileged Label 확인 
kubectl get ns --show-labels
NAME                 STATUS   AGE     LABELS
kube-flannel         Active   2m49s   k8s-app=flannel,kubernetes.io/metadata.name=kube-flannel,pod-security.kubernetes.io/enforce=privileged

kubectl get ds,pod,cm -n kube-flannel

kubectl describe cm -n kube-flannel kube-flannel-cfg
cni-conf.json:
----
{
  "name": "cbr0",
  "cniVersion": "0.3.1",
  "plugins": [
    {
      "type": "flannel",
      "delegate": {
        "hairpinMode": true,
        "isDefaultGateway": true
      }
    },
    {
      "type": "portmap",
      "capabilities": {
        "portMappings": true
      }
    }
  ]
}

net-conf.json:
----
{
  "Network": "10.244.0.0/16",
  "EnableNFTables": false,
  "Backend": {
    "Type": "vxlan"
  }
}

kubectl describe ds -n kube-flannel kube-flannel-ds
...
    Mounts:
      /etc/cni/net.d from cni (rw)
      /etc/kube-flannel/ from flannel-cfg (rw)
    ...
    Mounts:
      /etc/kube-flannel/ from flannel-cfg (rw)
      /run/flannel from run (rw)
      /run/xtables.lock from xtables-lock (rw)
  Volumes:
   run:
    Type:          HostPath (bare host directory volume)
    Path:          /run/flannel
    HostPathType:  
   cni-plugin:
    Type:          HostPath (bare host directory volume)
    Path:          /opt/cni/bin
    HostPathType:  
   cni:
    Type:          HostPath (bare host directory volume)
    Path:          /etc/cni/net.d
    HostPathType:  
   flannel-cfg:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      kube-flannel-cfg
    Optional:  false
   xtables-lock:
    Type:               HostPath (bare host directory volume)
    Path:               /run/xtables.lock
    HostPathType:       FileOrCreate
  Priority Class Name:  system-node-critical
...

kubectl exec -it ds/kube-flannel-ds -n kube-flannel -c kube-flannel -- ls -l /etc/kube-flannel


# failed to find plugin "bridge" in path [/opt/cni/bin]
kubectl get pod -A -owide
kubectl describe pod -n kube-system -l k8s-app=kube-dns
  Warning  FailedCreatePodSandBox  35s               kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "786e9caec9c312a0b8af70e14865535575601d024ec02dbb581a1f5ac0b8bb06": plugin type="flannel" failed (add): loadFlannelSubnetEnv failed: open /run/flannel/subnet.env: no such file or directory
  Warning  FailedCreatePodSandBox  23s               kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "6ccd4607d32cbb95be9ff40b97a436a07e5902e6c24d1e12aa68fefc2f8b548a": plugin type="flannel" failed (add): failed to delegate add: failed to find plugin "bridge" in path [/opt/cni/bin]

#
kubectl get pod -A -owide

#
docker cp bridge myk8s-control-plane:/opt/cni/bin/bridge
docker cp bridge myk8s-worker:/opt/cni/bin/bridge
docker cp bridge myk8s-worker2:/opt/cni/bin/bridge
docker exec -it myk8s-control-plane  chmod 755 /opt/cni/bin/bridge
docker exec -it myk8s-worker         chmod 755 /opt/cni/bin/bridge
docker exec -it myk8s-worker2        chmod 755 /opt/cni/bin/bridge

#
docker exec -it myk8s-control-plane  ls -l /opt/cni/bin/
docker exec -it myk8s-worker  ls -l /opt/cni/bin/
docker exec -it myk8s-worker2 ls -l /opt/cni/bin/
for i in myk8s-control-plane myk8s-worker myk8s-worker2; do echo ">> node $i <<"; docker exec -it $i ls /opt/cni/bin/; echo; done
bridge	flannel  host-local  loopback  portmap	ptp

#
kubectl get pod -A -owide

[ kubelet 및 기타 정보 ]

# kubelet config 정보
kubectl describe cm -n kube-system kubelet-config
docker exec -it myk8s-control-plane cat /var/lib/kubelet/config.yaml
...
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
staticPodPath: /etc/kubernetes/manifests
...

#
docker exec -it myk8s-control-plane cat /var/lib/kubelet/kubeadm-flags.env
KUBELET_KUBEADM_ARGS="--container-runtime-endpoint=unix:///run/containerd/containerd.sock --node-ip=172.18.0.2 --node-labels= --pod-infra-container-image=registry.k8s.io/pause:3.9 --provider-id=kind://docker/myk8s/myk8s-control-plane"

for i in myk8s-control-plane myk8s-worker myk8s-worker2; do echo ">> node $i <<"; docker exec -it $i cat /var/lib/kubelet/kubeadm-flags.env; echo; done

#
for i in myk8s-control-plane myk8s-worker myk8s-worker2; do echo ">> node $i <<"; docker exec -it $i ls /etc/kubernetes/manifests; echo; done
>> node myk8s-control-plane <<
etcd.yaml  kube-apiserver.yaml	kube-controller-manager.yaml  kube-scheduler.yaml

>> node myk8s-worker <<

>> node myk8s-worker2 <<

#
kubectl describe pod -n kube-system kube-apiserver-myk8s-control-plane
kubectl describe pod -n kube-system kube-controller-manager-myk8s-control-plane
kubectl describe pod -n kube-system kube-scheduler-myk8s-control-plane

#
for i in myk8s-control-plane myk8s-worker myk8s-worker2; do echo ">> node $i <<"; docker exec -it $i pstree -aT; echo; done
for i in myk8s-control-plane myk8s-worker myk8s-worker2; do echo ">> node $i <<"; docker exec -it $i pstree -atl; echo; done

#
docker exec -it myk8s-control-plane  systemctl status kubelet -l --no-pager
docker exec -it myk8s-worker  systemctl status kubelet -l --no-pager
docker exec -it myk8s-worker2 systemctl status kubelet -l --no-pager

 

 


[ 실행 결과 ]

1. 클러스터 설치 및 정보 확인

2. CNI 설치 및 내용 확인 - 전과 후 비교

 

[ bridge 파일 오류로 실패 .. => 추후 fix 시도해 보아야 겠네요 ㅠㅠ ]

 

[ Bridge 파일 수동생성 하기 ]

step 1. control plane 접속하여, 파일 생성하기

 $ docker exec -it myk8s-control-plane bash

apt install golang -y
git clone https://github.com/containernetworking/plugins
cd plugins
chmod +x build_linux.sh

#
./build_linux.sh
Building plugins
  bandwidth
  firewall
  portmap
  sbr
  tuning
  vrf
  bridge
  host-device
  ipvlan
  loopback
  macvlan
  ptp
  vlan
  dhcp
  host-local
  static

 step2. 파일 로컬 내보내기 && node로 복사하기

  - # 자신의 PC에 복사 : -a 권한 보존하여 복사(755)
docker cp -a myk8s-control-plane:/plugins/bin/bridge .
ls -l bridge

 

[ bridge 생성 및 복사 후, core-DNS 정상 기동 확인 !! ~~ ^0^/ ]

 


[ Flannel 정보 확인 ]

#
kubectl get ds,pod,cm -n kube-flannel -owide
kubectl describe cm -n kube-flannel kube-flannel-cfg

# iptables 정보 확인
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-control-plane  iptables -t $i -S ; echo; done
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-worker  iptables -t $i -S ; echo; done
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-worker2 iptables -t $i -S ; echo; done


# flannel 정보 확인 : 대역, MTU
for i in myk8s-control-plane myk8s-worker myk8s-worker2; do echo ">> node $i <<"; docker exec -it $i cat /run/flannel/subnet.env ; echo; done
>> node myk8s-control-plane <<
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24 # 해당 노드에 파드가 배포 시 할당 할 수 있는 네트워크 대역
FLANNEL_MTU=65485 # MTU 지정
FLANNEL_IPMASQ=true # 파드가 외부(인터넷) 통신 시 해당 노드의 마스커레이딩을 사용
...

# 노드마다 할당된 dedicated subnet (podCIDR) 확인
kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}' ;echo

# 노드 정보 중 flannel 관련 정보 확인 : VXLAN 모드 정보와, VTEP 정보(노드 IP, VtepMac) 를 확인
kubectl describe node | grep -A3 Annotations

# 각 노드(?) 마다 bash 진입 후 아래 기본 정보 확인 : 먼저 worker 부터 bash 진입 후 확인하자
docker exec -it myk8s-worker        bash
docker exec -it myk8s-worker2       bash
docker exec -it myk8s-control-plane bash
----------------------------------------
# 호스트 네트워크 NS와 flannel, kube-proxy 컨테이너의 네트워크 NS 비교 : 파드의 IP와 호스트(서버)의 IP를 비교해보자!
lsns -p 1
lsns -p $(pgrep flanneld)
lsns -p $(pgrep kube-proxy)


# 기본 네트워크 정보 확인
ip -c -br addr
ip -c link | grep -E 'flannel|cni|veth' -A1
ip -c addr
ip -c -d addr show cni0     # 네트워크 네임스페이스 격리 파드가 1개 이상 배치 시 확인됨

ip -c -d addr show flannel.1
	5: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
    link/ether 1e:81:fc:d5:8a:42 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535
    vxlan id 1 local 192.168.100.10 dev enp0s8 srcport 0 0 dstport 8472 nolearning ttl auto ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    inet 172.16.0.0/32 brd 172.16.0.0 scope global flannel.1
    
brctl show
bridge name	bridge id		STP enabled	interfaces
cni0		8000.663bf746b6a8	no		vethbce1591c
							vethc17ba51b
							vethe6540260

# 라우팅 정보 확인 : 다른 노드의 파드 대역(podCIDR)의 라우팅 정보가 업데이트되어 있음을 확인		
ip -c route
default via 172.18.0.1 dev eth0 
10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink 
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1 
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink 
172.18.0.0/16 dev eth0 proto kernel scope link src 172.18.0.3 

# flannel.1 인터페이스를 통한 ARP 테이블 정보 확인 : 다른 노드의 flannel.1 IP와 MAC 정보를 확인
ip -c neigh show dev flannel.1

# 브리지 fdb 정보에서 해당 MAC 주소와 통신 시 각 노드의 enp0s8 
bridge fdb show dev flannel.1

# 다른 노드의 flannel.1 인터페이스로 ping 통신 : VXLAN 오버레이를 통해서 통신
ping -c 1 10.244.0.0
ping -c 1 10.244.1.0
ping -c 1 10.244.2.0

# iptables 필터 테이블 정보 확인 : 파드의 10.244.0.0/16 대역 끼리는 모든 노드에서 전달이 가능
iptables -t filter -S | grep 10.244.0.0

# iptables NAT 테이블 정보 확인 : 10.244.0.0/16 대역 끼리 통신은 마스커레이딩 없이 통신을 하며,
# 10.244.0.0/16 대역에서 동일 대역(10.244.0.0/16)과 멀티캐스트 대역(224.0.0.0/4) 를 제외한 나머지 (외부) 통신 시에는 마스커레이딩을 수행
iptables -t nat -S | grep 'flanneld masq' | grep -v '! -s'

----------------------------------------
[ Flannel 정보 확인 - masqurading 으로 외부 통신 가능 ]

  

[참고] (참고) Deploying Flannel with helm - Link

더보기
# Needs manual creation of namespace to avoid helm error
kubectl create ns kube-flannel
kubectl label --overwrite ns kube-flannel pod-security.kubernetes.io/enforce=privileged

#
helm repo add flannel https://flannel-io.github.io/flannel/
helm show values flannel/flannel

# 설치
helm install flannel --set podCidr="10.244.0.0/16" --namespace kube-flannel flannel/flannel

> helm show values flannel/flannel

helm show values flannel/flannel
---
global:
  imagePullSecrets:
# - name: "a-secret-name"

# The IPv4 cidr pool to create on startup if none exists. Pod IPs will be
# chosen from this range.
podCidr: "10.244.0.0/16"
podCidrv6: ""

flannel:
  # kube-flannel image
  image:
    repository: docker.io/flannel/flannel
    tag: v0.25.6
  image_cni:
    repository: docker.io/flannel/flannel-cni-plugin
    tag: v1.5.1-flannel2
  # flannel command arguments
  enableNFTables: false
  args:
  - "--ip-masq"
  - "--kube-subnet-mgr"
  # Backend for kube-flannel. Backend should not be changed
  # at runtime. (vxlan, host-gw, wireguard, udp)
  # Documentation at https://github.com/flannel-io/flannel/blob/master/Documentation/backends.md
  backend: "vxlan"
  # Port used by the backend 0 means default value (VXLAN: 8472, Wireguard: 51821, UDP: 8285)
  #backendPort: 0
  # MTU to use for outgoing packets (VXLAN and Wiregurad) if not defined the MTU of the external interface is used.
  # mtu: 1500
  #
  # VXLAN Configs:
  #
  # VXLAN Identifier to be used. On Linux default is 1.
  #vni: 1
  # Enable VXLAN Group Based Policy (Default false)
  # GBP: false
  # Enable direct routes (default is false)
  # directRouting: false
  # MAC prefix to be used on Windows. (Defaults is 0E-2A)
  # macPrefix: "0E-2A"
  #
  # Wireguard Configs:
  #
  # UDP listen port used with IPv6
  # backendPortv6: 51821
  # Pre shared key to use
  # psk: 0
  # IP version to use on Wireguard
  # tunnelMode: "separate"
  # Persistent keep interval to use
  # keepaliveInterval: 0
  #
  # General daemonset configs
  #
  tolerations:
  - effect: NoExecute
    operator: Exists
  - effect: NoSchedule
    operator: Exists

netpol:
  enabled: false
  args:
  - "--hostname-override=$(MY_NODE_NAME)"
  - "--v=2"
  image:
    repository: registry.k8s.io/networking/kube-network-policies
    tag: v0.4.0

 

 

4-2-2. 통신 흐름의 이해

 > 파드 2개 생성

더보기
# [터미널1,2] 워커 노드1,2 - 모니터링
docker exec -it myk8s-worker  bash
docker exec -it myk8s-worker2 bash
-----------------------------
watch -d "ip link | egrep 'cni|veth' ;echo; brctl show cni0"
-----------------------------

# [터미널3] cat & here document 명령 조합으로 즉석(?) 리소스 생성
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: pod-1
  labels:
    app: pod
spec:
  nodeSelector:
    kubernetes.io/hostname: myk8s-worker
  containers:
  - name: netshoot-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-2
  labels:
    app: pod
spec:
  nodeSelector:
    kubernetes.io/hostname: myk8s-worker2
  containers:
  - name: netshoot-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

# 파드 확인 : IP 확인
kubectl get pod -o wide

> 파트 통신흐름 체크 ( 시나리오 3가지 )

> bridge 네트웍 연동 및 분석 실습

더보기

 

 

 

> 패킷 캡쳐 후, 확인하기

더보기
# [터미널1,2] 워커 노드1,2
docker exec -it myk8s-worker  bash
docker exec -it myk8s-worker2 bash
-----------------------------
tcpdump -i cni0 -nn icmp
tcpdump -i flannel.1 -nn icmp
tcpdump -i eth0 -nn icmp
tcpdump -i eth0 -nn udp port 8472 -w /root/vxlan.pcap
# CTRL+C 취소 후 확인 : ls -l /root/vxlan.pcap

conntrack -L | grep -i icmp
-----------------------------

# [터미널3]
docker cp -a myk8s-worker:/root/vxlan.pcap .

 

[ 하기 실습을 통해 네트웍 위치 별 packet 이 흐르는 경로를 확인할 수 있다

[ 노드 간 POD 통신으로 flannel 네트웍 packet 이 확인됨 ]
[ 내부 POD에서 외부 인터넷 구간으로 통신의 경우, flannel network layer를 거치지 않기 때문에 패킷 안보임 ]
[ eth0 인터페이스는 내부 POD에서 외부 통신으로 흐르는 구간이므로, packet dump가 잡힌다!! ]

 

> ubuntu 가상머신 내부에 파일을 호스트(윈도우)에 복사하기 : cmd 창(관리자 권한) 실행

더보기
# scp 실행(root 계정) 암호 qwe123 입력
scp root@192.168.50.10:/root/vxlan.pcap .
root@192.168.50.10's password: qwe123

#
D:\Vagrant-Lab\kind> dir
 Volume in drive D is NEW
 Volume Serial Number is 5263-D701

 Directory of D:\Vagrant-Lab\kind

2024-09-03  오후 02:57    <DIR>          .
2024-09-03  오후 02:57    <DIR>          ..
2024-09-02  오후 10:12    <DIR>          .vagrant
2024-09-03  오후 02:38             2,828 init_cfg.sh
2024-09-02  오후 10:08               727 Vagrantfile
2024-09-03  오후 02:57               680 vxlan.pcap
               3 File(s)          4,235 bytes
               3 Dir(s)  176,679,444,480 bytes free

> Wire shark 실행 및 분석

 - Wireshark 에서 vxlan 기본 udp port 를 4789 를 사용합니다. Flannel 은 8472 를 사용하니 udp port 정보를 맞춰주시면 됩니다

더보기

 

> 옵션 설정 변경 후, 분석 시도 : 옵션 설정 - Protocols - VXLAN : 4789 → 8472

 

> VXlan 정보 확인

 

 

[ 기타 도움이 되는 툴 설정 ]

더보기

1. kube-ops-view

# helm show values geek-cookbook/kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30000 --set env.TZ="Asia/Seoul" --namespace kube-system

# 설치 확인
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view

# myk8s-control-plane 노드?에 배치
kubectl get nodes myk8s-control-plane -o jsonpath={.metadata.labels} | jq
kubectl describe node myk8s-control-plane | grep Taints
kubectl -n kube-system get deploy kube-ops-view -o yaml | k neat
kubectl -n kube-system edit deploy kube-ops-view
---
spec:
  ...
  template:
    ...
    spec:
      nodeSelector:
        mynode: control-plane
      tolerations:
      - key: "node-role.kubernetes.io/control-plane"
        operator: "Equal"
        effect: "NoSchedule"
---

kubectl -n kube-system get pod -o wide -l app.kubernetes.io/instance=kube-ops-view

# kube-ops-view 접속 URL 확인 (1.5 , 2 배율) : macOS 사용자
echo -e "KUBE-OPS-VIEW URL = http://localhost:30000/#scale=1.5"
echo -e "KUBE-OPS-VIEW URL = http://localhost:30000/#scale=2"

# kube-ops-view 접속 URL 확인 (1.5 , 2 배율) : Windows 사용자
echo -e "KUBE-OPS-VIEW URL = http://192.168.50.10:30000/#scale=1.5"
echo -e "KUBE-OPS-VIEW URL = http://192.168.50.10:30000/#scale=2"


# (참고) 삭제
helm uninstall -n kube-system kube-ops-view

 

2. metrics-server

# metrics-server
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm upgrade --install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system

kubectl get all -n kube-system -l app.kubernetes.io/instance=metrics-server
kubectl get apiservices |egrep '(AVAILABLE|metrics)'

# 확인
kubectl top node
kubectl top pod -A --sort-by='cpu'
kubectl top pod -A --sort-by='memory'

# (참고) 삭제
helm uninstall -n kube-system metrics-server

 

 3. prometheus-stack

#
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

# 파라미터 파일 생성
cat <<EOT > monitor-values.yaml
prometheus:
  service:
    type: NodePort
    nodePort: 30001

  prometheusSpec:
    podMonitorSelectorNilUsesHelmValues: false
    serviceMonitorSelectorNilUsesHelmValues: false
    nodeSelector:
      mynode: control-plane
    tolerations:
    - key: "node-role.kubernetes.io/control-plane"
      operator: "Equal"
      effect: "NoSchedule"


grafana:
  defaultDashboardsTimezone: Asia/Seoul
  adminPassword: kans1234

  service:
    type: NodePort
    nodePort: 30002
  nodeSelector:
    mynode: control-plane
  tolerations:
  - key: "node-role.kubernetes.io/control-plane"
    operator: "Equal"
    effect: "NoSchedule"

defaultRules:
  create: false
alertmanager:
  enabled: false

EOT

# 배포
kubectl create ns monitoring
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 62.3.0 -f monitor-values.yaml --namespace monitoring

# 확인
helm list -n monitoring

# Grafana 접속 계정 : admin / kans1234 : macOS 사용자
echo -e "Prometheus URL = http://localhost:30001"
echo -e "Grafana URL = http://localhost:30002"

# Grafana 접속 계정 : admin / kans1234 : Windows 사용자
echo -e "Prometheus URL = http://192.168.50.10:30001"
echo -e "Grafana URL = http://192.168.50.10:30002"


# (참고) helm 삭제
helm uninstall -n monitoring kube-prometheus-stack

 

> Prometheus Target connection refused 관련 정상 동작 설정 : kind k8s yaml 파일에 이미 적용되어 있음

  - kube-controller-manager , kube-scheduler , etcd , kube-proxy

# Prometheus Target connection refused 관련 정상 동작 설정 : kube-proxy
kubectl edit cm -n kube-system kube-proxy
...
metricsBindAddress: "0.0.0.0:10249" ## 추가
...
kubectl rollout restart daemonset -n kube-system kube-proxy

# Prometheus Target connection refused 관련 정상 동작 설정 : kube-controller-manager , kube-scheduler , etcd
docker exec -it myk8s-control-plane bash
----------------------------------------
cat /etc/kubernetes/manifests/kube-controller-manager.yaml | grep bind-address
sed -i "s/bind-address=127.0.0.1/bind-address=0.0.0.0/g" /etc/kubernetes/manifests/kube-controller-manager.yaml

cat /etc/kubernetes/manifests/kube-scheduler.yaml | grep bind-address
sed -i "s/bind-address=127.0.0.1/bind-address=0.0.0.0/g" /etc/kubernetes/manifests/kube-scheduler.yaml

cat /etc/kubernetes/manifests/etcd.yaml | grep 127.0.0.1:2381
sed -i "s/127.0.0.1:2381/0.0.0.0:2381/g" /etc/kubernetes/manifests/etcd.yaml
----------------------------------------

 

> 설치 후, Iptables 확인

# iptables 정보 확인
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-control-plane  iptables -t $i -S ; echo; done
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-worker  iptables -t $i -S ; echo; done
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-worker2 iptables -t $i -S ; echo; done

☞ 실습 완료 후 kind 삭제 : kind delete cluster --name myk8s


[ 마무리 ]

 . Flannel CNI 설치 과정을 통해 kubernetes 내부와 외부 통신의 과정에 대해서 심도있게 들여 다 볼 수 있는 계기가 주어 진 것 같아 좋았습니다. 

 . 아쉬운 점은 bridge 파일의 오류로 인해 뒷 과정에 대한 bridge네트웍 생성 이후, 내부/외부 통신 과정을 동영상 통해서

 간접적으로만 체험해 볼 수 있었다는 점입니다.

 다음 calico 과정에서 오류 없이 배워 볼 수 있었으면 좋겠습니다.


[ 도움이 되는 참고링크 모음 ]

더보기
  • K8S 아키텍쳐 소개 : 김태민 기술 블로그 -  링크 아키텍처
  • Highly Available 구성 - 링크
  • [sam.0] 쿠버네티스 딥다이브 - Link

    1. 추천 👍🏻 쿠버네티스 hard?way - Link

    2. Live Debugging K8s - Link

    3. 추천 👍🏻 쿠버네티스의 인증 - Link

    4. kube-apiserver - Link

    5. kube-controller-manager - Link

    6. 추천 👍🏻 kube-scheduler - Link

    7. 추천 👍🏻 kubelet - Link