[GenAI] GenAI on K8s: 11.4 - Argo Workflows와 ML 파이프라인 오케스트레이션

· 7 분 소요

Kubernetes for Generative AI Solutions(Packt 2025, ISBN 978-1-83620-993-5, 저자 Ashok Srirama / Sukirti Gupta) 11장의 학습 내용을 바탕으로 합니다


11.2에서 Kubeflow Pipelines가 Argo Workflows를 백엔드로 쓴다고 했다. 이번 글은 그 백엔드 자체를 본다. Argo Workflows가 워크플로우를 실제로 어떻게 실행하는지(CRD 구조와 step이 Pod가 되는 과정), Kubeflow Pipelines·Argo CD와의 관계, ML·인프라·CI/CD 유스케이스, 그리고 Ray와 한 클러스터에 있을 때 겹쳐 보이는 parallelism 제한까지 정리한다.


TL;DR

  • Argo Workflows = K8s-native 범용 DAG 실행 엔진. 워크플로우를 Workflow CRD로 정의하고, 그 안의 step/task 하나가 Pod 하나로 뜬다
  • Pod 안은 init(입력 artifact·parameter fetch) → main(사용자 이미지 실행) → wait(출력 저장·cleanup) 3컨테이너 구조. v3.4 이후 실행은 Emissary executor(argoexec)가 담당한다
  • 템플릿은 일을 하는 것(container·script·resource·suspend)과 흐름을 짜는 것(steps·dag)으로 갈린다. dagdependencies로 의존 그래프를, steps는 list-of-lists로 순차·병렬을 표현한다
  • Kubeflow Pipelines = Argo 위에 ML DSL·artifact tracking·metadata store를 얹은 layer다. Argo CD = GitOps 상태 reconcile — 같은 Argo 패밀리지만 “절차 실행” vs “상태 동기화”로 책임이 다르다.
  • parallelism세 층위(컨트롤러 = 동시 워크플로우 수, workflow spec.parallelism = 워크플로우 내 동시 Pod 수, template 수준)로 나뉘고, 이는 Ray task scheduling과는 별개 레이어다


Argo Workflows 개요

Argo Workflows는 K8s 위에서 복잡한 파이프라인을 실행하기 위한 범용 워크플로우 엔진이다. 특정 도메인(ML 전용 등)에 묶이지 않고, “여러 단계를 순서·의존 관계에 맞춰 컨테이너로 돌린다”는 일을 K8s-native 방식으로 한다.

핵심 설계는 한 문장으로 줄일 수 있다 — 워크플로우의 각 step을 K8s Pod로 실행한다. 이 결정 하나에서 Argo의 성격 대부분이 따라 나온다.

  • step이 Pod이므로, K8s의 스케줄링·자원 할당·fault tolerance·노드 확장을 그대로 물려받는다. Argo가 별도 실행자 인프라를 두지 않는 이유다
  • 워크플로우는 DAG(의존 그래프) 또는 step-by-step 시퀀스로 정의하고, step 간에는 artifact(파일)와 parameter(값)를 주고받는다
  • 조건 분기, 자동 retry, 에러 핸들링, 수평 확장(수천 워크플로우 동시 실행)이 엔진 차원에서 제공된다

전부 K8s CRD로 표현된다. 워크플로우 정의·재사용·스케줄이 각각 별도 리소스 종류로 나뉘는데, 이건 뒤에서 따로 본다.


Workflow 실행 모델

개요의 “step = Pod”가 실제로 어떻게 돌아가는지를 CRD 구조부터 따라가 보자. 이 절이 이 글에서 다른 Argo 소개 글과 갈리는 지점이다.


Workflow spec — entrypoint와 templates

Workflow 리소스의 spec은 본질적으로 템플릿 목록 + 시작점이다.

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: hello-world-   # Workflow 인스턴스 이름 prefix
spec:
  entrypoint: main             # 어느 템플릿부터 실행할지 (main 함수 격)
  arguments:                   # 워크플로우 전역 입력
    parameters:
      - name: message
        value: hello
  templates:                   # 실행 단위 정의 목록
    - name: main
      # ...
  • spec.entrypoint: 워크플로우를 시작할 템플릿 이름. 프로그램의 main()에 해당한다
  • spec.templates: 실행 단위(template) 정의의 목록
  • spec.arguments: 워크플로우 전체에 넘기는 입력 parameter·artifact

templates에 정의만 나열한다고 실행 순서가 정해지는 건 아니다. 순서는 흐름을 짜는 템플릿이 따로 표현한다.


일을 하는 템플릿 vs 흐름을 짜는 템플릿

Argo 템플릿은 역할이 둘로 갈린다. 이 구분을 잡아야 YAML이 읽힌다.

분류 템플릿 타입 하는 일
정의 템플릿 (일을 함) container 사용자 이미지를 컨테이너로 실행
  script source:에 인라인 스크립트를 넣어 실행 (container의 wrapper)
  resource 클러스터 리소스 조작 (get/create/apply/delete/patch)
  suspend 지정 시간 또는 수동 resume까지 일시정지
호출 템플릿 (흐름을 짬) steps step들을 순차·병렬로 나열
  dag task 간 의존 관계를 그래프로 선언

container·script는 실제로 Pod를 띄워 일을 하고, steps·dag는 그 일들을 어떤 순서로 엮을지만 정한다. 호출 템플릿이 정의 템플릿을 template: 이름으로 참조하는 구조다.


steps vs dag

흐름을 짜는 방식이 둘인데, 표현력과 가독성이 다르다. 결론부터 보면, 단순한 직선·고정 분기는 steps가 읽기 쉽고, 의존 관계가 복잡해질수록 dag가 그래프를 그대로 옮기기 좋다. ML 파이프라인(전처리 → 학습 → 평가 → 배포)처럼 단계가 갈라지고 합쳐지면 보통 dag를 쓴다. 둘을 차례로 본다.

steps

list-of-lists 구조다. 바깥 리스트는 순차 실행, 안쪽 리스트는 병렬 실행이다. - -(이중 하이픈)이 “이 단계는 앞 단계가 끝난 뒤”를 뜻한다.

  - name: hello-hello-hello
    steps:
      - - name: step1
          template: prepare-data      # 먼저 실행
      - - name: step2a
          template: run-first-half    # step1 이후, step2a·step2b는
        - name: step2b                #   서로 병렬
          template: run-second-half

dag

dag.tasks에 task를 나열하고 각 task의 dependencies로 선행 조건을 선언한다. 의존이 없는 task는 즉시 시작되고, 나머지는 의존이 충족되는 대로 풀린다.

  - name: diamond
    dag:
      tasks:
        - name: A
          template: echo
        - name: B
          dependencies: [A]   # A가 끝나야 B
          template: echo
        - name: C
          dependencies: [A]   # A가 끝나야 C (B와 병렬)
          template: echo
        - name: D
          dependencies: [B, C]  # B·C 둘 다 끝나야 D
          template: echo


step이 Pod가 되기까지 — controller와 3컨테이너

정의·흐름을 선언했으면, 그걸 실제 Pod로 바꾸는 주체가 있어야 한다. 그게 workflow-controller다.

  • workflow-controllerWorkflow 리소스와 그 Pod들을 watch하면서 reconcile한다. 워크플로우를 큐에 넣고 worker goroutine이 하나씩 처리하며, 의존이 충족된 step에 대해 Pod를 생성한다
  • 사용자가 API·UI로 워크플로우를 제출·조회하는 창구는 별도 컴포넌트인 argo-server다 (API + UI 호스팅)

여기서 핵심은 step Pod 내부 구조다. step/task 하나가 Pod 하나로 뜨는데, 그 Pod 안에는 컨테이너가 셋 들어간다.

컨테이너 역할
init (InitContainer) 입력 artifact·parameter를 받아 main이 쓸 수 있게 준비
main 사용자가 지정한 이미지 실행. argoexec가 volume mount되어 entrypoint로 들어가고, 원래 커맨드를 subprocess로 실행하며 출력을 캡처
wait 출력 parameter·artifact 저장과 cleanup

main 컨테이너가 곧장 사용자 커맨드를 PID 1로 실행하는 게 아니라, argoexec(executor)가 한 겹 감싸서 입출력 수집과 라이프사이클 관리를 한다. 이 executor를 Emissary라고 부르며, v3.4 이후로는 유일한 executor다. 과거 docker·pns·k8sapi·kubelet executor는 제거됐으므로, 옛 자료의 containerRuntimeExecutor 설정은 최신 버전에서 더 이상 유효하지 않다.


artifact passing — step 간 파일 전달

step이 각자 다른 Pod라는 건, step 사이에 메모리·로컬 디스크를 공유하지 않는다는 뜻이다. 그래서 단계 간 데이터 전달은 두 갈래로 표현된다.

  • parameter: 작은 값(문자열·숫자). 한 step의 output을 다음 step의 input으로 `` 식으로 참조
  • artifact: 파일·디렉토리. 한 step이 outputs.artifacts로 내보내고 다음 step이 inputs.artifacts로 받는다

artifact는 Pod 로컬에 머물 수 없으므로 artifact repository(S3 호환 object storage 등)를 거쳐 전달된다. init 컨테이너가 저장소에서 받아오고 wait 컨테이너가 올려보내는, 앞 절의 3컨테이너 구조가 여기서 그대로 쓰인다.

이 artifact 저장소를 직접 설정하고 MinIO를 붙여 ML 파이프라인 step 간에 데이터를 넘긴 실습은 따로 정리해 둔 적이 있다 — Argo Workflow Artifact 기능 사용하기에서 workflow-controller-configmapartifactRepository 설정과 S3 호환 저장소 연동을 다룬다.


CRD 패밀리 — Workflow / WorkflowTemplate / CronWorkflow

Argo는 “한 번 실행”, “재사용 정의”, “스케줄 실행”을 각각 다른 CRD로 나눈다.

CRD 성격
Workflow 워크플로우 한 번의 실행 인스턴스. 제출하면 곧장 돈다
WorkflowTemplate 클러스터에 저장해 두는 재사용 정의(namespace 범위). 그 자체로는 실행 단위가 아니라, 다른 워크플로우가 참조하거나 --from으로 인스턴스화
ClusterWorkflowTemplate WorkflowTemplate클러스터 범위 버전
CronWorkflow cron 스케줄에 맞춰 Workflow를 주기 생성

WorkflowTemplate은 두 방식으로 참조된다 — 전체 spec을 끌어다 쓰는 workflowTemplateRef, 그리고 steps·dag 안에서 개별 템플릿만 불러오는 templateRef다. 같은 전처리·학습 정의를 여러 워크플로우가 공유할 때 정의를 한 곳에 모으는 용도다.


Argo와 다른 도구의 관계

Argo가 범용 엔진이다 보니, 여러 도구가 Argo를 자기 백엔드로 끌어다 쓰거나(Pipelines) 같은 패밀리로 역할을 나눠 갖는다(Argo CD). 이 관계를 세 각도로 본다.


Argo를 내부 엔진으로 쓰는 도구

여러 ML 도구가 자기 실행 백엔드로 Argo를 끌어다 쓴다.

도구 사용 방식
Kubeflow Pipelines Pipelines DSL → Argo Workflow로 컴파일해 실행
Katib (옵션) trial orchestrator로 Argo 선택 가능
Seldon Core 모델 배포 워크플로우 일부
MLflow Projects mlflow run을 Argo step으로 오케스트레이션

이 중 가장 밀접한 Kubeflow Pipelines와의 관계를 다음 절에서 본다.


Kubeflow Pipelines와의 레이어 관계

Kubeflow Pipelines와 Argo Workflows는 같은 백엔드(Argo)를 공유하지만, 추상화 레벨과 타깃 사용자가 다르다.

argo-workflows

항목 Argo Workflows Kubeflow Pipelines
카테고리 범용 K8s workflow engine ML 특화 플랫폼
인터페이스 YAML / WorkflowTemplate Python DSL (@dsl.component, @dsl.pipeline)
타깃 사용자 플랫폼/DevOps 데이터 사이언티스트
ML 통합 일반 컨테이너 step artifact tracking, metadata, metric 시각화
백엔드 (본인) Argo Workflows (default)

Kubeflow Pipelines는 Argo 위에 ML metadata·UI·SDK를 얹은 layer다. 데이터 사이언티스트는 Python DSL로 파이프라인을 짜고, 그게 내려가면 결국 앞에서 본 Workflow CRD로 컴파일돼 같은 controller가 Pod를 띄운다. 둘은 경쟁이 아니라 레이어 관계다.


Argo Workflows vs Argo CD

이름이 비슷해 헷갈리지만, 같은 Argo 패밀리 안에서 책임이 명확히 다르다.

도구 책임
Argo Workflows step-by-step task DAG 실행. “build → test → deploy” 절차 자동화
Argo CD GitOps 컨트롤러. Git의 desired state ↔ K8s의 actual state 상태 reconcile

한쪽은 “정해진 절차를 한 번 끝까지 돌린다”(Workflows), 다른 쪽은 “선언된 상태를 계속 맞춰 둔다”(Argo CD)는 점이 다르다. CI/CD에서는 둘이 보완 관계로 엮인다 — Workflows로 빌드·테스트·이미지 push까지 하고, 그 결과 manifest를 Git에 반영하면, Argo CD가 그 Git 상태를 클러스터에 지속적으로 동기화한다.


유스케이스

범용 엔진이라 적용 범위가 넓다. 책이 드는 대표 사례는 네 갈래다.

  • ML 파이프라인 — 데이터 전처리 → 학습 → 평가 → 배포를 DAG로 묶는다. 11.1 파이프라인 5단계 중 adaptation·serving 사이를 자동화하는 지점이다
  • 데이터 배치 처리 — ETL, 스케줄된 데이터 sync. 주기 실행은 CronWorkflow로 표현한다
  • 인프라 자동화 — EKS 프로비저닝, namespace·RBAC 자동 생성, 테넌트 온보딩. resource 템플릿으로 클러스터 리소스를 직접 다루는 패턴이 여기 들어맞는다
  • CI/CD — 코드 빌드 → 테스트 → 이미지 push → manifest apply


parallelism 세 층위와 Ray scheduling

Argo Workflows와 Ray가 같은 클러스터에 있으면 “동시 실행 제한”이 겹쳐 보이지만 레이어가 다르다. 먼저 Argo 안에서도 parallelism이 한 가지가 아니라는 점부터 짚는다.

층위 설정 위치 무엇을 제한
컨트롤러 controller configmap parallelism / namespaceParallelism 동시에 도는 워크플로우 개수(전체 / namespace 단위)
워크플로우 spec.parallelism 한 워크플로우 안에서 동시에 뜨는 Pod(step) 수
템플릿 steps/dag 템플릿의 parallelism 그 템플릿 범위 내 동시 실행 수

이 셋은 전부 Argo가 띄우는 워크플로우 step Pod를 대상으로 한다. 반면 Ray와는 다음과 같이 갈린다.

레이어 제한 주체 무엇을 제한
Argo 위 세 층위의 parallelism 동시 실행 워크플로우 step Pod
Ray Ray scheduler + autoscaler task/actor/PG bundle이 Ray node에 배치되는 수
K8s Resource quota, scheduler namespace·노드 단위 Pod·자원

Argo step 안에서 Ray cluster를 띄우는 구성을 생각하면 경계가 분명해진다. Argo는 “이 step Pod가 떠 있는가”까지만 관리하고, 그 Pod 안에서 Ray가 task를 어느 raylet에 배치하는지는 Ray scheduler의 몫이다. 병목이 Argo의 동시 Pod 상한인지, Ray의 logical resource인지, K8s 노드 capacity인지를 분리해서 봐야 한다 — Ray 쪽 계층적 스케줄링은 11.5에서 다룬다.


정리

영역 핵심
실행 모델 Workflow CRD, step/task = Pod 1개, 그 안 init/main/wait 3컨테이너
executor v3.4+ Emissary(argoexec)가 사용자 커맨드를 subprocess로 감싸 입출력 수집
흐름 표현 steps(list-of-lists) vs dag(dependencies 그래프)
CRD 패밀리 Workflow(1회) / WorkflowTemplate(재사용 정의) / CronWorkflow(스케줄)
vs Pipelines Argo = 엔진, Pipelines = 그 위 ML layer
vs Argo CD 절차 실행 vs GitOps 상태 reconcile
parallelism 컨트롤러·워크플로우·템플릿 세 층위 — Ray task scheduling과는 별개 레이어


참고 링크




hit count

댓글남기기