Kubernetes

[ Kans 3 Study - 2w ] 2. kind 기본 사용

su''@ 2024. 9. 8. 08:53
CloudNetaStudy - Kubernets Networtk 3기 실습 스터디 게시글입니다.

 

 

https://kind.sigs.k8s.io/

 

kind 는 Docker 컨테이너 "노드"를 사용하여 로컬 Kubernetes 클러스터를 실행하기 위한 도구입니다.
kind는 주로 Kubernetes 자체를 테스트하기 위해 설계되었지만 로컬 개발이나 CI에도 사용될 수 있습니다.

 

 

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

 

 

2. 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

 

3. 기본 정보 확인
더보기
# 클러스터 배포 전 확인
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  # api 서버 health 체크
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'

 

# cluster 및 노드 생성

 

# node, pod 정보, namespace 확인

 

#cat ~/.kube/config

더보기
cat ~/.kube/config
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJ...U5EIENFUlRJRklDQVRFLS0tLS0K
    server: https://127.0.0.1:43591
  name: kind-myk8s
contexts:
- context:
    cluster: kind-myk8s
    user: kind-myk8s
  name: kind-myk8s
current-context: kind-myk8s
kind: Config
preferences: {}
users:
- name: kind-myk8s
  user:
    client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS...eDExT2xNWmpIbFIwWDJLaGJRN0pNeCtWeW01c0E1c0J6WmtlCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
    client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSB...d6cisvK0xDcUFCWkxTUFJObFM0SnVENHJCVW1CQ2plOVE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=

 

4. 쿠버네티스 관련 정보 조사
더보기
# 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
---------------------------------

 

# kubelet 상태가 running 으로 나왔으나 error log 있어 containerd,  kubelet 재시작 후 다시 status 확인

 

# kubelet 정상 동작 확인

 

#  컨테이너 확인, WorkerNode안에서는 containerd 실행으로 crictl을 사용해야 함

 

# kube-proxy 확인

더보기
root@myk8s-worker:/# pstree
systemd-+-containerd---9*[{containerd}]
        |-containerd-shim-+-kube-proxy---7*[{kube-proxy}]
        |                 |-pause
        |                 `-12*[{containerd-shim}]
        |-containerd-shim-+-kindnetd---9*[{kindnetd}]
        |                 |-pause
        |                 `-12*[{containerd-shim}]
        |-kubelet---11*[{kubelet}]
        `-systemd-journal
root@myk8s-worker:/# pstree -p
systemd(1)-+-containerd(1582)-+-{containerd}(1583)
           |                  |-{containerd}(1584)
           |                  |-{containerd}(1585)
           |                  |-{containerd}(1586)
           |                  |-{containerd}(1587)
           |                  |-{containerd}(1588)
           |                  |-{containerd}(1589)
           |                  |-{containerd}(1590)
           |                  `-{containerd}(1616)
           |-containerd-shim(274)-+-kube-proxy(376)-+-{kube-proxy}(392)
           |                      |                 |-{kube-proxy}(393)
           |                      |                 |-{kube-proxy}(394)
           |                      |                 |-{kube-proxy}(395)
           |                      |                 |-{kube-proxy}(398)
           |                      |                 |-{kube-proxy}(435)
           |                      |                 `-{kube-proxy}(438)
           |                      |-pause(313)
           |                      |-{containerd-shim}(275)
           |                      |-{containerd-shim}(276)
           |                      |-{containerd-shim}(277)
           |                      |-{containerd-shim}(278)
           |                      |-{containerd-shim}(279)
           |                      |-{containerd-shim}(280)
           |                      |-{containerd-shim}(286)
           |                      |-{containerd-shim}(287)
           |                      |-{containerd-shim}(288)
           |                      |-{containerd-shim}(396)
           |                      |-{containerd-shim}(397)
           |                      `-{containerd-shim}(669)
           |-containerd-shim(298)-+-kindnetd(554)-+-iptables(1734)
           |                      |               |-{kindnetd}(573)
           |                      |               |-{kindnetd}(574)
           |                      |               |-{kindnetd}(575)
           |                      |               |-{kindnetd}(576)
           |                      |               |-{kindnetd}(579)
           |                      |               |-{kindnetd}(580)
           |                      |               |-{kindnetd}(581)
           |                      |               |-{kindnetd}(604)
           |                      |               `-{kindnetd}(607)
           |                      |-pause(332)
           |                      |-{containerd-shim}(299)
           |                      |-{containerd-shim}(300)
           |                      |-{containerd-shim}(301)
           |                      |-{containerd-shim}(302)
           |                      |-{containerd-shim}(303)
           |                      |-{containerd-shim}(304)
           |                      |-{containerd-shim}(305)
           |                      |-{containerd-shim}(309)
           |                      |-{containerd-shim}(311)
           |                      |-{containerd-shim}(566)
           |                      |-{containerd-shim}(577)
           |                      `-{containerd-shim}(578)
           |-kubelet(1642)-+-{kubelet}(1643)
           |               |-{kubelet}(1644)
           |               |-{kubelet}(1645)
           |               |-{kubelet}(1646)
           |               |-{kubelet}(1647)
           |               |-{kubelet}(1648)
           |               |-{kubelet}(1649)
           |               |-{kubelet}(1651)
           |               |-{kubelet}(1654)
           |               |-{kubelet}(1657)
           |               `-{kubelet}(1658)
           `-systemd-journal(1691)
root@myk8s-worker:/# ps afxuwww |grep proxy
root        1739 16.6  0.0   3324  1568 pts/1    S+   05:29   0:00  \_ grep proxy
root         376  0.2  0.6 1290896 53932 ?       Ssl  04:51   0:04  \_ /usr/local/bin/kube-proxy --config=/var/lib/kube-proxy/config.conf --hostname-override=myk8s-worker
root@myk8s-worker:/# iptables -t filter -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-N KUBE-EXTERNAL-SERVICES
-N KUBE-FIREWALL
-N KUBE-FORWARD
-N KUBE-KUBELET-CANARY
-N KUBE-NODEPORTS
-N KUBE-PROXY-CANARY
-N KUBE-PROXY-FIREWALL
-N KUBE-SERVICES
-A INPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes load balancer firewall" -j KUBE-PROXY-FIREWALL
-A INPUT -m comment --comment "kubernetes health check service ports" -j KUBE-NODEPORTS
-A INPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes externally-visible service portals" -j KUBE-EXTERNAL-SERVICES
-A INPUT -j KUBE-FIREWALL
-A FORWARD -m conntrack --ctstate NEW -m comment --comment "kubernetes load balancer firewall" -j KUBE-PROXY-FIREWALL
-A FORWARD -m comment --comment "kubernetes forwarding rules" -j KUBE-FORWARD
-A FORWARD -m conntrack --ctstate NEW -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A FORWARD -m conntrack --ctstate NEW -m comment --comment "kubernetes externally-visible service portals" -j KUBE-EXTERNAL-SERVICES
-A OUTPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes load balancer firewall" -j KUBE-PROXY-FIREWALL
-A OUTPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A OUTPUT -j KUBE-FIREWALL
-A KUBE-FIREWALL ! -s 127.0.0.0/8 -d 127.0.0.0/8 -m comment --comment "block incoming localnet connections" -m conntrack ! --ctstate RELATED,ESTABLISHED,DNAT -j DROP
-A KUBE-FORWARD -m conntrack --ctstate INVALID -m nfacct --nfacct-name  ct_state_invalid_dropped_pkts -j DROP
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
root@myk8s-worker:/# iptables -t raw -S
-P PREROUTING ACCEPT
-P OUTPUT ACCEPT
root@myk8s-worker:/# iptables -t security -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

 

 

5. 파드 생성 및 확인
더보기
# 파드 생성
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>

 

 

6. 컨트롤플레인 컨테이너 정보 확인 : 아래 “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  # 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

 

# DinD 컨테이너 확인 : crictl 사용 확인

 

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

DinD 컨테이너는 호스트에서 확인 불가

 

 

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

 

## DinD 구성 단점 ( 정리 내용 출처 https://blog.naver.com/yu3papa/223571248958 )

  • DinD 이용하기 위해서는 새로운 도커 데몬 컨테이너를 실행시킬 때, --privileged 옵션을 이용할 필요가 있습니다. 이 옵션은 컨테이너가 호스트 머신의 대부분의 권한을 얻을 수 있게 해줍니다.
  • 하지만 이 방법은 보안상으로 아주 좋지 않은 환경을 만듭니다... 예를들면, 거의 대부분의 장치들에 접근이 가능하여, 호스트의 ”/boot“를 컨테이너에서 마운트 하여, initramfs나, 부트로더를 수정하거나 삭제하는 등등의 작업을 할 수도 있습니다.

https://cloudnativenow.com/topics/cloudnativesecurity/why-running-a-privileged-container-is-not-a-good-idea/

 

Why Running a Privileged Container is Not a Good Idea

A privileged container has all the capabilities a host can perform. And that's a bad idea. Here's why, and what you can do about it.

cloudnativenow.com

 

 

8.  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: 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 - 링크
  • 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"

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

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

 

 

# nginx : NodePort 31001

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

더보기
# 디플로이먼트와 서비스 배포
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