50. Scheduling 개요
Kubernetes 스케줄링이란?
“새로 만들어진 Pod를 어느 Node에 배치할지 결정하는 과정”
기본적으로 kube-scheduler가 자동으로 처리. 하지만 실무에서는 세밀한 제어가 필요한 경우가 많음:
| 상황 | 사용할 도구 |
|---|---|
| ”DB Pod는 SSD 노드에만” | NodeSelector / NodeAffinity |
| ”관리자 노드에는 일반 Pod 못 들어오게” | Taints / Tolerations |
| ”모든 노드에 로그 수집기 1개씩” | DaemonSet |
| ”컨트롤 플레인 컴포넌트 배포” | Static Pod |
| ”중요한 Pod가 우선 스케줄” | Priority Class |
| ”자체 스케줄링 로직” | Custom Scheduler |
스케줄링은 “어디에” 만 결정. 실제 생성은 해당 노드의 kubelet이 담당.
51. Manual Scheduling
공식문서: Assigning Pods to Nodes
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
name: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 8080
nodeName: # 보통은 비어있고 scheduler가 알아서 채워넣음- binding object
[!] The
nodeNamemust be set during pod creation. Once the pod is running, Kubernetes does not permit modifications to thenodeNamefield.
52. lab
# 포드 설정 강제 덮어 쓰기
kubectl replace --force -f nginx.yaml54. Labels and Selectors
공식문서: Labels and Selectors
# 모든 리소스 조회
kubectl get all
# 헤더 없이 리소스 조회
kubectl get all --no-headers
# 검색 시 라벨 필터 + 출력 라인 세기
kubectl get pods --selector=env=~~~ --no-headers | wc -l 57. Taints and Tolerations
공식문서: Taints and Tolerations
- taint: 임의의 pod가 할당되는 것을 방지하기 위해 node에 설정
- tolerations: taint가 있는 node에 할당 가능하도록 pod에 toleration을 설정
Taint Effect 3종
| Effect | 동작 |
|---|---|
NoSchedule | toleration 없으면 스케줄 불가 (기존 Pod 유지) |
PreferNoSchedule | toleration 없으면 가능한 피함 (강제 X) |
NoExecute | toleration 없으면 기존 Pod까지 추방 |
Taint imperative 치트 (시험 필수)
# ── 추가 ──────────────────────────────────────────────────
kubectl taint nodes node1 key=value:NoSchedule
kubectl taint nodes node1 dedicated=db:NoSchedule
kubectl taint nodes node1 critical=true:NoExecute
# ── 제거 (마지막에 '-' 붙임) ───────────────────────────────
kubectl taint nodes node1 key=value:NoSchedule-
kubectl taint nodes node1 key:NoSchedule- # value 없어도 OK
kubectl taint nodes node1 key- # 모든 effect 제거
# ── 모든 노드에 일괄 적용 ──────────────────────────────────
kubectl taint nodes --all key=value:NoSchedule
# ── 특정 라벨의 노드에만 적용 ──────────────────────────────
kubectl taint nodes -l tier=prod key=value:NoSchedule
# ── master/control-plane taint 확인 ────────────────────────
# kubeadm으로 설치한 마스터는 자동으로 NoSchedule taint 부여됨
kubectl describe node master | grep Taints
# Taints: node-role.kubernetes.io/control-plane:NoSchedule
# ── master에 Pod 스케줄 허용 (단일 노드 테스트) ───────────
kubectl taint nodes master node-role.kubernetes.io/control-plane:NoSchedule-- tolerations 값은 쌍따옴표로 작성
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: nginx-container
image: nginx
tolerations:
- key: "app"
operator: "Equal"
value: "blue"
effect: "NoSchedule"
# operator: "Exists" 사용 시 value 생략 가능
# - key: "app"
# operator: "Exists"
# effect: "NoSchedule"Toleration 확인
# 노드의 taint 확인
kubectl describe node node1 | grep -i taint
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.taints}{"\n"}{end}'
# Pod의 toleration 확인
kubectl describe pod mypod | grep -i toleration -A5
kubectl get pod mypod -o yaml | grep -A5 tolerations60. NodeSelector
- 부하가 큰 pod를 스펙이 낮은 node에 할당해버리는 경우 node가 다운되기 때문에 NodeSelctor를 이용해 이런일이 없도록 함
Label imperative 치트 (노드/파드 공통)
# ── 추가 ──────────────────────────────────────────────────
kubectl label nodes node1 size=Large
kubectl label nodes node1 tier=prod env=production # 여러 개
kubectl label pods mypod app=web
# ── 제거 (key에 '-' 붙임) ──────────────────────────────────
kubectl label nodes node1 size-
kubectl label pod mypod app-
# ── 덮어쓰기 (기존 라벨이 있으면 --overwrite 필수) ────────
kubectl label nodes node1 size=XLarge --overwrite
kubectl label pod mypod app=webapp --overwrite
# ── 모든 노드/파드에 일괄 적용 ──────────────────────────────
kubectl label nodes --all zone=us-east
kubectl label pods --all env=test
# ── 특정 라벨 가진 것들에만 적용 ──────────────────────────
kubectl label nodes -l tier=prod critical=true
# ── 라벨 확인 ──────────────────────────────────────────────
kubectl get nodes --show-labels
kubectl get nodes -L size,tier # 특정 라벨만 컬럼으로
kubectl get pods -l app=web # 라벨로 필터
kubectl get pods -l 'env in (dev,stg)' # 고급 selectorNodeSelector 예시
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: data-processor
image: data-processor
nodeSelector:
size: Large61. NodeAffinity
NodeAffinity란?
“NodeSelector의 상위 호환 — 복잡한 조건과 선호도를 표현”
NodeSelector의 한계:
- “size=Large 이거나 size=Medium” 같은 OR 조건 표현 불가
- “있으면 좋지만 없어도 OK” 같은 선호 표현 불가
- “size가 Small만 아니면 OK” 같은 부정 조건 불가
NodeAffinity는 이런 복잡한 조건을 2가지 강제 수준으로 지원:
| 타입 | 의미 |
|---|---|
requiredDuringSchedulingIgnoredDuringExecution | 필수 — 조건 만족 노드 없으면 Pending |
preferredDuringSchedulingIgnoredDuringExecution | 선호 — 있으면 좋지만 없어도 다른 노드에 배치 |
IgnoredDuringExecution의 의미: Pod가 이미 실행 중이면 노드 라벨이 바뀌어도 쫓아내지 않음 (스케줄 때만 검사).
- NodeSelector 보다 복잡한 조건이 필요할 때 사용
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: data-processor
image: data-processor
affinity:
nodeAffinity:
#
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: size
operator: In
values:
- Large65. Taints and Tolerations vs Node Affinity
- Taints/Tolerations만 사용: 특정 pod가 toleration이 있어도 다른 노드에 갈 수 있음 (보장 X)
- Node Affinity만 사용: 원하는 pod를 특정 노드에 배치 가능하지만, 다른 pod가 해당 노드에 들어오는 것을 막지 못함
- 둘 다 사용해야 특정 pod만 특정 노드에 exclusive하게 배치 가능
핵심
Taints/Tolerations → 노드가 원하지 않는 pod를 밀어냄
Node Affinity → pod가 원하는 노드를 끌어당김
둘을 조합하면 전용 노드(dedicated node) 구성 가능
66. Resources amd Requirements
- Requets: 최소 요구량
- Limits: 제한
리소스 관련 imperative 치트
# ── 기존 Deployment의 리소스 변경 ─────────────────────────
kubectl set resources deploy/web --requests=cpu=100m,memory=256Mi
kubectl set resources deploy/web --limits=cpu=500m,memory=512Mi
kubectl set resources deploy/web --requests=cpu=100m --limits=cpu=500m
# ── ResourceQuota 생성 (imperative 가능!) ──────────────────
kubectl create quota my-quota \
--hard=pods=10,cpu=4,memory=8Gi,requests.cpu=2,limits.cpu=4 \
-n dev
# YAML 뽑기
kubectl create quota my-quota --hard=pods=10,cpu=4,memory=8Gi $do > quota.yaml
# ── 조회 ──────────────────────────────────────────────────
kubectl get resourcequota -n dev
kubectl describe resourcequota my-quota -n dev
# → Used / Hard 비교 표시LimitRange (YAML 전용)
LimitRange는 imperative 생성 명령이 없음. 템플릿 복사해서 수정.
apiVersion: v1
kind: LimitRange
metadata:
name: cpu-resource-constraint
spec:
limits:
- default: # this section defines default limits
cpu: 500m
defaultRequest: # this section defines default requests
cpu: 500m
max: #l imit
cpu: "1"
min: # request
cpu: 100m
type: ContainerResourceQuota 예시
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-resources
spec:
hard:
requests.cpu: "1"
requests.memory: "1Gi"
limits.cpu: "2"
limits.memory: "2Gi"
requests.nvidia.com/gpu: 4
pods: "10"
persistentvolumeclaims: "5"
services.loadbalancers: "2"- limit range가 적용된 후 새로 생성된 파드들에만 적용됨
- ResourceQuota가 설정된 namespace에서는 Pod가 반드시 resources 필드를 가져야 함
67. DaemonSets
공식문서: DaemonSet
- 모든 노드에 정확히 하나의 pod 복제본을 실행
- 노드 추가 → 자동으로 pod 배포 / 노드 제거 → pod 자동 삭제
대표 사용 사례:
- 모니터링 에이전트, 로그 수집기 (Fluentd, Filebeat 등)
- kube-proxy (모든 워커 노드에 필요)
- 네트워킹 솔루션 (weave-net, calico 등)
DaemonSet은
kubectl createimperative 명령이 없음 → YAML 필수
DaemonSet YAML 빠르게 만들기 (시험 치트)
# Deployment YAML을 뽑아서 kind만 DaemonSet으로 변경하는 패턴
kubectl create deploy monitoring --image=fluentd --dry-run=client -o yaml > ds.yaml
# 수정할 것:
# 1. kind: Deployment → kind: DaemonSet
# 2. spec.replicas 줄 삭제
# 3. spec.strategy 줄 삭제 (있으면)
# 4. metadata.creationTimestamp / status: {} 등 불필요 필드 제거
kubectl apply -f ds.yamlapiVersion: apps/v1
kind: DaemonSet
metadata:
name: monitoring-daemon
spec:
selector:
matchLabels:
app: monitoring-agent
template:
metadata:
labels:
app: monitoring-agent
spec:
containers:
- name: monitoring-agent
image: monitoring-agentkubectl get daemonsets
kubectl get ds # 약어
kubectl describe daemonset monitoring-daemon
kubectl get ds -A # 모든 namespace
# 특정 노드에만 배포 (nodeSelector 활용)
# spec.template.spec.nodeSelector 추가- v1.12 이전:
nodeName속성으로 직접 스케줄링 - v1.12 이후: default scheduler + NodeAffinity 활용
68. Static Pods
공식문서: Create static Pods
- kubelet이 kube-apiserver 없이 독립적으로 관리하는 pod
- 지정된 디렉토리(
/etc/kubernetes/manifests)에 YAML 파일을 두면 kubelet이 자동 생성 - 파일 변경 → pod 재생성, 파일 삭제 → pod 삭제
- Pod만 생성 가능 (ReplicaSet, Deployment 등 상위 리소스 불가)
설정 방법 1: kubelet 서비스 파일에 직접 지정
--pod-manifest-path=/etc/kubernetes/manifests
설정 방법 2: config 파일 사용
# kubelet config (kubeconfig.yaml)
staticPodPath: /etc/kubernetes/manifests클러스터 내 동작:
- kubelet이 static pod 생성 시 kube-apiserver에 mirror object (읽기 전용) 생성
kubectl get pods로 확인 가능하지만 API로 수정/삭제 불가- pod 이름에 노드명이 suffix로 붙음 (예:
static-web-node01)
kubeadm 클러스터에서 컨트롤 플레인 컴포넌트가 Static Pod으로 실행됨
Static Pod 실습 (시험 빈출)
# ── 1. staticPodPath 찾기 ─────────────────────────────────
# kubelet config 경로 확인
ps -ef | grep kubelet | grep -o 'config=[^ ]*'
# 예: config=/var/lib/kubelet/config.yaml
grep -i staticPodPath /var/lib/kubelet/config.yaml
# staticPodPath: /etc/kubernetes/manifests
# ── 2. Static Pod 생성 (특정 노드에서) ────────────────────
# 해당 노드에 SSH 접속 후
sudo tee /etc/kubernetes/manifests/static-web.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: static-web
spec:
containers:
- name: web
image: nginx
ports:
- containerPort: 80
EOF
# kubelet이 자동 감지하여 Pod 생성 (보통 20초 이내)
# 마스터에서 확인
kubectl get pods -A | grep static-web
# → static-web-node01 (노드명 suffix)
# ── 3. Static Pod 삭제 ────────────────────────────────────
# ❌ kubectl delete pod 는 안됨 (kubelet이 재생성)
# ✅ 파일 삭제로 제거
sudo rm /etc/kubernetes/manifests/static-web.yaml
# ── 4. imperative로 Static Pod YAML 생성 ──────────────────
# 일반 Pod처럼 dry-run으로 뽑아서 manifests/에 저장
kubectl run static-web --image=nginx --restart=Never --dry-run=client -o yaml \
> /etc/kubernetes/manifests/static-web.yamlStatic Pod 식별 방법
# Mirror Pod의 owner가 Node면 Static Pod
kubectl get pod kube-apiserver-master -n kube-system -o yaml | grep -A3 ownerReferences
# kind: Node
# 이름에 노드명이 붙어있음
kubectl get pods -n kube-system | grep -E "kube-apiserver|etcd|kube-controller|kube-scheduler"
# → 모두 -master 같은 suffix 있음| 구분 | Static Pods | DaemonSets |
|---|---|---|
| 생성 주체 | kubelet이 직접 관리 | DaemonSet controller (via API server) |
| Control Plane 필요 | X | O |
| 용도 | 컨트롤 플레인 컴포넌트 배포 | 모든 노드에 에이전트 배포 |
| Scheduler 관여 | X | X |
75. Priority Classes
PriorityClass란?
“Pod들 중에 누가 더 중요한지 숫자로 표시 — 자원 부족 시 덜 중요한 Pod를 내쫓음”
왜 필요한가?
- 클러스터 자원이 부족할 때 중요한 Pod(결제, 모니터링) 가 먼저 자리를 차지해야 함
- 낮은 우선순위 Pod가 자원을 선점하고 있으면 Preemption(선점) 으로 공간 확보
동작 방식:
- 각 Pod에 숫자 부여 (
value: 1000000) - 자원 부족 시 스케줄러가 “이 Pod는 중요한데 자리가 없네” 감지
- 우선순위 낮은 Pod를 evict(제거) 하고 새 Pod 배치
- Pod에 우선순위(숫자)를 부여하여 스케줄링 순서를 제어
- 값이 높을수록 먼저 스케줄링
- 사용자 범위: -2B ~ +1B / 시스템 예약: ~2B
kubectl get priorityclassapiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000000
description: "미션 크리티컬 pod용"Pod에 적용:
spec:
priorityClassName: high-priorityPreemption (선점):
- 기본 정책:
PreemptLowerPriority→ 낮은 우선순위 pod를 evict하고 자원 확보 preemptionPolicy: Never→ 선점 없이 대기
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority-no-preempt
value: 1000000000
preemptionPolicy: NeverglobalDefault: true로 설정하면 priorityClassName 미지정 pod의 기본값이 됨 (하나만 가능)- 미지정 시 기본 우선순위는 0
78. Multiple Schedulers
- 기본 스케줄러 외에 커스텀 스케줄러를 추가 배포 가능
- 각 스케줄러는 고유한 이름 필요 (기본:
default-scheduler)
스케줄러 설정 파일:
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: my-schedulerPod에서 커스텀 스케줄러 지정:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
schedulerName: my-custom-scheduler배포 방법:
- 별도 서비스로 실행 (kube-scheduler 바이너리 + 별도 config)
- Static Pod으로 배포
- Deployment로 배포 (ServiceAccount + RBAC + ConfigMap 필요)
확인:
# 어떤 스케줄러가 pod를 배치했는지 확인
kubectl get events -o wide
# 스케줄러 로그 확인
kubectl logs my-custom-scheduler -n kube-system- 잘못 구성된 경우 pod가
Pending상태 유지
80. Configuring Scheduler Profiles
공식문서: Scheduler Configuration
스케줄링 단계:
- Scheduling Queue - 우선순위 정렬 (PrioritySort 플러그인)
- Filter - 자원 부족 노드 제거 (NodeResourcesFit, NodeName, NodeUnschedulable 등)
- Score - 남은 노드 점수 매김 (NodeResourcesFit, ImageLocality 등)
- Bind - 최종 노드에 pod 바인딩 (DefaultBinder)
Extension Points: 각 단계에 플러그인을 추가/제거할 수 있음
다중 프로필 (v1.18+): 단일 바이너리에서 여러 스케줄러 프로필 실행 가능
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: my-scheduler-2
plugins:
score:
disabled:
- name: TaintToleration
enabled:
- name: MyCustomPluginA
- name: MyCustomPluginB
- schedulerName: my-scheduler-3
plugins:
preScore:
disabled:
- name: '*'
score:
disabled:
- name: '*'- 별도 바이너리 대신 하나의 프로세스에서 여러 프로필을 실행하면 race condition 방지
82 Admission Controllers
Admission Controller란?
“API Server가 요청을 ETCD에 저장하기 직전에, ‘이 요청을 허락할까? 뭔가 수정할까?’ 검토하는 관문”
왜 필요한가?
- RBAC은 “누가 뭘 할 수 있나” 까지는 제어하지만 요청 내용 자체는 검사 안함
- 예: RBAC으로 “Pod 생성 권한”을 줬다면 → 어떤 이미지를 쓰든, 루트 권한이든 다 통과
Admission Controller가 메꾸는 부분:
- 특정 이미지 레지스트리에서 온 이미지만 허용
- 루트 사용자(UID 0) Pod 생성 금지
- 리소스 limits 없으면 자동으로 기본값 삽입
- Secret/ConfigMap이 빠진 Pod 생성 거부
1. kubectl → API Server 요청
2. Authentication (누구?)
3. Authorization (할 수 있나?)
4. Admission Controllers (내용은 OK?)
5. Resource 생성 (ETCD 저장)- RBAC만으로는 세밀한 제어가 불가능한 경우 Admission Controller 사용
- 예: 특정 이미지 레지스트리만 허용, 루트 권한 실행 금지, 특정 capability 제한 등
종류:
AlwaysPullImages: 항상 이미지를 pullDefaultStorageClass: PVC에 StorageClass 미지정 시 기본값 자동 부여EventRateLimit: API 서버 요청 속도 제한NamespaceExists/NamespaceAutoProvision: 네임스페이스 존재 여부 검증 / 자동 생성
활성화된 Admission Controller 확인:
kube-apiserver -h | grep enable-admission-plugins
# kubeadm 환경
kubectl exec -n kube-system kube-apiserver-controlplane -- kube-apiserver -h | grep enable-admission-plugins활성화/비활성화:
--enable-admission-plugins=NodeRestriction,NamespaceAutoProvision
--disable-admission-plugins=DefaultStorageClass
85. Validating and Mutating Admission Controllers
두 가지 유형:
- Mutating: 요청 객체를 변경할 수 있음 (예:
DefaultStorageClass가 StorageClass 추가) - Validating: 요청을 검증하고 허용/거부 (예:
NamespaceExists가 네임스페이스 존재 확인)
Mutating이 먼저 실행되고, 이후 Validating이 실행됨
외부 Admission Controller — Webhook:
MutatingAdmissionWebhook/ValidatingAdmissionWebhook- 외부 서버에 admission review 요청을 보내고 응답에 따라 허용/거부
- 클러스터 내 Deployment 또는 외부 서비스로 구현 가능
Webhook 서버 구성 흐름:
- webhook 로직이 있는 서버 개발 (보통
/validate또는/mutate엔드포인트) - 클러스터에 Deployment + Service로 배포
ValidatingWebhookConfiguration또는MutatingWebhookConfiguration리소스 생성
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: "pod-policy.example.com"
webhooks:
- name: "pod-policy.example.com"
clientConfig:
service:
namespace: webhook-namespace
name: webhook-service
caBundle: <CA_BUNDLE>
rules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["pods"]
admissionReviewVersions: ["v1"]
sideEffects: None