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 ..."을 실행해 보면 됩니다. (만약 실제로 서비스 계정이 없다면 오류가 발생하는 것이 맞습니다.)




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

[연관 글]


donaricano-btn



[최초 등록일: ]
[최종 수정일: 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)
12639정성태5/12/20215오류 유형: 715. Arduino: Open Serial Monitor - The module '...\detection.node' was compiled against a different Node.js version using NODE_MODULE_VERSION
12638정성태5/12/202117사물인터넷: 63. NodeMCU v1 ESP8266 - 펌웨어 내 파일 시스템(SPIFFS, LittleFS) 및 EEPROM 활용
12637정성태5/10/202122사물인터넷: 62. NodeMCU v1 ESP8266 보드의 A0 핀에 다중 아날로그 센서 연결
12636정성태5/10/202128사물인터넷: 61. NodeMCU v1 ESP8266 보드의 A0 핀 사용법 - FSR-402 아날로그 압력 센서 연동파일 다운로드1
12635정성태5/9/202149기타: 81. OpenTabletDriver를 (관리자 권한으로 실행하지 않고도) 관리자 권한의 프로그램에서 동작하게 만드는 방법
12634정성태5/9/202154개발 환경 구성: 572. .NET에서의 신뢰도 등급 조정 - 외부 Manifest 파일을 두는 방법파일 다운로드1
12633정성태5/7/202171개발 환경 구성: 571. UAC - 관리자 권한 없이 UIPI 제약을 없애는 방법
12632정성태5/8/202172기타: 80. (WACOM도 지원하는) Tablet 공통 디바이스 드라이버 - OpenTabletDriver
12631정성태5/7/202190사물인터넷: 60. ThingSpeak 사물인터넷 플랫폼에 ESP8266 NodeMCU v1 + 조도 센서 장비 연동파일 다운로드1
12630정성태5/12/202169사물인터넷: 59. NodeMCU v1 ESP8266 보드의 A0 핀 사용법 - CdS Cell(GL3526) 조도 센서 연동파일 다운로드1
12629정성태5/5/2021141.NET Framework: 1057. C# - CoAP 서버 및 클라이언트 제작 (UDP 소켓 통신)파일 다운로드1
12628정성태5/4/202136Linux: 39. Eclipse 원격 디버깅 - Cannot run program "gdb": Launching failed
12627정성태5/4/202143Linux: 38. 라즈베리 파이 제로 용 프로그램 개발을 위한 Eclipse C/C++ 윈도우 환경 설정
12626정성태5/3/202159.NET Framework: 1056. C# - Thread.Suspend 호출 시 응용 프로그램 hang 현상 (2)파일 다운로드1
12625정성태5/3/202138오류 유형: 714. error CS5001: Program does not contain a static 'Main' method suitable for an entry point
12624정성태5/3/2021258.NET Framework: 1055. C# - struct/class가 스택/힙에 할당되는 사례 정리 [6]파일 다운로드1
12623정성태5/2/2021111.NET Framework: 1054. C# 9 최상위 문에 STAThread 사용파일 다운로드1
12622정성태5/3/202136오류 유형: 713. XSD 파일을 포함한 프로젝트 - The type or namespace name 'TypedTableBase<>' does not exist in the namespace 'System.Data'
12621정성태5/1/2021144.NET Framework: 1053. C# - 특정 레지스트리 변경 시 알림을 받는 방법파일 다운로드1
12620정성태5/3/2021358.NET Framework: 1052. C# - 왜 구조체는 16 바이트의 크기가 적합한가? [1]파일 다운로드1
12619정성태4/29/2021308.NET Framework: 1051. C# - 구조체의 크기가 16바이트가 넘어가면 힙에 할당된다? [1]파일 다운로드1
12618정성태5/12/2021110사물인터넷: 58. NodeMCU v1 ESP8266 CP2102 Module을 이용한 WiFi UDP 통신파일 다운로드1
12617정성태4/26/202167.NET Framework: 1050. C# - ETW EventListener의 Keywords 별 EventId에 따른 필터링 방법파일 다운로드1
12616정성태4/26/2021128.NET Framework: 1049. C# - ETW EventListener를 상속받았을 때 초기화 순서파일 다운로드1
12615정성태4/26/202148오류 유형: 712. Microsoft Live 로그인 - 계정을 선택하는(Pick an account) 화면에서 진행이 안 되는 문제
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...