Pod를 생성한 후에 내용 하단을 보면 status 라는 내용을 볼 수 있다.
status:
phase: Pending
conditions:
- type: Initalized
....
여기 내용에 포함되는 정보는 다음과 같다.
Pod가 최초상태이다. InitContainer 라고해서 실제 컨테이너가 기동 되기전에 초기화 시켜야하는 내용들이 있을경우 그 내용을 담는 컨테이너다.
볼륨 혹은 보안셋팅을 해야할 일이 있을경우 initContainers 안에 항목에 초기화 스크립트를 넣을 수 있다.
apiVersoin: v1
kind: Pod
...
spec:
containers:
...
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running!']
initConatiers의 스크립트가 성공적으로 실행 했거나 아예 설정하지 않았을 때는 Initalized의 값이 True, 실패하게 되면 False 가 된다.
그 다음 Pod를 직접 Node에 지정을 했을 때는 해당 Node에 Pod를 기동하고, 아닐 경우엔 k8s가 직접 노드의 자원의 상태를 고려하여 Pod를 생성한다. 이게 완료가 되면 PodScheduled: True 가 되고 Image를 다운로드 한다. 이 작업이 끝나면 Running 상태로 간다.
일반적으로 Pod의 상태가 Running이 될때는 정상적으로 기동이 되겠지만 하나의 컨테이너 혹은 모든 컨테이너가 기동중에 문제가 발생해서 재시작 될수도 있다. 그럼 이때 Container의 상태는 CrashLoopBackOff가 될것이고 Pod는 이런상태를 Running이라고 간주한다.
하지만 내부 Condition 값에는 ContainerReady: False Ready: False의 값을 줄것이다.
그러다가 결국 모든 Container들이 제대로 기동이 되어서 원활하게 돌아간다면 ContainerReady: True Ready: True 상태로 바뀔것이다.
여기서 만약 Job 으로 생성된 Pod일 경우 Job이 돌아가는 경우에는 Running 상태 이지만 Job이 끝나거나 실패했을 땐 Succeeded, Failed 상태로 된다.
Job이 성공을 한 상태라도 Pod의 상태는 ContainerReady: False Ready: False 의 상태로 변해 있을 것이다. 다만 Container의 상태가 Terminiated: Completed로 Failed 일때와는 차이가 있다.
Job이 돌다가 실패를 하게 되며면 ContainerReady: False Ready: False로 있고 Container의 Terminiated 상태도 Error로 되어있다.
한 서비스에 2개의 Pod가 있다고 가정을 해보면 이 각각의 Pod들은 50%씩 트래픽을 나눠가질 것이다.
만약에 Pod2가 죽으면 Pod1로 트래픽이 100% 몰릴껀데, Pod1이 견뎌 준다면 AutoHealing을 통해 Pod2는 그 동안 새롭게 재생성 될것이다. 이 때 Pod와 Container는 Running 중이고 Service에도 붙을것이다. 하지만 Pod와 Container는 Running 중이지만 application이 구동이 되고 있을 때 Pod2로 트래픽이 유입이 되는 사용자는 오류를 경험 할것이다. 이 순간에 ReadinessProbe 옵션을 주게 되면 앱이 구동되기 전까지 Service와 연결이 되지 않고 application이 완전히 올라온 상태여야 Service와 연결을 시켜준다.
또 한가지 경우가 있는데 Pod2 와 Continer는 정상적으로 Running이 되고 있지만 application이 죽어 있는 경우가 있다. 가령 OOM (Out of Memory) 상태나 기타 여러 상황들이 있는데 이때 Pod2로 흐르는 트래픽들은 또 계속 오류를 뱉게 된다. 이때 application의 장애 상황을 탐지해주는게 LivenessProbe 인데 application에 문제가 생기면 Pod를 재생성 시키도록 도와준다.
Probe를 확인하는 방법은 3가지가 있다.
또다른 옵션 값들이 있는데 다음과 같다.
ReadinessProbe 에선 Exec 옵션을 통해 application이 구동 되었는지 확인 해보겠다.
apiVersion: v1
kind: Pod
metadata:
name: pod-readiness-exec1
labels:
app: readiness
spec:
containers:
- name: readiness
image: kubetm/app
ports:
- containerPort: 8080
readinessProbe:
exec:
command: ["cat", "/readiness/ready.txt"]
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 3
volumeMounts:
- name: host-path
mountPath: /readiness
volumes:
- name : host-path
hostPath:
path: /tmp/readiness
type: DirectoryOrCreate
terminationGracePeriodSeconds: 0
여기서 spec.containers.readinessProbe 에 exec을 옵션으로 줬고 실제 파일을 체크하여 application이 정상 구동되었는지 확인을 할수 있다.
LivenessProbe는 api 로 health check를 해보겠다.
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-httpget1
labels:
app: liveness
spec:
containers:
- name: liveness
image: kubetm/app
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 3
terminationGracePeriodSeconds: 0
여기서 spec.containers.livenessProbe 에 httpGet 옵션으로 줬고 api 를 호출하여 application이 정상적으로 돌아가고 있는지 확인할 수 있다.
Pod가 기본적으로 k8s 클러스터의 Shceduler를 통해 할당이 되지만 임의로 운영자나 개발자가 Node를 지정해서 스케쥴 할수도 있다.
다음과 같은 노드들이 있다고 가정해보자.
NodeName을 사용하여 Pod에 Node1 을 선택하면 NodeName으로 지정한 Node1 에 바로 Pod가 생성이 된다. 명시적이긴 하지만 Node가 삭제될 수도 있고 이름이 변경될 수도 있기에 이렇게 잘 사용은 하지 않는다.
이번엔 Pod에 Node의 key 와 value를 KR: az-2 라고 달아서 생성했다고 가정해보자. 그러면 Node3에 Pod가 생성이 될것이다. 여기서 재미있는건 Node1 과 Node2는 key value가 동일한데 이 때는 자원이 많은 Node로 Pod가 생성이 된다. 다만 조금 불편한점은 key value가 정확하지 않으면 Pod가 생성이 되지 않는다.
Pod에 Key만 설정해도 자원이 많은 Node에 할당이 된다 예를들어 US 라는 Key를 단 Pod를 생성하면 Node4에 생성이 될것이다. 그리고 Key가 아예 없는 CH를 생성하더라도 마찬가지로 Pod 가 가장 자원이 많이 남는 Node에 생성이 될것이다.
apiVersion: v1
kind: Pod
metadata:
name: pod-match-expressions1
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- {key: kr, operator: Exists}
containers:
- name: container
image: kubetm/app
terminationGracePeriodSeconds: 0
apiVersion: v1
kind: Pod
metadata:
name: pod-required
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- {key: ch, operator: Exists}
containers:
- name: container
image: kubetm/app
terminationGracePeriodSeconds: 0
apiVersion: v1
kind: Pod
metadata:
name: pod-preferred
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- {key: ch, operator: Exists}
containers:
- name: container
image: kubetm/app
terminationGracePeriodSeconds: 0
다음과 같은 노드들이 있다고 가정해보자.
예를 들어 Server1 과 Server2가 항상 같은 Node에 있어야 한다고 가정을 해보면 Pod Affinity를 사용해야 한다. type: Server 라는 라벨을 가진 Server1 Pod가 Node1 에 생성이 된다고 가정을 해보자. Pod Affinity 속성을 넣고 type: Server 라벨을 넣으면 Server2의 Pod는 항상 Server1의 Pod와 같은 Node에 생성 될것이다.
Server1은 다음과 같은 yaml 이 있다.
apiVersion: v1
kind: Pod
metadata:
name: server1
labels:
type: server
spec:
nodeSelector:
a-team: '1'
containers:
- name: container
image: kubetm/app
terminationGracePeriodSeconds: 0
여기에 Pod Affinity를 통해 Server1과 항상 같은 Node에 생성시켜야 하는 Pod는 다음과 같다.
apiVersion: v1
kind: Pod
metadata:
name: server2
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: a-team
labelSelector:
matchExpressions:
- {key: type, operator: In, values: [server]}
containers:
- name: container
image: kubetm/app
terminationGracePeriodSeconds: 0
spec.affinity.requiredDuringSchedulingIgnoredDuringExecution.labelSelector.matchExpressions 를 통해 Server1의 Pod와 항상 같은 Pod에 생성할 수 있다.
Pod Affinity 와 반대 되는 개념이다. Type: Master 라는 라벨을 가진 Master Pod가 있으면 Pod Anti Affinity 옵션에 라벨을 Type: Master 를 주고 Slave라는 Pod를 생성하면 항상 Master Pod가 있는 Node에는 Slave Pod는 생성이 안될것이다.
Master Pod를 다음과 같이 생성해보자.
apiVersion: v1
kind: Pod
metadata:
name: master
labels:
type: master
spec:
nodeSelector:
a-team: '1'
containers:
- name: container
image: kubetm/app
terminationGracePeriodSeconds: 0
Slave Pod는 Master Pod가 있는 Node에 생성되면 안되기에 다음과 같이 yaml 을 생성할 수 있다.
apiVersion: v1
kind: Pod
metadata:
name: slave
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: a-team
labelSelector:
matchExpressions:
- {key: type, operator: In, values: [master]}
containers:
- name: container
image: kubetm/app
terminationGracePeriodSeconds: 0
spec.affinity.podAntiAffinity 를 통해 master pod와 항상 다른 노드에 slave pod를 만들 수 있다.
특정 Node에는 아무 Pod나 할당할 수 없도록 만들어놓은 제약이다. 예를 들어 GPU 가 담긴 Node에는 ai 관련된 Pod만 올라와야 하는데 웹서버 Pod를 못올라오게 막으려고 할당을 제한할수 있다.
다음과 같은 노드들이 있다고 가정해보자.
Node1에 Traint 옵션을 걸어놓으면 Pod에 Node 이름을 Node1로 지정해서 생성하려해도 생성이 되지 않는다. Node5 에 Pod를 생성을 하려면 Toleration을 설정 해야한다.
Traint 내용은 label 과 effect 로 구성할 수 있는데, label 은 우리가 여태 사용했던 label과 동일하고 effect는
NoSchedule: 아예 Pod가 스케쥴이 안됨
PreferNoSchedule: 다른 Node에 전혀 리소스가 남지 않는다면 여기에 배치
이렇게 두가지 옵션이 있다.
kubectl taint nodes k8s-node1 hw=gpu:NoSchedule
Toleration 옵션은 Pod를 생성할 때 Node의 Traint 의 값과 항상 일치해야한다.
apiVersion: v1
kind: Pod
metadata:
name: pod-with-toleration
spec:
nodeSelector:
gpu: no1
tolerations:
- effect: NoSchedule
key: hw
operator: Equal
value: gpu
containers:
- name: container
image: kubetm/app
terminationGracePeriodSeconds: 0
metadata.spec.tolerations 에 값을 Traint에 설정한 node와 동일하게 해줘야 Pod가 생성이 된다 값이 하나라도 틀리면 생성이 되지 않는다.