주녁, DevNote
article thumbnail

개요

Kubeflow를 통해 ML Ops 생태계를 이해하고, 전 주기 파이프라인를 구성해본다.

목표

  • ML 모델을 관리하고 배포하는 과정을 이해한다.
  • ML Serving 프레임워크인 Bento와 Kubernetes 배포를 도와주는 Yatai Stack을 이해한다.

여정

BentoML 및 Yatai Stack의 개념

BentoML과 Yatai Stack 아키텍쳐

Yatai Stack의 주요 컴포넌트

  • Bento : ML 모델을 API로 호출할 수 있도록 감싼 소스코드 집합(tar파일 O, 컨테이너 X)
  • Yatai : 모델 및 Bento를 Object Storage에 저장하는 Registry 관리 대시보드 컴포넌트
  • Yatai Image Builder : Bento를 컨테이너 이미지로 빌드하는 컴포넌트
  • Yatai Deployment : Bento 컨테이너 이미지를 Swagger형태로 배포하는 컴포넌트

Yatai Stack의 주요 CRD(Custom Resource Definition)

  • BentoRequest CRD : 사용할 이미지 태그와 Runner를 지정하여 Bento CRD를 생성
  • Bento CRD : Bento 파일의 Registry 위치와 Runner 타입을 지정(수동 생성도 가능)
  • BentoDeployment CRD : Bento CRD에 맞게 Node 리소스(CPU, Memory) 할당 후 배포

BentoML 및 Yatai 설치


필자의 경우, On-premise 환경에서 설치를 진행했으며,

공식 설치 스크립트가 제대로 동작하지 않아 직접 입력하면서 진행하였다.

변경한 설정은
DB : Postgresql HA  이미지 → Postgresql 이미지
Minio 비밀번호 : 랜덤 String → 기본 설정(minio/minio123) 이다.

더 자세한 내용은 공식문서 를 참조하자.
# 네임스페이스 생성
kubectl create ns yatai-system

Postgresql DB 설치

# postrgres 이미지 설치
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update bitnami
helm upgrade --install yatai-postgresql bitnami/postgresql \
    -n yatai-system --create-namespace \
    --set postgresqlPassword=postgres \
    --set postgresqlDatabase=yatai
# You need to be patient for a while until the status of all pods becomes Running,
# the number of pods depends on how many nodes you have

# 환경 변수 설정
export PG_PASSWORD=$(kubectl get secret -n yatai-system yatai-postgresql -o jsonpath="{.data.postgres-password}" | base64 -d)
export PG_HOST=yatai-postgresql
export PG_PORT=5432
export PG_DATABASE=yatai
export PG_USER=postgres
export PG_SSLMODE=disable

# DB 연결 테스트
# psql -h : 호스트 주소 지정 -> postgres와 연결된 서비스 명 입력
kubectl run postgresql-test --rm --tty -i --restart='Never' \
    --namespace yatai-system \
    --image docker.io/bitnami/postgresql-repmgr:14.4.0-debian-11-r13 \
    --env="PGPASSWORD=$PG_PASSWORD" \
    --command -- psql -h yatai-postgresql -p 5432 -U postgres -d postgres -c "select 1"

# DB 생성
kubectl run postgresql-client --rm --tty -i --restart='Never' \
    --namespace yatai-system \
    --image docker.io/bitnami/postgresql-repmgr:14.4.0-debian-11-r13 \
    --env="PGPASSWORD=$PG_PASSWORD" \
    --command -- psql -h yatai-postgresql -p 5432 -U postgres -d postgres -c "create database $PG_DATABASE"

Minio Operator 설치

# minio-operator 설치
export S3_ENDPOINT=minio.yatai-system.svc.cluster.local
export S3_REGION=foo
export S3_BUCKET_NAME=yatai
export S3_SECURE=false
export S3_ACCESS_KEY=minio
export S3_SECRET_KEY=minio123
#export S3_ACCESS_KEY=$(kubectl -n yatai-system get secret yatai-minio -o jsonpath='{.data.accesskey}' | base64 -d)
#export S3_SECRET_KEY=$(kubectl -n yatai-system get secret yatai-minio -o jsonpath='{.data.secretkey}' | base64 -d)

cat <<EOF | helm upgrade --install minio-operator minio/minio-operator -n yatai-system -f -
tenants:
- image:
    pullPolicy: IfNotPresent
    repository: quay.io/bentoml/minio-minio
    tag: RELEASE.2021-10-06T23-36-31Z
  metrics:
    enabled: false
    port: 9000
  mountPath: /export
  name: yatai-minio
  namespace: yatai-system
  pools:
  - servers: 1
    size: 20Gi
    volumesPerServer: 4
  secrets:
    accessKey: $S3_ACCESS_KEY
    enabled: true
    name: yatai-minio
    secretKey: $S3_SECRET_KEY
  subPath: /data
EOF

# minio-operator 설치 확인
kubectl -n yatai-system get pod -l app.kubernetes.io/name=minio-operator
kubectl -n yatai-system get pod -l app=minio
kubectl -n yatai-system delete pod s3-client 2> /dev/null || true; \
kubectl run s3-client --rm --tty -i --restart='Never' \
    --namespace yatai-system \
    --env "AWS_ACCESS_KEY_ID=$S3_ACCESS_KEY" \
    --env "AWS_SECRET_ACCESS_KEY=$S3_SECRET_KEY" \
    --image quay.io/bentoml/s3-client:0.0.1 \
    --command -- sh -c "s3-client -e http://$S3_ENDPOINT listbuckets && echo successfully"

Yatai 설치

# Yatai 설치
helm upgrade --install yatai yatai \
    --repo https://bentoml.github.io/helm-charts \
    -n yatai-system \
    --set image.pullPolicy=Always \
    --set replicaCount=1 \
    --set postgresql.host=$PG_HOST \
    --set postgresql.port=$PG_PORT \
    --set postgresql.user=$PG_USER \
    --set postgresql.database=$PG_DATABASE \
    --set postgresql.password=$PG_PASSWORD \
    --set postgresql.sslmode=$PG_SSLMODE \
    --set s3.endpoint=$S3_ENDPOINT \
    --set s3.region=$S3_REGION \
    --set s3.bucketName=$S3_BUCKET_NAME \
    --set s3.secure=$S3_SECURE \
    --set s3.accessKey=$S3_ACCESS_KEY \
    --set s3.secretKey=$S3_SECRET_KEY \
    --devel=true \
    --version 1.1.8

Yatai Console 접속 설정

# 첫 로그인 시 Setup 토큰 확인
export YATAI_INITIALIZATION_TOKEN=$(kubectl get secret yatai-env --namespace yatai-system -o jsonpath="{.data.YATAI_INITIALIZATION_TOKEN}" | base64 --decode)
echo "$YATAI_INITIALIZATION_TOKEN"

# Yatai 콘솔 접속 설정(Port : 31235)
kubectl patch -n yatai-system svc yatai --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"LoadBalancer"}, {"op": "replace", "path": "/spec/ports/'0'/nodePort", "value":'31235'}]'

# Yatai 접속 URL
http://${마스터노드 IP}:31235/setup?token=$YATAI_INITIALIZATION_TOKEN

# 이후 계정 생성 후 접속
admin / 12341234

# 우상단 프로필 클릭 > API 토큰 > 생성(권한은 적절하게)

Yatai 배포를 위한 컴포넌트 설치


필자의 경우, On-premise 환경에서 설치를 진행했으며,

공식 설치 스크립트가 제대로 동작하지 않아 직접 하나씩 입력하면서 진행하였다.

변경한 설정은
Private Container Registry → Gitlab Container Registry로 변경하였다.
# 네임스페이스 생성
kubectl create ns yatai-image-builder
kubectl create ns yatai-deployment
kubectl create ns yatai

Yatai-image-builder 설치

# Gitlab Registry 등록
export DOCKER_REGISTRY_SERVER=registry.gitlab.com
export DOCKER_REGISTRY_USERNAME= #### 여기에 gitlab 로그인 정보 입력
export DOCKER_REGISTRY_PASSWORD= #### 여기에 gitlab 로그인 정보 입력
export DOCKER_REGISTRY_SECURE=false
export DOCKER_REGISTRY_BENTO_REPOSITORY_NAME=/my-group/my-repo

# Yatai Image Builder CRD 설치
helm upgrade --install yatai-image-builder-crds yatai-image-builder-crds \
    --repo https://bentoml.github.io/helm-charts \
    -n yatai-image-builder

# 설치 확인
kubectl wait --for condition=established --timeout=120s crd/bentorequests.resources.yatai.ai
kubectl wait --for condition=established --timeout=120s crd/bentoes.resources.yatai.ai

# Yatai Image Builder 설치
helm upgrade --install yatai-image-builder yatai-image-builder \
    --repo https://bentoml.github.io/helm-charts \
    -n yatai-image-builder \
    --set dockerRegistry.server=$DOCKER_REGISTRY_SERVER \
    --set dockerRegistry.inClusterServer=$DOCKER_REGISTRY_IN_CLUSTER_SERVER \
    --set dockerRegistry.username=$DOCKER_REGISTRY_USERNAME \
    --set dockerRegistry.password=$DOCKER_REGISTRY_PASSWORD \
    --set dockerRegistry.secure=$DOCKER_REGISTRY_SECURE \
    --set dockerRegistry.bentoRepositoryName=$DOCKER_REGISTRY_BENTO_REPOSITORY_NAME

# 설치 확인
kubectl -n yatai-image-builder get pod -l app.kubernetes.io/name=yatai-image-builder

Yatai-Deployment 설치

# Metrics Server 설치
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# Yatai Deployment CRD 설치
helm upgrade --install yatai-deployment-crds yatai-deployment-crds \
    --repo https://bentoml.github.io/helm-charts \
    -n yatai-deployment

# Yatai Deployment 설치 확인
kubectl wait --for condition=established --timeout=120s crd/bentodeployments.serving.yatai.ai

# Yatai Deployment 설치
export INGRESS_CLASS=$(kubectl get ingressclass -o jsonpath='{.items[0].metadata.name}' 2> /dev/null)
export HELM_REPO_NAME=bentoml
export HELM_REPO_URL=https://bentoml.github.io/helm-charts

helm repo add ${HELM_REPO_NAME} ${HELM_REPO_URL}
helm repo update ${HELM_REPO_NAME}
helm upgrade --install yatai-deployment yatai-deployment --repo ${HELM_REPO_URL} -n yatai-deployment \
  --set layers.network.ingressClass=${INGRESS_CLASS} \
  --set layers.network.automaticDomainSuffixGeneration=false \
  --version=1.1.8

# 이후 원하는 DNS로 Ingress 설정을 해주면 된다.

실행과정(Kubeflow + BentoML)

Jupyter Notebook에서

  1. Jupyter Notebook에서 모델 생성
  2. BentoML을 위한 Service 코드 작성
  3. Bento Build
  4. Yatai에서 발행한 토큰과 엔드포인트로 Bento Login & Push

Yatai Console에서

  1. Storage Bucket에 업로드된 Bento 모델 확인
  2. 배포할 Bento 모델과 할당한 Resource 설정 후 배포 시작
  3. Bento 파일을 컨테이너 이미지로 빌드
  4. 빌드된 이미지를 Container Registry에 업로드
  5. Resource가 할당된 Runner가 Image Pull 후 실행됨

Kubernetes Cluster에서

  1. 배포한 deployment에 대한 Service 접속 설정(Ingress 등등)
  2. 해당 IP 및 Port로 접속하여 Swagger 형태로 결과 확인

Clean Up

kubectl delete crd tenants.minio.min.io
kubectl delete ns yatai-system

cd ~/kubeflow/manifests/contrib/bentoml
kustomize build bentoml-yatai-stack/default | kubectl delete -n kubeflow -f -

# or
bash -c "$(curl https://raw.githubusercontent.com/bentoml/yatai-chart/main/delete-yatai.sh)"

Q & A

  • 설치 중 에러가 발생했어요 Unable to continue with install: CustomResourceDefinition "tenants.minio.min.io" in namespace "" exists and cannot be imported into the current.
# CRD가 삭제되지 않아서 발생하는 에러
kubectl delete crd tenants.minio.min.io
  • Minio Console에 접속하고 싶어요
# minio 콘솔 접속 설정(Port : 31236)
kubectl patch -n yatai-system svc yatai-minio-console --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"LoadBalancer"}, {"op": "replace", "path": "/spec/ports/'0'/nodePort", "value":'31236'}]'

# HTTPS로 접속
https://${마스터노드 IP}:31236

S3_ENDPOINT=minio.yatai-system.svc.cluster.local
S3_BUCKET_NAME=yatai
S3_ACCESS_KEY=$(kubectl -n yatai-system get secret yatai-minio -o jsonpath='{.data.accesskey}' | base64 -d)
# dIhB6ZnHgDcK3CXRqb2j
S3_SECRET_KEY=$(kubectl -n yatai-system get secret yatai-minio -o jsonpath='{.data.secretkey}' | base64 -d)
# vUcyCfUndHmEP8kS4ngU
  • Yatai Console 이 아니라 YAML파일로 배포하고 싶어요
# Console이 아니라 yaml 파일로 배포하고 싶은 경우
vi example.yaml
---
apiVersion: resources.yatai.ai/v1alpha1
kind: Bento
metadata:
  name: test-yatai
  namespace: yatai

# 필요한 경우 변경
spec:
  image: quay.io/bentoml/iris_classifier:r4zint4b567i4usu 
  runners:
    - name: iris_clf
      runnableType: SklearnRunnable
  tag: iris_classifier:r4zint4b567i4usu

---
apiVersion: serving.yatai.ai/v2alpha1
kind: BentoDeployment
metadata:
  name: test-yatai
  namespace: yatai
spec:
  autoscaling:
    maxReplicas: 2
    metrics:
      - resource:
          name: cpu
          target:
            averageUtilization: 80
            type: Utilization
        type: Resource
    minReplicas: 1
  bento: test-yatai
  ingress:
    enabled: false
  resources:
    limits:
      cpu: 1000m
      memory: 1024Mi
    requests:
      cpu: 100m
      memory: 200Mi
  runners:
    - autoscaling:
        maxReplicas: 2
        metrics:
          - resource:
              name: cpu
              target:
                averageUtilization: 80
                type: Utilization
            type: Resource
        minReplicas: 1
      name: iris_clf
      resources:
        limits:
          cpu: 1000m
          memory: 1024Mi
        requests:
          cpu: 100m
          memory: 200Mi

---
# 서비스에서 요청을 보낼 수 있도록 권한 정책 추가
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: yatai-api
  namespace: yatai
spec:
  selector:
    matchLabels:
      yatai.ai/selector: yatai

  action: ALLOW
  rules:
    - to:
        - operation:
            methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]

---
# 예제 실행
kubectl apply -f example.yaml

# 서비스 접속 설정
# Port 번호는 30255로 임의로 지정했음 
kubectl patch -n yatai svc test-yatai --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"LoadBalancer"}, {"op": "replace", "path": "/spec/ports/'0'/nodePort", "value":'30255'}]'

# 접속 후 Swagger 형태로 API 테스트 페이지가 나오면 정상
http://${마스터 노드 IP}:30255
# Clean Up
kubectl delete all -n yatai -l yatai.ai/bento-deployment=test-yatai

마무리

이번 시간에는 ML 모델을 Serving할 수 있는 프레임워크 중 하나인 BentoML을 사용해보았다.

이를 통해 최초로 ML 모델 훈련부터 배포 후 테스트까지 진행해볼 수 있었다.

다음 시간에는 이를 파이프라인으로 구성하여 더 간소화해보는 과정을 구축할 것이다.


참고자료

Installation - BentoML

Installation - Yatai (bentoml.org)

manifests/contrib/bentoml at master · kubeflow/manifests (github.com)

BentoML/examples/kubeflow at main · bentoml/BentoML (github.com)

 

profile

주녁, DevNote

@junwork

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!