Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

Docker Desktop for Windows 기반의 Kubernetes 구성 - WSL 2 인스턴스에 kind 도구로 k8s 클러스터 구성

이전 글을 통해 "Docker Desktop for Windows" 환경에서 쉽게 k8s 구성을 실습할 수 있었는데요,

Docker Desktop for Windows에서 DockerDesktopVM 기반의 Kubernetes 구성
; https://www.sysnet.pe.kr/2/0/12575

위의 글은 DockerDesktopVM 기반이었지만, 새롭게 WSL 2로의 통합 환경에서 k8s를 구성하는 것도 가능합니다.

하지만 아쉽게도 제 경우에 아래의 이유로 실패를 했는데요,

WSL 2 기반으로 "Enable Kubernetes" 활성화 시 초기화 실패
; https://www.sysnet.pe.kr/2/0/12571

^^ 상관없습니다. 그냥 k8s를 구성해도 되기 때문입니다. (아마도 저 문제가 해결되면 저 방법으로 하는 것이 더 좋겠지만!) 어차피 Docker Desktop for Windows의 목적 자체가 로컬 개발 환경인데다, k8s 클러스터를 그런 환경으로 빠르게 구축해 주는 kind라는 도구가 있으므로 환경 구성이 매우 쉽습니다.

WSL+Docker: Kubernetes on the Windows Desktop
; https://kubernetes.io/blog/2020/05/21/wsl-docker-kubernetes-on-the-windows-desktop/

Kind를 이용한 쿠버네티스 클러스터 만들기
; https://jupilhwang.github.io/post/kind%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EB%A7%8C%EB%93%A4%EA%B8%B0/

kind를 이용해 직접 WSL 2 환경에서 k8s를 운영하면 좋은 한 가지 이점이라면, Hyper-V에서 더 이상 "DockerDesktopVM" 인스턴스가 (필요가 없으므로) 올라오지 않는다는 점입니다.

반면 kind를 이용해 k8s를 구성할 때 한 가지 단점이라면 DockerDesktopVM을 사용할 때와는 달리 WSL 2를 사용하는 경우 약간 혼란스러울 수 있다는 점입니다. DockerDesktopVM을 사용하면 해당 VM에 설치된 리눅스 인스턴스에 (공식적으로) 접근할 수 있는 방법이 없으므로 모든 구성을 윈도우 측의 kubectl.exe, docker.exe를 이용해 통합/구성하게 됩니다. 반면 WSL 2를 이용해 docker/k8s + kind로 구성하면 docker/kubectl 명령을 WSL 2와 윈도우 호스트 측 모두에서 사용 가능하다는 차이점이 발생합니다.

여기서 문제는, docker의 경우 "Use the WSL 2 based engine" 옵션 덕분에 잘 통합이 되어서 WSL 2와 윈도우 호스트 측에서 실행할 때 단일한 docker 인스턴스로 작업을 하는 반면, kind의 경우에는 WSL 2와 윈도우 호스트 측에서 실행할 때 각기 별개의 k8s 클러스터를 생성한다는 것입니다.

이게 어떤 상황인지 좀 더 자세하게 살펴보겠습니다.




이미 언급한 대로 kind 구성은, 1) WSL 2 인스턴스 내에서 할 수도 있고, 2) 윈도우 호스트 측에서 실행하는 것도 가능합니다. 우선, WSL 2 내에서 kind를 실행해 k8s 클러스터를 구성해 보겠습니다.

/* kind 실행 모듈 다운로드 */
$ curl -Lo ./kind https://github.com/kubernetes-sigs/kind/releases/download/v0.10.0/kind-linux-amd64
$ chmod +x ./kind
$ sudo mv ./kind /usr/local/bin/

/* kind로 클러스터 구성 */
$ kind create cluster --name mycluster1
Creating cluster "mycluster1" ...
 ✓ Ensuring node image (kindest/node:v1.17.0) 🖼
 ✓ Preparing nodes 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Set kubectl context to "kind-mycluster1"
You can now use your cluster with:

kubectl cluster-info --context mycluster1

Not sure what to do next? 😅 Check out https://kind.sigs.k8s.io/docs/user/quick-start/

저게 끝입니다. kind는 "create cluster ..." 명령어로 이미 k8s 클러스터 관련한 모든 구성을 완료합니다. 이렇게 구성한 "mycluster1" 클러스터에 대한 정보는 이렇게 알아낼 수 있습니다.

$ kubectl cluster-info
Kubernetes master is running at https://127.0.0.1:39339
KubeDNS is running at https://127.0.0.1:39339/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

$ kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://127.0.0.1:39339
  name: kind-mycluster1
contexts:
- context:
    cluster: kind-mycluster1
    user: kind-mycluster1
  name: kind-mycluster1
current-context: kind-mycluster1
kind: Config
preferences: {}
users:
- name: kind-mycluster1
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED

즉, kind가 하는 일은 k8s 클러스터를 위한 모든 서비스를 구성하고, 현재 사용자의 프로파일 디렉터리($HOME/.kube)에 config 파일을 만들어 해당 클러스터에 대한 연결 정보를 구성합니다.

참고로, WSL 2에서 실행한 kind는 윈도우 호스트 환경을 인식하지는 않으므로 kubectl.exe 명령을 윈도우 호스트 측에서 실행하면 다음과 같은 출력이 나옵니다.

c:\temp\knd> kubectl config view
apiVersion: v1
clusters: null
contexts: null
current-context: ""
kind: Config
preferences: {}
users: null

c:\temp\knd> kubectl get pods
Unable to connect to the server: dial tcp [::1]:8080: connectex: No connection could be made because the target machine actively refused it.

그래서 이런 경우에는 WSL 2 인스턴스 측에서 kubectl proxy를 실행해 두면,

$ kubectl proxy -p=8080
Starting to serve on 127.0.0.1:8080

(WSL 2의 네트워크 구성의 특징에 따라) 윈도우 호스트 측에서 kubectl이 정상적으로 실행됩니다.

C:\temp> kubectl run nginx-app --image nginx --port=80
pod/nginx-app created

C:\temp> kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
nginx-app   1/1     Running   0          16s

C:\temp> kubectl expose pod nginx-app --type=NodePort
service/nginx-app exposed

C:\temp> kubectl get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP        16h
nginx-app    NodePort    10.96.199.21   <none>        80:32539/TCP 6s

/* 삭제
kubectl delete svc nginx-app
kubectl delete pod nginx-app

kind delete cluster --name mycluster1
*/




위에서는 WSL 2 인스턴스 환경 내에서 kind를 실행해 클러스터를 생성했지만, 윈도우 호스트 측에서 하는 것도 가능합니다. 이를 위해 PowerShell을 이용해 chocolatey 패키지 관리자를 먼저 설치한 후,

Running kind in Windows
; https://blog.nillsf.com/index.php/2020/08/28/running-kind-in-windows/

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

이것을 이용해 kind 도구를 구성할 수 있습니다.

choco install kind -y

이후 클러스터 생성 방법은 WSL 2에서의 실행과 동일합니다.

c:\temp> kind create cluster --name cluster2

c:\temp> kubectl cluster-info
Kubernetes master is running at https://127.0.0.1:43574
KubeDNS is running at https://127.0.0.1:43574/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

위의 출력 결과에서 볼 수 있듯이, kind는 WSL 2 환경을 인식하지 않으므로 윈도우 명령행에서 실행하면 또다시 새롭게 k8s 클러스터를 만듭니다. 그리고, 역시 윈도우 사용자의 프로파일 디렉터리(%USERPROFILE%\.kube)에 새롭게 생성한 k8s 클러스터를 바라보는 config 파일을 구성하게 됩니다.




이런 식으로 따로 구성을 하기 때문에 혼란스러운 점이 하나 있는데요.

WSL 2에서 구성했을 때는 이후 윈도우 호스트 측에서 WSL 2로 kubectl proxy를 이용해 접속하는 것이 가능했지만, 그 반대인 이번 경우에는 WSL 2에서 호스트 측으로의 "localhost"를 이용한 통신이 안 되므로, 윈도우 호스트 환경 내에서만 kubectl로 해당 클러스터를 사용할 수 있다는 차이가 있습니다. (물론, kubectl proxy를 이용한 방법은 안 되지만 "kubectl 수행 시 다른 k8s 클러스터로 접속하는 방법"를 이용하면 WSL 2 측의 kubectl로 윈도우 호스트에 구성한 클러스터를 접근하는 것이 가능합니다.)

개인적인 의견인데, 저렇게 2개의 클러스터를 사용하면 자칫 실수를 할 수 있기 때문에 제 생각에는 (docker desktop for windows에서 "Use the WSL 2 based engine" 옵션을 켜고 kind를 이용해 구성할 때는) 어느 한 쪽에서 클러스터를 만든 다음 .kube 디렉터리에 생성한 config 파일을,

[WSL 2]
$HOME/.kube/config

[윈도우]
%USERPROFILE\.kube\config

다른 한 쪽에 복사하는 것을 권장합니다. 그럼 같은 클러스터를 바라보게 되므로 사용 중의 혼란을 없앨 수 있습니다. (아마도 이 부분은 "Enable Kubernetes" 옵션이 WSL 2와의 문제없이 잘 실행이 되었다면 config 파일을 양쪽 모두 동일한 클러스터를 바라보도록 공유하지 않았을까... 추측해 봅니다.)




그런데, Docker Desktop for Windows 구성을 DockerDesktopVM에 하는 경우와는 달리 WSL 2에 kind를 이용해 구성하면 불편한 점이 하나 더 있습니다. 일례로 다음과 같이 서비스를 생성한 경우,

c:\temp\knd> kubectl run nginx-app --image nginx --port=80
pod/nginx-app created

c:\temp\knd> kubectl expose pod nginx-app --type=NodePort
service/nginx-app exposed

c:\temp\knd> kubectl get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP        8m45s
nginx-app    NodePort    10.96.56.220   <none>        80:30814/TCP   9s

기존에 DockerDesktopVM을 이용해 구성한 k8s 환경의 경우 curl을 이용해 저 서비스로 곧바로 접속할 수 있었습니다. 하지만, ("Use the WSL 2 based engine" 옵션을 켜고) kind로 구성한 경우에는 curl로 접속이 안 됩니다.

c:\temp\knd> curl localhost:31739
curl: (7) Failed to connect to localhost port 30814: Connection refused

이에 대해서는 다음의 문서에서 다루고 있는데요,

Accessing a Kubernetes Service running in WSL2
; https://kind.sigs.k8s.io/docs/user/using-wsl2/#accessing-a-kubernetes-service-running-in-wsl2

실습을 위해 이전에 생성해 둔 클러스터를 삭제하고, 새롭게 다음의 config으로 클러스터를 생성한 후,

# cluster-config.yml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
    protocol: TCP

c:\temp> kind create cluster --config=cluster-config.yml

nginx 서비스를 deployment를 이용해 구성하면,

c:\temp> kubectl create deployment nginx --image=nginx --port=80

c:\temp> kubectl create service nodeport nginx --tcp=80:80 --node-port=30000

curl 명령어로 nginx로의 접근이 가능합니다.

c:\temp> curl localhost:30000

(왜 이런 차이가 발생하는지에 대한 자세한 설명은 "Docker Desktop for Windows 기반의 Kubernetes 구성 (2) - WSL 2 인스턴스에 kind가 구성한 k8s 서비스 위치" 글을 참고하세요.)




WSL 2 인스턴스 내에서 kind로 클러스터 생성 시 다음과 같은 오류가 발생한다면?

$ kind create cluster --name wslkind
ERROR: failed to list clusters: command "docker ps -q -a --no-trunc --filter label=io.x-k8s.kind.cluster=wslkind --format '{{.Names}}'" failed with error: exit status 1

실제로 문제가 되는 명령어를 직접 실행해 보면 답이 나옵니다.

$ docker ps -q -a --no-trunc --filter label=io.x-k8s.kind.cluster-wslkind --format '{{.Names}}'
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json?all=1&filters=%7B%22label%22%3A%7B%22io.x-k8s.kind.cluster-wslkind%22%3Atrue%7D%7D: dial unix /var/run/docker.sock: connect: permission denied

따라서, docker 그룹에 현재 로그인 사용자를 등록해 주면 됩니다. (혹은, 권한에 걸리지 않는 root 계정으로 전환 후 실행해도 됩니다.)




간혹, kubectl run 했을 때 다음과 같은 오류가 발생한다면?

c:\temp\knd> kubectl run nginx-app --image nginx --port=80
Error from server (Forbidden): pods "nginx-app" is forbidden: error looking up service account default/default: serviceaccount "default" not found

오류 메시지의 의미는, default 네임스페이스에 default 서비스 계정이 없다는 것입니다. 이에 대한 확인은 다음과 같이 할 수 있는데,

c:\temp\knd> kubectl get serviceaccount -n default
NAME      SECRETS   AGE
default   1         3m50s

저렇게 나오면 다시 "kubectl run ..."을 실행해 보면 됩니다. (만약 실제로 서비스 계정이 없다면 오류가 발생하는 것이 맞습니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 3/29/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13091정성태7/3/202229.NET Framework: 2027. C# - IPv4, IPv6를 모두 지원하는 서버 소켓 생성 방법
13090정성태6/29/202247오류 유형: 815. PyPI에 업로드한 패키지가 반영이 안 되는 경우
13089정성태6/28/202260개발 환경 구성: 646. HOSTS 파일 변경 시 Edge 브라우저에 반영하는 방법
13088정성태6/27/202253개발 환경 구성: 645. "Developer Command Prompt for VS 2022" 명령행 환경의 폰트를 바꾸는 방법
13087정성태6/23/2022125스크립트: 41. 파이썬 - FastAPI / uvicorn 호스팅 환경에서 asyncio 사용하는 방법
13086정성태6/22/2022215.NET Framework: 2026. C# 11 - 문자열 보간 개선 2가지파일 다운로드1
13085정성태6/22/2022177.NET Framework: 2025. C# 11 - 원시 문자열 리터럴(raw string literals)파일 다운로드1
13084정성태6/21/202266개발 환경 구성: 644. Windows - 파이썬 2.7을 msi 설치 없이 구성하는 방법
13083정성태6/20/2022183.NET Framework: 2024. .NET 7에 도입된 GC의 메모리 해제에 대한 segment와 region의 차이점
13082정성태6/19/2022114.NET Framework: 2023. C# - Process의 I/O 사용량을 보여주는 GetProcessIoCounters Win32 API파일 다운로드1
13081정성태6/17/2022180.NET Framework: 2022. C# - .NET 7 Preview 5 신규 기능 - System.IO.Stream ReadExactly / ReadAtLeast파일 다운로드1
13080정성태6/17/2022102개발 환경 구성: 643. Visual Studio 2022 17.2 버전에서 C# 11 또는 .NET 7.0 preview 적용
13079정성태6/17/202283오류 유형: 814. 파이썬 - Error: The file/path provided (...) does not appear to exist
13078정성태6/16/2022175.NET Framework: 2021. WPF - UI Thread와 Render Thread파일 다운로드1
13077정성태6/15/2022112스크립트: 40. 파이썬 - postgresql 환경 구성
13075정성태6/15/2022184Linux: 50. Linux - apt와 apt-get의 차이 [2]
13074정성태6/13/2022187.NET Framework: 2020. C# - NTFS 파일에 사용자 정의 속성값 추가하는 방법파일 다운로드1
13073정성태6/12/2022225Windows: 207. Windows Server 2022에 도입된 WSL 2
13072정성태6/10/2022146Linux: 49. Linux - ls 명령어로 출력되는 디렉터리 색상 변경 방법
13071정성태6/9/2022183스크립트: 39. Python에서 cx_Oracle 환경 구성
13070정성태6/8/2022235오류 유형: 813. Windows 11에서 입력 포커스가 바뀌는 문제 [1]
13069정성태5/26/2022474.NET Framework: 2019. C# - .NET에서 제공하는 3가지 Timer 비교
13068정성태5/24/2022392.NET Framework: 2018. C# - 일정 크기를 할당하는 동안 GC를 (가능한) 멈추는 방법파일 다운로드1
13067정성태5/23/2022209Windows: 206. Outlook - 1년 이상 지난 메일이 기본적으로 안 보이는 문제
13066정성태5/23/2022223Windows: 205. Windows 11 - Windows + S(또는 Q)로 뜨는 작업 표시줄의 검색 바가 동작하지 않는 경우
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...