Custom Pod Scheduling
GPU, 데이터 로컬리티, 멀티 테넌시 등의 특별한 요구사항이 있다면 Scheduler Extender를 먼저 고려하고, 근본적인 변경이 필요하면 Custom Scheduler를 구축하는 것이 좋다
맞춤형 스케줄링(Custom Scheduling)이 필요한 유즈케이스
Kubernetes 기본 스케줄러는 일반적인 워크로드 배치를 최적화하지만, 특정한 요구사항을 가진 애플리케이션에는 맞춤형 스케줄링이 필요할 수 있습니다.
맞춤형 스케줄링이 필요한 유즈케이스
1. GPU 워크로드 최적화
• 특정 GPU 모델(A100, V100 등)을 요구하는 경우
• 사용 가능한 GPU 메모리를 기반으로 Pod을 배치해야 하는 경우
2. 데이터 로컬리티 최적화
• Pod이 특정 데이터 노드와 가까운 곳에서 실행되어야 성능이 최적화됨 (예: HDFS, Ceph, Cassandra 등)
3. 특정 하드웨어 요구사항
• FPGA, NVMe, ARM 기반 노드에서만 실행되어야 하는 경우
4. 멀티 테넌시 환경
• 팀/부서별로 특정 노드 그룹을 사용해야 하는 경우
• 특정 테넌트의 Pod이 특정 노드에만 배치되도록 해야 하는 경우
5. Pod 간 특정 배치 전략 필요
• 한 노드에는 하나의 Pod만 실행하도록 제한해야 하는 경우
• 특정 Pod들은 같은 노드에서 실행되면 안 되는 경우 (예: Active-Active HA 구성을 위해 분산 배치)
6. Custom Metric 기반 스케줄링
• CPU, Memory가 아닌 네트워크 트래픽, I/O 대역폭 등의 지표를 기반으로 최적의 노드를 선택해야 하는 경우
맞춤형 스케줄링 구현 방법
맞춤형 스케줄링을 구현하는 방법은 크게 두 가지가 있습니다.
1. Scheduler Extender: 기본 스케줄러의 기능을 확장하여 필터링 및 점수 평가 단계를 커스텀 API로 보완
2. Custom Scheduler: 기본 스케줄러를 사용하지 않고, 완전히 새로운 스케줄러를 개발하여 Kubernetes에서 사용
1️⃣ Scheduler Extender (기본 스케줄러 확장)
Kubernetes 기본 스케줄러를 유지하면서, 추가적인 로직을 적용하는 방식입니다.
장점: 기존 스케줄러와 호환 가능, 일부 로직만 확장 가능
단점: 스케줄러 전반적인 로직을 바꾸는 것은 불가능
(1) Scheduler Extender 아키텍처
• 기본 스케줄러는 Filter 및 Score 과정에서 외부 API를 호출하여 추가적인 검증을 수행할 수 있음
• kube-scheduler의 --policy-config-file 옵션을 사용하여 extender를 지정할 수 있음
(2) Scheduler Extender API 예제
Extender는 HTTP API 서버를 제공해야 하며, 아래와 같은 엔드포인트를 구현해야 합니다.
{
"predicates": [
{
"name": "CustomGPUFilter",
"httpGet": {
"path": "/filter",
"port": 8000
}
}
],
"priorities": [
{
"name": "CustomGPUPriority",
"weight": 10,
"httpGet": {
"path": "/prioritize",
"port": 8000
}
}
]
}
(3) Custom Scheduler Extender 구현 (Python)
간단한 GPU 필터링 및 우선순위 적용을 수행하는 예제:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/filter", methods=["POST"])
def filter_nodes():
data = request.get_json()
filtered_nodes = []
for node in data["nodes"]["items"]:
# Custom logic: Only allow nodes with label "gpu: enabled"
if "gpu" in node["metadata"]["labels"] and node["metadata"]["labels"]["gpu"] == "enabled":
filtered_nodes.append(node)
return jsonify({"nodes": {"items": filtered_nodes}})
@app.route("/prioritize", methods=["POST"])
def prioritize_nodes():
data = request.get_json()
scores = []
for node in data["nodes"]["items"]:
score = 10 # 기본 점수
if node["metadata"]["labels"].get("gpu") == "enabled":
score = 100 # GPU가 있는 노드는 높은 점수
scores.append({"node": node["metadata"]["name"], "score": score})
return jsonify({"scores": scores})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)
(4) Scheduler Extender 배포
• 위 Python API 서버를 Kubernetes 클러스터에 배포 (Deployment & Service)
• kube-scheduler 설정을 변경하여 --config 옵션으로 Scheduler Extender를 추가
2️⃣ Custom Scheduler (완전한 맞춤형 스케줄러)
기본 스케줄러를 사용하지 않고, 독립적인 스케줄러를 만들어 Kubernetes API와 직접 통신하여 스케줄링을 수행하는 방식입니다.
장점: 완전한 커스텀 로직 적용 가능
단점: 유지보수가 어려울 수 있음
(1) Custom Scheduler 아키텍처
• watch를 사용하여 Pending 상태의 Pod을 감지
• 적절한 Node를 선택하여 Kubernetes API에 Binding 요청을 보냄
(2) Custom Scheduler 구현 (Python)
from kubernetes import client, config, watch
# Kubernetes 클라이언트 초기화
config.load_kube_config()
v1 = client.CoreV1Api()
scheduler_name = "custom-scheduler"
def find_best_node():
nodes = v1.list_node().items
for node in nodes:
if "gpu" in node.metadata.labels and node.metadata.labels["gpu"] == "enabled":
return node.metadata.name
return nodes[0].metadata.name # 기본적으로 첫 번째 노드 선택
def schedule_pod(pod):
best_node = find_best_node()
binding = client.V1Binding(
metadata=client.V1ObjectMeta(name=pod.metadata.name),
target=client.V1ObjectReference(
kind="Node",
api_version="v1",
name=best_node
)
)
v1.create_namespaced_binding(namespace=pod.metadata.namespace, body=binding)
print(f"Pod {pod.metadata.name} assigned to {best_node}")
def watch_pods():
w = watch.Watch()
for event in w.stream(v1.list_pod_for_all_namespaces):
pod = event["object"]
if pod.status.phase == "Pending" and pod.spec.scheduler_name == scheduler_name:
schedule_pod(pod)
if __name__ == "__main__":
watch_pods()
(3) Custom Scheduler 배포
• 위 Python 스크립트를 컨테이너화하여 Kubernetes에 배포
• Pod 생성 시 schedulerName: custom-scheduler을 지정하여 사용
apiVersion: v1
kind: Pod
metadata:
name: custom-scheduler-pod
spec:
schedulerName: custom-scheduler
containers:
- name: app
image: nginx
결론
• Scheduler Extender는 기본 스케줄러의 Filter와 Score 단계만 확장하는 방식이며, 기존 Kubernetes 스케줄러와 함께 사용할 수 있어 유지보수가 쉽다.
• Custom Scheduler는 Kubernetes의 Binding API를 직접 사용하여 스케줄링을 수행하는 방식이며, 완전히 새로운 스케줄링 로직을 적용할 수 있다.
어떤 방식이 좋을까?
• 기존 Kubernetes 스케줄링을 유지하면서 일부 로직만 추가해야 한다면 Scheduler Extender
• 완전히 새로운 방식으로 스케줄링해야 한다면 Custom Scheduler