kind(kubernetes in docker)란?
KANS 스터디 2주차 실습환경인 kind 알아보기
💡 KANS 스터디
CloudNet에서 주관하는 KANS(Kubernetes Advanced Networking Study)으로 쿠버네티스 네트워킹 스터디입니다. 아래의 글은 스터디의 내용을 기반으로 작성했습니다.스터디에 관심이 있으신 분은 CloudNet Blog를 참고해주세요.
들어가며
이번 네트워크 실습은 kind(Kubernetes in Docker)로 클러스터를 배포하여 진행합니다. 쿠버네티스 네트워크에 대해 알아보기전에 우리의 실습 환경인 kind에 대해 알아봅니다.
아래의 글은 CloudNet 블로그를 참고해서 작성했습니다.
kind
kind란?
kind의 리포지터리 About에는, “Kubernetes IN Docker - local clusters for testing Kubernetes”라고 소개되어있습니다.
문서에 나온 kind의 장점으로는 아래와 같습니다.
- Multi-node(HA) 클러스터 지원
- kind supports building Kubernetes release builds from source
- support for make / bash or docker, in addition to pre-published builds
- Linux, macOS, 윈도우 지원
- CNCF 인증된 Kubernetes installer
이번 실습을 하면서 느낀 장점은 노드를 컨테이너로 사용하기에 로컬에서 여러 노드를 돌릴 수 있고, 포트포워딩을 통해 편하게 통신도 가능하다는 점같습니다.
- 실제 VM 혹은 서버를 준비해서 배포한다면 클러스터 프로비저닝이 까다롭습니다. 반면, kind는 그런 과정이 필요 없습니다.
아키텍처
내부적으로 kubeadm을 사용하여 클러스터를 형성한다고 합니다.
그림 출처: https://kind.sigs.k8s.io/
실습
설치방법
사전에 Docker가 설치된 환경이라고 가정합니다.
- kind 설치(윈도우 환경이라면, 문서를 참고)
1
brew install kind
- 아래의 YAML파일을 기반으로 배포를 진행합니다.
1
2
3
4
5
6
7
8
9
10
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
extraPortMappings:
- containerPort: 30000
hostPort: 30000
- containerPort: 30001
hostPort: 30001
클러스터 생성
1
kind create cluster --config {위의 YAML 경로} --name {원하는 클러스터명}
- 정보 확인
1
docker ps
사진을 통해 정보를 확인하면 controlplane의 포트포워딩은 61208 > 6443으로 된 것을 확인할 수 있습니다. worker node의 포워딩은 위에서 설정한 것과 같이 30000,30001번을 포워딩했습니다.
kind 명령어로 클러스터를 배포하면, 자동으로 kubeconfig 파일에 관련 내용을 추가해주는데요, 한번 내용을 확인해봅니다.
1
cat ~/.kube/config
“server: https://127.0.0.1:61208” 으로 설정값을 넣었습니다. 위에서 봤던 controlplane 포트워딩 정보와 같고, 우리가 로컬에서 kubectl 명령을 날리면 61208 > 6443 으로 접속하여 apiserver에 전달됩니다.
1
2
3
4
k get nodes
NAME STATUS ROLES AGE VERSION
myk8s-control-plane Ready control-plane 24h v1.30.0
myk8s-worker Ready <none> 24h v1.30.0
아래의 명령어를 통해 api-server의 경로가 :6443이 맞다는 것을 다시 한번 확인할 수 있습니다.
1
2
docker exec -it myk8s-control-plane curl -k https://localhost:6443/livez ;echo
ok
간단한 웹서버 배포
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-websrv
spec:
replicas: 2
selector:
matchLabels:
app: deploy-websrv
template:
metadata:
labels:
app: deploy-websrv
spec:
terminationGracePeriodSeconds: 0
containers:
- name: deploy-websrv
image: nginx:alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: deploy-websrv
spec:
ports:
- name: svc-webport
port: 80
targetPort: 80
nodePort: 30001
selector:
app: deploy-websrv
type: NodePort
EOF
- 배포 확인
1
2
3
4
5
6
7
k get svc,deploy
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/deploy-websrv NodePort 10.96.205.151 <none> 80:30001/TCP 18s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 24h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/deploy-websrv 2/2 2 2 18s
- 접속확인
클러스터를 생성할 때 포트를 열어뒀기에, 아래와 같이 로컬에서 테스트가 가능합니다.
만약, 원하시는 노드 포트 범위가 있다면 배포할 때 열어둡니다.
이제 클러스터를 삭제합니다.
1
2
3
kind delete cluster --name {클러스터명}
Deleting cluster "myk8s" ...
Deleted nodes: ["myk8s-worker" "myk8s-control-plane"]
Ingress 적용해보기
Ingress 적용을 위해, 아래의 YAML 파일로 클러스터를 프로비저닝합니다.
** ingress는 L7 라우팅을 제공하는 리소스입니다. 외부 LB와 연동되어 별도의 Controller Pod에서 도메인에 대한 처리를 진행하고 entpoint(pod)로 라우팅합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
이번에는 api-server 포트포워딩이 50017로 지정됐습니다.
1
2
3
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bd7dcca07462 kindest/node:v1.30.0 "/usr/local/bin/entr…" 31 seconds ago Up 30 seconds 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:30000->30000/tcp, 127.0.0.1:50017->6443/tcp myk8s-control-plane
Nginx-Ingress 배포
1
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
- Nginx Ingress 배포 확인
1
2
3
4
5
k get pods -n ingress-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ingress-nginx-admission-create-lbm56 0/1 Completed 0 3m8s 10.244.0.5 myk8s-control-plane <none> <none>
ingress-nginx-admission-patch-z4dqs 0/1 Completed 0 3m8s 10.244.0.6 myk8s-control-plane <none> <none>
ingress-nginx-controller-8fb8cdb7c-q2s8v 1/1 Running 0 3m8s 10.244.0.7 myk8s-control-plane <none> <none>
Sample 앱 살펴보기
우리가 사용할 YAML 파일은 https://kind.sigs.k8s.io/examples/ingress/usage.yaml입니다. 리소스에 대해 간단히 살펴보면 k8s에서 제공하는 “e2e-test-images/agnhost” 이미지를 사용합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
해당 이미지는 kubernetes에서 제공하는 end to end 테스트 도구입니다. 우리는 여기서 사용하는 netexec는 HTTP로 오는 요청에 대해 다양한 응답을 리턴합니다.
/
: Timestamp/clientip
: 요청을 보낸 클라이언트 IP 반환/hostname
: 호스트네임 반환/echo
: 파라미터로 넘긴 메시지를 반환. (/echo?msg=echoed_msg
)
Ingress는 /foo/ 혹은 /foo로 시작하는 모든 명령을 foo-services(bar-services)로 라우팅한다는 내용입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: networking.k8s.io/v1
kind: Ingress
...
spec:
rules:
- http:
paths:
- pathType: Prefix
path: /foo(/|$)(.*)
backend:
service:
name: foo-service
port:
number: 8080
...
- 배포 진행
1
kubectl apply -f https://kind.sigs.k8s.io/examples/ingress/usage.yaml
배포된 리소스를 확인하면 아래와 같습니다.
- 한번 테스트를 해봅니다.
1
2
3
4
curl localhost/foo/hostname
foo-app
curl localhost/bar/hostname
bar-app
아주 잘 작동됩니다. 위에서 확인한 netexec의 다른 옵션으로 테스트를 진행해봅니다.
1
2
3
4
5
6
curl localhost/foo/
NOW: 2024-09-02 12:55:54.205389173 +0000 UTC m=+1352.963004871
curl localhost/foo/clientip
10.244.0.7:39658
curl http://localhost/foo//echo\?msg\=test_msg
test_msg
클라이언트 IP를 지속적으로 날려봅니다. 임시포트를 사용하기에, 포트가 계속 바뀌는 것을 확인할 수 있습니다.
1
2
3
4
5
6
curl localhost/foo/clientip
10.244.0.7:38562%
curl localhost/foo/clientip
10.244.0.7:58766%
curl localhost/foo/clientip
10.244.0.7:43040%
위와 같이 ingress가 정상적으로 동작하는 것을 확인할 수 있습니다.
Ingress controller에 로그 확인
아래의 명령어를 실행합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller -f
-------------------------------------------------------------------------------
NGINX Ingress controller
Release: v1.11.2
Build: 46e76e5916813cfca2a9b0bfdc34b69a0000f6b9
Repository: https://github.com/kubernetes/ingress-nginx
nginx version: nginx/1.25.5
-------------------------------------------------------------------------------
W0902 12:30:41.709884 10 client_config.go:659] Neither --kubeconfig nor --master was specified. Using the inClusterConfig. This might not work.
I0902 12:30:41.710019 10 main.go:205] "Creating API client" host="https://10.96.0.1:443"
...
W0902 12:33:14.755691 10 controller.go:1216] Service "default/foo-service" does not have any active Endpoint.
W0902 12:33:14.755744 10 controller.go:1216] Service "default/bar-service" does not have any active Endpoint.
W0902 12:33:22.128651 10 controller.go:1216] Service "default/bar-service" does not have any active Endpoint.
I0902 12:33:43.037560 10 status.go:304] "updating Ingress status" namespace="default" ingress="example-ingress" currentValue=null newValue=[{"hostname":"localhost"}]
I0902 12:33:43.048937 10 event.go:377] Event(v1.ObjectReference{Kind:"Ingress", Namespace:"default", Name:"example-ingress", UID:"b0a9eddb-4bf5-43d4-9420-8dac73ff3286", APIVersion:"networking.k8s.io/v1", ResourceVersion:"1050", FieldPath:""}): type: 'Normal' reason: 'Sync' Scheduled for sync
172.18.0.1 - - [02/Sep/2024:12:35:00 +0000] "GET /foo/hostname HTTP/1.1" 200 7 "-" "curl/8.4.0" 84 0.003 [default-foo-service-8080] [] 10.244.0.9:8080 7 0.003 200 7c7c48eb2349724f9299560e49ff98eb
172.18.0.1 - - [02/Sep/2024:12:35:16 +0000] "GET /bar/hostname HTTP/1.1" 200 7 "-" "curl/8.4.0" 84 0.001 [default-bar-service-8080] [] 10.244.0.8:8080 7 0.002 200 d2b2b45924a2d78017d51e236556a3e0
마지막 줄을 확인하면 /bar/hostname으로 들어온 트래픽을 10.244.0.8:8080
으로 라우팅하는 것을 확인할 수 있습니다. GET /bar/hostname HTTP/1.1" 200 7 "-" "curl/8.4.0" 84 0.001 [default-bar-service-8080] [] 10.244.0.8:8080 7 0.002 200
10.244.0.8
는 아래 그림과 같이 bar-app 파드의 IP입니다.
이와 같이 kind에서도 nginx-ingress가 잘 작동되는 것을 확인할 수 있습니다.