pod를 실행하고 계속해서 관리하는 데 있어서, 개발자가 직접 제어하는 것은 매우 번거로운 일이다. 자동으로 pod가 배포되고, 다운되면 다시 살려주는 리소스가 있으면 kubernetes 클러스터를 관리하는데 매우 쉬울 것이다. replication controller와 deployment라면 이것이 가능하다. 또한, pod를 실행하는 방법 중 한 가지 작업만 수행한 뒤 중지되는 파드도 존재한다.
pod는 kubernetes 클러스터에서 배포되기 시작하면, pod는 배포될 worker node가 할당되고 해당 worker node에서 pod의 컨테이너가 실행되도록 kubelet이 실행이 된다.
pod 리소스 시작 -> Woker Node 할당 -> Kubelet으로 container실행 -> pod 실행
문제는 pod안의 컨테이너 중 하나가 에러가 발생하는 경우이다. kubelet은 컨테이너의 메인 프로세스가 죽는 상황이 발생하면, 이를 캐치하여 재시작해주는 로직이 있다. 그러나, 다음과 같이 container의 process가 죽는 문제가 아니라, 교착 상태, 메모리 누수와 같은 문제가 발생했을 때는 container의 process가 명백하게 죽었다는 시그널이 없어, kubernetes 클러스터는 이 사실을 알지 못한다. 때문에 application이 kubernetes에게 정상성을 알려주는 것이 아니라, application 외부인 kubernetes가 application의 상태를 체크하는 로직이 필요하다.
라이브니스 프로브는 pod의 컨테이너가 죽었는 지 살았는 지 확인하는 kubernetes에서 application으로 요청을 보내 정상성을 확보하는 로직을 제공해준다.
이후에 readiness probe도 배울텐데 이는 liveness probe와는 다르므로 주의하도록 하자.
kubernetes에서 3가지 매커니즘을 제공하여 container의 probe를 제공한다.
1. HTTP: http 방식으로 정해진 port, path에 맞게 GET요청을 보내고 2XX, 3XX status code가 오게된다면 성공이고, 이외의 status code가 온다면 실패한 것이므로 container를 kubelet에서 다시 시작하도록 로직을 수행한다.
2. TCP: 정해진 IP, port에 TCP연결 요청을 보내고 연결이 성공하면 성공이고, 실패한다면 container를 kubelet에서 다시 시작하도록 로직을 수행한다.
3. Exec: container내에서 정해진 명령어를 수행하여 결과 상태 코드가 0이면 성공이고 다른 코드가 발생하면 실패한 것으로 간주한다.
이제 HTTP 요청을 보내는 라이브니스 프로브를 만들어 pod를 배포해보도록 하자. luksa/kubia-unhealthy
라는 이미지를 사용할 것이고, 이는 5번의 라이브니스 체크 요청은 정상적으로 체크하지만 그 이후는 500 status code를 반환하여 에러 상황을 만든다.
apiVersion: v1
kind: Pod
metadata:
name: kubia-liveness
spec:
containers:
- name: kubia
image: luksa/kubia-unhealthy
livenessProbe:
httpGet:
path: /
port: 8080
containers
에 livenessProbe
를 넣어 httpGet
을 만들어 넣으면 된다. 이는 liveness probe가 각 container마다 설정할 수 있다는 것을 알 수 있다.
이제 pod를 만들어보도록 하자.
kubectl create -f ./kubia-liveness-probe.yaml
다음으로 kubectl get pod -A
를 통해서 pod의 정보를 확인해보도록 하자.
AMESPACE NAME READY STATUS RESTARTS AGE
default kubia-liveness 1/1 Running 6 (45s ago) 8m58s
계속해서 재시작된 것을 알 수 있다. 현재 로그를 확인하기 위해서는 kubectl logs
로 확인할 수 있는데, 이전에 crash난 로그 정보를 알기 위해서는 --previous
를 붙이면 된다.
kubectl logs kubia-liveness --previous
kubernetes cluster의 내용을 보기위해서 describe로 pod의 정보를 확인해보도록 하자.
kubectl describe pod kubia-liveness
...
Normal Killing 55m (x3 over 58m) kubelet Container kubia failed liveness probe, will be restarted
Warning BackOff 9m45s (x143 over 53m) kubelet Back-off restarting failed container kubia in pod kubia-liveness_default(2c02b75c-f83c-488a-9b9a-fb85b0b1d798)
Warning Unhealthy 4m12s (x49 over 58m) kubelet Liveness probe failed: HTTP probe failed with statuscode: 500
계속해서 크래시가 발생하고 재시작되고를 반복하는 것을 알 수 있다. 이는 kubernetes에서 해당 container에 대한 liveness probe가 실패하여 container가 비정상적인 상태임을 감지해 재시작을 반복하였기 때문이다.
liveness probe에 추가적인 속성을 넣을 수 있는데, 가령 컨테이너가 시작된 후에 바로 컨테이너의 서버가 실행되지 않을 수 있다. 따라서, 이 delay시간을 설정할 수 있다.
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 15
initialDelaySeconds
가 15라면 초기에 컨테이너가 만들어지고 첫 번째 프로브 실행까지 15초를 delay하는 것이다.
레플리케이션 컨트롤러는 쿠버네티스 리소스로 파드가 항상 실행되도록 보장한다. 즉, 실행 중인 파드 목록을 지속적으로 모니터링하고 의도한 파드 수와 동일한 수를 유지하도록 한다. pod의 특정 label만 맞으면 label selector에의해 레플리케이션 컨트롤러의 제어를 받게 된다.
레플리케이션 컨트롤러에는 3가지 요소가 있다.
1. label selector: 레플리케이션 컨트롤러의 범위에 있는 파드를 결정
2. replica count: 실행하려는 pod의 수
3. pod template: pod를 실행할 때의 template로 새로운 pod replica를 만들 때 사용한다.
레플리케이션 컨트롤러를 만드는 방법들은 다음과 같다.
apiVersion: v1
kind: ReplicationController
metadata:
name: kubia
spec:
replicas: 3
selector:
app: kubia
template:
metadata:
labels:
app: kubia
spec:
containers:
- name: kubia
image: luksa/kubia
ports:
- containerPort: 8080
레플리케이션 컨트롤러는 kubernetes resource이기 때문에 ReplicationController
로 kind
를 적어준다. 레플리케이션 컨트롤러 3가지 요소는 다음과 같다.
1. spec.replicas
: replica개수로 몇개의 pod를 지속적으로 만들고 관리할 지 결정한다. 위에서는 3개를 만들고 관리한다.
2. spec.selector
: app: kubia
는 label selctor로 app=kubia
라벨을 가진 pod를 관리한다는 것이다.
3. spec.template
: 생성할 pod의 템플릿으로 metadata
로 label
을 app: kubia
로 갖는 것을 주목하도록 하자.
label selector를 지정하지 않는 것도 선택 가능한 옵션이다. selector를 지정하지 않으면 template의 label로 자동 설정된다. 사실 이러한 방식이 좀 더 간결한 yaml파일을 만들어낼 수 있다.
kubectl create -f
로 레플리케이션 컨트롤러를 생성할 수 있다.
kubectl create -f kubia-rc.yaml
replicationcontroller/kubia created
확인해보면 다음과 같다.
default kubia-6r9n4 0/1 ContainerCreating 0 3s
default kubia-ggc6j 0/1 ContainerCreating 0 3s
default kubia-n9bgs 0/1 ContainerCreating 0 3s
실제 3개의 pod가 생성된 것을 볼 수 있다. 이들이 레플리케이션 컨트롤러의 제어를 받으면 삭제해도 다시 추가 생성될 것이다.
kubectl delete pod kubia-n9bgs
pod "kubia-n9bgs" deleted
다시 생성되었는 지 확인해보도록 하자.
kubectl get po -A
default kubia-6r9n4 1/1 Running 0 9m30s
default kubia-bws85 1/1 Running 0 36s
default kubia-ggc6j 1/1 Running 0 9m30s
이전에는 없던 kubia-bws85
pod가 생성된 것을 볼 수 있다. 이는 replication controller가 replica개수 3개를 맞추기 위해 template에서 pod를 만들어낸 것이다.
kubectl get
으로 레플리케이션 컨트러롤러의 정보를 얻을 수 있다.
kubectl get rc
NAME DESIRED CURRENT READY AGE
kubia 3 3 3 11m
DESIRED
가 원했던 replica 수이고, CURRENT
가 현재 pod의 개수이다. READY
는 준비 상태인 pod가 몇개인지를 나타낸다.
더 자세한 정보는 kubectl describe
명령어로 확인할 수 있다.
kubectl describe rc kubia
Name: kubia
Namespace: default
Selector: app=kubia
Labels: app=kubia
Annotations: <none>
Replicas: 3 current / 3 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=kubia
Containers:
kubia:
Image: luksa/kubia
Port: 8080/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 12m replication-controller Created pod: kubia-n9bgs
Normal SuccessfulCreate 12m replication-controller Created pod: kubia-ggc6j
Normal SuccessfulCreate 12m replication-controller Created pod: kubia-6r9n4
Normal SuccessfulCreate 3m24s replication-controller Created pod: kubia-bws85
event
부분을 통해서 현재까지 4개자의 pod가 생생되었고, 1개의 pod가 죽은 것을 알 수 있다.
재밌는 것은 pod를 일부러 삭제하는 경우가 아닌, node가 다운되는 경우 다운된 node에서 구동되던 pod를 중지시키고 새로운 pod를 동작시켜 replica수를 유지한다. node의 네트워크를 연결을 중단시키면 다음과 같이된다.
kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready control-plane 22h v1.28.2
node1 NotReady <none> 22h v1.28.2
node2 Ready <none> 22h v1.28.2
다음과 같이 node가 다운된 것을 볼 수 있다.
pod의 상태를 확인하면 node에 스케줄링되었던 pod들은 node에 연결이 안되었기 때문에 Unknown
상태가 된다.
default kubia-6r9n4 0/1 Unknown 0 18m
default kubia-bws85 0/1 Unknown 0 9m32s
default kubia-ggc6j 1/1 Running 0 18m
이후 pod가 재생성되는 것을 볼 수 있다.
따라서, 노드에 장애가 발생한 경우, 인간의 개입없시도 시스템이 자동적으로 스스로 치유하는 것을 볼 수 있다.
재밌는 것은 replication controller의 지배를 받은 pod들은 label selector를 통해서 지배를 받는 것이기 때문에 label이 달라지면 replication controller의 영향에서 벗어날 수 있다. 즉 일반적인 pod가 되는 것이다.
레플리케이션 컨트롤러의 pod template는 언제든지 수정이 가능하다. 수정한 사항은 현재 배포된 pod에는 영향을 주지 않고, 새로 만들어질 pod에만 영향을 준다. 따라서, 기존 pod에 영향을 주고 싶다면 기존 pod를 삭제하여 다시 시작하도록 하는 것이 좋다.
kubectl edit rc kukia
template가 나오고 원하는대로 변경할 수 있다.
수평 파드 스케일링은 레플리케이션 컨트롤러가 항상 실행하도록 보장하는 복제본의 수를 늘렸다, 줄였다하는 것이다. replicas
필드 값을 변경하기만 하면 된다.
replicas를 5개로 늘려보도록 하자.
kubectl scale rc kubia --replicas=5
replicationcontroller/kubia scaled
조회해보면 다음과 같다.
default kubia-6r9n4 1/1 Running 1 (15m ago) 33m
default kubia-bspcw 1/1 Running 0 22s
default kubia-bws85 1/1 Running 1 (15m ago) 24m
default kubia-ggc6j 1/1 Running 0 33m
default kubia-lgvpk 1/1 Running 0 22s
이번에는 kubectl edit
으로 replicas 수를 변경하도록 하자.
kubectl edit rc kubia
apiVersion: v1
kind: ReplicationController
metadata:
creationTimestamp: "2023-09-19T13:11:23Z"
generation: 2
labels:
app: kubia
name: kubia
namespace: default
resourceVersion: "15714"
uid: ffd5047f-c3cd-49b0-847a-b0fa81ab41f7
spec:
replicas: 7
selector:
app: kubia
template:
...
replicas
를 5에서 7로 바꾼다음 저장하였다.
kubectl get rc
NAME DESIRED CURRENT READY AGE
kubia 7 7 7 35m
7
개씩 늘어난 것으로 볼 수 있다.
kubectl edit
보다는 kubectl scale
과 같은 방식으로 스케일을 줄였다 늘였다하는 것이 편해보인다. 이는 선언적인 명령어 방식으로 kubernetes에게 원하는, 의도하는 상태를 지정하는 것이다.
줄이는 방법도 마찬가지이다.
kubectl scale rc kubia --replicas=3
replicationcontroller/kubia scaled
레플리케이션 컨트롤러의 삭제는 kubectl delete
를 통해서도 가능하다. 레플리케이션 컨트롤러를 삭제하면 pod도 삭제된다. 그러나, 이를 원치않을 수 있다. 왜냐하면 레플리케이션 컨트롤러는 pod를 관리할 뿐 관리자가 죽는다고 pod까지 죽는 것을 원치않을 수 있기 때문이다. 이때 사용하는 option이 --cascade=false
이다.
kubectl delete rc kubia --cascade=false
warning: --cascade=false is deprecated (boolean value) and can be replaced with --cascade=orphan.
replicationcontroller "kubia" deleted
이렇게 삭제하면 파드에 영향을 주지 않고 작업을 수행할 수 있으며, pod를 관리하는 replication controller가 없어도 서비스 중단없이 실행할 수 있다.
현재는 레플리케이션 컨트롤러를 거의 사용하지 않는다. 이유는 레플리카셋이 레플리케이션 컨트롤러를 거의 대체할 수 있으며 좀더 풍부한 표현식을 하는 파드 셀렉터를 갖고 있다. 더 나아가 나중에 배울 deployment에서는 ReplicaSet을 자동으로 만들어주어 직접 ReplicaSet을 만드는 일은 거의 없다. 그러나 학습에 있어서의 가치는 매우 풍부하다.
ReplicaSet은 특정 레이블의 키만으로도 pod를 매칭시킬 수 있다. 즉, replication controller가 key=value
형식이 완전히 매칭되는 label selector를 갖는 반면에 ReplicaSet은 pod의 label이 지정한 key
만 가지고 있어도 관리의 대상이 된다.
가장 대표적으로 replication controller는 env=prod
와 env=dev
인 label을 동시에 매칭시킬 수 없다. 즉, label로 env
key를 pod를 5개만 유지시키고 싶어도 env=prod
, env=dev
각각 5개 씩만 유지가 가능하다는 것이다. 합쳐서 5개는 불가능하다. 반면 ReplicaSet은 env
key 하나로 그룹핑을 할 수 있어서 env
key를 가진 pod를 전체 5개만 유지가 가능하다.
ReplicaSet정의는 다음과 같다. kubia-replicaset.yaml
파일을 만들어보도록 하자.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: kubia
spec:
replicas: 3
selector:
matchLabels:
app: kubia
template:
metadata:
labels:
app: kubia
spec:
containers:
- name: kubia
image: luksa/kubia
ReplicaSet
은 v1
API의 일부가 아니므로 API그룹, API버전을 명시해야해서 apps/v1
로 API버전을 표현해야한다. apiVersion
은 다음의 두 가지로 이루어져 있다.
1. API 그룹 (apps
)
2. API 버전 (v1
)
core API에 속하는 쿠버네티스 리소스들은 apiVersion
에서 API그룹
을 생략할 수 있어서 v1
만 써줘도 되는 것을 볼 수 있다. 가령 pod
의 경우는 v1
만 써줘도 되었다.
selector.matchLabels
부분이 ReplicationController
와 다른 부분으로 label
의 key
만 매칭되어도 pod
의 개수를 manipulation할 수 있다.
이제 생성해보도록 하자. 이전에 생성했던 ReplicationController
이 삭제되었지만 pod는 그대로 유지되어 있을 것이다. 따라서, ReplicaSet
을 생성한다고 해서, 새롭게 pod가 이루어지지 않을 것이다.
kubectl create -f ./kubia-replicaset.yaml
replicaset.apps/kubia created
kubectl describe rs
Name: kubia
Namespace: default
Selector: app=kubia
Labels: <none>
Annotations: <none>
Replicas: 3 current / 3 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=kubia
Containers:
kubia:
Image: luksa/kubia
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Events: <none>
어떤 파드도 새롭게 생기지 않은 것을 볼 수 있다. 이는 이전에 만들었던 ReplicationController
에 의해 만들어진 3개의 pod가 모두 label
로 app
을 갖고 있기 때문이다.
이전에 레플리카셋이 레플리케이션 컨트롤러보다 더 다양한 label
selector를 가진다고 했다. 이를 위해서는 selector.matchLabels
가 아니라 selector.matchExpressions
을 사용해야한다.
selector:
matchExpressions:
- key: app
operator: In
values:
- kubia
- gyu
...
selector.matchExpressions
는 3가지 요소로 이루어져 있는데, key
는 label
의 key를 의미히고 operator
는 label
의 key와 value의 관계를 말한다. In
이라고 표현하면 key
의 value
들 중에 하라도 매칭된다면 성립한다는 것을 의미한다. values
는 value
의 리스트로 app=kubia
, app=gyu
로 만들 수 있다.
operator는 다음 4가지 유효한 연산이 있다.
1. In: 레이블의 value가 하나라도 일치해야한다.
2. NotIn: 레이블의 값이 지정된 값과 일치하지 않아야 한다.
3. Exists: value는 중요하지 않고 key
만 일치하면 된다는 것이다. 따라서 values
필드에는 아무것도 지정하지 않는다.
4. DoesNotExist: 파드에 지정된 key
를 가진 레이블이 포함되어 있지 않아야 한다. 따아서 values
필드를 지정하지 않아야 한다.
이제 ReplicaSet
에 대해서 알아보았다. 삭제하는 방법은 delete
를 사용하면 된다.
kubectl delete rs kubia
replicaset.apps "kubia" deleted
ReplicationController
, ReplicaSet
의 경우는 정해진 replicas
수를 유지하는 것이 관전이다. 따라서, replicas
수가 5개라면 3개의 node로 이루어진 cluster에서 각 pod들은 램덤하게 나누어져 노드에 배포된다. 가령 node1은 pod3개 node2는 pod2개 node3은 pod 0개로 이루어 질 수 있다. 이때 node1의 pod 1개가 다운되면 replicas
수가 현재 4개로 되며 1개가 node1, node2, node3 3개의 node 중 하나에 랜덤하게 배포된다. 즉, 다시 자신이 죽었던 node1에 배포되는 것이 아니다.
그런데, 모든 node
에 걸쳐 하나의 pod가 실행되었으면 하는 경우들이 있다. 가령, log 수집기라던지, 모니터링 프로그램이라면 각 node
마다 한 개씩 파드가 있어야 한다. 이를 위해서는 ReplicaSet
을 사용할 것이 아니라, DaemonSet
을 사용해야한다. Daemon
이라는 말은 사용자가 직접 제어하지 않고 백그라운드에서 돌면서 여러 작업을 하는 프로그램을 말한다. DaemonSet
역시도 사용자가 직접 제어하는 것이 아니라, node
마다 지정한 pod를 백그라운드에서 실행하도록 하는 것이다.
따라서, DaemonSet
은 Replica
수를 를 유지하지 않고 각 node마다 1개씩 유지한다. 따라서 node1
에서 죽은 pod
가 DaemonSet
의 관할을 받는다면 해당 pod는 node1
에서 다시 부활하지 node2
에서 실행되진 않는다.
물론, DeamonSet
의 제어를 받을 node를 선택할 수도 있다. 이를 위해서는 node-selector
를 사용하여 node를 지정하면 된다.
참고로, DaemonSet
에 의해 관리받는 pod
들은 스케줄링을 받지 않는데, 이는 DaemonSet
이 관리하는 pod들은 스케줄러와 무관하게 동작한다는 것이다. 즉, 일반적으로 스케줄링되지 않는 노드에서도 시스템 서비스를 실행할 수 있어야 하기 때문에 DaemonSet
을 사용하는 것이 바람직하다.
만약, node1, node2가 있는데 node1에서는 ssd
로 구동되어 ssd-monitor
라는 daemon이 실행되어야 한다고 하자. 그렇다면 DeamonSet
의 nodeSelector
를 이용하여 node
를 지정하면 된다. 정리하면 다음과 같다.
node1 (ssd) -> ssd-monitor 대상
node2 (hdd) -> 대상 x
이를 위해서 node1
에 disk: ssd
라는 label
을 만들어주도록 하자.
kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready control-plane 12d v1.28.2
node1 Ready <none> 12d v1.28.2
node2 Ready <none> 12d v1.28.2
kubectl label nodes node1 disk=ssd
node/node1 labeled
다음으로 DaemonSet
을 만들어주도록 하자.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ssd-monitor
spec:
selector:
matchLabels:
app: ssd-monitor
template:
metadata:
labels:
app: ssd-monitor
spec:
nodeSelector:
disk: ssd
containers:
- name: main
image: luksa/ssd-monitor
DamonSet
역시도 apps/v1
으로 apiVersion
을 가진다. 또한, spec
에서 selector
를 가지는데, 이는 생성할 template
파드의 label
을 말한다. template
는 DamonSet
으로 생성할 pod를 말하며 nodeSelector
를 통해서 어떤 node
에 damon
으로 동작할 지 결정할 수 있다.
다음의 명령어로 DamonSet
을 생성해보도록 하자.
kubectl create -f ./ssd-monitor-daemonset.yaml
daemonset.apps/ssd-monitor created
kubectl get daemonsets.apps -A
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
calico-system calico-node 3 3 3 3 3 kubernetes.io/os=linux 12d
calico-system csi-node-driver 3 3 3 3 3 kubernetes.io/os=linux 12d
default ssd-monitor 1 1 1 1 1 disk=ssd 2m12s
kube-system kube-proxy 3 3 3 3 3 kubernetes.io/os=linux 12d
ssd-monitor
damonSet이 잘만들어진 것을 확인할 수 있다. 그렇다면 node1
의 label이 disk=ssd
이기 때문에 제대로 pod가 한 개만 만들어졌는 지 확인해보도록 하자.
kubectl get po
NAME READY STATUS RESTARTS AGE
ssd-monitor-wsm78 1/1 Running 0 3m
node는 두 개지만 하나만 생성된 것을 확인할 수 있다. 그렇다면 node1
의 disk=ssd
라벨을 삭제해보면 어떨까? pod가 다운되는 지를 확인해보도록 하자.
kubectl label nodes node1 disk-
node/node1 unlabeled
다음으로 pod의 상태를 확인하면
kubectl get po
NAME READY STATUS RESTARTS AGE
`ssd-monitor-wsm78` 1/1 Terminating 0 4m45s
ssd-monitor-wsm78
가 다운되는 것을 볼 수 있다.
레플라케이션 컨트롤러와 레플리카셋, 데몬셋의 경우는 계속해서 pod를 관리하고 구동한다. 그러나 클러스터를 구성하는데 있어 초기 설정에만 잠시 실행되고, 특정 테스크를 완료하면 종료되어야 할 프로그램도 있다. 이런 경우 kubernetes에서는 job이라는 리소스를 통해서 구현할 수 있다. job은 pod를 생성하지만 pod가 일정 테스크를 완료하면 container가 종료되고 모든 container가 종료되면 pod도 종료된다. 모든 컨테이너가 실행이 완료되어 종료되었다면 성공적으로 pod가 실행에 완료된 것으로 간주되는 것이다. 이후에는 다시 실행되지 않는다.
한 가지 오해하면 안될 것은, cluster가 배포될 때 job이 가장 먼저 실행되고 다른 pod들이 이를 기다리는 것은 아니다. job이 한 번 실행되고 정상적으로 실행되었다면 종료된다는 것이 중요한 것이다.
만약, job에서 만들어낸 pod가 실행중에, node가 다운되거나 장애가 발생하면 replicaSet
처럼 다시 다른 node에 해당 pod를 다시 실행시킨다. container의 종료코드가 에러로 반환되면 다시 실행시킬 수도 있는데 이 경우는 yaml
파일에서 restartPolicy
를 설정해야 한다.
job을 통해서 데이터를 어딘가에 저장하고, 데이터를 변환하여 전송할 수 있는데 우리의 경우는 2분간 sleep을 하는 job을 만들어보도록 하자.
apiVersion: batch/v1
kind: Job
metadata:
name: batch-job
spec:
template:
metadata:
labels:
app: batch-job
spec:
restartPolicy: OnFailure
containers:
- name: main
image: luksa/batch-job
apiVersion: batch/v1
라는 것에 집중하도록 하자.
spec
부분에서 replicaSet처럼 template를 만드는데 selector
필드가 생략된 것을 볼 수 있다. selector
를 써주어 label
을 명시할 수 있지만, 만약 생략한 경우는 template
의 metadata.labels
를 타겟으로 자동 생성된다.
restartPolicy
는 오직 OnFailure
와 Never
만 가능하다. job은 컨테이너 프로세스를 실행한 다음 종료되기 때문에 Always
는 불가능하다.
이제 job
을 실행해보도록 하자.
kubectl create -f ./batch-job
job.batch/batch-job created
kubectl get jobs.batch
NAME COMPLETIONS DURATION AGE
batch-job 0/1 21s 21s
아직 컨테이너의 sleep이 2분이 지나지 않았기 때문에 2분간 기다리도록 하자.
2분이 지나면 다음과 같이 나오게 된다.
kubectl get jobs.batch
NAME COMPLETIONS DURATION AGE
batch-job 1/1 2m3s 2m4s
kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
default batch-job-8bxhg 0/1 Completed 0 2m26s
pod가 Completed
상태가 되고 삭제되지 않은 이유는 해당 pod의 로그를 확인하기 위함이다. 즉, 로그를 확인할 수 있다.
이외에도 job의 pod를 여러번 실행하는 경우 completions
로 순차적으로 몇 번 실행할 지 결정할 수 있으며 activeDeadlineSeconds
로 job이 전체적인 pod 수행에 있어 제한 시간을 걸 수 있다. 가령 100s로 잡으면 100초 후에 모든 job의 모든 컨테이너가 실행이 완료되지 않으면 type: Failed
가 되며, reason: DeadlineExceeded
가 된다. 이외에도 job
을 사용하는 여러 방법들이 있다. https://kubernetes.io/docs/concepts/workloads/controllers/job/
job을 삭제해보도록 하자.
kubectl delete -f ./batch-job
job.batch "batch-job" deleted
job은 배포되면 바로 즉시 pod를 실행한다. 정상적으로 실행된 이후에는 다시 실행되지 않는다. 그러나 특정한 프로세스들은 설정된 시간마다 반복적으로 수행되고 종료되고를 반복해야하는 경우가 있다. 대표적으로 cron작업이 그렇다. 가령, 15분마다 임시 파일의 정보를 압축하여 다른 서버에 전송하고 삭제하기를 반복한다면 일반적은 job
으로는 불가능하고 cron
작업으로 job
을 수행해야한다. kubernetes
에서는 cron작업을 위해서 CronJob
리소스를 제공한다. CronJob
은 Job이지만 정해진 시간에 실행되고 종료되기를 반복한다.
15분마다 job을 수행하는 CronJob
을 만들어보도록 하자.
apiVersion: batch/v1
kind: CronJob
metadata:
name: batch-job-every-fifteen-minutes
spec:
schedule: "0,15,30,45 * * * *"
jobTemplate:
spec:
template:
metadata:
labels:
app: periodic-batch-job
spec:
restartPolicy: OnFailure
containers:
- name: main
image: luksa/batch-job
apiVersion
는 batch/v1
이며 spec
에 cron작업에 필요한 schedule
을 표현할 수 있다. 이는 일반적인 리눅스 cron과 같으며 간단하게 설명하면 다음과 같다.
spec:
schedule: "분 시 일 월 요일"
위의 예제는 0분, 15분, 30분 ,45분 마다 매시, 매일, 매월, 매요일마다 실행되라는 의미이다.
만약 매달 첫째날 30분마다 실행하고 싶다면 다음과 같이 쓰면 된다.
spec:
schedule: "0,30 * 1 * *"
spec.jobTemplate
필드는 CronJob
에서 쓰일 job
리소스를 지정하는 부분이다.
CronJob
역시도 Job
과 같이 timeout 등을 설정할 수 있다. startingDeadlineSeconds
을 설정하면 job이 실행되고 나서 n초가 지난 후까지 job이 완료되지 않으면 실패로 표시하는 것이다.
apiVersion: batch/v1
kind: CronJob
metadata:
name: batch-job-every-fifteen-minutes
spec:
schedule: "0,15,30,45 * * * *"
startingDeadlineSeconds: 15
다음은 15초가 지난 후에도 job이 완료되지 않으면 job이 실패했다고 표시하는 것이다.
이제 15분마다 CronJob
이 잘 실행되었는 지 확인해보도록 하자.
kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
default batch-job-every-fifteen-minutes-28269600-7jjdg 0/1 Completed 0 15m
default batch-job-every-fifteen-minutes-28269615-hkmg9 1/1 Running 0 27s
...
15분 후에 Completed
된 pod이외에 새로운 pod
가 생겨나는 것을 볼 수 있다.