쿠버네티스
Kubernetes Components : K8S 클러스터는 Controle Plane(마스터)와 Node(노드)로 구성 - 링크
마스터노드
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
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) 확인
기본 정보 확인
# 클러스터 배포 전 확인
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 - 링크
# '컨트롤플레인, 워커 노드 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 - 링크
- kube-ops-view : NodePort 31000
# 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
# 클러스터 배포 : 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
- 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
- Kubelet은 gRPC 프레임워크를 사용하여 Unix 소켓을 통해 컨테이너 런타임(또는 런타임용 CRI shim)과 통신합니다.
- 여기서 kubelet은 클라이언트 역할을 하고 CRI shim은 서버 역할을 합니다.
- 프로토콜 버퍼 API에는 두 가지 gRPC 서비스인 ImageService와 RuntimeService가 포함되어 있습니다. 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
실습 환경 구성 : 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개의 컨테이너가 동작
- 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 |