KIND 소개와 일부 예제
Kubernetes IN Docker (KIND)
배경
쿠버네티스 실습을 하려고 하는데, 윈도우에서는 현재 hyperv를 사용하고 있고, 맥의 사양이 좋지 못해 환경 구성이 쉽지 않았으나 윈도우에서 실습환경을 구성하는 것보다는 맥에서 환경을 구성하는 것으로 결정하고 저 멀리 바라보던 kind를 드디어 써 봐야 할 때가 왔다고 느낌
Cloudneta 스터디 KANS에서 “mac : k8s 실습 환경 구성 with kind”을 통해 제대로 된 환경 구축을 해보려고 함
Kind?
k8s 클러스터를 Docker 컨테이너 내에서 실행할 수 있도록 하는 도구.
로컬 개발 환경에서 가볍고 빠르게 Kubernetes 클러스터를 배포하고 테스트 가능
kind achitecture
kind는 쿠버네티스 노드를 도커 컨테이너를 사용해서 간단하게 클러스터를 구축 할 수 있게 해줌.
설정이 간단하고, 클러스터를 바로 생성 및 삭제 가능. 실환경과 비슷한 구성을 만드는데 최적.
Kind 설치
아키텍처 확인
~ ❯ sysctl -a |grep -i brand
machdep.cpu.brand_string: Apple M1
~ ❯ arch
arm64
자신과 맞는 환경의 CPU architecture를 선택 해야함.
https://hub.docker.com/_/ubuntu
도커 데스크탑 설치
https://docs.docker.com/desktop/install/mac-install/
kind 툴 설치
kind를 설치할때 gh가 없으면 설치가 안될 수 있음.
kind, kubectl, helm 설치
# Install Kind
**brew install kind gh**
kind --version
# Install kubectl
**brew install kubernetes-cli**
kubectl version --client=true
# Install Helm
**brew install helm**
helm version
kind cluster 생성
필요 경로를 생성하고 KUBECONFIG 환경변수를 설정하고 cluster 생성
mkdir -p ~/study/kind/
export KUBECONFIG=~/study/kind/config
**kind create cluster**
클러스터 배포 확인
~/study/kind ❯ kind get clusters
kind
~/study/kind ❯ kind get nodes
kind-control-plane
~/study/kind ❯ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:50201
CoreDNS is running at https://127.0.0.1:50201/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
~/study/kind ❯ kubectl get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
kind-control-plane Ready control-plane 63s v1.31.0 172.18.0.2 <none> Debian GNU/Linux 12 (bookworm) 5.15.49-linuxkit-pr containerd://1.7.18
~/study/kind ❯ kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-6f6b679f8f-ng62x 1/1 Running 0 70s
kube-system coredns-6f6b679f8f-zk9rg 1/1 Running 0 70s
kube-system etcd-kind-control-plane 1/1 Running 0 78s
kube-system kindnet-s6f5w 1/1 Running 0 70s
kube-system kube-apiserver-kind-control-plane 1/1 Running 0 77s
kube-system kube-controller-manager-kind-control-plane 1/1 Running 0 78s
kube-system kube-proxy-vs8sg 1/1 Running 0 70s
kube-system kube-scheduler-kind-control-plane 1/1 Running 0 77s
local-path-storage local-path-provisioner-57c5987fd4-j8s9d 1/1 Running 0 70s
~/study/kind ❯ kubectl get componentstatuses
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME STATUS MESSAGE ERROR
controller-manager Healthy ok
scheduler Healthy ok
etcd-0 Healthy ok
nginx pod 생성 테스트
kind클러스터 위에 실제 nginx pod 생성.
~/study/kind ❯ kubectl run nginx --image=nginx:alpine
pod/nginx created
~/study/kind ❯ kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 18s 10.244.0.5 kind-control-plane <none> <none>
~/study/kind ❯ kubectl describe node | grep Taints
Taints: <none>
kind는 기본적으로 kind create cluster
명령어를 사용하면 싱글 노드 클러스터가 생성되며 control plane + worker node 역할을 동시에 수행함.
싱글 노드 클러스터에는 taints 설정이 없으므로 파드가 직접 배포 될 수 있음
https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/
KIND로 특정 버전 클러스터 배포
# 클러스터 배포 : 노드 이미지 버전과 sha256 같이 맞지 않을 경우 워커 노드가 제대로 join 되지 않는다
cat <<EOT> **kind-v131.yaml**
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
image: **kindest/node:v1.31.0@sha256:53df588e04085fd41ae12de0c3fe4c72f7013bba32a20e7325357a1ac94ba865**
- role: worker
image: **kindest/node:v1.31.0@sha256:53df588e04085fd41ae12de0c3fe4c72f7013bba32a20e7325357a1ac94ba865**
EOT
kind create cluster --config **kind-v131.yaml**
# 노드 버전 확인 : 워커노드가 join 시 시간이 조금 소용된다. 기다리면 잘 join 완료 된다
**kubectl get nodes**
kind 레포의 releases 페이지에서 아래 형태의 image를 사용한다 sha256도 포함
v1.31.0: kindest/node:v1.31.0@sha256:53df588e04085fd41ae12de0c3fe4c72f7013bba32a20e7325357a1ac94ba865
v1.30.4: kindest/node:v1.30.4@sha256:976ea815844d5fa93be213437e3ff5754cd599b040946b5cca43ca45c2047114
v1.30.3: kindest/node:v1.30.3@sha256:bf91e1ef2f7d92bb7734b2b896b3dddea98f0496b34d96e37dd5d7df331b7e56
v1.29.8: kindest/node:v1.29.8@sha256:d46b7aa29567e93b27f7531d258c372e829d7224b25e3fc6ffdefed12476d3aa
v1.29.7: kindest/node:v1.29.7@sha256:f70ab5d833fca132a100c1f95490be25d76188b053f49a3c0047ff8812360baf
v1.28.13: kindest/node:v1.28.13@sha256:45d319897776e11167e4698f6b14938eb4d52eb381d9e3d7a9086c16c69a8110
v1.28.12: kindest/node:v1.28.12@sha256:fa0e48b1e83bb8688a5724aa7eebffbd6337abd7909ad089a2700bf08c30c6ea
v1.27.16: kindest/node:v1.27.17@sha256:3fd82731af34efe19cd54ea5c25e882985bafa2c9baefe14f8deab1737d9fabe
v1.26.15: kindest/node:v1.26.15@sha256:1cc15d7b1edd2126ef051e359bf864f37bbcf1568e61be4d2ed1df7a3e87b354
v1.25.16: kindest/node:v1.25.16@sha256:6110314339b3b44d10da7d27881849a87e092124afab5956f2e10ecdb463b025
https://github.com/kubernetes-sigs/kind/releases
KIND로 멀티 노드 클러스터 생성
2 노드 클러스터를 생성하는 예시
control plane 1, worker node 1 구성으로 배포
아래 설정파일에는 control plane과 worker node가 하나씩 정의되어 있음. worker node에는 extraPortMapping이 설정되어 있어 클러스터 내부의 특정 포트를 호스트 포트에 매핑.
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
클러스터 배포 확인
정상 배포 된 부분 확인
~/study/kind ❯ kind get clusters
myk8s
~/study/kind ❯ kind get nodes --name $CLUSTERNAME
myk8s-control-plane
myk8s-worker
~/study/kind ❯ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
myk8s-control-plane Ready control-plane 2m59s v1.31.0 172.18.0.3 <none> Debian GNU/Linux 12 (bookworm) 5.15.49-linuxkit-pr containerd://1.7.18
myk8s-worker Ready <none> 2m48s v1.31.0 172.18.0.2 <none> Debian GNU/Linux 12 (bookworm) 5.15.49-linuxkit-pr containerd://1.7.18
nginx deployment & service 배포
디플로이먼트에는 replicas:2로 설정되어 2개의 pod로 배포하고 80포트를 열어줌.
서비스에는 외부 접근 및 내부 포트는 80으로 설정하고 클러스터 외부에서 접근 가능하도록 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
정상 동작 확인
삭제
**kubectl delete deploy,svc deploy-websr**
docker image 를 kind로 주입
이미지 생성
custom nginx 이미지를 생성해서 kind로 이미지를 주입.
dockerfile로 build 후
# index.html 파일 작성
echo "Hello myk8s" > index.html
# Dockerfile 파일 작성
**cat << EOF > Dockerfile**
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
**EOF**
docker build -t m**mweb:1.0** .
docker images
이미지 주입
현재 kind 내에 이미지가 존재 하지 않는 사실 확인 후 위에서 생성한 이미지를 kind load 명령어로 이미지를 주입하고 배포
**~/study/kind ❯ docker exec -it $CLUSTERNAME-control-plane crictl images ryusstory@Rucas-MacBookPro
IMAGE TAG IMAGE ID SIZE
docker.io/kindest/kindnetd v20240813-c6f155d6 6a23fa8fd2b78 33.3MB
docker.io/kindest/local-path-helper v20230510-486859a6 d022557af8b63 2.92MB
docker.io/kindest/local-path-provisioner v20240813-c6f155d6 282f619d10d4d 17.4MB
registry.k8s.io/coredns/coredns v1.11.1 2437cf7621777 16.5MB
registry.k8s.io/etcd 3.5.15-0 27e3830e14027 66.5MB
registry.k8s.io/kube-apiserver-arm64 v1.31.0 add78c37da6e2 92.6MB
registry.k8s.io/kube-apiserver v1.31.0 add78c37da6e2 92.6MB
registry.k8s.io/kube-controller-manager-arm64 v1.31.0 c50d473e11f63 86.9MB
registry.k8s.io/kube-controller-manager v1.31.0 c50d473e11f63 86.9MB
registry.k8s.io/kube-proxy-arm64 v1.31.0 c573e1357a14e 95.9MB
registry.k8s.io/kube-proxy v1.31.0 c573e1357a14e 95.9MB
registry.k8s.io/kube-scheduler-arm64 v1.31.0 8377f1e14db4c 67MB
registry.k8s.io/kube-scheduler v1.31.0 8377f1e14db4c 67MB
registry.k8s.io/pause 3.10 afb61768ce381 268kB
~ ❯ docker exec -it $CLUSTERNAME-worker crictl images ryusstory@Rucas-MacBookPro
IMAGE TAG IMAGE ID SIZE
docker.io/kindest/kindnetd v20240813-c6f155d6 6a23fa8fd2b78 33.3MB
docker.io/kindest/local-path-helper v20230510-486859a6 d022557af8b63 2.92MB
docker.io/kindest/local-path-provisioner v20240813-c6f155d6 282f619d10d4d 17.4MB
docker.io/library/nginx alpine 70594c812316a 19.6MB
registry.k8s.io/coredns/coredns v1.11.1 2437cf7621777 16.5MB
registry.k8s.io/etcd 3.5.15-0 27e3830e14027 66.5MB
registry.k8s.io/kube-apiserver-arm64 v1.31.0 add78c37da6e2 92.6MB
registry.k8s.io/kube-apiserver v1.31.0 add78c37da6e2 92.6MB
registry.k8s.io/kube-controller-manager-arm64 v1.31.0 c50d473e11f63 86.9MB
registry.k8s.io/kube-controller-manager v1.31.0 c50d473e11f63 86.9MB
registry.k8s.io/kube-proxy-arm64 v1.31.0 c573e1357a14e 95.9MB
registry.k8s.io/kube-proxy v1.31.0 c573e1357a14e 95.9MB
registry.k8s.io/kube-scheduler-arm64 v1.31.0 8377f1e14db4c 67MB
registry.k8s.io/kube-scheduler v1.31.0 8377f1e14db4c 67MB
registry.k8s.io/pause 3.10 afb61768ce381 268kB
~/study/kind ❯ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mmweb 1.0 5ad1a0471d00 About a minute ago 47MB
kindest/node <none> 9d05f134f12f 3 weeks ago 1.04GB
arm64v8/ubuntu latest 1a799365aa63 5 weeks ago 101MB
amd64/ubuntu latest edbfe74c41f8 5 weeks ago 78.1MB
gcr.io/k8s-minikube/kicbase v0.0.42 62753ecb37c4 10 months ago 1.11GB
gcr.io/k8s-minikube/kicbase v0.0.37 55c37b5c9b24 19 months ago 1.06GB
~/study/kind ❯ kind load docker-image mmweb:1.0 --name $CLUSTERNAME
Image: "mmweb:1.0" with ID "sha256:5ad1a0471d0079a9394e53787574cf2a1f98be70735fbfdf6dfe0d5cf48616f7" not yet present on node "myk8s-control-plane", loading...
Image: "mmweb:1.0" with ID "sha256:5ad1a0471d0079a9394e53787574cf2a1f98be70735fbfdf6dfe0d5cf48616f7" not yet present on node "myk8s-worker", loading...**
custom 이미지 pod 배포
mmweb:1.0 이미지로 배포 후 replias:2로 스케일링
~/study/kind ❯ kubectl create deployment deploy-myweb --image=mmweb:1.0
deployment.apps/deploy-myweb created
~/study/kind ❯ kubectl get deploy,pod
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/deploy-myweb 1/1 1 1 10s
NAME READY STATUS RESTARTS AGE
pod/deploy-myweb-79cd55b85d-d26vr 1/1 Running 0 10s
~/study/kind ❯ kubectl scale deployment deploy-myweb --replicas 2
deployment.apps/deploy-myweb scaled
~/study/kind ❯ kubectl get deploy,pod
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/deploy-myweb 2/2 2 2 19s
NAME READY STATUS RESTARTS AGE
pod/deploy-myweb-79cd55b85d-5tkbp 1/1 Running 0 5s
pod/deploy-myweb-79cd55b85d-d26vr 1/1 Running 0 19s
~/study/kind ❯
서비스 배포
클러스터 내부에서 실행중인 custom nginx 애플리케이션에 외부에서 접근할 수 있도록 서비스 설정
nodeport 31001로 해서 설정
# 서비스 배포
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: **Service**
metadata:
name: deploy-myweb
spec:
type: **NodePort**
ports:
- name: svc-mywebport
nodePort: **31001**
port: 80
selector:
**app: deploy-myweb**
EOF
확인
배포한 서비스와 엔드포인트가 제대로 설정되었는지 확인 및 실제 호출
~/study/kind ❯ kubectl get svc,ep deploy-myweb
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/deploy-myweb NodePort 10.96.178.5 <none> 80:31001/TCP 15s
NAME ENDPOINTS AGE
endpoints/deploy-myweb 10.244.1.4:80,10.244.1.5:80 15s
~/study/kind ❯ curl localhost:31001
Hello myk8s
클러스터 삭제
**kind delete cluster --name $CLUSTERNAME**
ingress
https://kind.sigs.k8s.io/docs/user/ingress/
클러스터 배포
Kind 클러스터를 생성하면서 노드 라벨과 포트 매핑을 설정하는 방법.
kubeletExtraArgs에서 node-labels: "ingress-ready=true"를 설정하면, 해당 노드에 "ingress-ready"라는 라벨이 붙음. Ingress 컨트롤러가 배포될 노드를 지정 가능
# 클러스터 배포 : 노드 라벨, 포트 맵핑
**cat <<EOT> kind-ingress.yaml**
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: **control-plane**
**kubeadmConfigPatches**:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
**node-labels**: "**ingress-ready=true**"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
- containerPort: 30000
hostPort: 30000
**EOT**
kind create cluster --config **kind-ingress.yaml** --name **myk8s**
라벨 확인
ingress-ready 라벨 확인
~/study/kind ❯ kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
myk8s-control-plane Ready control-plane 3h35m v1.31.0 beta.kubernetes.io/arch=arm64,beta.kubernetes.io/os=linux,
ingress-ready=true,kubernetes.io/arch=arm64,kubernetes.io/hostname=myk8s-control-plane,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=
ingress-nginx 배포
클러스터에서 ingress nginx 컨트롤러 배포.
클러스터 외부에서 80, 443 포트를 통해 Ingress 컨트롤러에 접근할 수 있도록 포트 포워딩이 설정됨
Ingress 컨트롤러가 Taints가 설정된 노드에서도 실행될 수 있도록 Toleration 설정
ingress-ready=true 라벨이 지정된 노드에 배포.
**kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml**
배포 확인
~/study/kind ❯ kubectl get deploy,svc,ep ingress-nginx-controller -n ingress-nginx
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-nginx-controller 1/1 1 1 13m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller NodePort 10.96.24.172 <none> 80:31883/TCP,443:32555/TCP 13m
NAME ENDPOINTS AGE
endpoints/ingress-nginx-controller 10.244.0.7:443,10.244.0.7:80 13m
ingress 트래픽 라우팅 설정
마이크로 서비스 아키텍처에서 경로별 pod를 다르게 할 때 배포하기가 쉬워짐.
~/study/kind ❯ kubectl apply -f https://kind.sigs.k8s.io/examples/ingress/usage.yaml
pod/foo-app created
service/foo-service created
pod/bar-app created
service/bar-service created
Warning: path /foo(/|$)(.*) cannot be used with pathType Prefix
Warning: path /bar(/|$)(.*) cannot be used with pathType Prefix
ingress.networking.k8s.io/example-ingress created
yaml 파일 설명
pod 2개를 배포 (foo, bar)하고 /foo 경로는 foo-service로 보내고, /bar는 bar-service로 보냄
foo-app은 8080포트 사용
nginx.ingress.kubernetes.io/rewrite-target 어노테이션을 통해 특정 경로로 들어온 경우 특정 서비스로 보냄. 관련 문서 : https://kubernetes.github.io/ingress-nginx/examples/rewrite/
이를 통해 http://localhost/foo http://localhost/bar 형태로 분리가 가능함
특정 도메인 아래 여러 경로에 대한 트래픽을 효율적으로 관리가 가능
kind: Pod
apiVersion: v1
metadata:
name: foo-app
labels:
app: foo
spec:
containers:
- command:
- /agnhost
- netexec
- --http-port
- "8080"
image: registry.k8s.io/e2e-test-images/agnhost:2.39
name: foo-app
---
kind: Service
apiVersion: v1
metadata:
name: foo-service
spec:
selector:
app: foo
ports:
# Default port used by the image
- port: 8080
---
kind: Pod
apiVersion: v1
metadata:
name: bar-app
labels:
app: bar
spec:
containers:
- command:
- /agnhost
- netexec
- --http-port
- "8080"
image: registry.k8s.io/e2e-test-images/agnhost:2.39
name: bar-app
---
kind: Service
apiVersion: v1
metadata:
name: bar-service
spec:
selector:
app: bar
ports:
# Default port used by the image
- port: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- http:
paths:
- pathType: Prefix
path: /foo(/|$)(.*)
backend:
service:
name: foo-service
port:
number: 8080
- pathType: Prefix
path: /bar(/|$)(.*)
backend:
service:
name: bar-service
port:
number: 8080
---
배포 확인
~/study/kind ❯ kubectl get ingress,pod -owide
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/example-ingress <none> * 80 5s
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/bar-app 0/1 ContainerCreating 0 5s <none> myk8s-control-plane <none> <none>
pod/foo-app 0/1 ContainerCreating 0 5s <none> myk8s-control-plane <none> <none>
~/study/kind ❯ kubectl get ingress,pod -owide
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/example-ingress <none> * localhost 80 7m1s
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/bar-app 1/1 Running 0 7m1s 10.244.0.9 myk8s-control-plane <none> <none>
pod/foo-app 1/1 Running 0 7m1s 10.244.0.8 myk8s-control-plane <none> <none>
호출
정상 동작 확인.
~/study/kind ❯ curl localhost/foo/hostname
foo-app%
~/study/kind ❯ curl localhost/bar/hostname
bar-app%
~/study/kind ❯ kubectl exec -it foo-app -- curl localhost:8080/foo/hostname
NOW: 2024-09-07 17:42:50.949731213 +0000 UTC m=+3515.197047484%
~/study/kind ❯ kubectl exec -it foo-app -- curl localhost:8080/hostname
foo-app%
~/study/kind ❯ kubectl exec -it bar-app -- curl localhost:8080/bar/hostname
NOW: 2024-09-07 17:43:03.215489719 +0000 UTC m=+3526.635941239%
~/study/kind ❯ kubectl exec -it bar-app -- curl localhost:8080/hostname
bar-app%
~/study/kind ❯
#