Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 3개 있습니다.)

WSL 2 기반으로 "Enable Kubernetes" 활성화 시 초기화 실패

Docker Desktop for Windows에서 "Use the WSL 2 based engine" 옵션을 켠 경우, "Enable Kubernetes"를 적용하면 다음과 같이 "Starting ..."에서 멈추지를 않습니다.

k8s_config_hang_1.png

이때의 docker 로그(%LOCALAPPDATA%\Docker\log.txt)를 보면 이런 메시지가 반복되기도 하고,

"cannot get lease for master node: an error on the server (\"\") has prevented the request from succeeding (get leases.coordination.k8s.io docker-desktop)"
"external: POST /events 200 \"DockerDesktopGo\" \"\""

또는, k8s 관련 서비스들이 시작될 때까지 대기하는 듯한 메시지가 반복됩니다.

"1/3 system pods running, found labels component=kube-apiserver but still waiting for labels k8s-app=kube-dns, component=kube-controller-manager..."

관련해서 검색해 보면,

Docker for Windows stuck at “Kubernetes is Starting” after updating to version 2.1.1.0 Edge (or Stable)
; https://stackoverflow.com/questions/57711639/docker-for-windows-stuck-at-kubernetes-is-starting-after-updating-to-version-2

"%LOCALAPPDATA%\Docker\pki" 디렉터리를 삭제하고 다시 docker desktop을 재시작하면 됐다는 글들이 나옵니다.




그런데, 제 경우에는 그게 통하지 않았습니다. ^^; 게다가 k8s 구성이 정상적이지 않아 kubectl의 실행도 잘 되지 않는데요, 와중에 간혹 실행이 되면 출력에서 다음과 같은 문제를 볼 수 있습니다.

C:\temp> kubectl get pods --all-namespaces
NAMESPACE     NAME                                     READY   STATUS             RESTARTS   AGE
kube-system   etcd-docker-desktop                      1/1     Running            0          18m
kube-system   kube-apiserver-docker-desktop            1/1     Running            4          18m
kube-system   kube-controller-manager-docker-desktop   0/1     CrashLoopBackOff   8          19m
kube-system   kube-scheduler-docker-desktop            1/1     Running            0          18m

kube-controller-manager의 로그를 (운 좋게) 살펴보면,

C:\temp> kubectl -n kube-system logs -p  kube-controller-manager-docker-desktop
Error from server (InternalError): an error on the server ("") has prevented the request from succeeding (get pods kube-controller-manager-docker-desktop)

C:\temp> kubectl -n kube-system logs -p  kube-controller-manager-docker-desktop
Flag --port has been deprecated, see --secure-port instead.
I0318 07:06:16.127978       1 serving.go:331] Generated self-signed cert in-memory
I0318 07:06:16.331572       1 controllermanager.go:175] Version: v1.19.7
I0318 07:06:16.332216       1 dynamic_cafile_content.go:167] Starting request-header::/run/config/pki/front-proxy-ca.crt
I0318 07:06:16.332300       1 dynamic_cafile_content.go:167] Starting client-ca-bundle::/run/config/pki/ca.crt
I0318 07:06:16.332492       1 secure_serving.go:197] Serving securely on 127.0.0.1:10257
I0318 07:06:16.332585       1 tlsconfig.go:240] Starting DynamicServingCertificateController
W0318 07:06:27.194500       1 controllermanager.go:628] fetch api resource lists failed, use legacy client builder: Get "https://192.168.65.4:6443/api/v1?timeout=32s": x509: certificate is valid for 10.96.0.1, 0.0.0.0, 192.168.65.3, 127.0.0.1, not 192.168.65.4
F0318 07:06:37.201977       1 controllermanager.go:244] error building controller context: failed to wait for apiserver being healthy: timed out waiting for the condition: failed to get apiserver /healthz status: Get "https://192.168.65.4:6443/healthz?timeout=32s": x509: certificate is valid for 10.96.0.1, 0.0.0.0, 192.168.65.3, 127.0.0.1, not 192.168.65.4

goroutine 1 [running]:
k8s.io/kubernetes/vendor/k8s.io/klog/v2.stacks(0xc000130001, 0xc00037a280, 0x167, 0x276)
        /workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/vendor/k8s.io/klog/v2/klog.go:996 +0xb9
k8s.io/kubernetes/vendor/k8s.io/klog/v2.(*loggingT).output(0x6a47fa0, 0xc000000003, 0x0, 0x0, 0xc000a0a150, 0x68bfef4, 0x14, 0xf4, 0x0)
        /workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/vendor/k8s.io/klog/v2/klog.go:945 +0x191
k8s.io/kubernetes/vendor/k8s.io/klog/v2.(*loggingT).printf(0x6a47fa0, 0x3, 0x0, 0x0, 0x449dd15, 0x25, 0xc001125348, 0x1, 0x1)
        /workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/vendor/k8s.io/klog/v2/klog.go:733 +0x17a
k8s.io/kubernetes/vendor/k8s.io/klog/v2.Fatalf(...)
        /workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/vendor/k8s.io/klog/v2/klog.go:1456
k8s.io/kubernetes/cmd/kube-controller-manager/app.Run.func1(0x4a691a0, 0xc00012e018)
        /workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/cmd/kube-controller-manager/app/controllermanager.go:244 +0x54e
k8s.io/kubernetes/cmd/kube-controller-manager/app.Run(0xc000b7eea0, 0xc0001140c0, 0xc000d0c950, 0xc000b7e458)
        /workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/cmd/kube-controller-manager/app/controllermanager.go:260 +0x9b1
k8s.io/kubernetes/cmd/kube-controller-manager/app.NewControllerManagerCommand.func2(0xc0006c8dc0, 0xc000d08b40, 0x0, 0x13)
        /workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/cmd/kube-controller-manager/app/controllermanager.go:124 +0x2b7
k8s.io/kubernetes/vendor/github.com/spf13/cobra.(*Command).execute(0xc0006c8dc0, 0xc000136010, 0x13, 0x13, 0xc0006c8dc0, 0xc000136010)
        /workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/vendor/github.com/spf13/cobra/command.go:846 +0x2c2
k8s.io/kubernetes/vendor/github.com/spf13/cobra.(*Command).ExecuteC(0xc0006c8dc0, 0x166d5dfe0031f738, 0x6a47c00, 0x406525)
        /workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/vendor/github.com/spf13/cobra/command.go:950 +0x375
k8s.io/kubernetes/vendor/github.com/spf13/cobra.(*Command).Execute(...)
        /workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/vendor/github.com/spf13/cobra/command.go:887
main.main()
        _output/dockerized/go/src/k8s.io/kubernetes/cmd/kube-controller-manager/controller-manager.go:46 +0xe5

아래의 메시지가 눈에 띕니다.

// Kubelet - x509: certificate is valid for 10.233.0.1 not for <IP>
// https://stackoverflow.com/questions/54303469/kubelet-x509-certificate-is-valid-for-10-233-0-1-not-for-ip

certificate is valid for 10.96.0.1, 0.0.0.0, 192.168.65.3, 127.0.0.1, not 192.168.65.4

누락된 192.168.65.4가 실제로 사용되고 있는 듯하고,

// 우선 kubectl proxy 실행

c:\temp> curl http://127.0.0.1:8001/api/
{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "192.168.65.4:6443"
    }
  ]
}

또한, 해당 인증서("%LOCALAPPDATA%\Docker\pki\apiserver.crt")를 보면,

k8s_config_hang_2.png

SAN에 "192.168.65.4" 항목이 없습니다. 아마도 "Docker for Windows stuck at “Kubernetes is Starting” after updating to version 2.1.1.0 Edge (or Stable)" 등의 Q&A에서 " pki 폴더를 삭제하고 재시작해 동작했다는 것이 저런 이유 때문으로 보입니다.




그런데, WSL 2 + k8s 초기화의 실패 원인이 저것만이 전부는 아닌 듯합니다. 왜냐하면, 위의 인증서는 192.168.65.4를 포함해 다시 생성하는 것이 가능하기 때문입니다. "%LOCALAPPDATA%\Docker\pki" 디렉터리를 보면, CA의 인증서 및 개인키 파일과 함께 apiserver에 대한 키파일도 있고,

  • ca.crt
  • ca.key
  • apiserver.key

k8s에서 요구하는 인증서의 요구 사항도 공개돼 있으므로,

PKI certificates and requirements
; https://kubernetes.io/docs/setup/best-practices/certificates/

Certificate Management with kubeadm
; https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-certs/

kube-apiserver
    Parent: kubernetes-ca   
    O (in Subject): (empty)
    kind: server
    hosts (SAN): <hostname>, <host_ip>, <advertise_ip>, [1]
    recommended key path: apiserver.key
    recommended cert path: apiserver.crt
    command: kube-apiserver
    key argument: --tls-private-key-file
    cert argument: --tls-cert-file

server == digital signature, key encipherment, server auth

[1] == any other IP or DNS name you contact your cluster on (as used by kubeadm the load balancer stable IP and/or DNS name, kubernetes, kubernetes.default, kubernetes.default.svc, kubernetes.default.svc.cluster, kubernetes.default.svc.cluster.local)

다음과 같은 식으로 cnf 파일을 만들어,

FQDN = kube-apiserver

# ALTNAMES는 기존 apiserver.crt의 Subject 목록을 포함한 후, "certificate is valid for ..., not 192.168.65.4"로 있는 IP를 추가해 구성
ALTNAMES = DNS:docker-for-desktop,DNS:kubernetes,DNS:kubernetes.default,DNS:kubernetes.default.svc,DNS:kubernetes.default.svc.cluster.local,DNS:kubernetes.docker.internal,DNS:vm.docker.internal,DNS:localhost,IP:10.96.0.1,IP:0.0.0.0,IP:192.168.65.3,IP:127.0.0.1,IP:192.168.65.4

[ req ]
default_bits = 2048
default_md = sha256
prompt = no
encrypt_key = no
distinguished_name = dn
req_extensions = req_ext

[ dn ]
CN = $FQDN

[ req_ext ]
extendedKeyUsage = serverAuth
subjectAltName = $ALTNAMES
keyUsage = digitalSignature,keyEncipherment

[ x509_ext ]
extendedKeyUsage = serverAuth
authorityKeyIdentifier = keyid,issuer
subjectAltName = $ALTNAMES
keyUsage = digitalSignature,keyEncipherment

openssl 명령어를 실행해 (누락된 IP를 포함한) apiserver.crt를 새로 만들 수 있습니다.

C:\temp\wsl> openssl req -key apiserver.key -new -out apiserver.csr -config apiserver.cnf

C:\temp\wsl> openssl x509 -extfile apiserver.cnf -extensions x509_ext -req -days 365 -in apiserver.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out apiserver.crt
Signature ok
subject=CN = kube-apiserver
Getting CA Private Key

위의 명령어로 crt 파일을 생성했으면 그 파일을 %LOCALAPPDATA%\Docker\pki\apiserver.crt 파일로 덮어쓰면 됩니다.

하지만, 이렇게까지 한 후 docker를 재시작해도 여전히 docker 로그 파일(%LOCALAPPDATA%\Docker\log.txt)의 오류 메시지는 반복되고 k8s 구성에 실패합니다.

c:\temp> tail -f %LOCALAPPDATA%\Docker\log.txt
[21:44:23.572][ApiProxy          ][Info   ] msg="cannot get lease for master node: an error on the server (\"\") has prevented the request from succeeding (get leases.coordination.k8s.io docker-desktop)"
[21:44:23.572][GoBackendProcess  ][Info   ] msg="external: POST /events 200 \"DockerDesktopGo\" \"\""




결론을 보면, 현재(2021-03-20 기준) "Docker desktop for Windows"에서는 "Enable Kubernetes"를 설정하려면 "Use the WSL 2 based engine" 옵션을 해제해야 합니다. (혹시, WSL 2 상에서 k8s 구성에 성공하신 분은 덧글 부탁드립니다. ^^)

마지막으로, 아래는 docker desktop for windows와 k8s 구성 관련 디렉터리 목록입니다.

  • %ProgramFiles%\Docker
  • %USERPROFILE%\.docker
  • %USERPROFILE%\.kube
  • %APPDATA%\Docker
  • %APPDATA%\Docker\Docker Desktop
  • %LOCALAPPDATA%\Docker
  • %LOCALAPPDATA%\Docker Desktop Installer




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 4/2/2021]

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

비밀번호

댓글 작성자
 



2021-04-05 02시35분
특정 Windows 10에서는 WSL 2 + k8s 활성화가 잘 되는데... 그 차이를 모르겠군요. ^^; 아무래도 이런 환경 상의 요인 때문에라도 Docker Desktop for Windows 측의 대응이 빠르지는 않을 듯합니다.
정성태

... 121  122  123  124  125  126  127  128  129  130  131  [132]  133  134  135  ...
NoWriterDateCnt.TitleFile(s)
1755정성태9/22/201434279오류 유형: 241. Unity Web Player를 설치해도 여전히 설치하라는 화면이 나오는 경우 [4]
1754정성태9/22/201424627VC++: 80. 내 컴퓨터에서 C++ AMP 코드가 실행이 될까요? [1]
1753정성태9/22/201420608오류 유형: 240. Lync로 세미나 참여 시 소리만 들리지 않는 경우 [1]
1752정성태9/21/201441069Windows: 100. 윈도우 8 - RDP 연결을 이용해 VNC처럼 사용자 로그온 화면을 공유하는 방법 [5]
1751정성태9/20/201438939.NET Framework: 464. 프로세스 간 통신 시 소켓 필요 없이 간단하게 Pipe를 열어 통신하는 방법 [1]파일 다운로드1
1750정성태9/20/201423832.NET Framework: 463. PInvoke 호출을 이용한 비동기 파일 작업파일 다운로드1
1749정성태9/20/201423732.NET Framework: 462. 커널 객체를 위한 null DACL 생성 방법파일 다운로드1
1748정성태9/19/201425383개발 환경 구성: 238. [Synergy] 여러 컴퓨터에서 키보드, 마우스 공유
1747정성태9/19/201428450오류 유형: 239. psexec 실행 오류 - The system cannot find the file specified.
1746정성태9/18/201426103.NET Framework: 461. .NET EXE 파일을 닷넷 프레임워크 버전에 상관없이 실행할 수 있을까요? - 두 번째 이야기 [6]파일 다운로드1
1745정성태9/17/201423035개발 환경 구성: 237. 리눅스 Integration Services 버전 업그레이드 하는 방법 [1]
1744정성태9/17/201431055.NET Framework: 460. GetTickCount / GetTickCount64와 0x7FFE0000 주솟값 [4]파일 다운로드1
1743정성태9/16/201420985오류 유형: 238. 설치 오류 - Failed to get size of pseudo bundle
1742정성태8/27/201426969개발 환경 구성: 236. Hyper-V에 설치한 리눅스 VM의 VHD 크기 늘리는 방법 [2]
1741정성태8/26/201421334.NET Framework: 459. GetModuleHandleEx로 알아보는 .NET 메서드의 DLL 모듈 관계파일 다운로드1
1740정성태8/25/201432503.NET Framework: 458. 닷넷 GC가 순환 참조를 해제할 수 있을까요? [2]파일 다운로드1
1739정성태8/24/201426528.NET Framework: 457. 교착상태(Dead-lock) 해결 방법 - Lock Leveling [2]파일 다운로드1
1738정성태8/23/201422045.NET Framework: 456. C# - CAS를 이용한 Lock 래퍼 클래스파일 다운로드1
1737정성태8/20/201419763VS.NET IDE: 93. Visual Studio 2013 동기화 문제
1736정성태8/19/201425573VC++: 79. [부연] CAS Lock 알고리즘은 과연 빠른가? [2]파일 다운로드1
1735정성태8/19/201418188.NET Framework: 455. 닷넷 사용자 정의 예외 클래스의 최소 구현 코드 - 두 번째 이야기
1734정성태8/13/201419846오류 유형: 237. Windows Media Player cannot access the file. The file might be in use, you might not have access to the computer where the file is stored, or your proxy settings might not be correct.
1733정성태8/13/201426359.NET Framework: 454. EmptyWorkingSet Win32 API를 사용하는 C# 예제파일 다운로드1
1732정성태8/13/201434474Windows: 99. INetCache 폴더가 다르게 보이는 이유
1731정성태8/11/201427080개발 환경 구성: 235. 점(.)으로 시작하는 파일명을 탐색기에서 만드는 방법
1730정성태8/11/201422164개발 환경 구성: 234. Royal TS의 터미널(Terminal) 연결에서 한글이 깨지는 현상 해결 방법
... 121  122  123  124  125  126  127  128  129  130  131  [132]  133  134  135  ...