[Kubeflow] kfp로 pipeline에 연결하는 방법 파헤치기

박종배·2023년 4월 7일
0

Kubeflow Pipeline

목록 보기
1/1
post-thumbnail

참고


배경과 목표

pipeline SDK로 kubeflow piplines 서버에 연결하는 방법은 kubeflow가 어떻게 구성되어 있냐에 따라 다른데 여기서는 full kubeflow 구성만 전제하자. 그리고 클러스터 내부/외부에 따라 인증/인가 방법이 다른데 이에 대해 각각 알아보자.

kfp.Client() 란?

그전에 Pipeline SDK가 pipeline API 서버와 통신 방법을 알아보자.

사용자는 kfp.Client() 클래스를 사용해 pipeline API와 연결함. 이 클래스에는 주요한 인자 2 가지가 아래와 같이 있음.

  • host : 연결하고자 하는 pipeline API의 hostname 엔드포인트. 따로 설정하지 않으면 클러스터 내에 파드인 경우에만 작동하는 service DNS name이 사용됨.
  • credentials : pipeline API 서버에 대해 인증하기 위한 자격 증명 TokenCredentialsBase 객체. ServiceAccount 토큰 경로를 path 인자로 받아 활용함.

클러스터 내부에서 인증/인가 하는 방법

먼저, full kubeflow로 설치했을 때 클러스터 내부에서(예를 들어, kubeflow notebook 인스턴스 파드에서) pipeline 서버로 연결하는 방법을 알아보자. pipeline API 서버는 kubeflow 네임스페이스에 ml-pipeline deployment로 띄워져 있고 이에 대한 service는 ml-pipline 으로 8888 포트를 포트포워딩 해주고 있음. 그러므로 클러스터 내부에서 다른 파드에 의해 pipeline API로 접근하려면 ml-pipeline.kubeflow.svc.cluster.local:8888 로 호출하면 됨. 이 값은 앞서 봤던 kfp.Client()host 인자 기본값과 똑같음. 즉, pipeline API 엔드포인트는 기본 설정이 되어있다는 말임.

한편, k8s에서는 Pod를 생성할 때, ServiceAccount token이 volume으로 마운트되므로 인증 수단으로 활용되곤 함. 이와 관련해서 kubeflow에서는 사용자가 notebook 인스턴스를 만들면 해당 사용자 네임스페이스의 default-editor 라는 ServiceAccount token이 마운트됨. 기본적으로 경로는 /var/run/secrets/kubernetes.io/serviceaccount/token에 마운트 됨. 이 토큰을 kubeflow pipeline API에 인증으로 사용할 수 있음.

단, ServiceAccount token을 인증 수단으로 활용하더라도 적절한 권한이 있는 인가를 하지 못하면 아래와 같이 RBAC access denied 에러가 발생함.

import kfp

host_url = "http://ml-pipeline.kubeflow.svc.cluster.local:8888"
credentials = kfp.auth.ServiceAccountTokenVolumeCredentials(path='/var/run/secrets/kubernetes.io/serviceaccount/token')

client = kfp.Client(host=host_url, credentials=credentials)

client.__dict__
client.get_kfp_healthz()
  • (앞서 말했듯이 host 인자를 설정하지 않아도 된다고 했는데 실제로 아래와 같이 host는 설정하지 안하고 credentials만 했을 때, 이상하게 아래와 같이 에러가 남. 그런데 이 에러 로그는 잘못 나오는 것 같음. 실제로는 예상대로 credentials를 override 하여 잘 작동함.)

그러므로 이제 해당 ServiceAccount에게 적절한 권한을 부여하자.

pipeline-rbac-authorizationPolicy.yaml

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
 name: bind-ml-pipeline-nb-kubeflow-kubeflow-jbpark8
 namespace: kubeflow
spec:
  selector:
    matchLabels:
      app: ml-pipeline
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/kubeflow-jbpark8/sa/default-editor"]
  • 필요한 권한은 kubeflow 네임스페이스에 있는 pipeline api 서버가 있는 파드에 대한 권한임.
  • 그러므로 위와 같이 권한을 부여하면 되는데 이 내용은 다음과 같음. kubeflow-jbpark8 네임스페이스에 있는 default-editor ServiceAccount에게 kubeflow 네임스페이스에서 app: ml-pipeline label이 있는 파드들에 대한 모든(?) 권한을 부여함.

다시 커넥션 테스트를 해보면 잘 됨.

  • health 체크도 잘 되고 list_pipeline()으로 파이프라인 조회도 가능함.

여기까지만 해도 연결이 잘 되지만 로직을 좀더 깔끔하게 해보자.

위에서 credentials 변수를 만들 때 kfp.auth.ServiceAccountTokenVolumeCredentials() 함수를 사용했는데 여기에 path 인자에 대한 인수로 기본 serviceAccount에 대한 token 경로 ‘/var/run/secrets/kubernetes.io/serviceaccount/token’ 를 추가해줬었음. 이는 번거로울 수 있으므로 따로 설정하지 않는 방법을 적용하자. 그러니까 앞서 봤던 path 인자의 기본값을 활용해보자.

path 인자의 기본값을 확인해보면 아래와 같음.

즉, path 인자는 KF_PIPELINES_SA_TOKEN_ENV 또는 KF_PIPELINES_SA_TOKEN_PATH 환경 변수를 읽어온다. 그러므로 우리는 이 환경 변수에 토큰이 저장되는 경로를 설정하거나 또는 이 환경 변수의 기본 경로에 토큰을 저장하면 됨.

더 자세히 보자. 이 변수들은 코드 상에서 아래와 같이 /var/run/secrets/kubeflow/pipelines/token 로 설정되어 있음. 즉, 우리는 이 경로에 token을 마운트해주면 쉽게 해결됨.

그런데, 최소 권한 원칙을 고려하여 pipeline에 대한 권한만 부여하는게 좋음. 그러므로 token의 인증 범위를 설정하자. audience에 대해서 pipelines.kubeflow.org 을 지정해주면 됨. 그런데 이를 위해서는 기본 token을 제한하면 또 다른 곳에서 문제가 생길 수 있므로 기본 token을 사용하지 않고 같은 default-editor 서비스어카운트를 사용하되 별도로 audience 설정을 한 token을 앞서 본 path 기본 경로에 마운트 해주면 됨.

다시 말해, 개선 사항으로는 아래 두 가지가 있음.

  1. default-editor SA에 대해 토큰의 인가 범위를 pipeline api 서버로 제한한 새로운 토큰 발급.
  2. 토큰 경로 기본값을 사용하기 위한 토큰 경로 환경변수 설정.

결국, 이 두 가지 작업은 pod에 환경변수를 추가하고 serviceAccount token을 마운트하는 걸로 수행할 수 있는데 이는 podDefault를 활용하는게 좋음. podDefault를 사용하면 kubeflow에서 notebook 인스턴스를 생성할 때, Configurations 에서 podDefault를 선택할 수 있음. 선택된 podDefault는 정의된 내용에 따라 notebook 인스턴스로 생성될 pod의 manifest를 수정해줌.

pipeline-rbac-podDefault.yaml

apiVersion: kubeflow.org/v1alpha1
kind: PodDefault
metadata:
  name: access-ml-pipeline
  namespace: "kubeflow-jbpark8"
spec:
  desc: Allow access to Kubeflow Pipelines
  selector:
    matchLabels:
      access-ml-pipeline: "true"
  env:
    - ## this environment variable is automatically read by `kfp.Client()`
      ## this is the default value, but we show it here for clarity
      name: KF_PIPELINES_SA_TOKEN_PATH
      value: /var/run/secrets/kubeflow/pipelines/token
  volumes:
    - name: volume-kf-pipeline-token
      projected:
        sources:
          - serviceAccountToken:
              path: token
              expirationSeconds: 7200
              ## defined by the `TOKEN_REVIEW_AUDIENCE` environment variable on the `ml-pipeline` deployment
              audience: pipelines.kubeflow.org      
  volumeMounts:
    - mountPath: /var/run/secrets/kubeflow/pipelines
      name: volume-kf-pipeline-token
      readOnly: true
  • audience를 pipelines.kubeflow.org 로 지정하여 pipeline api 관련 인가만 진행할 수 있도록 제한했으며
  • 앞서 말했듯이 토큰 기본 경로에 대한 참조 KF_PIPELINES_SA_TOKEN_PATH 환경변수의 값을 /var/run/secrets/kubeflow/pipelines/token 으로 지정했고
  • 이런 토큰을 /var/run/secrets/kubeflow/pipelines 에 마운트 시켰음.

이제 다시 연결을 해보자.

import kfp

#host_url = "http://ml-pipeline.kubeflow.svc.cluster.local:8888"

# the KF_PIPELINES_SA_TOKEN_PATH environment variable is used when no `path` is set
# the default KF_PIPELINES_SA_TOKEN_PATH is /var/run/secrets/kubeflow/pipelines/token
#credentials = kfp.auth.ServiceAccountTokenVolumeCredentials(path=None)

#client = kfp.Client(host=host_url, credentials=credentials)
client = kfp.Client()

  • 위에 kfp.Client() 에서 host와 credentials를 기본값으로 활용했음.

클러스터 외부에서 인증/인가 하는 방법

Kubeflow는 인증/인가를 통해 Kubeflow Pipelines 퍼블릭 엔드포인트를 보호함. 그러므로 multi-user 모드의 Kubeflow Pipelines의 경우 인증이 필요하기 때문에 kubectl port-forward를 사용하여 API에 액세스할 수 없음. 또한 Kubeflow 배포판에 따라 인증/인가 요구 사항이 다를 수 있으므로 각 배포판 마다 권고되는 방법을 따르기 위해 각 문서들을 참조하자. 나는 Amazon EKS 위에 올렸으므로 [kubeflow on aws docs : Pipelines] 를 참고했음.

먼저, chrome 브라우저를 열어서 Kubeflow에 로그인한 후 쿠키를 복사하자.

쿠키 예시

  • 이름 : authservice_session
  • 경로 : /
  • 도메인 : internal-k8s-istiosys-istioing-****.ap-northeast-2.elb.amazonaws.com
  • 콘텐츠 : MTY4MDgyNzkyMH*

클러스터 밖 환경에서 pipeline 연결 예제

import kfp

kubeflow_gateway_endpoint = "internal-k8s-istiosys-istioing-****.ap-northeast-2.elb.amazonaws.com"

authservice_session_cookie = "MTY4MDgyNzkyMH*****"

namespace='kubeflow-jbpark8'

client = kfp.Client(host=f"https://{kubeflow_gateway_endpoint}/pipeline", cookies=f"authservice_session={authservice_session_cookie}")
client.list_experiments(namespace=namespace)
profile
기록하는 엔지니어 되기 💪

0개의 댓글