본문 바로가기

study/KANS 3기

KANS 3기 2주차 첫번째

쿠버네티스

Kubernetes Components : K8S 클러스터는 Controle Plane(마스터)와 Node(노드)로 구성 - 링크

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

 

마스터노드

kube-apiserver : 마스터로 전달되는 모든 요청을 받아 드리는 API 서버 - (심화) 링크1 링크2 링크3

etcd : 클러스터내 모든 메타 정보를 저장하는 서비스

kube-scheduler : 사용자의 요청에 따라 적절하게 컨테이너를 워커 노드에 배치하는 스케줄러

kube-controller-manager : 현재 상태와 바라는 상태를 지속적으로 확인하며 특정 이벤트에 따라 특정 동작을 수행하는 컨트롤러 - 링크

cloud-controller-manager : 클라우드 플랫폼(AWS, GCP, Azure 등)에 특화된 리소스를 제어하는 클라우드 컨트롤러 - 링크

 

워커노드

kubelet : 마스터의 명령에 따라 컨테이너의 라이프 사이클을 관리하는 노드 관리자

kube-proxy : 컨테이너의 네트워킹을 책임지는 프록시, 네트워크 규칙을 유지 관리

Container Runtime : 실제 컨테이너를 실행하는 컨테이너 실행 환경, (Docker & containerD & CRI-O)

 

그외 Add on

CNI : Container Network Interface 는 k8s 네트워크 환경을 구성해준다 - 링크, 다양한 플러그인이 존재 - 링크

 

DNS : 쿠버네티스 서비스를 위해 DNS 레코드를 제공해주며, Service Discovery 기능을 제공을 한다. 대표적으로 CoreDNS 가 있다 - 링크

 

 

쿠버네티스 핵심 개념

애완동물 vs 가축

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

 

쿠버네티스도 서버를 바라보는 관점이 이와 유사합니다.

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

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

 

 

 

네임스페이스(Namespace)

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

선언형 커맨드(Declarative Command)

쿠버네티스에서는 선언형 커맨드(Declarative Command)를 지향합니다.

선언형 커맨드사용자가 직접 시스템의 상태를 바꾸지 않고 사용자가 바라는 상태를 선언적으로 기술하여 명령을 내리는 방법을 말합니다.

쿠버네티스는 YAML 형식을 이용하여 선언형 명령을 내립니다. 다음은 쿠버네티스에서 사용하는 YAML 형식의 리소스(Pod) 예시입니다.

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

 

 

Highly Available 구성 - 링크

Control Plane(마스터 노드) HA 설정 : 마스터 노드 3대(5대...) 구성 및 외부 LB(혹은 keepalived)를 통한 API 단일 진입점 구성

 

 

2. kind 소개 및 설치

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

 

 

설치

1. Docker Desktop 설치 - Link

 

Install Docker Desktop on Mac

Install Docker for Mac to get started. This guide covers system requirements, where to download, and instructions on how to install and update.

docs.docker.com

 

 

2. kind 및 툴 설치

# Install Kind
brew install kind
kind --version

# Install kubectl
brew install kubernetes-cli
kubectl version --client=true

# Install Helm
brew install helm
helm version

(옵션) 유용한 툴 설치

brew install krew
brew install kube-ps1
brew install kubecolor
brew install kubectx

 

3.kind 기본 사용 - 클러스터 배포 및 확인

(옵션) 별도 kubeconfig 지정 후 사용

# 방안1 : 환경변수 지정
export KUBECONFIG=/Users/<Username>/Downloads/kind/config

# 방안2 : 혹은 --kubeconfig ./config 지정 가능

# 클러스터 생성
kind create cluster

# kubeconfig 파일 확인
ls -l /Users/<Username>/Downloads/kind/config
-rw-------  1 <Username>  staff  5608  4 24 09:05 /Users/<Username>/Downloads/kind/config

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

# 클러스터 삭제
kind delete cluster
unset KUBECONFIG

 

# 클러스터 배포 전 확인
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
혹은
cat $KUBECONFIG # KUBECONFIG 변수 지정 사용 시

# 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
혹은
cat $KUBECONFIG # KUBECONFIG 변수 지정 사용 시

 

확인

 

 

기본 사용 : kind 동작 원리(Docker in Docker) 확인

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

 

기본 정보 확인

# 클러스터 배포 전 확인
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'

 

확인

 

포트 확인

 

 

 

 

쿠버네티스 관련 정보 조사

# 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
---------------------------------
# kubelet 상태 확인
systemctl status kubelet

# 컨테이너 확인
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
---------------------------------

 

 

파드 생성 확인

# 파드 생성
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>

 

 

 

컨트롤플레인 컨테이너 정보 확인 : 아래 “Node” Container 은 ‘myk8s-control-plane’ 컨테이너 (그림에 빨간색) 입니다

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

 

# 도커 컨테이너 확인
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

# 기본 사용자 확인
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

 

ip확인

 

dind컨테이너 확인

 

클러스터 삭제

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

 

 

 

Multi-Node Cluster (Control-plane, Nodes) with kube-ops-view & Mapping ports - 링크

 

kind – Quick Start

Quick Start This guide covers getting started with the kind command. If you are having problems please see the known issues guide. NOTE: kind does not require kubectl, but you will not be able to perform some of the examples in our docs without it. To inst

kind.sigs.k8s.io

# '컨트롤플레인, 워커 노드 1대' 클러스터 배포 : 파드에 접속하기 위한 포트 맵핑 설정
cat <<EOT> kind-2node.yaml
# two node (one workers) cluster config
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
  extraPortMappings:
  - containerPort: 31000
    hostPort: 31000
    listenAddress: "0.0.0.0" # Optional, defaults to "0.0.0.0"
    protocol: tcp # Optional, defaults to tcp
  - containerPort: 31001
    hostPort: 31001
EOT

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

# 배포 확인
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 호스트에 31000 포트 접속 시, 워커노드(실제로는 컨테이너)에 TCP 31000 포트로 연결
# 즉, 워커노드에 NodePort TCP 31000 설정 시 자신의 PC 호스트에서 접속 가능!
docker ps
docker port $CLUSTERNAME-worker
31000/tcp -> 0.0.0.0:31000
31001/tcp -> 0.0.0.0:31001

# 컨테이너 내부 정보 확인 : 필요 시 각각의 노드(?)들에 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 – Quick Start

Quick Start This guide covers getting started with the kind command. If you are having problems please see the known issues guide. NOTE: kind does not require kubectl, but you will not be able to perform some of the examples in our docs without it. To inst

kind.sigs.k8s.io

- kube-ops-view : NodePort 31000

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

 

# 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=31000 --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://localhost:31000/#scale=1.5"
echo -e "KUBE-OPS-VIEW URL = http://localhost:31000/#scale=2"

 

 

 

nginx : NodePort 31001

# 디플로이먼트와 서비스 배포
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: 31001
  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:31000-31001->31000-31001/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:31001/TCP   55s
...

# 자신의 PC에 호스트 포트 31001 접속 시 쿠버네티스 서비스에 접속 확인
open http://localhost:31001
curl -s localhost:31001 | grep -o "<title>.*</title>"
<title>Welcome to nginx!</title>

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

 

 

- 이렇게 쿠버네티스 파드에 접속 시 NodePort <포트넘버> 고정하고, kind 배포 시 사용할 포트들은 추가하여 사용하시면 됩니다.

 

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

 

 

 

Setting Kubernetes version : 특정 버전의 쿠버네티스 클러스터 배포 - 링크 Release Hub

 

https://hub.docker.com/r/kindest/node/tags

 

hub.docker.com

# 클러스터 배포 : image 로 특정 버전을 바로 지정, 해당 버전 정보는 위 도커 Hub 링크 확인
kind create cluster --image kindest/node:v1.30.4
docker images

# 노드 버전 확인
kubectl get nodes

# 삭제
kind delete cluster

 

 

 

 

 

3. 파드 & PAUSE 컨테이너

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

 

k8s CRI - Old_Docs

 

Introducing Container Runtime Interface (CRI) in Kubernetes

Editor's note: this post is part of a series of in-depth articles on what's new in Kubernetes 1.5 At the lowest layers of a Kubernetes node is the software that, among other things, starts and stops containers. We call this the “Container Runtime”. The

kubernetes.io

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

 

- 쿠버네티스 노드의 가장 낮은 계층(Low Level)에는 컨테이너를 시작하고 중지하는 것 외에도 소프트웨어가 있습니다. 이를 "컨테이너 런타임(Runc 등)"이라고 합니다. 가장 널리 알려진 컨테이너 런타임은 Docker이지만 이 분야에서 유일한 것은 아닙니다. 사실, 컨테이너 런타임 공간은 빠르게 진화해 왔습니다. 쿠버네티스를 더 확장 가능하게 만들기 위한 노력의 일환으로, "CRI"라고 하는 쿠버네티스의 컨테이너 런타임을 위한 새로운 플러그인 API를 개발해 왔습니다.

 

 

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

 

  각 컨테이너 런타임에는 고유한 강점이 있으며, 많은 사용자가 Kubernetes가 더 많은 런타임을 지원하도록 요청했습니다. Kubernetes 1.5 릴리스에서, 우리는 컨테이너 런타임 인터페이스 (CRI)를 소개하게 되어 자랑스럽습니다. 이 플러그인 인터페이스kubelet재컴파일할 필요 없이 다양한 컨테이너 런타임을 사용할 수 있도록 해줍니다. CRI는 프로토콜 버퍼 와 gRPC API , 라이브러리 로 구성되어 있으며, 추가 사양과 도구가 활발하게 개발 중입니다. CRI는 Kubernetes 1.5 에서 알파로 릴리스됩니다 .

 

쿠버네티스에서 상호 교환 가능한 컨테이너 런타임을 지원하는 것은 새로운 개념이 아닙니다. 1.3 릴리스에서 Docker 컨테이너 런타임의 대안으로 rkt 컨테이너 엔진을 활성화하는 rktnetes 프로젝트를 발표했습니다 . 그러나 Docker와 rkt는 모두 내부적이고 휘발성 있는 인터페이스를 통해 kubelet 소스 코드에 직접적이고 깊이 통합되었습니다. 이러한 통합 프로세스에는 Kubelet 내부 구조에 대한 심층적인 이해가 필요하며 쿠버네티스 커뮤니티에 상당한 유지 관리 오버헤드가 발생합니다. 이러한 요소는 신생 컨테이너 런타임에 대한 진입 장벽을 높입니다. 명확하게 정의된 추상화 계층을 제공함으로써 장벽을 제거하고 개발자가 컨테이너 런타임을 빌드하는 데 집중할 수 있도록 합니다. 이는 플러그형 컨테이너 런타임을 진정으로 활성화하고 보다 건강한 생태계를 구축하기 위한 작지만 중요한 단계입니다.

 

 

CRI

 

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

- 여기서 kubelet클라이언트 역할을 하고 CRI shim서버 역할을 합니다.

- 프로토콜 버퍼 API에는 두 가지 gRPC 서비스인 ImageServiceRuntimeService가 포함되어 있습니다. ImageService는 리포지토리에서 이미지를 가져오고, 이미지를 검사하고, 제거하는 RPC를 제공합니다. RuntimeService에는 포드와 컨테이너의 수명 주기를 관리하는 RPC와 컨테이너와 상호 작용하기 위한 호출(exec/attach/port-forward)이 포함되어 있습니다. 이미지와 컨테이너(예: Docker 및 rkt)를 모두 관리하는 모놀리식 컨테이너 런타임은 단일 소켓으로 두 서비스를 동시에 제공할 수 있습니다. 소켓은 Kubelet에서 --container-runtime-endpoint 및 --image-service-endpoint 플래그로 설정할 수 있습니다.

 

 

 

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

우리는 의도적으로 컨테이너 런타임이 내부적으로 작동하는 방식에 따라 PodSandbox를 다르게 해석할 수 있는 여지를 남겨둡니다. 하이퍼바이저 기반 런타임의 경우 PodSandbox는 가상 머신을 나타낼 수 있습니다. Docker와 같은 다른 경우 Linux 네임스페이스일 수 있습니다. PodSandbox는 Pod 리소스 사양을 준수해야 합니다. v1alpha1 API에서 이는 kubelet이 생성하여 런타임에 전달하는 Pod 수준 cgroup 내의 모든 프로세스를 시작하여 달성됩니다.

 

- 포드를 시작하기 전에 kubelet RuntimeService.RunPodSandbox호출하여 환경을 만듭니다. 여기에는 포드에 대한 네트워킹 설정(예: IP 할당)이 포함됩니다. PodSandbox가 활성화되면 개별 컨테이너를 독립적으로 생성/시작/중지/제거할 수 있습니다. 포드를 삭제하기 위해 kubelet은 PodSandbox를 중지하고 제거하기 전에 컨테이너를 중지하고 제거합니다.

 

- Kubelet은 RPC를 통해 컨테이너의 수명 주기를 관리하고, 컨테이너 수명 주기 후크와 활성/준비 확인을 실행하며, Pod의 재시작 정책을 준수합니다.

 

 

파드(Pod) : 컨테이너 애플리케이션의 기본 단위를 파드(Pod)라고 부르며, 파드는 1개 이상의 컨테이너로 구성된 컨테이너의 집합 - 링크 , code

 

  • Pod 는 1개 이상의 컨테이너를 가질 수 있습니다 ⇒ sidecar 패턴 등
  • Pod 내에 실행되는 컨테이너들은 반드시 동일한 노드에 할당되며 동일한 생명 주기를 갖습니다 → Pod 삭제 시, Pod 내 모든 컨테이너가 삭제
  • Pod IP - Pod 는 노드 IP 와 별개로 클러스터 내에서 접근 가능한 IP를 할당 받으며, 다른 노드에 위치한 Pod 도 NAT 없이 Pod IP로 접근 가능 ⇒ 요걸 CNI 해줌!
  • IP 공유 - Pod 내에 있는 컨테이너들은 서로 IP를 공유, 컨테이너끼리는 localhost 통해 서로 접근하며 포트를 이용해 구분
    • pause 컨테이너가 'parent' 처럼 network ns 를 만들어 주고, 내부의 컨테이너들은 해당 net ns 를 공유 ← 세상에서 제일 많이 사용되는 컨테이너!
    • 쿠버네티스에서 pause 컨테이너는 포드의 모든 컨테이너에 대한 "부모 컨테이너" 역할을 합니다 - Link
    • 일시 중지 컨테이너에는 두 가지 핵심 책임이 있습니다.
    • 첫째, 포드에서 Linux 네임스페이스 공유의 기반 역할을 합니다.
    • 둘째, PID(프로세스 ID) 네임스페이스 공유가 활성화되면 각 포드에 대한 PID 1 역할을 하며 좀비 프로세스를 거둡니다.
  • volume 공유 - Pod 안의 컨테이너들은 동일한 볼륨과 연결이 가능하여 파일 시스템을 기반으로 서로 파일을 주고받을 수 있음
  • Pod는 리소스 제약이 있는 격리된 환경의 애플리케이션 컨테이너 그룹으로 구성됩니다. CRI에서 이 환경을 PodSandbox라고 합니다.
  • 포드를 시작하기 전에 kubelet은 RuntimeService.RunPodSandbox를 호출하여 환경을 만듭니다. (파드 네트워킹 설정 ’IP 할당’ 포함)
  • Kubelet은 RPC를 통해 컨테이너의 수명 주기를 관리하고, 컨테이너 수명 주기 후크와 활성/준비 확인을 실행하며, Pod의 재시작 정책을 준수합니다

pause.c - Code

 

kubernetes/build/pause/linux/pause.c at master · kubernetes/kubernetes

Production-Grade Container Scheduling and Management - kubernetes/kubernetes

github.com

 

 

 

 

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

# '컨트롤플레인, 워커 노드 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 배율)
echo -e "KUBE-OPS-VIEW URL = http://localhost:31000/#scale=1.5"
echo -e "KUBE-OPS-VIEW URL = http://localhost:31000/#scale=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/

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

 

퍼즈 컨테이너 확인

 

소켓 확인

 

 

신규 파드를 배포하고 확인

# [터미널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

 

퍼즈 컨테이너 정보는 볼 수 없다.

 

 

myweb2 파드 정보 : myweb2 파드에 2개의 컨테이너가 동작

그림 가운데 uts ns 추가 필요

- myweb2.yaml : containers 가 복수형을 주목!

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

 

 

배포 후 확인NET IPC UTS 공유 확인

# [터미널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

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 f0401ef30af36 ifconfig
crictl exec -its 1f83b080de52d 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 f0401ef30af36 | jq
crictl inspect 1f83b080de52d | 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"
          }
        ],
...

 

 

netshoot 내부에 80이 열려있는 건 네트워크를 공유하고 있기 때문이다.

 

curl로 접근

 

 

동일한 파드의 두개의 컨테이너가 개별 id를 갖고 있다

 

 

컨테이너의 pid확인 

 

퍼즈컨테이너

 

 

 

 

 

파드 삭제: kubectl delete pod myweb2

 

'study > KANS 3기' 카테고리의 다른 글

KANS 3기 3주차 첫번째  (0) 2024.09.18
KANS 3기 3주차 실습환경 구축  (0) 2024.09.08
KANS 3기 2주차 두번째  (0) 2024.09.07
KANS 3기 1주차 두번째  (0) 2024.08.31
KANS 3기 1주차 첫번째  (0) 2024.08.31