Kubernetes

워크로드 API 카테고리(잡)

jih0ssang 2024. 1. 14. 08:31

워크로드 API 카테고리

컨테이너 실행에 관련된 리소스

- 파드

- 레플리케이션 컨트롤러

- 레플리카셋

- 디플로이먼트

- 데몬셋

- 스테이트풀셋

- 잡

- 크론잡

 

잡(job)은 컨테이너를 사용하여 한 번만 실행되는 리소스이다.

N개의 병렬로 실행하면서 지정한 횟수의 컨테이너 실행(정상 종료)을 보장하는 리소스이다.

 

레플리카셋과 잡과의 차이

  • 잡은 기동 중인 파드가 정지되는 것을 전제로 만들어짐

 

잡 생성

apiVersion: batch/v1
kind: Job
metadata:
  name: sample-job
spec:
  completions: 1
  parallelism: 1
  backoffLimit: 10
  template:
    spec:
      containers:
      - name: tools-container
        image: amsy810/tools:v2.0
        command: ["sleep"]
        args: ["60"]
      restartPolicy: Never

60초 동안만 sleep하는 잡을 생성한다.

레플리카셋과 마찬가지로 레이블과 셀렉터는 명시적으로 지정할 수 있지만,

쿠버네티스가 유니크한 uuid를 자동 생성하기 때문에 잡에서는 명시적으로 지정하는 것을 권장하지 않는다.

# 잡 생성
$ kubectl apply -f sample-job.yaml
job.batch/sample-job created

 

# 잡 목록 표시(잡 생성 직후)
$ kubectl get jobs
NAME	    COMPLETIONS   DURATION  AGE
sample-job  0/1     	  44s	  44s

# 잡이 생성한 파드 확인
$ kubectl get pods --watch
NAME	    	 READY 	STATUS	RESTARTS   AGE
sample-job-mbx2s  1/1   Running	0	   68s

# 잡 목록 표시(파드 실행 완료 후)
$ kubectl get jobs
NAME		  READY	  STATUS	RESTARTS	AGE
sample-job-mbx2s  0/1	 Completed	0		73s

kubectl get으로 잡을 확인해보면 레플리카셋 등에서는 READY 상태의 컨테이너 수를 표시하지만

잡에서는 정상 종료한 파드 수 (COMPLETIONS)를 표시한다.

또한 잡은 레플리카셋 등과 마찬가지로 파드를 생성하는 리소스이다.

 

restartPolicy에 따른 동작 차이

잡의 manifest에는 spec.template.spec.restartPolicy에

  • OnFailure
  • Never

두 가지 중에 하나를 지정해야 한다.

Never를 지정한 경우 파드에 장애가 발생하면 신규 파드가 생성된다.

반대로 OnFailure의 경우 다시 동일한 파드를 사용하여 잡을 다시 시작한다.

 

restartPolicy: Never의 경우

 

apiVersion: batch/v1
kind: Job
metadata:
  name: sample-job-never-restart
spec:
  completions: 1
  parallelism: 1
  backoffLimit: 10
  template:
    spec:
      containers:
      - name: tools-container
        image: amsy810/tools:v2.0
        command: ["sh", "-c"]
        args: ["$(sleep 3600)"]
      restartPolicy: Never

프로세스 ID=1에서 sleep 명령어를 실행하면 SIGKILL 신호를 보내도 프로세스를 정지할 수 없기 때문에

셸에서 별도 프로세스를 시작하는 형태로 기동해야 한다.

# 잡이 생성한 파드 확인
$ kubectl get jobs
NAME	    		     READY  STATUS     RESTARTS   AGE
sample-job-never-restart-5vh  1/1   Running  	0	  5s

# 컨테이너상의 sleep 프로세스 정지
$ kubectl exec -it sample-job-never-restart-5vh -- sh -c 'kill -9 `pgrep sleep`'

# 생성된 파드가 기동됨
$ kubectl get pods 
NAME		              READY     STATUS	 RESTARTS  AGE
sample-job-never-restart-5vh   0/1	Error 	  0	   69s
sample-job-never-restart-8fg   1/1	Running   0	   4s

실제로 컨테이너상에 sleep 프로세스를 정지시키면

잡 용도로 생성한 파드 내부 프로세스가 정지되면 신규 파드를 생성하여 잡을 계속 실행하려고 한다.

 

restartPolicy: OnFailure의 경우

apiVersion: batch/v1
kind: Job
metadata:
  name: sample-job-onfailure-restart
spec:
  completions: 1
  parallelism: 1
  backoffLimit: 10
  template:
    spec:
      containers:
      - name: tools-container
        image: amsy810/tools:v2.0
        command: ["sh", "-c"]
        args: ["$(sleep 3600)"]
      restartPolicy: OnFailure

셸로 sleep용 별도 프로세스를 기동하여 실행한다.

잡 용도로 생성된 파드 내부 프로세스가 정지되면 RESTARTS 카운터가 증가하고, 사용했던 파드를 다시 사용하여 잡을 실행하려고 한다. 파드가 기동하는 노드나 파드 IP 주소는 변경되지 않지만,

영구 볼륨이나 쿠버네티스 노드의 영역(hostPath)을 마운트하지 않는 경우라면 데이터 자체가 유실된다.

# 잡이 생성한 파드 확인
$ kubectl get jobs
NAME	    		          READY  STATUS     RESTARTS   AGE
sample-job-onfailure-restart-cgz  1/1   Running     0	       4s

# 컨테이너상의 sleep 프로세스 정지
$ kubectl exec -it sample-job-onfailure-restart-cgz -- sh -c 'kill -9 `pgrep sleep`'

# 생성된 파드가 기동됨
$ kubectl get pods 
NAME		                   READY     STATUS	 RESTARTS  AGE
sample-job-onfailure-restart-cgz   1/1	    Running         1	   4s
# 잡이 생성한 파드 확인
$ kubectl get pods

 

태스크와 작업 큐 병렬 실행

apiVersion: batch/v1
kind: Job
metadata:
  name: sample-paralleljob
spec:
  completions: 10
  parallelism: 2
  backoffLimit: 10
  template:
    spec:
      containers:
      - name: tools-container
        image: amsy810/tools:v2.0
        command: ["sleep"]
        args: ["30"]
      restartPolicy: Never

다음은 두 개의 병렬로 실행하고 10회 성공하면 종료하는 잡이다.

 

잡에서는 성공 횟수를 지정하는 completions와 병렬성을 지정하는 parallelism 설정 항목에 각각 1을 지정했다. 이 설정들의 기본 값은 1이기 때문에 명시적으로 지정하지 않아도 동작은 똑같다. 이 두 파라미터는 잡을 병렬화하여 실행할 때 사용하는 옵션이다. 

backoffLimit는 실패를 허용하는 횟수이다.

 

워크로드 completions parallelism backoffLimit
1회만 실행하는 태스크 1 1 0
N개 병렬로 실행시키는 태스크 M N P
한 개씩 실행하는 작업 큐 미지정 1 P
N개 병렬로 실행하는 작업 큐 미지정 N P

 

다음 파라미터들 중에서 성공 횟수(completions)는 나중에 변경할 수 없으며,

병렬성(parallelism)과 실패를 허용하는 횟수(backoffLimit)는 도중에 변경할 수 있다.

변경하려면 메니페스트를 수정하고 kubectl apply 명령어를 실행한다.

# 병렬성을 2에서 3으로 변경한 manifest를 apply
$ sed -e 's|parallelism: 2|parallelism: 3|' sample-paralleljob.yaml | kubectl apply -f -
job.batch/sample-parallejob configured

 

One Shot Task: 1회만 실행하는 태스크

apiVersion: batch/v1
kind: Job
metadata:
  name: sample-oneshot-task-job
spec:
  completions: 1
  parallelism: 1
  backoffLimit: 0
  template:
    spec:
      containers:
      - name: tools-container
        image: amsy810/tools:v2.0
        command: ["sleep"]
        args: ["30"]
      restartPolicy: Never

1회만 실행하려는 태스크의 경우 completions=1/parallelism=1backoffLimit=0을 지정한다.

(= '한 개 병렬로 실행/성공 횟수가 1이 되면 종료/ 실패 횟수가 0이 되면 종료' )

라는 조건 때문에 성공 유무에 관계없이 반드시 1회만 실행된다.

 

Multi Task: N개 병렬로 실행시키는 태스크

apiVersion: batch/v1
kind: Job
metadata:
  name: sample-multi-task-job
spec:
  completions: 5
  parallelism: 3
  backoffLimit: 5
  template:
    spec:
      containers:
      - name: tools-container
        image: amsy810/tools:v2.0
        command: ["sleep"]
        args: ["30"]
      restartPolicy: Never

성공 횟수(completions)와 병렬 수(parallelism)를 변경하여 병렬 태스크를 생성할 수 있다.

예를 들어 completions=5/parallelism=3을 지정하고 잡을 실행한 경우

파드가 5회 정상 종료할 때까지 세 개 병렬로 실행한다.

manifest 예제에서는 먼저 세 개의 파드가 생성되고 30초 후에 세 개의 잡이 종료된다.

그 이후 남은 두 개의 잡을 완료하면 조건이 만족되어 

파드의 병렬 수가 세 개가 아닌 두 개만 생성되는 것에 주의해야 한다.

 

성공률이 낮은 잡 등의 경우에는 '남은 필요한 성공 횟수가 2라고 해도 3 병렬로 실행해서 성공하는 파드가 나오면 종료한다'  라는 방식으로는 동작하지 않는다 !!!

 

Multi WorkQueue: N개 병렬로 실행하는 작업 큐

지금까지 태스크 잡처럼 개별 파드의 정상 종료가 성공 횟수에 도달할 때까지 실행하는 것이 아니라,

큰 처리 전체가 정상 종료할 때까지 몇 개의 병렬 수로 계속 실행하고 싶은 경우가 있다.

그런 경우 작업 큐의 잡을 사용한다.

작업 큐의 잡은 성공 횟수(completions)를 지정하지 않고 병렬 수(parallelism)만 지정한다.

 

이 경우 parallelism으로 지정한 병렬 수로 파드를 실행하고,

그중 하나라도 정상 종료하면 그 이후는 파드를 생성하지 않는다.

또 그때 이미 실행중인 나머지 파드는 강제적으로 정지하지 않고 개별 처리가 종료할 때까지 계속 동작한다.

 

작업 큐의 잡을 사용하려면 처리 전체의 진행을 관리하기 위한 어떤 메시지 큐를 사용해야 한다.

그리고 파드 내의 애플리케이션에는 그 메시지로부터 반복하여 데이터를 계속 가져오는 처리를 구현해 둔다.

또한, 메시지 큐가 빈 것을 확인하면 그 파드를 정상 종료하도록 구현해 둠으로써 작업 큐의 잡에서 처리 전체를 정지시킬 수 있게 된다. 또 메시지 큐의Topic(큐의 송신자)에 공통으로 정상 종료 후에도 새로운 데이터가 큐에 들어오는 경우 등은 잡을 정기적으로 시작하는 크론잡 리소스 등과 조합하도록 구현해 두자.

메시지 큐에 모인 메시지 양에 따라 병렬 수를 늘리는 구조를 만들면 작업 큐의 잡을 자동으로 스케일하는 것도 가능하다.

apiVersion: batch/v1
kind: Job
metadata:
  name: sample-multi-workqueue-job
spec:
  # 지정하지 않음
  # completions: 1
  parallelism: 3
  backoffLimit: 1
  template:
    spec:
      containers:
      - name: tools-container
        image: amsy810/tools:v2.0
        command: ["sleep"]
        args: ["30"]
      restartPolicy: Never

태스크와 작업 큐는 잡을 표시할 때 COMPLETIONS의 출력 형식이 다르다.

앞서 설명한 Multi Task 예에서는 다섯 개가 정상 종료할 때까지 최대 세 개 병렬로 실행되기 때문에 처음 세 개가 정상 종료한 시점에서 3/5로 출력된다. 앞서 설명한 Multi WorkQueue 예에서는 세 개 중 하나만 정상 종료하면 되기 때문에 하나도 정상 종료하지 않는 시점에서 0/1 of 3 으로 출력도니다. 이는 세 개 병렬로 실행 중 하나가 정상 종료하면 되지만 아직 하나도 정상 종료되지 않는 것을 나타낸다.

# 'Multi Task' 실행 중인 잡 확인
$ kubectl get job sample-multi-task-job
NAME	    	       COMPLETIONS  DURATION   AGE
sample-multi-task-job  3/5         38s        38s

# 'Multi WorkQueue' 실행 중인 잡 표시
$ kubectl get job sample-multi-workqueue-job
NAME	    	    	   COMPLETIONS  DURATION   AGE
sample-multi-workqueue-job  0/1         6s         6s

# 'Multi WorkQueue' 실행 중인 잡 표시
$ kubectl get pods 
NAME		                 READY     STATUS	 RESTARTS  AGE
sample-multi-workqueue-job-h8b   1/1	   Running        0	   23s
sample-multi-workqueue-job-nz6   1/1	   Running        0	   23s
sample-multi-workqueue-job-vhd   1/1	   Running        0	   23s

 

Single WorkQueue: 한 개씩 실행하는 작업 큐

apiVersion: batch/v1
kind: Job
metadata:
  name: sample-single-workqueue-job
spec:
  # 지정하지 않음
  # completions: 1
  parallelism: 1
  backoffLimit: 1
  template:
    spec:
      containers:
      - name: tools-container
        image: amsy810/tools:v2.0
        command: ["sleep"]
        args: ["30"]
      restartPolicy: Never

Multi WorkQueue 응용으로 성공 횟수(completions)를 지정하지 않고 병렬 수(parallelism)에 1을 지정한 경우에는 한 번 정상 종료할 때까지 한 개씩 실행하는 작업 큐가 된다.

# 성공 횟수 변경
$ kubectl patch job sample-single-workqueue-job -p '{"spec": {"completions": 2}}'
The Job "sample-single-workqueue-job" is invalid: spec.completions: Invalid value: 2: field is immutable

성공 횟수(completions)는 나중에 변경할 수 없지만 병렬 수(parallelism)는 나중에 변경할 수 있는 파라미터이다.

이를 사용하여 completions를 지정하지 않은 한 개 병렬의 single WorkQueue는 나중에 prallelism을 변경하여 N개 병렬의Multi WorkQueue로 변경할 수 있다. parallelism 변경은 kubectl patch 명령어를 사용하거나 매니페스트를 수정한 후에 다시 kubectl apply 등으로 적용한다.

$ kubectl get jobs
NAME	    	             COMPLETIONS  DURATION   AGE
sample-single-workqueue-job   1/1         31s       118s

$ kubectl patch job sample-simple-workqueue-job -p '{"spec": {"parallelism": 2}}'
job.batch/sample-single-workqueue-job patched

$ kubectl get jobs 
NAME	    	             COMPLETIONS  DURATION   AGE
sample-single-workqueue-job   1/1 of 2    31s        2m41s

 

일정 기간 후 잡 삭제

apiVersion: batch/v1
kind: Job
metadata:
  name: sample-job-ttl
spec:
  ttlSecondsAfterFinished: 30
  completions: 1
  parallelism: 1
  backoffLimit: 10
  template:
    spec:
      containers:
      - name: tools-container
        image: amsy810/tools:v2.0
        command: ["sleep"]
        args: ["60"]
      restartPolicy: Never

잡 리소스는 한 번만 실행하는 태스크로 생성하는 경우가 있는데, 잡은 종료 후에 삭제되지 않고 계속 남는다.

spec.ttlSecondsAfterFinished를 설정하여 잡이 종료한 후에 일정 기간(초) 경과 후 삭제하도록 설정할 수 있다.

# Job 상태 모니터링
$ kubectl get jobs sample-job-ttl --watch --output-watch-events
EVENT     NAME	          COMPLETIONS  DURATION   AGE
ADDED     sample-job-ttl   0/1          1s        1s        # 잡 기동
MODIFIED  sample-job-ttl   1/1          71s       71s        # 잡 완료(+60초후)
DELETED   sample-job-ttl   1/1          71s       105s        # 잡 자동삭제(+30초후)

60초 후에 잡이 종료되고, 종료 이후 30초가 지나면 잡이 삭제된다.

 

메니페스트를 사용하지 않고 잡을 생성

# manifest를 사용하지 않고 명령어로 잡 생성
$ kubectl created job sample-job-by-cli `
--image=amsy810/tools:v2.0 `
-- sleep 30
job.batch/sample-job-by-cli created

kubectl create job 명령어를 사용하여 메니페스트를 사용하지 않고도 거의 동일한 크론잡을 생성할 수 있다.

그러나 어디까지나 간단한 작업 용도이기 때문에 상세한 설정은 할 수 없다.

서비스 환경에서는 메니페스트를 사용하는 것을 권장한다.

$ kubectl created job sample-job-from-cronjob --from cronjob/sample-cronjob
job.batch/sample-job-from-cronjob created

--from 옵션을 이용하여 크론잡을 기반으로 잡을 작성할 수 있다.