1. 스케줄링이란 무엇이고, 스케줄러는 백엔드에서 어떻게 움직일까?
쿠버네티스에서 Pod가 어느 노드에 올라갈지를 결정하는 주체는 기본적으로 kube-scheduler입니다.
스케줄러는 클러스터의 Pod들을 계속 감시하면서, 아직 노드가 정해지지 않은 Pod들을 찾아 “스케줄링 후보”로 삼습니다.
Pod 스펙에는 기본적으로 비어 있는 필드가 하나 있습니다.
spec.nodeName: “이 Pod가 올라갈 노드”
일반적으로 우리는 이 값을 직접 쓰지 않습니다.
스케줄러가 적절한 노드를 선택한 뒤, Binding(바인딩) 과정을 통해 spec.nodeName이 채워지면서 “이 노드에 배치 확정”이 일어납니다.
정리하면 내부 흐름은 대략 이렇게 이해하면 됩니다.
spec.nodeName이 비어 있는 Pod를 찾는다- 후보 노드들을 대상으로 필터링/점수화 알고리즘을 수행한다
- 최종 노드를 결정한다
- 바인딩을 통해
spec.nodeName을 채우고 스케줄 확정
2. 스케줄러가 없다면? 수동 스케줄링 2가지
클러스터에 스케줄러가 없거나(혹은 의도적으로 우회하고 싶거나), 특정 Pod를 직접 노드에 붙이고 싶다면 대표적으로 2가지 방법이 있습니다.
2.1 가장 쉬운 방법: Pod 생성 시 spec.nodeName 지정
Pod 매니페스트에 spec.nodeName을 지정하면 스케줄러를 거치지 않고 해당 노드로 바로 배치됩니다.
apiVersion: v1
kind: Pod
metadata:
name: manual-pod
spec:
nodeName: worker-1
containers:
- name: nginx
image: nginx
주의점:
nodeName은 생성 시점에만 의미 있게 사용됩니다.- 이미 생성된 Pod에 대해
nodeName을 바꿔서 재배치하는 것은 기본적으로 불가능합니다(immutable 성격).
2.2 기존 Pod를 “스케줄러가 한 것처럼” 바인딩하기: Binding API
이미 만들어진 Pod에 대해 “스케줄러가 하는 바인딩 작업을 흉내” 내고 싶다면 Binding 오브젝트를 만들어 Pod binding API로 보내는 방식이 있습니다.
(실무에서 자주 쓰기보다는 “스케줄러가 실제로 무슨 일을 하는지” 이해용으로 중요합니다.)
핵심은:
- Binding 오브젝트에 target node를 지정
- 해당 내용을 JSON으로 API에 POST
- 결과적으로 Pod의
spec.nodeName이 채워지는 효과
3. Labels & Selectors: 오브젝트를 “그룹화”하고 “연결”하는 표준
쿠버네티스는 시간이 지나면 오브젝트가 수백/수천 개가 됩니다. 이때 가장 중요한 도구가 라벨과 셀렉터입니다.
3.1 라벨은 “속성”, 셀렉터는 “조건”
- Label: 오브젝트에 붙이는 key/value 메타데이터
- Selector: 라벨 조건으로 원하는 오브젝트만 선택
예:
metadata:
labels:
app: app1
tier: backend
조회:
kubectl get pods -l app=app1
kubectl get pods --selector app=app1
-l 과 --selector는 같은 기능으로 이해하면 됩니다.
3.2 라벨/셀렉터는 “연결”에도 쓰인다
쿠버네티스 내부에서는 라벨/셀렉터로 오브젝트를 연결합니다.
- ReplicaSet/Deployment가 “내가 관리할 Pod”를 찾을 때
- Service가 “트래픽을 보낼 Pod”를 찾을 때
초보자가 흔히 헷갈리는 포인트:
metadata.labels(ReplicaSet/Deployment 자신 라벨)spec.template.metadata.labels(생성될 Pod 라벨)
“셀렉터가 매칭해야 하는 대상”은 대부분 Pod 템플릿 라벨입니다.
4. Taints & Tolerations: “이 노드는 아무나 못 올라와”
이 파트는 반드시 이렇게 기억하면 됩니다.
- Taint는 Node에 설정
- Toleration은 Pod에 설정
- 역할: “이 노드는 특정 조건을 견디는 Pod만 받아들인다”
4.1 효과(Effect) 3가지
NoSchedule: toleration 없으면 스케줄링 불가PreferNoSchedule: 가능한 피하려 하지만 보장 XNoExecute: 새 Pod 스케줄링도 막고, 기존 Pod도 toleration 없으면 축출(evict)
4.2 사용 예시
Node에 taint:
kubectl taint nodes worker-1 app=blue:NoSchedule
Pod에 toleration:
spec:
tolerations:
- key: "app"
operator: "Equal"
value: "blue"
effect: "NoSchedule"
중요한 결론:
- Taint/Toleration은 “특정 Pod를 특정 노드로 보내는 기능”이 아닙니다.
- “이 노드가 받아줄 Pod의 자격을 제한”하는 기능입니다.
즉, toleration이 있어도 다른 노드로 갈 수 있습니다(다른 조건이 없다면).
5. NodeSelector & NodeAffinity: “이 Pod는 여기로만 가야 한다”
5.1 NodeSelector: 단순하고 빠른 제약
노드에 라벨을 먼저 붙이고:
kubectl label node worker-1 size=large
Pod에서 선택:
spec:
nodeSelector:
size: large
단점: 표현력이 낮습니다. (large 또는 medium, not small 같은 조건이 어려움)
5.2 NodeAffinity: 고급 표현식으로 제약
NodeAffinity는 더 복잡한 조건을 제공합니다.
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: size
operator: In
values:
- large
- medium
requiredDuringSchedulingIgnoredDuringExecution- 스케줄링 시점에는 반드시 만족해야 함(불만족이면 Pending)
- 실행 중에는 노드 라벨이 바뀌어도 보통 즉시 강제 이동/퇴출하지는 않음(ignored during execution)
6. Resource 기반 스케줄링: Requests / Limits의 진짜 의미
스케줄러는 Pod가 필요한 리소스를 보고 “올릴 수 있는 노드”를 필터링합니다.
여기서 핵심은 requests 입니다.
6.1 Requests: 스케줄링 기준 + 최소 보장
resources:
requests:
cpu: "500m"
memory: "256Mi"
- 스케줄러는 requests를 기준으로 노드를 고릅니다.
- requests는 “최소로 보장받는 양”으로 이해하면 좋습니다.
CPU 단위:
1= 1 vCPU100m= 0.1 vCPU
메모리 단위:
Mi,Gi(2진 기반)M,G(10진 기반) — 혼동 주의
6.2 Limits: 런타임 상한
resources:
limits:
cpu: "1"
memory: "512Mi"
- CPU는 limit을 넘으면 throttling(스로틀링) 이 걸립니다.
- 메모리는 limit을 넘으면 OOMKill 로 Pod가 죽을 수 있습니다.
6.3 “기본값이 없으면?”: LimitRange / ResourceQuota
- LimitRange: requests/limits 기본값과 min/max 강제(네임스페이스 단위)
- ResourceQuota: 네임스페이스 전체 총량 제한
7. Pod 수정(Edit): “대부분 immutable”이 기본 원칙
실무/시험에서 자주 부딪히는 규칙:
실행 중인 Pod는 대부분의 spec 변경이 불가능하다.
일반적으로 Pod에서 제한적으로만 가능한 대표 항목:
spec.containers[*].imagespec.initContainers[*].imagespec.activeDeadlineSecondsspec.tolerations
7.1 Pod를 바꿔야 한다면 2가지 패턴
패턴 A) kubectl edit → 임시파일 저장 → 삭제 후 재생성
kubectl edit pod webapp
# 저장 시 거부될 수 있음(immutable 필드 변경)
kubectl delete pod webapp
kubectl create -f /tmp/kubectl-edit-xxxx.yaml
패턴 B) YAML로 export → 수정 → 삭제 후 create
kubectl get pod webapp -o yaml > my-new-pod.yaml
vi my-new-pod.yaml
kubectl delete pod webapp
kubectl create -f my-new-pod.yaml
7.2 Deployment는 다르다: 템플릿 수정하면 자동 롤링
Deployment는 Pod 템플릿을 수정하면 쿠버네티스가 자동으로 새 Pod를 만들며 교체합니다.
kubectl edit deployment my-deployment
8. DaemonSet: “모든 노드에 1개씩 항상 떠 있어야 하는 Pod”
DaemonSet은 ReplicaSet처럼 Pod 복제본을 관리하지만, 목적이 다릅니다.
- 각 노드에 1개씩 Pod를 유지
- 노드가 추가되면 자동 배치
- 노드가 제거되면 해당 Pod도 자동 제거
대표 사용처:
- 로그 수집기(Fluentd 등)
- 모니터링 에이전트
- CNI 네트워크 에이전트
- kube-proxy 같은 노드 단위 컴포넌트
9. Static Pods: API Server 없이도 kubelet이 “로컬 디렉터리”를 보고 만든다
Static Pod는 kubelet이 특정 디렉터리(매니페스트 경로) 를 감시해 Pod를 만드는 방식입니다.
- kubelet이 주기적으로 디렉터리를 확인
- YAML 파일이 있으면 Pod 생성
- YAML 변경 시 Pod 재생성
- YAML 삭제 시 Pod 삭제
중요한 제한:
- 이 방식으로 만들 수 있는 것은 Pod뿐입니다.
- Deployment/ReplicaSet/Service 같은 컨트롤러 오브젝트는 만들 수 없습니다.
9.1 manifest 파일 경로는 어디서 보나?
환경마다 다르지만 kubeadm 기반에서는 흔히:
/etc/kubernetes/manifests
kubelet 옵션/설정에서 확인하는 포인트:
--pod-manifest-path=...- 또는 kubelet config의
staticPodPath: ...
9.2 “yaml이 바뀌면 바로 적용되나?”
Static Pod는 파일 변경을 kubelet이 감지한 뒤 재생성하는 형태로 반영됩니다.
즉, “적용”은 되는데 보통 재생성을 통해 반영된다고 이해하는 게 정확합니다.
10. PriorityClass & Preemption: “급한 Pod가 덜 급한 Pod를 밀어낸다”
클러스터 자원이 부족할 때도 중요한 워크로드가 먼저 살아야 합니다.
이를 위해 PriorityClass(우선순위 클래스)가 존재합니다.
- 숫자 클수록 우선순위 높음
- 우선순위가 높은 Pod가 스케줄링 불가하면, 스케줄러가 낮은 우선순위 Pod를 종료(선점)할 수 있음
preemptionPolicy로 선점 허용/금지 가능
PriorityClass 예시:
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
preemptionPolicy: PreemptLowerPriority
globalDefault: false
description: "Critical workloads"
Pod에 적용:
spec:
priorityClassName: high-priority
11. 여러 스케줄러 / Scheduler Profiles: 하나의 클러스터에 여러 스케줄링 전략
쿠버네티스는 확장 가능하기 때문에:
- 기본 스케줄러 외에 추가 스케줄러를 둘 수 있고
- Pod마다 어떤 스케줄러를 쓸지 지정할 수도 있습니다.
Pod에서 선택:
spec:
schedulerName: my-custom-scheduler
또한 최근에는 “여러 스케줄러 바이너리”를 각각 띄우는 방식 대신,
Scheduler Profiles로 하나의 스케줄러에서 서로 다른 프로필(플러그인 구성)을 운용하는 방식도 중요해졌습니다.
12. Admission Controller: “요청을 승인하기 전, 정책으로 검사/변형한다”
kubectl로 보낸 요청은 API Server에 도달한 뒤 일반적으로:
- 인증(Authentication)
- 인가(Authorization, RBAC)
- Admission Controller
- etcd 저장
RBAC가 “누가 무엇을 할 수 있나”라면, Admission Controller는 “요청 내용이 정책에 맞는가”를 다룹니다.
예시 정책:
- DockerHub public 이미지 금지
:latest태그 금지- runAsNonRoot 강제
- 특정 라벨 필수
Admission Controller는 크게 2가지:
- Validating: 허용/거부
- Mutating: 요청 내용 자체를 수정(기본값 주입 등)
12.1 Webhook으로 커스텀 정책 붙이기
내장 Admission만으로 부족하면 Webhook을 붙일 수 있습니다.
사용자가 보여준 MutatingWebhookConfiguration의 의미는 정확히 이것입니다:
- Pod CREATE 요청이 들어오면
- API Server가
webhook-demo/webhook-server서비스의/mutate로 호출 - 웹훅 서버가 JSONPatch로 Pod를 변형할 수 있음
caBundle은 TLS 검증을 위한 CA 인증서(Base64)
마무리: Scheduling은 “한 기능”이 아니라 “레이어들의 조합”이다
Scheduling은 단일 설정으로 끝나는 게 아니라, 다음 레이어들이 겹쳐서 “최종 배치 결과”를 만듭니다.
- 라벨/셀렉터(그룹화/연결)
- 테인트/톨러레이션(노드 입장 제한)
- 노드 셀렉터/어피니티(노드 지정 배치)
- 리소스 requests/limits(가능한 노드 필터링 + 런타임 상한)
- 우선순위/선점(자원 부족 시 무엇을 살릴지)
- 데몬셋/정적 파드(스케줄러를 “우회/대체”하는 운영 패턴)
- 멀티 스케줄러/프로필(정책 자체를 확장)
- 어드미션/웹훅(요청 단계에서 정책 강제)
헷갈리는 내용 간략 정리
- 노드에 taint를 적용하면 API에 즉시 반영되고, 새 스케줄링은 바로 영향을 받는다. 기존 Pod 퇴출은 NoExecute일 때 발생하며(tolerationSeconds 있으면 유예 후 퇴출).
- Pod/Deployment에 toleration은 YAML에 넣는 게 정석이고, 명령어로도 kubectl run --overrides나 kubectl patch로 가능하지만 Pod는 대부분 spec 변경이 불가라서 보통 Deployment 템플릿에 넣어 롤링으로 적용한다.
- NoExecute로 퇴출되면: 단독 Pod는 자동 복구 없음, Deployment/StatefulSet/Job 등 컨트롤러가 있으면 새 Pod를 만들어 재스케줄링한다(가능한 노드가 있어야 함).
- preferredDuringSchedulingIgnoredDuringExecution / requiredDuringSchedulingIgnoredDuringExecution은 정해진 필드명이어서 단어를 임의 조합하는 게 아니다(하드/소프트 조건 + 실행 중 변경은 무시).
- 노드 레이블은 kubectl label node <node> key=value로 추가/수정/삭제(--overwrite, key-)한다.
- 스케줄러는 requests 기준으로 노드에 배치하므로 limit 합은 노드 메모리를 넘을 수 있다(오버커밋 가능). 그래서 limit이 있어도 OOM/eviction 가능성이 0이 되지는 않는다. 예: 노드 10Gi, request 1Gi면 스케줄러 기준 최대 10개(allocatable 기준).
- 실무에서 메모리 request=limit(Guaranteed)은 안정성 우선일 때 흔하고, request<limit(Burstable)은 효율/밀집도와 트레이드오프다.
- ResourceQuota는 네임스페이스 단위 총량 제한이며 노드 단위가 아니다.
- Pod는 대부분 immutable이라 image/activeDeadlineSeconds/tolerations 등 일부만 수정 가능하고, 나머지는 보통 컨트롤러 템플릿 변경 후 재생성으로 처리한다. Deployment 롤링 업데이트는 maxUnavailable=0, maxSurge>0, readinessProbe, graceful shutdown 등으로 순단을 줄인다.
- Static Pod는 kubelet이 로컬 매니페스트(staticPodPath, 흔히 /etc/kubernetes/manifests)를 감시하며 생성/복구하고, 왜 쓰냐면 control-plane 부트스트랩과 API 장애 시 자기복구를 위해서다.
- preemptionPolicy는 PreemptLowerPriority(기본)와 Never 두 가지다.
- CKA custom scheduler 핵심은 spec.schedulerName으로 선택하는 법, 추가 스케줄러 인스턴스/프로파일 개념, leader election/config 충돌 포인트 진단이다. kube-scheduler는 (환경에 따라) Pod로 보이고, KubeSchedulerConfiguration은 스케줄러 설정 스키마이다. 스케줄러를 “다른 schedulerName”으로 분리하려면 보통 새 config(또는 기존 config에 새 profile 추가)가 필요하다.
'CKA' 카테고리의 다른 글
| Logging - 쿠버네티스 로깅 기초 (0) | 2025.12.30 |
|---|---|
| Monitoring - Metric Server (0) | 2025.12.30 |
| Scheduling - Admission Controller 심화(Custom Webhook) (0) | 2025.12.30 |
| Scheduling - Admission Controller (0) | 2025.12.30 |
| Scheduling- Scheduler Profiles (0) | 2025.12.30 |