Minikube NFS 설정하기
오늘은 NFS를 설정해서 Kubeflow Pipeline에서 저장되는 DL model을 호스트에 저장시키는 방법을 알아봅시다!
Kubeflow, kfp SDK의 한계
Minikube-Kubeflow로 MLOps를 진행하다보니 몇가지 한계점이 느껴지더군요.
-
PV/PVC를 설정해도 pod 내부(훈련 컨테이너)에서 모델을 저장해도 PV에 저장될 뿐, 이 PV가 호스트에 연결되어 있지 않다.(아래서 진행하려는 설정 문제죠, 클라우드는 이런 부분을 신경쓰지 않아도 자동화 시켜준다는게 편합니다)
-
모델 레지스트리(Model Registry)를 구성하여 모델의 버전을 관리하고, 모델 간의 성능도 비교하고, 로그를 착실히 쌓아가야 하는데 이러한 부분이 자동으로 구축되지 않는다.(AWS sagemaker는 모델 그룹으로 관리할 수 있습니다.)
-
Minikube의 PV에서 바로 모델을 서빙해도, 외부에서 해당 External-uri로 접근할 수가 없다.
-
Kubeflow는 Artifact를 Minio에 저장한다. 하지만 Minio에 저장할 수 있는 파일은 용량이 적어야하고, 컴포넌트 간 데이터의 IO는 용량이 큰 파일을 사용할 수 없다.(Kubeflow의 한계점입니다.)
-
결국 A 컴포넌트의 결과(훈련된 모델)를 B 컴포넌트의 Input으로 활용하기 위해선 kfp SDK의 기능을 활용할 수 없기 때문에, A와 B가 동시에 접근 가능한 모델 스토리지가 있어야 저장/로드를 활용할 수 있다.
이런 다양한 문제로 인해
1. 서버의 Host Path를 NFS로 설정하기
2. 쿠버네티스(Minikube)의 Node에선 PV로 NFS 경로를 마운트하기
3. 파이프라인에선 Node의 PV에 PVC를 연결해서 모델을 저장하기.
위와 같이 문제를 해결해보고자 합니다. 결국 모델이 서버 자체에 저장되도록 하는거죠!(사실 AWS S3 등으로 스토리지를 설정하면 편합니다. 하지만 해결방법이 있는데도 복잡하다고 지속적으로 클라우드 과금을 할 순 없잖아요?)
Pre
Server: Ubuntu 18.04
Client: Minikube GPU node(None driver)
Ubuntu에 NFS 서버 설치 및 구성
아래 내용은 우분투 18.04를 기반으로 진행합니다.
OS 및 버전에 따라 내용이 달라지니 각 운영체제의 NFS 구성 방법은 따로 확인해주시기 바랍니다!
서버에 nfs-kernel-server 패키지를 설치
- nfs-kernel-server는 파일 공유 설정에 중요한 nfs-common 및 rpcbind와 같은 추가 패키지를 저장합니다.
sudo apt update
sudo apt install nfs-kernel-server
- 위와 같은 안내창이 뜨면 install the package maintainer's version
(패키지 관리자 버전 설치)를 선택합시다.
NFS 내보내기 디렉터리(시스템 간에 공유 할 디렉토리) 생성
- 모든 클라이언트 머신이 공유 디렉토리에 액세스하기를 원하므로 디렉토리 권한의 제한을 제거하겠습니다.
- 디렉토리명은 바꾸셔도 됩니다.
- 권한은 중요한 문제지만 구축 테스트를 위해 아래와 같이 진행합니다. 실제 프로덕션 환경에선 적절한 권한을 고려해주세요.
sudo mkdir -p /mnt/nfs_share
sudo chown -R nobody:nogroup /mnt/nfs_share/
sudo chmod 777 /mnt/nfs_share/
NFS server 엑세스 권한 확인
- NFS 서버에 액세스하기위한 권한은/etc/exports 파일에 정의되어 있습니다
- 단일 클라이언트, 여러 클라이언트에 대한 액세스를 제공하거나 전체 서브넷을 지정할 수 있습니다.(여러 클라이언트는 여러 줄로 입력하시면 됩니다.)
- 이 포스팅에서는 전체 서브넷이 NFS 공유에 액세스 할 수 있도록 허용했습니다./mnt/nfs_share 192.168.0.14/24(rw,sync,no_subtree_check)
sudo vim /etc/exports
- 아래는 옵션에 대한 설명입니다.
- rw: Stands for Read/Write.
- sync: Requires changes to be written to the disk before they are applied.
- No_subtree_check: Eliminates subtree checking.
nfs-kernel-server 재시작
- 변경사항을 적용하고 nfs-kernel-server를 재시작합니다.
sudo exportfs -a
sudo systemctl restart nfs-kernel-server
방화벽 설정
- 클라이언트가 NFS 공유에 액세스하려면 방화벽을 통한 액세스를 허용해야합니다. 그렇지 않으면 공유 디렉토리에 액세스하고 마운트 할 수 없습니다.
sudo ufw allow from 192.168.0.14/24 to any port nfs
- 방화벽이 꺼져있는 경우 다시로드하거나 활성화하고 방화벽의 상태를 확인합니다
- 기본 파일 공유 인 포트 2049를 열어야합니다.
sudo ufw enable
sudo ufw status
클라이언트 시스템에 NFS 클라이언트 설치
보통의 클라이언트(OS나 VM에 존재하는 k8s Node 등)은 해당 링크 참고하여 진행하시면 됩니다.
저는 Minikube GPU 설정으로 인해 driver가 None이라 호스트 서버 자체가 Node입니다.
그래서 Node에 ssh접속(minikube ssh)에 접속해서 작업할 수 없기 때문에, 일반적인 방법이 아닌 바로 PV를 nfs-server 경로로 설정하는 방식으로 진행해보겠습니다.
(사실 None driver에서 로컬 마운트가 아닌 NFS를 사용하는게 맞는건가 싶긴한데, 로컬 마운트보다 NFS가 10배 이상 IO이 빠르다고 하여 시도해봅니다)
현재 PV 분석
현재 코드에서(코드는 제공드릴 수 없습니다만, 아래서 설명을 위해 일부 첨부하니 참고부탁드립니다.)
PV가 어떻게 선언되어 있는지 살펴보자.
kubectl get pv -A
kubectl describe pv <NAME> -n <NAMESPACE>
공식 문서에 따른 볼륨 및 볼륨 마운트 지정
https://kubernetes.io/ko/docs/concepts/storage/volumes/
볼륨을 사용하려면, .spec.volumes 에서 파드에 제공할 볼륨을 지정하고 .spec.containers[*].volumeMounts 의 컨테이너에 해당 볼륨을 마운트할 위치를 선언한다. 컨테이너의 프로세스는 컨테이너 이미지의 최초 내용물과 컨테이너 안에 마운트된 볼륨(정의된 경우에 한함)으로 구성된 파일시스템을 보게 된다. 프로세스는 컨테이너 이미지의 최초 내용물에 해당되는 루트 파일시스템을 보게 된다. 쓰기가 허용된 경우, 해당 파일시스템에 쓰기 작업을 하면 추후 파일시스템에 접근할 때 변경된 내용을 보게 될 것이다. 볼륨은 이미지의 특정 경로에 마운트된다. 파드에 정의된 각 컨테이너에 대해, 컨테이너가 사용할 각 볼륨을 어디에 마운트할지 명시해야 한다.
위와 같은 내용을 아래 코드를 보면서 이해해보자.
.spec.containers[*].volumeMounts
에는 컨테이너에 특정(nfs_shared)를 마운트 할 위치를 선언하는 것이다.
즉 컨테이너의 /mnt/export
에 nfs를 마운트하여 동기화시키고자 한다.
.spec.volumes
에 제공할 볼륨을 지정한다.
- 여기에 nfs 폴더의 경로를 설정해주어야 할 것 같은데, 기존엔 pvc에 대한 내용밖에 들어가 있지 않다.
# Generate TFJob Chief and Worker specs with the best hyperparameters.
tfjob_chief_spec = {
"replicas": 1,
"restartPolicy": "OnFailure",
"template": {
"metadata": {"annotations": {"sidecar.istio.io/inject": "false"}},
"spec": {
"containers": [
{
"name": "tensorflow",
"image": "docker.io/moey920/train_forecasting_model:latest",
"command": ["sh", "-c"],
"args": [
"python /code/main.py --tf_export_dir=/mnt/export --df_me={} {}".format(
df_me, best_hps
)
],
"resources": {
"limits": {
"ggaman.com/vram": 2
}
},
"volumeMounts": [
{"mountPath": "/mnt/export", "name": "forecasting-model-volume"}
],
}
],
"volumes": [
{
"name": "forecasting-model-volume",
"persistentVolumeClaim": {
"claimName": str(model_volume_op.outputs["name"])
},
}
],
},
},
}
- 그럼 앞서 볼륨을 생성해놓은 VolumeOp의 내용도 살펴보자.
kfp.dsl.VolumeOp는 PVC를 생성하는 오퍼레이터이며, 이렇게 요청된 PVC에 따라 PV가 없으면 동적 프로비저닝을 통해 PV를 생성하고 PV-PVC간 마운트가 이루어진다.
model_volume_op = dsl.VolumeOp(
name="model-volume",
resource_name="model-volume",
size="20Gi",
modes=dsl.VOLUME_MODE_RWM,
)
======
해야할 것
아래 이미지처럼 /dev/nvme0n1p1
이 mnt/nfs_shared
에 마운트 되어야함? 하지만 /dev/nvme0n1p1은 루트인데..?
helm install nfs-client -n kube-system \
--set nfs.server=10.10.10.10 \
--set nfs.path=/n123456_cluster \
stable/nfs-client-provisioner
현재 storage-provisioner가 자동으로 host-path로 동적 프로비저닝을 지원하고 있다. 이걸 nfs 동적 프로비저닝으로 바꿔야하는데..
또한 Reclaimpolicy도 Retain으로 변경해야 한 컨테이너 PVC가 해제되고(모델은 저장되고) 다른 컨테이너에 PVC가 PV에 연결되었을 때 데이터가 남아있다.(버전을 하드코딩으로 /1
로 지정하고 있는 부분은 해결이 필요)
다음 포스팅으로 이어집니다.
다음 포스팅에서 실제로 외부 프로비저닝 도구를 활용해서 PVC 생성 시 설정한 NFS 서버에 PV가 생성됩니다!
Author And Source
이 문제에 관하여(Minikube NFS 설정하기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@moey920/Minikube-NFS-설정하기저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)