이번 포스팅에서는 Kubernetes 의 워크로드 API에 대해서 살펴보도록 할 생각 입니다. 이 부분을 정리하는 이유는 실제로 k8s의 리소스 카테고리 5가지 종류 중 컨테이너의 실행과 관련된 제일 중요한 부분이기 때문에 정리해 두고자 하는 이유입니다.
우선 k8s의 리소스 카테고리는 다음과 같이 5가지 입니다.
- 워크로드 API : 컨테이너 실행을 관리
- 서비스 API : 컨테이너의 외부로부터의 접속을 위한 엔드포인트 관리
- 컨피그 & 스토리지 API : PV, Secret, Config 등에 관련된 정보 관리
- 클러스터 API : 보안이너 쿼터 등에 관한 정보 관리
- 메타데이터 API : 클러스터 내부의 다른 리소스를 관리
오늘 다루고자 하는 리소스는 클러스터에 컨테이너를 가동시키기 위해서 사용되는 워크로드 API 입니다. 이때 관리하게 되는 리소스는 다음과 같이 8가지 입니다.
- pod
- replication controller
- replica set
- deployment
- daemon set
- stateful set
- job
- cronjob
# 실제로 MLOps 관련된 사항으로는 Deployment와 Job 들을 많이 사용하게 될 것입니다. Job 과 같은 경우는 Kubeflow를 사용한다면 PytorchJob, TFJob 등을 사용하게 됩니다.
위의 리소스들에서 기본이 되는 것은 pod 입니다. 위의 리소스들의 가장 기초적인 작업을 담당하는 것은 pod 이며 나머지들은 pod 보다 상위의 리소스로 pod를 관리하는 리소스입니다.
이 중 상위 관계를 가지는 리소스들은 2가지 있습니다.
- replica set < deployment
- job < cronjob
위의 두가지 deployment와 cronjob은 각각 replica set과 job 이라는 리소스를 관리하는 상위 리소스로 pod < replicaset < deployment 그리고 pod < job < cronjob 의 형태로 관리가 되어집니다.
그러면 하나하나 리소스에 대해서 살펴보도록 하겠습니다.
Pod
파드는 워크로드 리소스에서 관리하는 리소스 중 가장 기초가 되는 리소스입니다. k8s 에 익숙하신 분들은 아시겠지만 하나의 pod는 1개의 container 갖는 것이 아닙니다. 이때 하나의 pod 내의 컨테이너들은 하나의 ip를 가지며 pod 내의 컨테이너 간에는 local host로 접근이 가능합니다.
pod 내의 컨테이너 구조 패턴은 다음과 같이 3가지로 정리하고 있습니다.
- side-car pattern
- ambassador pattern
- adapter pattern
side-car pattern은 pod 내의 main container가 있고 이를 옆에서 보조해주는 방식의 container를 붙이는 방식입니다. 대표적으로 구글에서 찾게되면 git-sync라는 방법에 사용되는 것으로 검색이 되는 것을 발견하실 수 있습니다. 주로 application이 작동하고 있을 때 개발자가 코드를 수정하거나 하는 경우 변경된 코드를 받아와서 반영이 되도록 하는 식의 형태를 위해서 사용됩니다. 다른 예시로는 log를 생성하는 main container가 있을 때 로그를 주기적으로 수집해서 외부의 저장소로 보내는 방식등이 있습니다.
ambassador pattern은 외부의 DB와 연결되는 application을 사용하는 경우에 사용되는 매우 흔한 패턴으로 중요합니다. main container를 외부 시스템과 접속할 수 있도록 proxy server 역할을 하는 container를 붙이는 경우로 생각하시면 될 것 같습니다. 이때의 포인트는 main container는 proxy server container를 localhost로 지정해서 query를 날리고, localhost에서 query에 따라 분산된 DB 중 적절한 DB를 동적으로 맵핑하는 등의 방법을 통해서 main container와 DB의 느슨한 결합을 가능하게 해주는 점이 중요 포인트입니다.
adapter pattern은 외부에서 원하는 데이터 형식과 pod 내에서 main container가 생성하는 로그같은 데이터의 형식이 다를 때 이 데이터들의 표현을 변환해주는 것에 사용되는 방법입니다.
Replicaset / Replication Controller
레플리카셋에서의 레플리카는 Pod 의 복사본을 의미합니다. 주로 같은 yaml 파일에서 생성된 pod를 레플리카라고 부릅니다. 주로 부하 분산을 위한 경우에 같은 기능을 하는 pod를 여러개 생성해서 load balancer가 적절하게 부하를 관리할 수 있도록 하는 형태가 k8s의 사용에서 일어납니다.
레플리카셋은 k8s에서 사전에 정의 된 레플리카 수를 유지하도록 self healing 기능을 보유하고 있다는 것이 큰 특징입니다. 하나의 pod가 오류 등으로 죽어버리는 경우 자동화된 복구 기능을 기반으로 레플리카의 수가 유지될 수 있도록 파드를 새로 생성합니다.
이때 k8s에서 pod의 레플리카 수를 판단하는 방법에서는 label 정보가 사용되기 때문에 다음과 같이 yaml 파일을 구성하여야 합니다.
apiVersion: apps/v1
kind: Replicaset
metadata:
name: sample-rs
spec:
replicas: 3
selector:
matchLabels:
app: sample-rs
template:
metadata:
labels:
app: sample-rs
spec:
containers:
- name: test
image: nginx:1.16
위에서 중요한 부분은 "kind"에서 Replicaset을 설정해주는 것과 "spec.replicas"의 숫자를 정의해주는 것이 우선 가장 중요하며, "spec.template.metadata.labels"의 정보인 "app: sample-rs"와 "spec.selector.matchLabels"의 정보를 일치시켜주는 것입니다. 이 두 부분이 틀리면 오류가 발생하며, 이 정보를 토대로 위에서 말한 것과 같이 레플리카의 수를 지속적으로 감시하며 갯수를 유지하게 됩니다.
Deployment
Deployment는 여러개의 Replicaset을 관리하며 동작하고 있는 Replicaset에 대한 rolling update 나 roll back 등의 기능이 service가 끊이지 않는 상황에서 가능하도록 해주는 리소스 입니다. 실제로 Replicaset이나 Pod는 모두 Deployment를 통해서 실행하는 것이 k8s에서는 권장되고 있으며 매번 코드의 수정등이 발생 했을 때 pod를 제거하고 시작하는 것이 아니라 작동중에 업데이트를 할 수 있다는 큰 장점이 있는 중요한 리소스 입니다.
Deployment를 활용하는 경우에 rolling update를 적용시키는 방법은 기존에 deployment 리소스를 만들어낸 yaml 파일을 수정하고 kubectl apply -f <yaml파일>을 하는 방법이 있습니다. 이 경우에 deployment가 rollout을 잘 하고 있는지 판단하려면 아래의 명령어를 입력하면 됩니다.
kubectl rollout status deployment <deployment의 metadata.name>
롤백의 경우는 조금 더 복잡합니다. kubectl rollout history를 통해서 Revision 번호를 확인할 수 있으며 해당 version이 어떤 명령어를 통해서 생성된 것인지를 확인할 수 있습니다.
kubectl rollout history deployment <metadata.name>
# Revision Change-Cause
# 1 kubctl apply --filename=..... ...
# 2 kubectl 변경후 apply .....
이후 rollout undo 명령어를 통해서 지정하는 버전의 revision으로 되돌릴 수 있습니다.
kubectl rollout undo deployment <metadata.name> --to-revision 1
Daemon set
데몬셋은 replicaset의 특이한 형태로써 replicaset은 어느 노드에 여러개가 존재할 수도 있고 없을 수도 있고 전체 k8s 클러스터 내부에서 갯수가 유지가 되면 되지만 daemon set은 하나의 노드에 1개의 replica만 존재해야 하는 경우에 사용합니다. 아예 특정 노드에 선택하고 싶지 않은 경우에는 nodeSelector 혹은 node-anti-affinity를 상용해서 pod가 생성되지 않아야 하는 node를 지정해 주는 것으로 가능하지만 이 경우를 제외한 노드에는 모두 1개의 레플리카셋이 올라가야 합니다.
Job
잡은 컨테이너를 사용해서 한 번만 실행되는 리소스를 의미합니다. 사전에 manifest 파일 (yaml 파일)에서 정의한 내용의 컨테이너가 특정 작동을 실행 후 정지되는 것이 전제일 때 사용하는 방법으로 MLOps에서는 모델의 학습을 위해 생성하는 pod는 Job을 통해서 생성하는 것이 맞습니다.
동시에 몇개의 컨테이너를 실행해서 몇 개의 completion이 보장되어야 종료가 되는지에 대한 설정등이 가능합니다.
apiVersion: batch/v1
kind: Job
metadata:
name: sample-job
spec:
completions: 10
parallelism: 2
backoffLimit: 10
template:
spec:
containers:
......
위와 같은 경우 completion 이 10이므로 Job의 pods가 10개가 완료될떄까지 실행하는 것을 의미하며, 동시에 최대 2개의 pod만 시도한다는 것을 의미합니다. backofflimit은 pod가 실패하는 경우 최대 10번까지만 시도를 한다는 것을 의미합니다.
여기까지가 워크로드 API 카테고리의 리소스들에 대한 정리입니다. 조금 더 구체적인 사항은 추후에 실제 k8s의 사용에 대해서 다루면서 살펴볼 수 있는 기회가 있을 것 같습니다.
References
[1] http://www.yes24.com/Product/Goods/102847901