LabHub
배우기 러닝패스 코스

CAPA — Argo 프로젝트 인증 어소시에이트 · 산출물의 수명과 동시 실행 안전성 · 이론

성공한 파이프라인의 결과 파일은 어디로 갔을까

LabHub 에서 이어서 보기

한 줄 요약

Workflow의 성공, 중간 파일의 정리, 최종 결과의 보존은 서로 다른 계약입니다.
세 계약을 각각 확인해야 CI의 초록불을 믿고 다음 작업으로 넘어갈 수 있습니다.

왜 이게 필요했나

야간 데이터 작업은 성공했습니다. 그런데 아침에 결과를 읽는 팀이 파일을 찾지 못합니다.
담당자는 재시도 횟수를 늘리고 컨테이너 메모리를 올립니다. 이것이 도움이 될까요?
생산 작업이 실패한 것이 아니라 성공 후 정리 정책이 최종 결과까지 지웠다면, 더 빨리
성공해 더 확실하게 파일을 지울 뿐입니다. 실패한 계층부터 구분해야 복구 방향이 맞습니다.

반대 사고도 있습니다. 결과는 제대로 만들어졌는데 임시 파일이 계속 쌓입니다. Workflow
목록에는 Succeeded가 보여 알림이 울리지 않습니다. 정리 담당 서비스 계정이 Kubernetes
권한을 잃은 경우라면 업무 컨테이너의 종료 코드와 GC의 오류는 동시에 서로 다를 수 있습니다.
이 모듈에서는 실제 S3 호환 저장소와 실제 컨테이너를 사용해 두 사고를 따로 재현합니다.

어떻게 동작하나

경로가 곧 실행의 경계다

생산자는 파드 안의 /work/temp.txt에 씁니다. executor는 그 파일을 객체 저장소의 key로
업로드합니다. 소비자는 저장소의 key에서 다시 내려받습니다. 로컬 파일 경로와 S3 key는
같은 개념이 아닙니다. 서로 다른 두 파드가 로컬에서 같은 경로를 사용해도 안전할 수 있지만,
같은 버킷의 동일 key로 업로드하면 나중 업로드가 먼저 쓴 결과를 덮을 수 있습니다.

generateName은 Kubernetes 객체 이름의 충돌을 줄입니다. 그것만으로 S3 key가 달라지는
것은 아닙니다. 출력 key에 {{workflow.uid}}를 넣으면 이번 실행의 식별자가 저장 경로까지
이어집니다. 파일 내용에도 생산자 UID를 넣고 소비자가 자기 UID와 비교하면, 단순히 파일이
존재하는지보다 강한 검사를 할 수 있습니다. 남의 정상 파일을 읽은 사고도 발견하기 때문입니다.

이번 실습의 두 정상 실행은 각각 runs/<실행 UID>/temp.txt
runs/<실행 UID>/final.json을 사용합니다. 충돌 실험은 같은 batch의 shared 경로를
두 실행이 공유합니다. 다른 실험 시도의 shared 파일까지 지우지 않도록 batch 자체는
실험 도우미가 매번 구분합니다. 같은 시도 안에서만 의도한 충돌이 발생합니다.

언제 지우고 무엇을 남길까

Workflow 수준의 spec.artifactGC.strategy는 정리의 기본 정책입니다.
OnWorkflowCompletion은 완료를 기준으로, OnWorkflowDeletion은 Workflow 삭제를
기준으로 동작합니다. 개별 출력 artifact의 artifactGC.strategy: Never는 그 파일을
정리 대상에서 제외합니다. 중간 산출물은 지우면서 최종 결과는 남기는 정책을 표현할 수 있습니다.

여기서 Never는 백업이 아닙니다. 저장소가 사라지거나 운영자가 객체를 삭제하면 파일은
없어집니다. 실습 저장소는 VM 안의 폐기 가능한 emptyDir이므로 세션 종료 후에는 유지되지
않습니다. 운영에서는 영속 볼륨·복구 가능한 백업·접근 제어·TLS·보존 기간을 별도로 설계해야 합니다.

Workflow의 성공을 읽는 것과 파일 보존을 확인하는 것도 다릅니다. 이 실습은 실제로 읽은
바이트의 UID와 해시를 비교하고, 삭제를 판단할 때는 인증된 목록 조회와 404 NoSuchKey를
함께 확인합니다. 연결 실패나 403을 보고 '파일이 지워졌다'고 결론 내리면 안 됩니다.

Kubernetes 권한과 저장소 권한은 별개다

GC 파드는 먼저 WorkflowArtifactGCTask를 보고 처리 결과를 status에 기록해야 합니다.
이때 필요한 Kubernetes 권한은 해당 리소스의 list/watch와 status 하위 리소스의 patch입니다.
그 다음 S3 객체 삭제에는 저장소 계정의 권한이 필요합니다. Kubernetes Role을 고쳤는데도
S3에서 AccessDenied라면 다른 계층을 보고 있는 것입니다. 두 권한 체계를 섞지 마세요.

kubectl auth can-i patch workflowartifactgctasks --subresource=status처럼 하위
리소스를 명시해서 확인합니다. workflowartifactgctasks/status라는 인자만 던지면
다른 의미로 해석되어 잘못된 no를 볼 수 있습니다. 명령이 나온 결과뿐 아니라 무엇을
질문한 것인지도 증거에 포함해야 합니다.

이 실습에서 일부러 권한이 없는 GC는 ArtifactGCError와 forbidden 로그를 남깁니다.
업무 상태는 여전히 Succeeded일 수 있습니다. 오류와 finalizer를 강제로 제거해 초록색으로
만드는 것이 복구는 아닙니다. 원래 실패 증거를 보존하고 최소 Role을 연결한 뒤, 새로운
Workflow UID로 정리와 보존이 정상인지 확인합니다. 이미 실패한 원래 GC가 자동으로
다시 처리된다는 가정을 하지 않습니다.

현장에서 만나는 모습

모델 학습에서는 대용량 중간 체크포인트를 지우되 최종 모델과 평가 보고서는 보존해야 합니다.
데이터 변환에서는 같은 시간대에 겹친 재실행 두 개가 서로의 입력을 지우지 않아야 합니다.
규제 대상 보고서라면 '작업이 성공했다'와 '결과를 다시 읽어 검증할 수 있다'를 별도 지표로
관리하는 편이 낫습니다. 실패한 업무, 실패한 정리, 잘못된 보존 설정의 대응 담당자가
같지 않을 수 있다는 점도 중요합니다.

LabHub의 환경 검증에서도 서비스를 Ready로 확인했지만 정상 S3 업로드는 500이었던
경우가 있었습니다. 작은 실습의 볼륨 한도를 초기 메타데이터가 먼저 차지한 것이 원인이었습니다.
익명 요청이 403이라는 사실만 검사했다면 이 장애를 놓쳤습니다. 허용할 요청이 성공하는
대조군과 거절할 요청이 실패하는 반례를 함께 두는 이유입니다.

다음 실습에서 할 것

먼저 저장소의 접근 경계를 확인합니다. 공유 키를 UID별 키로 고치고 두 실행을 비교한 뒤,
최종 보존 누락과 GC 권한 실패를 따로 분석합니다. 최소 권한으로 새 실행을 검증하고,
공유 키 충돌과 삭제 시 GC를 관찰합니다. 마지막에는 같은 Succeeded가 서로 다른 저장
결과를 가질 수 있다는 것을 실제 UID를 넣어 설명합니다. 도우미의 완료 메시지는 실험
실행이 끝났다는 뜻이며, 실습 채점의 통과와 같지 않습니다.

공식 문서