정적 작업 할당 병렬 처리를 위한 Indexed Job
정적 작업 할당 병렬 처리를 위한 Indexed Job
이 예시에서는 여러 병렬 워커 프로세스를 사용하는 Kubernetes Job을 실행해요. 각 워커는 자체 파드에서 실행되는 서로 다른 컨테이너예요. 파드에는 컨트롤 플레인이 자동으로 설정하는 인덱스 번호가 있으며, 각 파드가 전체 작업의 어느 부분을 처리할지 파악할 수 있게 해줘요.
파드 인덱스는 어노테이션 batch.kubernetes.io/job-completion-index에서 10진수 값을 나타내는 문자열로 사용할 수 있어요. 컨테이너화된 작업 프로세스가 이 인덱스를 얻으려면 downward API 메커니즘을 사용해 어노테이션 값을 게시할 수 있어요. 편의를 위해 컨트롤 플레인은 downward API가 JOB_COMPLETION_INDEX 환경 변수에 인덱스를 노출하도록 자동 설정해요.
이 예시의 단계 개요:
- indexed completion을 사용하는 Job 매니페스트를 정의해요. downward API는 파드 인덱스 어노테이션을 환경 변수나 파일로 컨테이너에 전달하게 해줘요.
- 그 매니페스트를 기반으로 Indexed Job을 시작해요.
출처: 문서
본문
시작하기 전에
Job의 기본적이고 비병렬적인 사용법에 이미 익숙해야 해요.
Kubernetes 클러스터가 있어야 하고 kubectl 명령줄 도구가 클러스터와 통신하도록 구성되어 있어야 해요. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 최소 두 개 있는 클러스터에서 실행하는 것을 권장해요. 아직 클러스터가 없다면 minikube를 이용해 만들거나, 아래 Kubernetes 플레이그라운드 중 하나를 사용할 수 있어요.
버전을 확인하려면 kubectl version을 입력해요.
접근 방식 선택
워커 프로그램에서 작업 항목에 접근하는 방법은 몇 가지가 있어요.
JOB_COMPLETION_INDEX환경 변수를 읽기. Job 컨트롤러가 이 변수를 완료 인덱스를 담은 어노테이션에 자동으로 연결해요.- 완료 인덱스를 담은 파일 읽기.
- 프로그램을 수정할 수 없다고 가정한다면, 위 방법 중 하나로 인덱스를 읽어 프로그램이 입력으로 사용할 수 있는 무언가로 변환하는 스크립트로 감쌀 수 있어요.
이 예시에서는 옵션 3을 선택하고 rev 유틸리티를 실행하고 싶다고 가정해요. 이 프로그램은 파일을 인자로 받아 그 내용을 뒤집어 출력해요.
rev data.txt
busybox 컨테이너 이미지의 rev 도구를 사용할 거예요.
이것은 단지 예시이므로 각 파드는 아주 작은 작업(짧은 문자열 뒤집기)만 해요. 실제 워크로드에서는 예를 들어 장면 데이터를 기반으로 60초 분량의 비디오를 만드는 작업을 나타내는 Job을 만들 수 있어요. 비디오 렌더링 Job의 각 작업 항목은 그 비디오 클립의 특정 프레임을 렌더링하는 것이 될 거예요. Indexed completion은 Job의 각 파드가 클립 시작부터 프레임을 세어 어떤 프레임을 렌더링하고 게시할지 알게 한다는 뜻이에요.
Indexed Job 정의
다음은 Indexed 완료 모드를 사용하는 샘플 Job 매니페스트예요.
apiVersion: batch/v1
kind: Job
metadata:
name: 'indexed-job'
spec:
completions: 5
parallelism: 3
completionMode: Indexed
template:
spec:
restartPolicy: Never
initContainers:
- name: 'input'
image: 'docker.io/library/bash'
command:
- "bash"
- "-c"
- |
items=(foo bar baz qux xyz)
echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt
volumeMounts:
- mountPath: /input
name: input
containers:
- name: 'worker'
image: 'docker.io/library/busybox'
command:
- "rev"
- "/input/data.txt"
volumeMounts:
- mountPath: /input
name: input
volumes:
- name: input
emptyDir: {}
위 예시에서 Job 컨트롤러가 모든 컨테이너에 설정하는 내장 JOB_COMPLETION_INDEX 환경 변수를 사용해요. init 컨테이너는 인덱스를 정적 값에 매핑하고, emptyDir 볼륨을 통해 워커를 실행하는 컨테이너와 공유되는 파일에 기록해요. 선택적으로 downward API를 통해 자체 환경 변수를 정의해 인덱스를 컨테이너에 게시할 수도 있어요. ConfigMap에서 값 목록을 환경 변수나 파일로 불러오는 것을 선택할 수도 있어요.
또는 다음 예시처럼 downward API를 직접 사용해 어노테이션 값을 볼륨 파일로 전달할 수 있어요.
apiVersion: batch/v1
kind: Job
metadata:
name: 'indexed-job'
spec:
completions: 5
parallelism: 3
completionMode: Indexed
template:
spec:
restartPolicy: Never
containers:
- name: 'worker'
image: 'docker.io/library/busybox'
command:
- "rev"
- "/input/data.txt"
volumeMounts:
- mountPath: /input
name: input
volumes:
- name: input
downwardAPI:
items:
- path: "data.txt"
fieldRef:
fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']
Job 실행
이제 Job을 실행해요.
# This uses the first approach (relying on $JOB_COMPLETION_INDEX)
kubectl apply -f https://kubernetes.io/examples/application/job/indexed-job.yaml
이 Job을 만들면 컨트롤 플레인이 지정한 각 인덱스에 대해 일련의 파드를 만들어요. .spec.parallelism의 값은 한 번에 몇 개가 실행될 수 있는지를, .spec.completions는 Job이 총 몇 개의 파드를 만드는지 결정해요.
.spec.parallelism이 .spec.completions보다 작으므로, 컨트롤 플레인은 처음 몇 파드 중 일부가 완료될 때까지 기다린 후 더 많은 파드를 시작해요.
타임아웃으로 Job이 성공할 때까지 기다릴 수 있어요.
# The check for condition name is case insensitive
kubectl wait --for=condition=complete --timeout=300s job/indexed-job
이제 Job을 describe하고 성공했는지 확인해요.
kubectl describe jobs/indexed-job
출력은 다음과 비슷해요.
Name: indexed-job
Namespace: default
Selector: controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
Labels: controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
job-name=indexed-job
Annotations: <none>
Parallelism: 3
Completions: 5
Start Time: Thu, 11 Mar 2021 15:47:34 +0000
Pods Statuses: 2 Running / 3 Succeeded / 0 Failed
Completed Indexes: 0-2
Pod Template:
Labels: controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
job-name=indexed-job
Init Containers:
input:
Image: docker.io/library/bash
Port: <none>
Host Port: <none>
Command:
bash
-c
items=(foo bar baz qux xyz)
echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt
Environment: <none>
Mounts:
/input from input (rw)
Containers:
worker:
Image: docker.io/library/busybox
Port: <none>
Host Port: <none>
Command:
rev
/input/data.txt
Environment: <none>
Mounts:
/input from input (rw)
Volumes:
input:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Medium:
SizeLimit: <unset>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 4s job-controller Created pod: indexed-job-njkjj
Normal SuccessfulCreate 4s job-controller Created pod: indexed-job-9kd4h
Normal SuccessfulCreate 4s job-controller Created pod: indexed-job-qjwsz
Normal SuccessfulCreate 1s job-controller Created pod: indexed-job-fdhq5
Normal SuccessfulCreate 1s job-controller Created pod: indexed-job-ncslj
이 예시에서는 각 인덱스에 대해 커스텀 값으로 Job을 실행해요. 파드 중 하나의 출력을 조사할 수 있어요.
kubectl logs indexed-job-fdhq5 # Change this to match the name of a Pod from that Job
출력은 다음과 비슷해요.
xuq