King of Stock

kind를 사용한 쿠버네티스 클러스터 만들기 본문

쿠버네티스

kind를 사용한 쿠버네티스 클러스터 만들기

king of stock 2023. 7. 22. 11:29

본 포스트는 로컬 환경에서 쿠버네티스 클러스터를 만드는 kind 도구를 사용하는 방법에 대한 포스트이다. 더 간단한 minikube도 있는데, 굳이 kind를 소개하는 이유로는 더 쿠버네티스 같이 구성할 수 있고 여러 쿠버네티스 버전을 설치할 수 있고 사용자가 좀 더 숙달이 되면 테스트 환경을 자동으로 만들어서 사용자가 개발한 operator를 테스트 해보는 등에 작업을 할 수 있다.


요구사항

  • 리눅스 환경
  • 도커 설치 - kind는 도커 환경에서 클러스터를 만들고 있다.
  • kind v0.20.0 - 아래 설치 과정부터 설명하고 있다.
  • kubectl

환경준비

이제부터 필자의 환경에 kind를 설치하고 kind를 통해서 쿠버네티스 클러스터 환경을 만들어보겠다.

kind 바이너리 설치

공식 문서에서는 크게 3가지 방식에 설치를 설명하는데 리눅스 환경에 사용자들이 범용적으로 이용 가능한 Installing From Release Binaries 방식으로 설치를 해보겠다. 링크를 따라가면 OS마다 커맨드를 제공하고 있으니 필자가 여기서 작성한 macOS 환경에 커맨드를 사용했다고 비난하지 말자.

$ [ $(uname -m) = x86_64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-darwin-amd64

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    99    0    99    0     0    299      0 --:--:-- --:--:-- --:--:--   311
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 6278k  100 6278k    0     0  3864k      0  0:00:01  0:00:01 --:--:-- 20.4M

적당한 경로에 릴리즈 바이너리를 다운로드 받는다.

$ ls -atlr
total 14392
drwxr-xr-x  20 smartkuk  staff      640 Apr  1 17:27 my_app
drwxr-xr-x   3 smartkuk  staff       96 Apr  1 17:42 temp_app
-rw-------@  1 smartkuk  staff   928232 Apr 28 21:35 index.js
-rw-r--r--@  1 smartkuk  staff     6148 Apr 28 21:36 .DS_Store
drwx------@ 29 smartkuk  staff      928 Jul  8 16:07 ..
drwxr-xr-x  38 smartkuk  staff     1216 Jul  8 16:59 .logs
drwxr-xr-x@  8 smartkuk  staff      256 Jul 22 11:23 .
-rw-r--r--   1 smartkuk  staff  6429552 Jul 22 11:23 kind
$ chmod +x ./kind

다운로드가 되었는지 확인해보고 실행할 수 있도록 권한을 조정한다.

$ ls -atlr
total 14392
drwxr-xr-x  20 smartkuk  staff      640 Apr  1 17:27 my_app
drwxr-xr-x   3 smartkuk  staff       96 Apr  1 17:42 temp_app
-rw-------@  1 smartkuk  staff   928232 Apr 28 21:35 index.js
-rw-r--r--@  1 smartkuk  staff     6148 Apr 28 21:36 .DS_Store
drwx------@ 29 smartkuk  staff      928 Jul  8 16:07 ..
drwxr-xr-x  38 smartkuk  staff     1216 Jul  8 16:59 .logs
drwxr-xr-x@  8 smartkuk  staff      256 Jul 22 11:23 .
-rwxr-xr-x   1 smartkuk  staff  6429552 Jul 22 11:23 kind
$ sudo mv ./kind /usr/local/bin/kind
Password:
$ which kind
/usr/local/bin/kind
$ kind --version
kind version 0.20.0

권한을 다시 확인하고 전역 디렉토리에 바이너리를 옮긴후 바이너리가 동작하는지 마지막으로 확인한다.

쿠버네티스 클러스터 설정 파일 생성

kind 바이너리만 사용해서 바로 클러스터를 생성할 수 있지만 그렇게 이용하는 것은 minikube를 사용하는 것과 별반 차이가 없으니 좀 더 설정을 만들어보자.

아래는 쿠버네티스 v1.24 기준으로 control-plane 및 worker 노드를 구성하는 설정 파일이다.

$ cat << EOF > k8s-1-24.yaml
---
apiVersion: kind.x-k8s.io/v1alpha4
kind: Cluster
name: k8s-1-24
nodes:
- role: control-plane
  image: kindest/node:v1.24.15@sha256:7db4f8bea3e14b82d12e044e25e34bd53754b7f2b0e9d56df21774e6f66a70ab
- role: worker
  image: kindest/node:v1.24.15@sha256:7db4f8bea3e14b82d12e044e25e34bd53754b7f2b0e9d56df21774e6f66a70ab
EOF

위에서 필자가 설치한 kind 바이너리의 버전이 0.20.0인데 릴리즈 노트로 이동하면 사용할 수 있는 쿠버네티스 버전과 이미지 주소가 나열되어있다.

만약에 다른 쿠버네티스 버전을 사용시 다른 kind 바이너리 버전에 릴리즈 노트를 참고해서 변경해주면 된다.

쿠버네티스 클러스터 생성

필요한 설정 파일을 만들었기 때문에 이제 kind 바이너리로 클러스터를 만들어 보자. 아래와 같이 클러스터를 생성하면 우리가 원한(?) 쿠버네티스 1.24 버전에 클러스터가 완성된다.

$ kind create cluster --config k8s-1-24.yaml
Creating cluster "k8s-1-24" ...
 ✓ Ensuring node image (kindest/node:v1.24.15) 🖼
 ✓ Preparing nodes 📦 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
 ✓ Joining worker nodes 🚜
Set kubectl context to "kind-k8s-1-24"
You can now use your cluster with:

kubectl cluster-info --context kind-k8s-1-24

Thanks for using kind! 😊

위와 같이 설치를 성공하면 다음과 같이 도커 컨테이너로 만들어진 것을 알 수 있다.

$ docker ps
CONTAINER ID   IMAGE                   COMMAND                  CREATED          STATUS          PORTS                       NAMES
fe444164df97   kindest/node:v1.24.15   "/usr/local/bin/entr…"   33 minutes ago   Up 33 minutes   127.0.0.1:57858->6443/tcp   k8s-1-24-control-plane
2fcc6b7f399f   kindest/node:v1.24.15   "/usr/local/bin/entr…"   33 minutes ago   Up 33 minutes                               k8s-1-24-worker

kubectl 사용해보기

kubectl 바이너리는 위에서도 언급했지만 미리 준비된 상태라 가정하겠다.

연결정보

우리는 kind 바이너리를 통해서 만든 쿠버네티스 환경에 접속해야 하니 먼저 연결정보를 확인해보겠다.

$ kubectl config get-contexts
CURRENT   NAME            CLUSTER             AUTHINFO                  NAMESPACE
          host            host-kubernetes     host-kubernetes-admin
*         kind-k8s-1-24   kind-k8s-1-24       kind-k8s-1-24
          member          member-kubernetes   member-kubernetes-admin

지금까지 과정에서 따로 쿠버네티스 설정 파일을 수정하는게 없는데, kind 바이너리는 친절하게 방금 설치한 클러스터를 연결할 수 있는 설정을 위에서 보는것과 같이 설정해놨다.

버전정보

연결정보가 설정된 것을 확인한 이상 이제 우리가 설치한 쿠버네티스 버전인지 확인이 필요하다.

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.0", GitCommit:"cb303e613a121a29364f75cc67d3d580833a7479", GitTreeState:"clean", BuildDate:"2021-04-08T21:16:14Z", GoVersion:"go1.16.3", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"24", GitVersion:"v1.24.15", GitCommit:"2c67202dc0bb96a7a837cbfb8d72e1f34dfc2808", GitTreeState:"clean", BuildDate:"2023-06-15T01:09:03Z", GoVersion:"go1.19.10", Compiler:"gc", Platform:"linux/amd64"}
WARNING: version difference between client (1.21) and server (1.24) exceeds the supported minor version skew of +/-1

확인을 해보니 우리가 지정한 버전으로 서버 버전이 출력되어 나오는 것을 알 수 있다.

kube-system 파드 확인

우리가 default 네임스페이스에 뭔가 설치한 적이 없으니 kube-system 네임스페이스에 설치된 파드를 한번 확인차 열람해보자.

$ kubectl get pods -n kube-system
NAME                                             READY   STATUS    RESTARTS   AGE
coredns-57575c5f89-gghj2                         1/1     Running   0          22m
coredns-57575c5f89-q2f8r                         1/1     Running   0          22m
etcd-k8s-1-24-control-plane                      1/1     Running   0          22m
kindnet-44rzq                                    1/1     Running   0          22m
kindnet-slc26                                    1/1     Running   0          21m
kube-apiserver-k8s-1-24-control-plane            1/1     Running   0          22m
kube-controller-manager-k8s-1-24-control-plane   1/1     Running   0          22m
kube-proxy-62dl5                                 1/1     Running   0          22m
kube-proxy-ppd79                                 1/1     Running   0          21m
kube-scheduler-k8s-1-24-control-plane            1/1     Running   0          22m

Nginx 파드 생성

로컬 환경에 생성한 클러스터가 정상 동작을 하는지 마지막으로 파드 1개를 생성해 보겠다. 쿠버네티스 공식 사이트에서 제공하는 파드 스펙을 배포해보겠다.

$ kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml
pod/nginx created
$ kubectl get pods
NAME    READY   STATUS              RESTARTS   AGE
nginx   0/1     ContainerCreating   0          6s
$ kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          14s

kubectl apply 커맨드를 통해서 파드가 생성된 것을 우리는 확인했다. kind 바이너리로 생성한 쿠버네티스 클러스터가 정상적으로 동작하는 것을 확인하였다.


kind 클러스터를 생성시 private registry 지정하기

위에서 일반적인 내용을 다루어 봤는데 이제 private registry 를 하나 만들고 이것을 kind 바이너리로 생성하는 클러스터가 사용하도록 설정 해보자.

docker registry 컨테이너 실행

도커 레지스트리 이미지를 이용하여 도커 컨테이너로 실행을 먼저 한다.

$ docker run -d --restart=always -p "127.0.0.1:5001:5000" --name "kind-registry" registry:2
Unable to find image 'registry:2' locally
2: Pulling from library/registry
31e352740f53: Pull complete
7f9bcf943fa5: Pull complete
3c98a1678a82: Pull complete
51f7a5bb21d4: Pull complete
3f044f23c427: Pull complete
Digest: sha256:9977826e0d1d0eccc7af97017ae41f2dbe13f2c61e4c886ec28f0fdd8c4078aa
Status: Downloaded newer image for registry:2
eba147dd0018d11636ba4c41e609b1404ad8170a3d633af3e674d72fa181e5cc

실행한 커맨드를 보면 컨테이너 이름을 kind-registry로 지정하였고 포트 맵핑을 로컬 환경에 5001 포트를 컨테이너에 5000 포트와 맵핑 시켰다.

쿠버네티스 클러스터 삭제 및 생성

우리는 private registry를 바라볼 수 있는 쿠버네티스 클러스터가 필요하기 때문에 위에서 만들었던 클러스터는 먼저 삭제를 한다. 위에서 우리가 만들었던 클러스터에 이름은 k8s-1-24 이다.

$ kind delete cluster --name k8s-1-24
Deleting cluster "k8s-1-24" ...
Deleted nodes: ["k8s-1-24-control-plane" "k8s-1-24-worker"]

위 처럼 삭제를 완료했다면 아래와 같이 private registry 설정이 추가된 kind 클러스터 설정 파일을 만들겠습니다.

$ cat << EOF > k8s-1-24-registry.yaml
---
apiVersion: kind.x-k8s.io/v1alpha4
kind: Cluster
name: k8s-1-24
nodes:
- role: control-plane
  image: kindest/node:v1.24.15@sha256:7db4f8bea3e14b82d12e044e25e34bd53754b7f2b0e9d56df21774e6f66a70ab
- role: worker
  image: kindest/node:v1.24.15@sha256:7db4f8bea3e14b82d12e044e25e34bd53754b7f2b0e9d56df21774e6f66a70ab
containerdConfigPatches:
- |-
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:5001"]
    endpoint = ["http://kind-registry:5000"]
EOF

생성한 설정 파일을 kind 바이너리를 사용해서 클러스터를 만들어 주겠습니다.

$ kind create cluster --config k8s-1-24-registry.yaml
Creating cluster "k8s-1-24" ...
 ✓ Ensuring node image (kindest/node:v1.24.15) 🖼
 ✓ Preparing nodes 📦 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
 ✓ Joining worker nodes 🚜
Set kubectl context to "kind-k8s-1-24"
You can now use your cluster with:

kubectl cluster-info --context kind-k8s-1-24

Thanks for using kind! 😊

여기까지 진행이 되었다면 우리가 만든 쿠버네티스 클러스터는 kind-registry:5000 주소를 private registry로 사용하게 됩니다.

docker 네트워크 설정

이제는 우리의 클러스터가 진짜로 네트워크 상에서 private registry를 연결할 수 있는 상태가 되어야 합니다. 우리는 여기서 docker network 커맨드를 이용하여 해결을 아래와 같이 해줍니다.

$ docker network ls
NETWORK ID     NAME              DRIVER    SCOPE
e751bba35d4c   atm-network       bridge    local
c4d9468f418a   bridge            bridge    local
79e94c2e49a8   dockers_default   bridge    local
d247e34b6ca1   host              host      local
3385f8221084   kind              bridge    local
1df1ff247c65   none              null      local
a653698c6fd9   steady            bridge    local
$ docker network connect "kind" "kind-registry"

위에서 보이는 목록에 kind 라는 네트워크는 kind 바이너리로 생성한 클러스터가 만들어질때 생성된 도커 네트워크 설정입니다. 우리는 이 네트워크를 앞에서 만들어준 private registry 도커 컨테이너와 연결을 시켜준 예시를 보여줍니다.

 

위와 같이 네트워크를 연결하면 아래와 같이 kind 네트워크와 kind-registry 컨테이너 정보를 보면 아래와 같다. 요약하면 kind 네트워크에서는 kind-registry 컨테이너가 같이 포함이 되어있는 상태이고, kind-registry 컨테이너에 네트워크 설정에는 kind 네트워크가 포함된 상태로 이해하면 되겠습니다.

$ docker network inspect kind
[
    {
        "Name": "kind",
        # ... 생략 ...
        "Containers": {
            "53a4dc694a917f02136c878488932b94874a0da6ce8b13d00852630b1e9a5a22": {
                "Name": "k8s-1-24-control-plane",
                "EndpointID": "00df22271f19717bfeac980199c4e5923eedcf8492c46cae0fe2b648462fcd3e",
                "MacAddress": "02:42:ac:15:00:03",
                "IPv4Address": "172.21.0.3/16",
                "IPv6Address": "fc00:f853:ccd:e793::3/64"
            },
            "eba147dd0018d11636ba4c41e609b1404ad8170a3d633af3e674d72fa181e5cc": {
                "Name": "kind-registry",
                "EndpointID": "bd142c5f8c5f1c93e64aa2634b4c91a07107e7bcf667090fc62593848a0508f9",
                "MacAddress": "02:42:ac:15:00:04",
                "IPv4Address": "172.21.0.4/16",
                "IPv6Address": "fc00:f853:ccd:e793::4/64"
            },
            "fd15c32dc6ec6331764c5914533e3ffa9e3c76a33b33c93e7c7451a0d97e23cc": {
                "Name": "k8s-1-24-worker",
                "EndpointID": "b55c78c196f3abe3b82dd7a89c7b34fa1056298c80ec01313fa62864bbf4a099",
                "MacAddress": "02:42:ac:15:00:02",
                "IPv4Address": "172.21.0.2/16",
                "IPv6Address": "fc00:f853:ccd:e793::2/64"
            }
        },
        "Options": {
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]
$ docker inspect kind-registry
[
    {
        "Id": "eba147dd0018d11636ba4c41e609b1404ad8170a3d633af3e674d72fa181e5cc",
        "Created": "2023-07-22T03:28:45.864727188Z",
        "Path": "/entrypoint.sh",
        "Args": [
            "/etc/docker/registry/config.yml"
        ],
        "State": {
            # ... 생략 ...
        },
        # ... 생략 ...
        "HostConfig": {
            # ... 생략 ...
        },
        "GraphDriver": {
            # ... 생략 ...
        },
        "Mounts": [
            # ... 생략 ...
        ],
        "Config": {
            # ... 생략 ...
        },
        "NetworkSettings": {
            "Bridge": "",
            # ... 생략 ...
            "Networks": {
                "bridge": {
                    # ... 생략 ...
                },
                "kind": {
                    "IPAMConfig": {},
                    "Links": null,
                    "Aliases": [
                        "eba147dd0018"
                    ],
                    "NetworkID": "3385f8221084d41d39dc234070d07de64cbdb6495a9a83c529ceb35cbdad34c0",
                    "EndpointID": "bd142c5f8c5f1c93e64aa2634b4c91a07107e7bcf667090fc62593848a0508f9",
                    "Gateway": "172.21.0.1",
                    "IPAddress": "172.21.0.4",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "fc00:f853:ccd:e793::1",
                    "GlobalIPv6Address": "fc00:f853:ccd:e793::4",
                    "GlobalIPv6PrefixLen": 64,
                    "MacAddress": "02:42:ac:15:00:04",
                    "DriverOpts": {}
                }
            }
        }
    }
]

Nginx 이미지 준비 & 배포

이제는 위에서 초반에 사용한 이미지를 준비한 private registry에 push 처리하고 배포를 해보는 작업이 남아 있습니다.

아래는 쿠버네티스 공식 사이트에서 사용하는 이미지를 저의 로컬 환경에 pull 처리하고 있습니다.

$ docker pull nginx:1.14.2
1.14.2: Pulling from library/nginx
27833a3ba0a5: Pull complete
0f23e58bd0b7: Pull complete
8ca774778e85: Pull complete
Digest: sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d
Status: Downloaded newer image for nginx:1.14.2
docker.io/library/nginx:1.14.2

아래와 같이 내려받은 이미지를 우리가 준비한 private registry에 push 처리 해봅니다.

$ docker tag nginx:1.14.2 127.0.0.1:5001/nginx:1.14.2
$ docker images
REPOSITORY             TAG       IMAGE ID       CREATED        SIZE
registry               2         4bb5ea59f8e0   5 weeks ago    24MB
kindest/node           <none>    5d5e5d5ad531   5 weeks ago    936MB
atm                    1.0.0     e25d42a1e3bf   8 months ago   446MB
mariadb                10.9      7d424d763ecb   8 months ago   384MB
postgres               14.5      cefd1c9e490c   8 months ago   376MB
127.0.0.1:5001/nginx   1.14.2    295c7be07902   4 years ago    109MB
nginx                  1.14.2    295c7be07902   4 years ago    109MB
$ docker push 127.0.0.1:5001/nginx:1.14.2
The push refers to repository [127.0.0.1:5001/nginx]
82ae01d5004e: Pushed
b8f18c3b860b: Pushed
5dacd731af1b: Pushed
1.14.2: digest: sha256:706446e9c6667c0880d5da3f39c09a6c7d2114f5a5d6b74a2fafd24ae30d2078 size: 948

docker push 처리까지 성공을 하였으니 이것을 이용한 파드 스펙을 yaml 파일로 만들겠습니다. 이 파일에 쓰여진 이미지 레지스트리 주소의 경우에 위에서 사용했던 127.0.0.1:5001 주소로 지정하면 안되고 kind 클러스터 설정 파일에 지정된 localhost:5001 주소를 사용해야 합니다.

$ cat << EOF > nginx-registry.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: localhost:5001/nginx:1.14.2
    ports:
    - containerPort: 80
EOF

이제 yaml 파일 스펙을 적용하고 파드가 정상적으로 동작하고 있는지 확인을 해볼 차례입니다. 아래는 적용을 하고 일정 시간이 지난후에 파드 목록을 열람하고 이 파드가 우리가 적용한 private registry 주소를 사용한 스펙인지 마지막으로 확인한 과정입니다.

$ kubectl apply -f nginx-registry.yaml
pod/nginx created
$ kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          9s
$ kubectl get pod nginx -o yaml | grep 'image:'
  - image: localhost:5001/nginx:1.14.2
    image: localhost:5001/nginx:1.14.2

이로써 kind 바이너리를 사용하는 간단한 방법을 소개하는 포스팅을 마치겠습니다.

Comments