일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- metallb
- kubernetes network policy
- 분산 traffic
- loxilb
- Jenkins
- ipvs
- bdp
- Ingress
- Kubernetes
- WSL
- service mesh
- aws lb controller
- vagrant
- aws eks
- CICD
- docker
- traffic manager
- windows에서 aws cli 설치하기
- sidecar injection
- vpc cni
- windows-aws-configure
- traffic management in istio
- gogs
- node ip
- CNI
- cilium
- ebdp
- k8s-service
- blue-green
- wsl & vagrant
- Today
- Total
WellSpring
2주차 : K8S Flannel CNI & PAUSE 본문
※ 본 게재 글은 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 의 개념도 ]
1-2. Kubernetes Components : master 와 node로 구성 - link
[ More ... ]
[ kubectl 명령어 수행 경로 ]
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 )
ADD-ON
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.
. 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 배포 관련 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
[ Tip & Tips ]
☞ Virtaual Box 설치 후, VM 기동에러 (rc=-5637) 발생 시,
☞ 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 를 이용한 자원 생성 및 동작여부를 확인 할 수 있다.
[ 주요 실습 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 호스트에서 접속하여 서비스 여부 확인
. 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) Kubelet은 gRPC 프레임워크를 사용하여 Unix 소켓을 통해 컨테이너 런타임(또는 런타임용 CRI shim)과 통신
2) 프로토콜 버퍼API에는 두 가지 gRPC 서비스인ImageService와 RuntimeService가 포함되어 있음
3) Pod는 리소스 제약이 있는 격리된 환경의 애플리케이션 컨테이너 그룹으로 구성되며, CRI에서 이 환경을 PodSandbox라고 합니다.
- pod를 시작하기 전, kubelet은 RuntimeService.RunPodSandbox를 호출하여 환경을 만듬
- PodSandbox 가 기동되면, 독립적으로 container 를 생성/기동/중지/삭제 가 가능해 짐
( 자원의 종속성에 따른 관리순서 : container가 먼저 삭제되어야 PodSandbox 삭제가능)
- Kubelet은 RPC를 통해 컨테이너의 수명 주기를 관리하고, 컨테이너 수명 주기 후크와 활성/준비 확인을 실행하며,
Pod의 재시작 정책을 준수함
: 컨테이너 어플리케이션의 기본 단위이며, 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/
----------------------------------
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 )
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"
}
],
...
[ 실행 결과 ]
4. Flannel CNI
요약 : 쿠버네티스는 네트워크 모델의 요건을 만족하는 CNI 플러그인이 있고, 대표적으로 'Calico, Cilium 등'이 있습니다.
- K8S 버전은 1.30.4 : CNI 는 flannel(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)에서 컨테이너의 네트워크 인터페이스 설정을 기술한 명세와 라이브러리로 구성되어 있음
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)를 이용해 시스템 실행 중 네트웍 경로 조정이 가능함
[ 내부 pod 간 통신경로 / 노드 간 통신 경로 ]
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 파일 수동생성 하기 ]
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
[ 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'
----------------------------------------
[참고] (참고) 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 이 흐르는 경로를 확인할 수 있다 ]
> 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 과정에서 오류 없이 배워 볼 수 있었으면 좋겠습니다.
[ 도움이 되는 참고링크 모음 ]
'KANS3 - k8s Advanced Networking Study' 카테고리의 다른 글
6주차 - Ingress & Gateway API (3) | 2024.10.07 |
---|---|
5주차 - LoadBalancer(MetalLB), IPVS + LoxiLB (0) | 2024.10.01 |
4주차 : Service : ClusterIP, NodePort (0) | 2024.09.26 |
3주차 : Calico CNI & Mode (0) | 2024.09.12 |
1주차 : 컨테이너 격리 & 네트워크 및 보안 (3) | 2024.08.27 |