1. Pod 특징
쿠버네티스에 존재하는 애플리케이션 실행 단위
- 모든 컨테이너는 항상 하나의 워커 노드에서 실행되며, 여러 워커 노드에 걸쳐 실행되지는 않는다.
- 컨테이너는 단일 프로세스를 실행하는 것을 목적으로 설계되었으며, 여러 프로세스를 단일 컨테이너로 묶지 않았기 때문에 컨테이너들을 함께 묶고 하나의 단위로 관리할 수 있는 또 다른 상위 구조가 필요하다. 이게 파드가 필요한 이유다.
- 파드의 모든 컨테이너는 동일한 IPC 네트워크 네임스페이스로 통신하며, 파일 시스템은 다른 컨테이너와 완전히 분리된다. 물론 '볼륨' 개념을 활용하면 각각의 컨테이너, 파드, 워커 노드 등 디스크 공간을 공유할 수 있다
각 파드는 고유한 IP를 가지며 통신한다.
- 파드 안에 존재하는 컨테이너들을 동일한 네트워크 네임스페이스에서 실행되기 때문에, 동일한 IP, Port를 공유한다.() 따라서 각각의 컨테이너들이 같은 포트 번호를 사용하지 않도록 해야한다.
- 한 호스트에 모든 유형의 앱을 넣었던 이전과 달리, 애플리케이션을 여러 파드로 구성하고 각 파드에는 밀접하게 관련있는 구성요소나 프로세스만 포함한다.
- 애플리케이션마다 사용할 리소스(메모리,cpu 등)가 다양하므로 이를 분리하면, 스케줄링 관점에서 인프라스트록처의 활용도를 향상시킬 수 있다.
- 쿠버네티스는 개별 컨테이너를 수평 확장할 수 없으며, 파드단위로 수평 확장한다. 따라서, 컨테이너너를 개별적으로 스케일링하고 싶다면, 별도 파드에 배포해야한다.
- 파드가 삭제되면 해당 로그도 같이 삭제돈다. 파드가 삭제된 후에도 파드의 로그를 보기 위해서는 모든 로그를 중앙 저장소에 저장하는 클러스터 전체의 중앙집중식 로깅을 설정해야한다.
2. Label
어플리케이션을 잘게 쪼개면서 수 많은 앱들이 존재하고, 이들을 그룹화하여 관리할 필요성이 생겼다. label, namespace, annotation 등을 활용하여 각각의 리소스를 묶어준다.
- MSA의 경우 수백 개 파드가 생길 수 있다. 어떤 파드가 어떤 것인지 쉽게 알 수 있도록 임의의 기준에 따라 작은 그룹으로 조직하는 방법이 필요하다. 예를 들어, 각 파드에 대한 작업을 개별적으로 하기보단, 특정 그룹에 속한 모든 파드에 관해 한 번에 작업하길 원할것이다. 이를 레이블을 통해 파드와 기타 쿠버네티스 오브젝트의 조직화가 이뤄지게한다.
- 레이블은 파드와 모든 다른 쿠버네티스 리소스를 조직활할 수 있는 단순하면서 강력한 쿠버네티스 기능이다. 레이블은 리소스에 첨부하는 키-값 쌍으로, 이 쌍은 레이블 셀렉터를 사용해 리소스를 선택할 때 활용된다.
[1] Label Selector
- 레이블 셀렉터는 특정 레이블로 태그된 파드의 부분 집합을 선택해 원하는 작업을 수행한다. "특정한 키를 포함하거나 포함하지 않는 레이블", "특정한 키와 값을 가진 레이블", "특정한 키를 갖고 있지만, 다른 값을 가진 레이블"
kubectl get po -l key1=value1, kubectl get po -l '!key1'
kubectl get po -l env in (prod, devel)
kubectl get po -l env notin (prod, devel)
kubectl get po -l app=pc 명령어
[2] Namespace
- label은 파드와 다른 오브젝트를 그룹으로 묶는데 사용했다. 오브젝트는 여러개의 label을 가질 수 있기 때문에 이 그룹은 서로 '겹칠' 수 있다.
- 오브젝트를 겹치지 않고, 그룹으로 분할하고자 할 때 namespace를 사용한다. 즉, namespace 를 사용해 리소스를 겹치지 않는 그룹으로 분리할 수 있다.
- 여러 사용자 또는 그룹으로 분리하면 다른 사용자의 pod를 실수로 삭제하는 것을 방지하며, 리소스 이름에 관한 접근 범위를 제공하기 때문에 리소스 이름이 충돌하는 것을 걱정할 필요가 없다.
3. Pod를 안정적으로 관리하는 방법
- 컨테이너 주 프로세스에 크래시가 발생하면 kublet은 컨테이너를 재시작한다.
- 쿠버네티스는 probe를 통해 컨테이너가 살아있는지 3가지 매커니즘으로 확인할 수 있다. (1) 지정한 ip, port로 HTTP GET을 보내어 2xx, 3xx인지 확인한다. 이때, 응답 오류 코드이면 실패로 간주한다. (2) TCP 소켓 probe는 컨테이너의 지정된 포트에 TCP 연결을 시도한다. 연결되면 성공이다. (3) Exec는 명령의 종료 상태 코드가 0인지 확인한다.
- 아래는 probe 설정 yaml 파일 예시로, probe가 요청할 url/port를 명시하고, 초기 몇초간 probe가 확인을 안하도록 할 수 있다. 이런 설정을 해두지 않으면, 앱이 실행되자마자 restart가 1인 경우가 발생할 수 있다.
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 15
- 서버가 단순히 응답하냐 아니냐(200, 4xx, 5xx) 뿐만 아니라, 여기에 health check와 같이 내부 구성요소들이 살아 있는지 확인하는 API를 연동하면 더욱 효율적으로 컨테이너가 정상인지 확인할 수 있다. 특정 ACL이 뚤려있는지, 혹은 외부 커넥션을 맺을 수 있는지(보안체크) 등을 검사하는 예시가 있다.
- 결국 쿠버네티스는 probe를 통해 컨테이너를 재시작한다. 이 작업을 파드를 호스팅하는 노드의 kublet에서 수행한다. 그러나 노드 자체에 크래시가 발생한 경우 파드를 재시작해야 하는 것은 컨트롤러의 몫이다.
4. Replication Controller
- RC는 쿠버네티스의 리소스로 항상 파드가 실행되도록 보장한다. 노드가 사라지거나 노드에서 파드가 제거된 경우 RS는 사라진 파드를 감지하고 스케줄링에 의해 괜찮은 노드로 교체하여 파드를 생성한다.
- 실행중인 파드 목록을 지속적으로 모니터링하고, 특정 타입의 실제 파드 수가 의도하는(명시한) 수와 일치하는지 항상 확인한다. 만약 더 많이 실행된다면 초과본을 제거한다. 문제가 있다면 의도한 수의 파드로 맞추기 위해 파드를 재생성한다.
- (1) Label Selector : 컨트롤러의 범위 안에 있는 파드를 결정한다. / (2) Replica Count : 실행할 파드의 수 / (3) Pod Template : 새로운 파드 Replica를 만들 때 사용한다. Replication Controller는 이렇게 3가지로 구성한다.
- Label Selector와 Pod Template를 변경해도 기본 파드에 영향을 미치지 않는다. Label Selector를 변경하면 기존 파드가 RC의 범위를 벗어나므로, 컨트롤러가 해당 파드에 대한 관리를 중지한다.
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
- RS가 파드를 관리하는 대상은 오로지 Label Selector에 달려있다. 이 Label Selector와 일치하는 파드만을 관리한다.
- 만약 파드의 라벨을 변경해 RS와 일치하지 않는다면 해당 파드는 수동으로 만든 파드와 같다. 파드에 문제가 생기면 수동으로 대응해야한다.
5. Replica Set
- Replica Set은 차세대 RC로 동일한 기능의 추가로, Pod Selector의 좀 더 풍부한 표현식을 갖고있다. 즉, 여러 라벨을 하나의 그룹으로 매칭시킬 수 있다. 예를들어, 특정 라벨이 없는 파드, 라벨과 상관없이 특정 키를 갖는 파드 등이있다.
apiVersion: apps/v1beta2
kind: ReplicaSet
metadata:
name: kubia
spec:
replicas: 3
selector:
matchExpressions:
- key: app
operator: In
values:
- kubia
template:
metadata:
labels:
app: kubia
spec:
containers:
- name: kubia
image: luksa/kubia
6. DemonSet
- RC, RS 모두 cluster 내 어딘가에서 pod 가 실행되길 원하지만, cluster 의 모든 node 에 1개의 pod 가 각각 실행되길 원할 때 daemon set 을 사용할 수 있다. 로그 수집기, 모니터링이 이에 해당하며, kube-proxy 가 해당한다.
- target node 가 지정돼 있고, scheduler 를 건너뛰는 것을 제외하면 RC, RS 와 유사하다
- 복제본 수라는 개념이 없다 대신 node 수에 의존한다. 새 node 가 cluster 에 추가되면 daemon set은 즉시 새 pod instance 를 새 node 에 배포한다.
7. 완료 가능한 단일 task를 수행하는 pod실행
- 지금까지 계속 실행하는 파드에 관해서만 다뤘는데, 하지만 작업이 완료된 후 종료되야 하는 경우도 있다.
- 이를 위해 쿠버네티스는 job이라는 리소스를 제공하며, 실행중인 프로세스가 성공적으로 완료되면 컨테이너를 다시 시작하지 않는다. 즉, 파드가 완료된 것으로 간주된다.
- 노드에 장애가 발생한다면 RC, RS와 같이 다른 노드로 스케줄링된다.
- 프로세스 자체에 장애가 발생한 경우 job에서 컨테이너를 다시 시작할 것인지 설정할 수 있다.
- job은 작업이 제대로 완료된지 중요한 임시 작업에 유용하다.
- job이 끝나면 일반적인 get 명령어로 조회가 안되어 --show--all 혹은 -a로 확인해야된다.
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
- linux, unix OS 에서 cron 작업과 같은 것도 k8s 가 제공한다 이것을 cronJob 이라 한다.
- job 이나 pod 가 상대적으로 늦게 생성되고 실행될 수 있다. 예정된 시간을 너무 초과해 시작돼서는 안된다는 엄격한 요구 사항을 갖는 경우도 있다. startingDeadlineSeconds 필드를 이용해 데드라인을 설정할 수 있다.
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: batch-job-every-fifteen-minutes
spec:
schedule: "0,15,30,45 * * * *"
startingDeadlineSecond: 15
jobTemplate:
spec:
template:
metadata:
labels:
app: periodic-batch-job
spec:
restartPolicy: OnFailure
containers:
- name: main
image: luksa/batch-job
8. 클라이언트가 파드를 검색하고 통신하는 방법
- 쿠버네티스는 노드에 파드를 스케줄링한 후 파드가 시작되기 바로 직전에 파드의 ip 주소를 할당하여, 클라이언트에서 특정 파드의 ip 주소를 미리 알 수 없다. 쿠버네티스의 파드는 일시적이고 ip가 언제든지 변경될 수 있다.
- 쿠버네티스의 서비스는 동일한 서비스를 제공하는 파드 그룹에 지속적인 단일 접점을 만들어주는 리소스다. 뒷단은 로드 밸런싱으로 처리되며, 여러 파드는 label, namespace, annotation 기능을 통해 그룹화한다.
- 서비스를 만들고, 클러스터 외부에서 엑세스 할 수 있도록 구성하면, 외부 클라이언트가 파드에 연결할 수 있는 하나의 고정 ip가 노출된다. 이때 서비스가 관리하는 파드들의 ip가 변경되더라도, 서비스의 ip 주소는 변경되지 않는다. 이에 따라 내부 클라이언트와 외부 클라이언트 모두 서비스 기능을 통해 파드에(자주변경되는ip) 접근할 수 있다.
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: kubia
sessionAffinity: ClientIP
9. Ingress Resource
- 인그레스는 한 IP 주소로 수십 개의 서비스에 접근이 가능하도록 지원한다.
- 애플리케이션 계층(HTTP)에서 작동하며, 서비스가 할 수 없는 쿠키 기반 세션 어피니티 등과 같은 기능 제공이 가능하다.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: kubia
spec:
rules:
- host: kubia.example.com
http:
paths:
- path: /
backend:
serviceName: kubia-nodeport
servicePort: 80
10. Readiness Probe
파드가 연결을 수락할 준비가 됐을 때 신호 보내기
- 레디니스 프로브는 요청을 처리할 준비가 된 파드의 컨테이너만 요청을 수신하도록 한다.
- 컨테이너가 시작될 때 쿠버네티스는 첫 번째 레디니스 점검을 수행하기 전에 구성 가능한 시간이 경과하기를 기다릴 수 있도록 구성할 수 있다.
- 컨테이너가 준비 상태 점검에 실패하더라도 컨테이너가 종료되거나 다시 시작시키지 않는다.
- 예를 들어, 초기에 파드 그룹이 다른 파드에서 제공하는 서비스에 의존한다고 했을때(웹 앱 -> 데이터베이스) 웹 앱 파드중 하나만 DB에 연결할 수 없는 경우, 재시작이 아니라 요청을 처리할 준비가 되지 않았다고 신호를 주는게 현명할 수 있다. (재시작이 아닌 대기가 맞을 수 있다)
spec:
containers:
- name: kubia
image: sungsu9022/kubia
ports:
- name: http
containerPort: 8080
readinessProbe:
exec:
command:
- ls
- /var/ready