本記事は https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests の講座と https://kodekloud.com/ の内容を学習しながら記録したものです。
- 53 Manual Scheduling
- 56 Labels and Selectors in Kubernetes
- 59 Taints and Tolerations
- 63. Node Affinity
- 67 Resource Requirements and Limits
- 71 DaemonSets
- 74 Static Pods
- 77 Multiple Schedulers
- 80 Configuring Scheduler Profiles
53 Manual Scheduling
もしKubernetesにスケジューラがなかったらどうなるでしょうか?Podはおそらくずっとpending状態のままになるでしょう。 代わりに、Pod定義ファイルにnodeNameを明示的に指定することで、Podを配置するノードを設定できます。

既に実行中のPodを特定のノードに割り当てるにはどうすればよいでしょうか?
Binding APIを活用します。
apiVersion: v1
kind: Binding
metadata:
name: nginx
target:
apiVersion:v1
kind: Node
name: node2
または、実行中のPodを停止し、nodeNameを指定してPodを再作成します。
56 Labels and Selectors in Kubernetes
Labelはkey-valueペアとして指定できます。
リソースにはmetadata -> labels配下にラベルを定義し、ReplicaSetのように特定のPodをフィルタリングする必要がある箇所ではselector -> matchLabelsにkey-valueペアを指定します。

グルーピングとセレクティングのためにLabelとSelectorが使用されます。
その他の情報を保存するためにAnnotationも使用されます。
envがdevのラベルを持つPodのみをセレクトする方法:
$ kubectl get pods --show-labels=true
NAME READY STATUS RESTARTS AGE LABELS
app-1-krzm7 1/1 Running 0 3m3s bu=finance,env=dev,tier=frontend
db-2-8lwj8 1/1 Running 0 3m2s bu=finance,env=prod,tier=db
db-1-gw7lc 1/1 Running 0 3m3s env=dev,tier=db
app-1-tbwcg 1/1 Running 0 3m3s bu=finance,env=dev,tier=frontend
app-2-5lt89 1/1 Running 0 3m3s env=prod,tier=frontend
db-1-btt4j 1/1 Running 0 3m3s env=dev,tier=db
auth 1/1 Running 0 3m2s bu=finance,env=prod
app-1-qqhbb 1/1 Running 0 3m3s bu=finance,env=dev,tier=frontend
db-1-chgq9 1/1 Running 0 3m3s env=dev,tier=db
db-1-wgvgf 1/1 Running 0 3m3s env=dev,tier=db
app-1-zzxdf 1/1 Running 0 3m2s bu=finance,env=prod,tier=frontend
$ kubectl get pods --selector env=prod
NAME READY STATUS RESTARTS AGE
db-2-8lwj8 1/1 Running 0 5m2s
app-2-5lt89 1/1 Running 0 5m3s
auth 1/1 Running 0 5m2s
app-1-zzxdf 1/1 Running 0 5m2s
$ kubectl get all --selector env=prod
NAME READY STATUS RESTARTS AGE
pod/db-2-8lwj8 1/1 Running 0 6m26s
pod/app-2-5lt89 1/1 Running 0 6m27s
pod/auth 1/1 Running 0 6m26s
pod/app-1-zzxdf 1/1 Running 0 6m26s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app-1 ClusterIP 10.43.7.99 <none> 3306/TCP 6m26s
NAME DESIRED CURRENT READY AGE
replicaset.apps/db-2 1 1 1 6m27s
replicaset.apps/app-2 1 1 1 6m27s
$ kubectl get pods --selector env=prod,bu=finance,tier=frontend
NAME READY STATUS RESTARTS AGE
app-1-zzxdf 1/1 Running 0 8m1s
59 Taints and Tolerations
Taintは「汚染された」という意味で、Tolerationは「耐性」という意味です。 特定のNodeにTaintを付与すると、Toleration(耐性)がないPodはそのノードにスケジュールされません。 逆に、Tolerationを持つPodはそのNodeにスケジュール可能です。

Taint Node
node-nameにはNodeの名前を指定し、taint-effectには NoSchedule、PreferNoSchedule、NoExecute の3つから1つを選択します。
$ kubectl taint nodes node-name key=value:taint-effect
# example
$ kubectl taint nodes node01 spray=mortein:NoSchedule
node/node01 modified
specセクションにTolerationを定義します。内部の値はすべてダブルクォートで囲む必要があります。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: nginx-container
image: nginx
tolerations:
- key: "app"
operator: "Equal"
value: "blue"
effect:"NoSchedule"
Taintアクションの中でNoExecuteはより詳しく見る必要があります。NoExecuteはNoScheduleの機能に加えて、既に動作中のPodも削除する機能を含みます。そのため、運用中のノードにTaintを設定する場合、NoExecuteは稼働中のPodに影響を与える可能性があるため、慎重に使用する必要があります。
TaintやTolerationを見ると、特定のPodを特定のNodeに移動させるものだと誤解されがちです。しかし正確には、特定のPodが特定のNodeにスケジュールされることを防ぐものです。Podを特定のNodeに配置するのはNode Affinityに関連しています。
KubernetesにはMaster Nodeが存在します。Master NodeにもPodを実行できる環境が整っています(実際にMaster NodeにPodを移動させることも可能です)。しかし通常、Master NodeではPodは実行されません。その理由は、Master NodeにはTaintが設定されており、ユーザーが実行したPodがスケジュールされないようになっているためです。
kubectl describe node kubemaster | grep Taint でTaintを確認できます。
Create another pod named bee with the nginx image, which has a toleration set to the taint mortein.
$ kubectl run bee --image nginx --dry-run=client -o yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: bee
name: bee
spec:
containers:
- image: nginx
name: bee
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
# Edit like below
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: bee
name: bee
spec:
tolerations:
- key: "spray"
operator: "Equal"
value: "mortein"
effect: "NoSchedule"
containers:
- image: nginx
name: bee
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
$ kubectl create -f bee.yaml
$ kubectl describe nodes node01 | grep mort
Taints: spray=mortein:NoSchedule
$ kubectl describe nodes controlplane | grep Taints
Taints: node-role.kubernetes.io/control-plane:NoSchedule
Remove taints of controlplane node.
$ kubectl taint nodes controlplane node-role.kubernetes.io/control-plane:NoSchedule-
node/controlplane untainted
$ kubectl describe nodes controlplane | grep Taints
Taints: <none>
63. Node Affinity
Node SelectorのラベルやNode Affinityを使用してノードを指定できます。事前にノードにラベルが設定されている必要があります。
requiredDuringSchedulingIgnoredDuringExecution と preferredDuringSchedulingIgnoredDuringExecution の2つのオプションがあり、必須か任意かの違いです。Requiredの場合、該当Podを割り当てられる適切なノードがなければ、Podはスケジュールされません。Preferredの場合は、スケジューラは最善を尽くしますが、必要に応じて他のノードにもスケジュールします。
nodeSelector:
size: Large



Apply label to a node.
kubectl label nodes node01 color=blue
node/node01 labeled
Create a new deployment named red with the nginx image and 2 replicas, and ensure it gets placed on the controlplane node only. Use the label key - node-role.kubernetes.io/control-plane - which is already set on the controlplane node.
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: red
name: red
spec:
replicas: 2
selector:
matchLabels:
app: red
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: red
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: Exists
containers:
- image: nginx
name: nginx
resources: {}
status: {}
TaintとTolerationだけでは、特定のPodが特定のNodeにスケジュールされることを保証できません。Node Affinityだけでも同様に保証できません。Taint & TolerationとNode Affinityの両方を組み合わせることで、特定のNodeに特定のPodのみがスケジュールされることを保証できます。
67 Resource Requirements and Limits
Podの実行に必要なハードウェアリソースをKubernetesにリクエストできます。Limitも設定可能です。
spec:
continers:
- name: simple-webapp
image: nginx
ports:
- containerPort: 8080
resources:
requests:
memory: "1Gi"
cpu: 1
limits:
memory: "2Gi"
cpu: 2
CPUリソースは1 vCPU未満の値も設定できます。例えば0.1は100msを意味します。CPU使用量がlimitを超えるとスロットリングがかかります。しかしメモリの場合は異なります。メモリ使用量が超過するとOOM(Out Of Memory)エラーが発生し、Podが停止されます。
Namespaceレベルでlimitの最小値、最大値の範囲を設定することもできます。

QuotaもNamespaceレベルで設定でき、そのNamespaceが作成可能な最大ハードウェアリソースを割り当てることができます。

71 DaemonSets
DaemonSetは1つのNodeに1つのPodだけを実行する場合に使用します。名前からも分かるように、デーモンとして常に停止せずに実行されるPodです。新しいノードが追加されると、DaemonSetが自動的にPodを追加します。

一般的にモニタリングソリューションやログビューアーを起動する際にDaemonSetが使用されます。Kubernetesで最も有名なDaemonSetを1つ挙げるなら kube-proxy です。
DaemonSetの定義はReplicaSetとほぼ同じです。

kubectl get daemonsets
DaemonSetがノードに必ず常駐することを保証できるでしょうか?答えは「保証できる」です。 v1.12ではDaemonSetのPod SpecificationのnodeNameプロパティにNodeを明示的に指定するためです。
v1.12以降のKubernetesバージョンでは少し異なり、明示的に設定せずにNodeAffinityとデフォルトスケジューラを活用してDaemonSetを作成します。
Question: Create Daemonset which requires below specification.
Name: elasticsearch Namespace: kube-system Image: registry.k8s.io/fluentd-elasticsearch:1.20
kubectl create deployment elasticsearch --namespace=kube-system --image=registry.k8s.io/fluentd-elasticsearch:1.20 --dry-run=client -o yaml
74 Static Pods
Kubernetes API Server、ETCD、Schedulerがすべて存在せず(Master Nodeが存在しない)、Worker Node 1台、つまりKubelet(コンテナランタイム含む)1つだけが存在する場合、Podを実行できるでしょうか?答えは「可能」です。Static Podという特別なPodがあり、これを利用すれば他のユーティリティシステム(APIサーバー、ETCD、Scheduler)なしでもPodの作成が可能です。
Static Podを実行するには、/etc/kubernetes/manifests ディレクトリ配下にPod定義ファイルを配置する必要があります。
Kubeletはこのディレクトリ配下のファイルを定期的にチェックし、Podを作成します。
以下はMaster Nodeで該当ディレクトリの内容を確認した例です。
$ ls -al /etc/kubernetes/manifests/
total 24
drwxr-xr-x 2 root root 4096 8월 12 10:56 ./
drwxr-xr-x 4 root root 4096 8월 12 10:56 ../
-rw------- 1 root root 2411 8월 12 10:56 etcd.yaml
-rw------- 1 root root 4047 8월 12 10:56 kube-apiserver.yaml
-rw------- 1 root root 3429 8월 12 10:56 kube-controller-manager.yaml
-rw------- 1 root root 1463 8월 12 10:56 kube-scheduler.yaml
youngjukim@cubi01:~$
このファイルが削除されると、Kubeletは自動的にPodを削除します。更新されると、Podを自動的に再作成します。
Static Podの定義パスは必ずしも /etc/kubernetes/manifests である必要はなく、別のディレクトリに設定することも可能です。ただし、パスを変更するにはKubeletの再起動が必要です。

または、設定ファイルを引数として渡し、staticPodPath で該当パスを設定する方法もあります。

Master Nodeなしで単独で実行されているKubeletの場合、Static Podが正常に実行されているかを確認するにはどうすればよいでしょうか?
当然ながらMaster Nodeがないため、APIサーバーも存在せず、kubectlコマンドも使用できません。この場合は docker ps や crictl ps でPodが正常に実行されているか確認する必要があります。
では、Master Nodeが存在する状況では、APIサーバーはWorker NodeのKubeletで実行中のStatic Podの存在を認識できるでしょうか?答えは「認識できる」です。 しかし存在の認識のみであり、Static Podに対する操作は不可能です。APIサーバーはStatic Podに対してread-only権限のみを持っています。 また特徴的な点として、Static PodはPod名にノード名が自動的に付加されます。
Static PodはKubernetesコントロールプレーンに依存せず、指定フォルダにPod定義ファイルを配置するだけでよいため、デプロイが簡単です。 kube-systemの大半のPodはStatic Podであり、kubeadmツールもこの仕組みを利用しています。

DaemonSetとStatic Podは混同されやすいですが、kube-schedulerの影響を受けない点は共通していますが、それ以外はまったく異なります。DaemonSetはノードごとに1つずつ実行され、作成主体はAPIサーバーです。一方、Static Podの作成主体はKubeletです。
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: static-busybox
name: static-busybox
spec:
containers:
- command:
- sleep
- "1000"
image: busybox
name: static-busybox
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
77 Multiple Schedulers
Kubernetesでは複数のSchedulerを使用できます。また、Podをスケジューリングする際にどのSchedulerを使用するかも設定可能です。
最も簡単なカスタムSchedulerの追加は以下のように行えますが、現在ではこの方法は使用されていません。

現在は以下のように、SchedulerもPod形式で実行する方法が一般的です。

最新の方法はKubernetes公式ガイドの Configuring Multiple Schedulers を参照してください。
Podを実行する際に明示的にSchedulerを指定するには、spec配下の schedulerName にScheduler名を指定します。

新しく作成したmy-custom-schedulerでスケジューリングが正しく行われたか確認するには、kubectl get events -o wide を使用します。
または kubectl logs my-custom-scheduler --name-space=kube-system でSchedulerのログを確認します。
apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: my-scheduler
leaderElection:
leaderElect: false
apiVersion: v1
data:
my-scheduler-config.yaml: |
apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: my-scheduler
leaderElection:
leaderElect: false
kind: ConfigMap
metadata:
creationTimestamp: null
name: my-scheduler-config
namespace: kube-system
ConfigMapの作成:
$ kubectl create configmap my-scheduler-config --from-file=/root/my-scheduler-config.yaml -n kube-system
configmap/my-scheduler-config created
カスタムSchedulerの作成:
apiVersion: v1
kind: Pod
metadata:
labels:
run: my-scheduler
name: my-scheduler
namespace: kube-system
spec:
serviceAccountName: my-scheduler
containers:
- command:
- /usr/local/bin/kube-scheduler
- --config=/etc/kubernetes/my-scheduler/my-scheduler-config.yaml
image: registry.k8s.io/kube-scheduler:v1.27.0
livenessProbe:
httpGet:
path: /healthz
port: 10259
scheme: HTTPS
initialDelaySeconds: 15
name: kube-second-scheduler
readinessProbe:
httpGet:
path: /healthz
port: 10259
scheme: HTTPS
resources:
requests:
cpu: '0.1'
securityContext:
privileged: false
volumeMounts:
- name: config-volume
mountPath: /etc/kubernetes/my-scheduler
hostNetwork: false
hostPID: false
volumes:
- name: config-volume
configMap:
name: my-scheduler-config
80 Configuring Scheduler Profiles
Podはスケジューリングされる前にScheduling Queueに入ります。ここでPodのPriorityに基づいてソートされます。 次にFiltering Phaseでは、該当Podを収容できるNodeだけが選別されます。フィルタリングの対象はNodeです。Scoring Phaseでは残りのNode間でスコアを付けて1つのNodeを決定します。最後にBindingが実行されます。
各Phaseにはそれぞれプラグインが存在し、以下の通りです。Kubernetesではこれらのプラグインをカスタマイズできるよう、Extension Pointを提供しています。

Kubernetes v1.18リリースでは、Multiple Scheduler間の衝突を防ぐためにMultiple Profileという概念が導入されました。 同じバイナリを使用する各プロファイルで、多数のプラグインのenable/disableを設定できるようになりました。例えば、Score Plugin Phaseをスキップすることも可能です。

クイズ
Q1: 「CKA_3_Scheduling」の主なトピックは何ですか?
CKA_3_Scheduling
Q2: Taint Nodeとは何ですか?
node-nameにはNodeの名前を指定し、taint-effectには NoSchedule、PreferNoSchedule、NoExecute
の3つから1つを選択します。
specセクションにTolerationを定義します。内部の値はすべてダブルクォートで囲む必要があります。
Taintアクションの中でNoExecuteはより詳しく見る必要があります。NoExecuteはNoScheduleの機能に加えて、既に動作中のPodも削除する機能を含みます。