나는 Data Science를 전공했기 때문에 사실 Kubernetes와는 아예 남남 사이였다. 그런데 최근 MLOps의 필요성을 느끼게 되었고 결국 이 친구들을 다뤄야 한다는 사실을 알게 되었다. 잘 모르지만 천천히 docker와 kubernetes를 찾아보며 활용법을 중심으로 찾아보고 있었고 그 중 가장 흥미로웠던 부분이 바로 kubeflow였는데... 나에게 한 가지 큰 문제가 생겼다. 바로, 아무리 시도해도 kubeflow가 설치되지 않는 것이다.
찾아보다보니 내가 얼마 전에 M1 Macbook Pro로 노트북을 바꾸었다는 점이 문제가 됐던 것 같다.이게 왜 문제가 되냐면, 나와 같은 apple silicon 동지들은 보편적으로 알려져있는 linux/amd64
기준 설치 방법을 따르면 안 되기 때문이다...^^ 아마 이 글을 찾아오신 분들은 이미 알고 계시겠지만 우리는 linux/arm64
를 키워드로 하는 설치 방법을 따라야 한다. 이 말을 믿기 어렵다면 Terminal에 아래와 같은 명령을 실행해볼 수 있다.
$ uname
Darwin
$ uname -m
arm64
이 글은 위와 같은 환경에서, kubeflow 최신 버전(2023.07 기준) 설치에 애를 먹고 있는 동지들을 위한 글이다. 나 또한 매우 애를 먹었던 부분이었기 때문에, 누구 한 명이라도 도움을 받았으면 좋겠다는 생각에 정리해보았다.
본 글을 시작하기에 앞서 나는 본 글 작성일자를 기준으로 docker, kubernetes, kubeflow 전문가가 전-혀 아니며 완전 뉴비임을 밝힌다.
우선 내 환경은 다음과 같다.
kubernetes version: v1.23.1 Single node deployment
minikube version: v1.30.1
kustomize version: v5.0.0
kubeflow를 설치할 수 있는 방법에는 굉장히 여러 가지가 있었는데, manifests repository를 통해서 설치하는 방법이 제일 정석이라고 해서 그대로 README.md의 안내를 따라봤다.
Kubeflow가 정상적으로 잘 설치되었다!라고 말하려면 모든 pod가 running이어야 하는데 나는 일부 pod에서 ErrImagePull
가 발생했다.
가장 먼저 했던 시도는 Mac에서 kubeflow를 설치한 사례를 찾는 것이었다. 다들 kustomize를 downgrade해서 설치하길래 나도 따라해보고 싶었는데 많이들 쓰는 kustomize 3.2.0은 linux/arm64를 지원하지 않는다. 어느 블로그 글에서 M1에 저 버전으로 설치해서 kubeflow를 사용하신 분을 봤는데, 어떻게 한 건지.. 잘 모르겠다. 그리고 내가 간과하고 있었던 게, kubeflow v1.7.0은 kustomize 5.0.0을 지원한다. 그러니까 새삥 버전을 쓰고 싶었던 나한테는 해당되지 않는 문제였다. ^^^
가장 첫 시도는 바로, ubuntu 가상 환경 생성이었다. VirtualBox를 이용하고 싶었는데 아직까지 apple silicon에 대해서 beta 버전을 지원하고 있고, 내 생각대로 설치가 안 되어서 UTM이라는 앱을 설치해보았다.
가상 환경을 생성하기 위해 Ubuntu에서 이미지를 가져왔는데, 사실 이때도 고민이 되었다. 나는 amd64 가상환경이 필요한데, 내 노트북은 arm64니까 이미지는 어떤 걸 가져와야 하지...? 그래서 먼저 arm64를 시도해보았다. 지금 생각해보면 바보같은 생각인데 당연히 내 맥북과 동일한 환경에서 설치를 시도하였기 때문에 안 됐다. 그래서 linux/amd64를 가져와서 에뮬레이션(Emulation)을 시도해보았다. 에뮬레이션의 경우 cpu architecture가 다를 경우 속도가 느릴 수 있다는 단점이 있다는 것은 알고 있었는데, 실제로 시도해보니 정말정말정말정말정말정말 많이 느렸다.. 굳이 시도해보지 않는 것을 추천드릴 정도이다.
실패한 시도였지만, 하나 확실히 알게 된 것은 다음과 같았다.
Reddit에서 multipass라는 걸 설치해서 하라는 말을 들어봤는데 이것도 잘 안 됐다. ㅋ minikube 버전도 바꿔보고 했는데 결국 아키텍쳐와 관련한 접근이 필요하다는 결론에 도달했다.
ErrImagePull
이 발생한 로그의 pod 기록을 살펴보면 대략 다음과 같이 나온다.
(중략)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 22s default-scheduler Successfully assigned kubeflow/profiles-deployment-54f457d59d-d44ts to minikube
Normal Pulled 21s kubelet Container image "docker.io/istio/proxyv2:1.16.0" already present on machine
Normal Created 21s kubelet Created container istio-init
Normal Started 21s kubelet Started container istio-init
Normal Pulling 21s kubelet Pulling image "docker.io/kubeflownotebookswg/kfam:v1.7.0-rc.0"
Normal Created 18s (x2 over 18s) kubelet Created container manager
Warning Failed 18s kubelet Error: ErrImagePull
Normal Pulled 18s (x2 over 18s) kubelet Container image "docker.io/vinaychandran/profile-controller:v1.7.0" already present on machine
Warning Failed 18s kubelet Failed to pull image "docker.io/kubeflownotebookswg/kfam:v1.7.0-rc.0": rpc error: code = Unknown desc = no matching manifest for linux/arm64/v8 in the manifest list entries
Normal Started 18s (x2 over 18s) kubelet Started container manager
Normal Pulled 18s kubelet Container image "docker.io/istio/proxyv2:1.16.0" already present on machine
Normal Created 18s kubelet Created container istio-proxy
Normal Started 18s kubelet Started container istio-proxy
Warning BackOff 16s (x2 over 17s) kubelet Back-off restarting failed container
Normal BackOff 15s (x4 over 18s) kubelet Back-off pulling image "docker.io/kubeflownotebookswg/kfam:v1.7.0-rc.0"
Warning Failed 15s (x4 over 18s) kubelet Error: ImagePullBackOff
나는 사실 docker에 대해서 아직 잘 모르지만 여기서 대략 답을 알 수 있었다.
"docker.io/kubeflownotebookswg/kfam:v1.7.0-rc.0": rpc error: code = Unknown desc = no matching manifest for linux/arm64/v8 in the manifest list entries Normal Started 18s (x2 over 18s) kubelet Started container manager
한 마디로 나의 환경인 linux/arm64와 일치하는 docker image를 찾을 수 없다는 것이었다. 그러니까 이제 해결 방법은, linux/arm64를 지원하는 docker image를 찾던지(없으면 만들던지...^^)해서 manifests 설치 과정에서 반영해주어야 한다는 것이다.
Docker hub를 찾아보니, 아무래도 나와 같은 문제를 겪었던 유저가 있었던듯 하다. 오류가난 pod에 대한 linux/arm64 image들을 여기서 확인할 수 있었다. https://hub.docker.com/u/vinaychandran
근데 도대체 manifests 코드 중에서 이걸 어디에 반영해야할지 모르겠던 나는 이리저리 헤매였다. 진짜 뭐가 뭔지 하나도 모르겠지만 오랜 삽질 끝에 아무튼 Repo의 docker image 주소를 변경해야지만 한다는 결론을 내린 나를 믿어보기로 했다. docker를 잘 몰라서 망가뜨릴까봐 좀 겁나긴 했지만(잘못되면 다시 clone 해오면 되니까 ㅎㅎ), 우선 Kustomization.yaml 파일들을 열어보기 시작했고 찾을 수 없는 docker image 주소가 적힌 것을 위에서 언급한 linux/arm64 이미지 주소로 아래와 같이 변경해두었다.
그리고 다음처럼 pod들을 삭제해준 뒤 (pod만 삭제하면 깔끔하게 안 지워지니까 다음과 같은 명령어로 수행하시길 권장한다.)
$ kustomize build apps/profiles/upstream/overlays/kubeflow | kubectl delete -f
다시 설치했다.
$ kustomize build apps/profiles/upstream/overlays/kubeflow | kubectl apply -f -
$ kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
auth dex-64775f9cf6-jtgnq 1/1 Running 0 10m
cert-manager cert-manager-b4b465456-f6vks 1/1 Running 0 10m
cert-manager cert-manager-cainjector-64d74f9c8f-k26pg 1/1 Running 0 10m
cert-manager cert-manager-webhook-66fff58cdf-zk59s 1/1 Running 0 10m
istio-system authservice-0 1/1 Running 0 10m
istio-system cluster-local-gateway-7f55dcfff7-hx5vm 1/1 Running 0 10m
istio-system istio-ingressgateway-869ccf7495-ddxmb 1/1 Running 0 10m
istio-system istiod-69d59d9787-kh6zp 1/1 Running 0 10m
knative-eventing eventing-controller-59f4b748d5-nrk49 1/1 Running 0 10m
knative-eventing eventing-webhook-d85c57bb4-2bdqv 1/1 Running 0 10m
knative-serving activator-866846c6d5-n2ngv 2/2 Running 0 9m14s
knative-serving autoscaler-7654f76758-zhvpr 2/2 Running 0 9m12s
knative-serving controller-756f7689b7-hjwvn 2/2 Running 0 9m11s
knative-serving domain-mapping-59c548885b-v4zr5 2/2 Running 0 9m11s
knative-serving domainmapping-webhook-77d7fcdd89-2p75m 2/2 Running 0 9m10s
knative-serving net-istio-controller-59cfff6cb6-tbtjz 2/2 Running 0 9m10s
knative-serving net-istio-webhook-55c7fbc94d-h668d 2/2 Running 0 9m9s
knative-serving webhook-6d7b7f6b54-6n7sc 2/2 Running 0 9m9s
kube-system coredns-64897985d-cvphm 1/1 Running 1 (12m ago) 13m
kube-system etcd-minikube 1/1 Running 1 (12m ago) 13m
kube-system kube-apiserver-minikube 1/1 Running 1 (12m ago) 13m
kube-system kube-controller-manager-minikube 1/1 Running 1 (12m ago) 13m
kube-system kube-proxy-8hvw4 1/1 Running 1 (12m ago) 13m
kube-system kube-scheduler-minikube 1/1 Running 1 (12m ago) 13m
kube-system storage-provisioner 1/1 Running 1 (12m ago) 13m
kubeflow-user-example-com ml-pipeline-ui-artifact-569fc96b97-m92hv 2/2 Running 0 20s
kubeflow-user-example-com ml-pipeline-visualizationserver-989cc86c9-qhghz 2/2 Running 0 20s
kubeflow admission-webhook-deployment-75d8f74f49-nq7xq 1/1 Running 0 9m29s
kubeflow cache-server-94888747d-fbftm 2/2 Running 0 9m28s
kubeflow centraldashboard-5454d95d5-8f8c4 2/2 Running 0 9m28s
kubeflow jupyter-web-app-deployment-694b9f69cc-rm4b6 2/2 Running 0 9m28s
kubeflow katib-controller-64877b975b-xlv5l 1/1 Running 0 9m28s
kubeflow katib-db-manager-5f69cfc55d-fvlp9 1/1 Running 0 9m27s
kubeflow katib-mysql-6975d6c6c4-8hbfl 1/1 Running 0 9m27s
kubeflow katib-ui-5cc65f6464-pgn79 2/2 Running 1 (2m13s ago) 9m27s
kubeflow kserve-controller-manager-574f455cd9-4gbw4 2/2 Running 0 9m27s
kubeflow kserve-models-web-app-85ccc875dd-4mx6m 2/2 Running 0 9m26s
kubeflow kubeflow-pipelines-profile-controller-6dc4ff7ffd-zpqwp 1/1 Running 0 9m26s
kubeflow metacontroller-0 1/1 Running 0 9m27s
kubeflow metadata-envoy-deployment-b58cd84bf-gf6ws 1/1 Running 0 9m26s
kubeflow metadata-grpc-deployment-679b49cc95-kk2fn 2/2 Running 5 (81s ago) 9m26s
kubeflow metadata-writer-b89f875db-kd2mk 2/2 Running 2 (107s ago) 9m26s
kubeflow minio-7955cfc9fc-q9dc5 2/2 Running 0 9m25s
kubeflow ml-pipeline-fd97b696-knj8x 2/2 Running 0 9m25s
kubeflow ml-pipeline-persistenceagent-76dd4f5778-dr5j8 2/2 Running 0 9m25s
kubeflow ml-pipeline-scheduledworkflow-5866bf76d7-pq7rp 2/2 Running 0 9m25s
kubeflow ml-pipeline-ui-c58ddcd6b-plg84 2/2 Running 0 9m25s
kubeflow ml-pipeline-viewer-crd-67f66b9746-dzw7h 2/2 Running 0 9m25s
kubeflow ml-pipeline-visualizationserver-8656969c7f-v266l 2/2 Running 0 9m24s
kubeflow mysql-8456ccf979-sb67g 2/2 Running 0 9m24s
kubeflow notebook-controller-deployment-579f9cd658-6ffg4 2/2 Running 2 (115s ago) 9m24s
kubeflow profiles-deployment-6cc58bc769-s4pj5 3/3 Running 1 (29s ago) 9m24s
kubeflow tensorboard-controller-deployment-787ddd8b77-g7g2m 3/3 Running 1 (3m ago) 9m24s
kubeflow tensorboards-web-app-deployment-5586dd9bf8-fkm49 2/2 Running 0 9m23s
kubeflow training-operator-55d56885cb-k5fvv 1/1 Running 0 9m23s
kubeflow volumes-web-app-deployment-7c8b56d8d7-7x5q2 2/2 Running 0 9m23s
kubeflow workflow-controller-555f64865-gvzxb 2/2 Running 1 (94s ago) 9m23s
이제 포트포워딩을 해주고
$ kubectl port-forward svc/istio-ingressgateway -n istio-system 8080:80
localhost에 들어갔는데, 또 하나의 문제가 생겼다. 바로 Katib 설치가 안 되는 것. 이것도 docker image 주소를 바꾸면 되지 않을까? 역시 image 버전 문제였다. 바로 upstream으로 받아오는 부분에서 katib 최신 버전으로 pull이 안 돼서 생긴 문제였던 것 같다. 그래서 다음과같이 upstream에 해당하는 부분에서 image를 최신 버전으로 변경해주었다.
다시 한 번 포트포워딩을 하고 localhost를 통해 들어가보면 해결이 되어있다.
공식 repo issue에 현재 문제 상황을 공유하였다.
내 시간은 못 지켰지만 누군가의 시간을 지켰다면 넘나 뿌듯할 것 같다. ㅎㅎ
https://github.com/kubeflow/manifests/issues/2472
위에서 언급한 문제가 수정된 manifests는 아래의 repo에서 확인할 수 있다.
https://github.com/hwang9u/manifests
확실히 개발 쪽은 변화가 빨라서 버전 문제에 민감한 것 같다. 특히 MLOps라고 하는 분야는 아직까지 정립되어있다고 볼 수 없는 것 같기도 하다. 나는 데이터사이언티스트인데 이런 부분에서 난관에 부딪혀야 하나 싶기도 했지만 맞닥뜨려야 하나 싶지만, 아직까지 이런 도구들을 활용할 수 있으려면 공부해야 하는 게 현실인 것 같다. 내가 못하는 것에 대해 언제까지고 툴툴댈 수는 없는 노릇임을 다시금 되새기는 날이었다.