kubeflow 아티팩트 저장소 minio, s3 자유자재로 변경해서 사용하기

노하람·2021년 12월 17일
2

cannot get key for artifact location. because it is invaild

kubeflow pipeline을 구축하다보면 전처리가 되지 않은 원시 데이터 파일을 어딘가에서 받아오거나,
원시 데이터 파일을 전처리 후 어딘가에 저장한 후, 훈련에 사용하기 위해 해당 파일을 다시 불러오거나,
훈련 후 matrics 및 각종 결과 파일 및 모델을 어딘가에 저장하고 시각화 및 배포 등을 진행하게 됩니다.

이 때 이 어딘가에 저장한다는 개념이 아티팩트, 오브젝트라고 생각하면 되는데
제대로 설정해놓지 않으면 artifact repository를 찾을 수 없다는 오류로 파이프라인이 정상적으로
진행되지 않는 모습을 확인할 수 있습니다.

예를 들어 이런 모습입니다.

정상적으로 데이터를 로드했지만, 로드한 데이터를 어떠한 과정(전처리 등)을 거친 후 저장하고 다음 파이프라인(training-pipeline)으로 넘어가야 하는데 오루가 발생합니다.
cannot get key for artifact location. because it is invaild

해당 오류를 검색해봐도 제대로 설명된 곳이 많이 없어서 이슈를 해결하기 위해 설정한 것들을 하나씩 정리해보고 넘어가려합니다.

argo 버전 업그레이드

  • argo 버전은 3.1.14 이상(필자는 해당 버전을 이용했습니다, 현재는 3.2 버전이 출시되었습니다.)으로 업그레이드 합니다.
  • argo 2.9 이상 버전은 artifact-repositories를 반드시 설정해놔야합니다.
  • containerRuntimeExecutor: emissary 혹은 pns를 사용합니다. (dockerd는 더이상 공식지원하지 않습니다, 오류도 많습니다.)
  • aws s3를 이용하고자 한다면 my-s3-credentials라는 secret을 만들어 aws s3 bucket에 접근하기 위한 aws accesskey와 secretkey를 base64로 encoding해서 생성합니다.(workflow-controller-configmap이 kubeflow namespace에 있기 때문에 kubeflow에 생성했습니다.)
  • kubectl edit -n kubeflow configmap/workflow-controller-configmap
  • 제 설정은 아래와 같습니다.
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  config: |
    {
    executorImage: argoproj/argoexec:v3.2,
    containerRuntimeExecutor: emissary,
    artifactRepository:
    {
        s3: {
            endpoint: s3.amazonaws.com,
            bucket: mlpl,
            accessKeySecret: {
                name: my-s3-credentials,
                key: accesskey
            },
            secretKeySecret: {
                name: my-s3-credentials,
                key: secretkey
            }
        }
    }
    }
kind: ConfigMap
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"config":"{\nexecutorImage: argoproj/argoexec:v2.3.0,\ncontainerRuntimeExecutor: docker,\nartifactRepository:\n{\n    s3: {\n        bucket: mlpipeline,\n        keyPrefix: artifacts,\n        endpoint: minio-service.kubeflow:9000,\n        insecure: true,\n        accessKeySecret: {\n            name: mlpipeline-minio-artifact,\n            key: accesskey\n        },\n        secretKeySecret: {\n            name: mlpipeline-minio-artifact,\n            key: secretkey\n        }\n    }\n}\n}\n"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app.kubernetes.io/component":"argo","app.kubernetes.io/name":"argo","kustomize.component":"argo"},"name":"workflow-controller-configmap","namespace":"kubeflow"}}
  creationTimestamp: "2021-11-16T23:38:33Z"
  labels:
    app.kubernetes.io/component: argo
    app.kubernetes.io/name: argo
    kustomize.component: argo
  name: workflow-controller-configmap
  namespace: kubeflow
  resourceVersion: "45712166"
  uid: f2db86b2-55d2-4755-b215-b20c64c11fc9
  1. minio, s3이용을 위한 secret을 생성해줍니다.
  • 저는 pipeline을 위한 namespace를 moey920으로 쓰고 있기 때문에 namespace: moey920에 secret을 2개 생성해주었습니다.
  • export를 이용해서 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY를 지정하고 시크릿을 아래와 같이 생성해주면 되지만 저는 minio키(minio/minio123)을 사용했을 때 kubectl 이용을 위한 권한 인증이 풀려버리는 이슈가 있어서 해당 명령을 입력하고 직접 kubectl edit으로 수정했습니다.
  • minio 용 secret(secret 이름은 자유롭게 설정해도 됩니다.)
kubectl -n moey920 create secret generic kfp-aws-secret \
    --from-literal=AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \
    --from-literal=AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
  • minio secret 수정
    - kubectl edit secret/kfp-aws-secret -n moey920
    • AWS_ACCESS_KEY_ID와 AWS_SECRET_ACCESS_KEY를 base64로 인코딩해서 넣어주시면 됩니다. EKS kubeflow 설치 시 기본 minio 키는 minio/minio123이기 때문에 그냥 보여드립니다. 동일한 인코딩 문자열을 사용해도 됩니다.
apiVersion: v1
data:
  AWS_ACCESS_KEY_ID: bWluaW8=
  AWS_SECRET_ACCESS_KEY: bWluaW8xMjM=
kind: Secret
metadata:
  creationTimestamp: "2021-12-16T07:39:11Z"
  name: kfp-aws-secret
  namespace: moey920
  resourceVersion: "46682073"
  uid: 6cee3a61-25c6-49a7-8289-342442c7b811
type: Opaque
  • s3 용 secret(secret 이름은 자유롭게 설정해도 됩니다.)
kubectl -n moey920 create secret generic aws-secret \
    --from-literal=AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \
    --from-literal=AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
  • s3 secret 수정
    - kubectl edit secret/aws-secret -n moey920
    • AWS_ACCESS_KEY_ID와 AWS_SECRET_ACCESS_KEY를 base64로 인코딩해서 넣어주시면 됩니다.
apiVersion: v1
data:
  AWS_ACCESS_KEY_ID: <당신의 AWS_ACCESS_KEY_ID>
  AWS_SECRET_ACCESS_KEY: <당신의 AWS_SECRET_ACCESS_KEY>
kind: Secret
metadata:
  creationTimestamp: "2021-12-16T08:24:22Z"
  name: aws-secret
  namespace: moey920
  resourceVersion: "46728108"
  uid: 5e9d0def-e05d-4ebf-94c1-f0fd4b8deeb6
type: Opaque

파이프라인에 시크릿 적용

minio의 경우

  1. 파이프라인 코드에 위에서 설정한 시크릿을 이용한 정보를 삽입합니다.
  • 파이프라인을 위한 kfp dsl SDK 코드 이외에는 넣을 필요 없습니다.
    - 이 방식을 이용하지 않고 arguments로 접속 정보를 받아 파이프라인을 생성하는 코드는 아래의 titanic 파이프라인 게시글을 참고해주시기 바랍니다.
  1. 먼저 minio-service를 포트포워딩으로 활성화하고 정상 가동되는지 확인해주세요.
  • minio 서비스를 포트포워딩 해놓은 상태에서는 아티팩트 저장소에 정상적으로 저장됐는데, 끈 상태에서는 확인해보지 않았습니다.
  • kubectl -n kubeflow port-forward svc/minio-service 9000:9000
  • localhost:9000 접속
  1. pipeline 전체 코드
  • 단순 예시입니다. 필요한대로 수정해서 사용하시면 됩니다.
  • 포인트는 secret_name = "kfp-aws-secret" 시크릿을 지정하고 엔드포인트를 포트포워딩한 서버 주소로 보내는 것입니다.
    - 필요한 환경변수는 V1EnvVar을 통해 전달해줍니다.
import kfp
from kfp import dsl
from kubernetes.client.models import V1EnvVar, V1EnvVarSource, V1SecretKeySelector


@dsl.pipeline(
    name='pipeline_minio',
    description = "pipeline_minio"
)

def pipeline_minio():
    secret_name = "kfp-aws-secret"
    s3_endpoint = 'minio-service.kubeflow.svc.cluster.local:9000'
    minio_endpoint = "http://" + s3_endpoint
    minio_region = "us-east-1"
    
    dsl.ContainerOp(
        name='mnist-s3',
        image='kangwoo/kfp-mnist-storage:0.0.1',
        arguments=['--model', 's3://mlpl']
    ).add_env_variable(V1EnvVar(name='S3_ENDPOINT', value=s3_endpoint)) \
        .add_env_variable(V1EnvVar(name='AWS_ENDPOINT_URL', value=minio_endpoint)) \
        .add_env_variable(V1EnvVar(name='AWS_ACCESS_KEY_ID',
                                   value_from=V1EnvVarSource(
                                       secret_key_ref=V1SecretKeySelector(name=secret_name, key='AWS_ACCESS_KEY_ID')))) \
        .add_env_variable(V1EnvVar(name='AWS_SECRET_ACCESS_KEY',
                                   value_from=V1EnvVarSource(secret_key_ref=V1SecretKeySelector(name=secret_name,
                                                                                                key='AWS_SECRET_ACCESS_KEY')))) \
        .add_env_variable(V1EnvVar(name='AWS_REGION', value=minio_region)) \
        .add_env_variable(V1EnvVar(name='S3_USE_HTTPS', value='0')) \
        .add_env_variable(V1EnvVar(name='S3_VERIFY_SSL', value='0'))
        
if __name__ == '__main__':
    my_run = kfp.Client().create_run_from_pipeline_func(pipeline_minio, arguments={},
                                                        experiment_name='Sample Experiment')
  1. tensorflow mnist 훈련 후 모델을 저장하는 파이프라인을 만들어놓고 실행한 결과 minio 서버의 버킷에 모델이 저장된 모습을 확인할 수 있습니다.(필요한 데이터로드, 훈련 등의 코드는 굳이 올리지 않겠습니다.)
    • minio/mlpl 버킷에서 데이터를 로드해서 훈련 후 mlpl/1 경로에 모델을 저장한 모습

aws s3의 경우 : use_aws_secret() 이용

  1. minio의 1번은 동일하게 참고해주세요.

  2. 파이프라인 코드

  • 핵심은 위에서 복잡하게 설정한 환경변수들과 다르게 aws.use_aws_secret을 이용하면 편리하게 인증을 거칠 수 있습니다. 다만 resion을 설정하지 않으면 us-east-1을 이용하지 않는 이상 region이 다르다는 오류가 발생하기 때문에 별도로 V1EnvVar를 이용하여 resion 변수를 설정해주었습니다.
import kfp
from kfp import aws
from kfp import dsl
from kubernetes.client.models import V1EnvVar

@dsl.pipeline(
    name='pipeline_s3',
    description = "pipeline_s3"
)

def pipeline_use_aws_secret():
    secret_name = "aws-secret"
    dsl.ContainerOp(
        name='mnist_use_aws_secret',
        image='kangwoo/kfp-mnist-storage:0.0.1',
        arguments=['--model', 's3://mlpl']
    ).apply(aws.use_aws_secret(secret_name,
                               aws_access_key_id_name='AWS_ACCESS_KEY_ID',
                               aws_secret_access_key_name='AWS_SECRET_ACCESS_KEY')) \
    .add_env_variable(V1EnvVar(name='AWS_REGION', value='ap-northeast-2'))
    
if __name__ == '__main__':
    my_run = kfp.Client().create_run_from_pipeline_func(pipeline_use_aws_secret, arguments={},
                                                        experiment_name='Sample Experiment')
  1. s3 버킷 루트 경로에서 데이터를 로드해서 /1/ 경로에 모델을 저장한 모습을 확인할 수 있습니다. s3를 이용할 경우 따로 디렉토리 경로를 지정할 수 없다는 이슈를 보았는데, 해결방법을 알게되면 포스팅을 수정해놓겠습니다.

해당 오류를 해결하기 위해 오랜 기간 레퍼런스를 찾아보며 수정했기 때문에 위에 적은 사항 이외에 설정한 것이 있는지 잘 기억이 나질 않습니다. 안되면 댓글 달아주시면 추가로 확인해보겠습니다.

profile
MLOps, MLE 직무로 일하고 있습니다😍

0개의 댓글