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)
12629정성태5/5/2021270.NET Framework: 1057. C# - CoAP 서버 및 클라이언트 제작 (UDP 소켓 통신)파일 다운로드1
12628정성태5/4/2021137Linux: 39. Eclipse 원격 디버깅 - Cannot run program "gdb": Launching failed
12627정성태5/4/2021177Linux: 38. 라즈베리 파이 제로 용 프로그램 개발을 위한 Eclipse C/C++ 윈도우 환경 설정
12626정성태5/14/2021221.NET Framework: 1056. C# - Thread.Suspend 호출 시 응용 프로그램 hang 현상 (2)파일 다운로드1
12625정성태5/3/2021193오류 유형: 714. error CS5001: Program does not contain a static 'Main' method suitable for an entry point
12624정성태5/3/2021358.NET Framework: 1055. C# - struct/class가 스택/힙에 할당되는 사례 정리 [6]파일 다운로드1
12623정성태5/2/2021201.NET Framework: 1054. C# 9 최상위 문에 STAThread 사용파일 다운로드1
12622정성태5/3/2021101오류 유형: 713. XSD 파일을 포함한 프로젝트 - The type or namespace name 'TypedTableBase<>' does not exist in the namespace 'System.Data'
12621정성태5/1/2021296.NET Framework: 1053. C# - 특정 레지스트리 변경 시 알림을 받는 방법파일 다운로드1
12620정성태5/3/2021524.NET Framework: 1052. C# - 왜 구조체는 16 바이트의 크기가 적합한가? [1]파일 다운로드1
12619정성태4/29/2021467.NET Framework: 1051. C# - 구조체의 크기가 16바이트가 넘어가면 힙에 할당된다? [1]파일 다운로드1
12618정성태5/14/2021193사물인터넷: 58. NodeMCU v1 ESP8266 CP2102 Module을 이용한 WiFi UDP 통신파일 다운로드1
12617정성태4/26/2021167.NET Framework: 1050. C# - ETW EventListener의 Keywords 별 EventId에 따른 필터링 방법파일 다운로드1
12616정성태4/26/2021166.NET Framework: 1049. C# - ETW EventListener를 상속받았을 때 초기화 순서파일 다운로드1
12615정성태4/26/2021103오류 유형: 712. Microsoft Live 로그인 - 계정을 선택하는(Pick an account) 화면에서 진행이 안 되는 문제
12614정성태4/24/2021248개발 환경 구성: 570. C# - Azure AD 인증을 지원하는 ASP.NET Core 5.0 웹 애플리케이션 예제 구성 [2]파일 다운로드1
12613정성태4/23/2021164.NET Framework: 1048. C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (2) 관리 코드파일 다운로드1
12612정성태4/23/2021166.NET Framework: 1047. C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (1) PInvoke파일 다운로드1
12611정성태4/22/2021191오류 유형: 711. 닷넷 EXE 실행 오류 - Mixed mode assembly is build against version 'v2.0.50727' of the runtime
12610정성태4/22/2021260.NET Framework: 1046. C# - 컴파일 시점에 참조할 수 없는 타입을 포함한 이벤트 핸들러를 Reflection을 이용해 구독하는 방법파일 다운로드1
12609정성태4/22/2021298.NET Framework: 1045. C# - 런타임 시점에 이벤트 핸들러를 만들어 Reflection을 이용해 구독하는 방법파일 다운로드1
12608정성태4/22/2021463.NET Framework: 1044. C# - Generic Host를 이용해 .NET 5로 리눅스 daemon 프로그램 만드는 방법 [7]파일 다운로드1
12607정성태4/21/2021242.NET Framework: 1043. C# - 실행 시점에 동적으로 Delegate 타입을 만드는 방법파일 다운로드1
12606정성태4/21/2021327.NET Framework: 1042. C# - enum 값을 int로 암시적(implicit) 형변환하는 방법? [2]파일 다운로드1
12605정성태4/18/2021193.NET Framework: 1041. C# - AssemblyID, ModuleID를 관리 코드에서 구하는 방법파일 다운로드1
12604정성태4/19/2021222VS.NET IDE: 163. 비주얼 스튜디오 속성 창의 "Build(빌드)" / "Configuration(구성)"에서의 "활성" 의미
1  2  [3]  4  5  6  7  8  9  10  11  12  13  14  15  ...