파이프라인은 컨테이너 이미지 기반으로 실행되기 때문에 개발 환경에 도커 클라이언트가 설치되어 있어야 합니다.
또한 파이프라인 SDK도 설치되어 있어야합니다.
파이프라인은 DSL(도메인 특화 언어)을 통해서 작성되며, 컴파일 과정을 거쳐 k8s 파이프라인 리소스로 변환되어 사용됩니다.
먼저 ML 워크플로의 특정 스텝에서 필요한 테스크를 수행하는 애플리케이션이 작성되어 있어야 하며, 그 애플리케이션이 도커 이미지로 패키징되어 단독으로도 실행할 수 있어야 합니다.
ContainerOp는 그 도커 이미지를 매개변수로 작성됩니다.
ContainerOp의 생성자는 아래와 같습니다.
ContainerOp(
name: str,
image: str,
command: String Or StringList = None,
arguments: String Or StringList = None,
init_containers: List[UserContainer] = None,
sidecars: List[Sidecar] = None,
container_kwargs: Dict = None,
artifact_argument_path: List[InputArgumentPath] = None,
file_outputs: Dict[str, str] = None,
output_artifact_path: Dict[str, str] = None,
pvolumes: Dict[str, V1Volume] = None
)
containerOp을 활용하여 파이프라인을 생성할 떄의 예제는 아래와 같습니다.
bash 컨테이너 이미지를 이용하여 콘솔창에 Hellow World라는 메세지를 찍는 작업을 수행하는 파이프라인입니다.
import kfp
from kfp import dsl
def echo_op() :
return dsl.ContainerOp(
name='echo',
image='library/bash:4.4.23',
command=['sh', '-c'],
arguments=['echo "Hello World"']
)
@dsl.pipeline(
name='ContainerOp pipeline',
description='ContainerOp'
)
def hellow_world_pipeline():
echo_task = echo_op()
## 쥬피터 노트북에서 사용할 경우
if __name__ == '__main__' :
kfp.compiler.Compiler().compile(hellow_world_pipeline, 'containerop.pipeline.tar.gz')
## dsl-compile 툴을 이용할 경우
$ dsl-compile --py containerop.py --output containerop.pipeline.tar.gz
)
1. name='echo' : 컴포넌트의 이름
2. @dsl.pipeline : 쿠버네티스의 리소스 메타정보 데코레이션, 필수입니다.
3. compiler.Compiler().compile : 함수 명을 매개변수로 받고 파이프라인 리소스를 포함하는 containerop.pipeline.tar.gz 파일 생성
두 번째로 파이썬 함수를 파이프라인으로 변환하는 방법이 있습니다. 여기선 별도의 컨테이너 이미지가 필요없습니다.
파이썬 함수를 파이프라인으로 생성할 때의 예제는 아래와 같습니다.
import kfp.dsl as dsl
@dsl.pipeline(
name='exampe_1',
description='description'
)
def my_pipeline(a: int = 1, b: str = "default value"):
print(a)
print(b)
if __name__ == "__main__" :
import kfp.compiler as compiler
compiler.Compiler().compile(my_pipeline, 'my_pipeline.pipeline.tar.gz')
실행하면 위와 같은 파일을 생성하면서 메세지를 노출합니다.
함수의 매개변수는 파이프라인의 PipelineParam 형태로 변환되어집니다.
이제 만들어진 파일을 파이프라인 UI를 통해서 등록해보겠습니다.
대시보드의 업로드 파이프라인 버튼을 눌러 등록차응로 이동합니다.
Kubeflow 1.0 이상의 버전부터는 파이프라인을 버전별로 관리가 가능합니다.
여기에 아까 생성한 containerop.pipeline.tar.gz를 선택합니다.(1번 예제에서 dsl-compile --py containerop.py --output containerop.pipeline.tar.gz
명령을 통해 생성합니다.)
파이프라인의 이름은 압축파일명의 제일 앞부분인 containerop로 자동입력됩니다.
물론 파이프라인의 이름은 중복 불가이며 수정은 가능합니다.
Create버튼을 누르면 파이프라인이 등록됩니다.
등록이 완료되면 파이프라인 리스트에서 containerop를 확인할 수 있습니다.
containerop를 클릭하면 파이프라인 그래프를 확인할 수 있으며,
그 그래프를 클릭하면 해당 컴포넌트의 상세정보를 확인할 수 있습니다.
만들어진 파이프라인으로 런(Run)을 하나 만들어 봅시다.
여기서 테스트로 containerop라는 Experiment도 같이 만들어서 그 안에 런을 실행시켜봅시다.
Experiment 메뉴로 이동하여 Create Experiment 버튼을 눌러 생성합니다.
Experiment를 생성하면 바로 런 생성 메뉴로 넘어가게 됩니다.
이 과정을 그냥 패스할 수도 있습니다. 실행할 파이프라인을 선택하고, 런 이름을 정한 후 Start를 누릅니다.
(run-type은 그냥 One-off, Experiment는 자동으로 선택됩니다.)
log에 'hello world'가 떠야하는데 안떠서 상태를 확인해보니 Pending excution
문제가 있습니다.
파드 로그를 살펴봅시다! 이벤트에 자세한 내용이 나와있군요.(Reason: FailedMount)
- kubectl describe pod containerop-pipeline-nhm5j-3333120606 -n moey920
MountVolume.SetUp failed for volume "docker-sock" : hostPath type check failed: /var/run/docker.sock is not a socket file
docker pull tutum/hello-world
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
kubectl get pods containerop-pipeline-nhm5j-3333120606 -n moey920 -o yaml
Kubeflow Pipelines를 설치한 네임스페스로 전환합니다.
kubectl config set-context --current --namespace moey920
현재 워크플로 실행자를 확인합니다.
kubectl describe configmap workflow-controller-configmap -n kubeflow | grep -A 2 containerRuntimeExecutor
워크플로 실행자를 PNS로 구성합니다.
kubectl patch configmap workflow-controller-configmap -n kubeflow --patch '{"data":{"containerRuntimeExecutor":"pns"}}'
워크플로 실행자가 정상적으로 바뀌었는지 확인합니다.
kubectl describe configmap workflow-controller-configmap -n kubeflow | grep -A 2 containerRuntimeExecutor
수동으로도 확인하고 deployment를 다시 로드해봅시다.
kubectl edit configmap/workflow-controller-configmap -n kubeflow
kubectl rollout restart deploy workflow-controller -n kubeflow