Skip to content

Commit 291ea1f

Browse files
authored
Merge pull request #1792 from future-architect/feature
実験して入門するKubernetes:Pod起動の裏側を追っていたら、どうしてもSchedulerを止めてみたくなった件
2 parents 0d81990 + 1500b43 commit 291ea1f

3 files changed

Lines changed: 329 additions & 0 deletions

File tree

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
---
2+
title: "実験して入門するKubernetes:Pod起動の裏側を追っていたら、どうしてもSchedulerを止めてみたくなった件"
3+
date: 2026/03/25 00:00:00
4+
postid: a
5+
tag:
6+
- Kubernetes
7+
- minikube
8+
- 初心者向け
9+
category:
10+
- Infrastructure
11+
thumbnail: /images/2026/20260325a/thumbnail.jpg
12+
author: 内堀航輝
13+
lede: "Kubernetesの基礎的な挙動を追ってみることにしました。今回は、どういう流れでPodが作られているのかを実験し、そのアウトプットとしてこの記事にまとめています。"
14+
---
15+
16+
<img src="/images/2026/20260325a/top.jpg" alt="" width="1024" height="572">
17+
18+
# はじめに
19+
20+
最近、コンテナオーケストレーションの技術領域に興味をもち、Kubernetesを触り始めました。早速自宅で飼ってるMiniPC(ホームサーバ)にArgoCDやLonghornを入れてみたものの、Deployment、Pod、Serviceなど初めましての概念ばかり。気づいたら、AIの指示通りにただコマンド打つマンになっていました。
21+
22+
このままではまずいと思い、Kubernetesの基礎的な挙動を追うことにしました。今回は、どのような流れでPodが作られているのかを実験し、そのアウトプットとしてこの記事にまとめています。
23+
24+
※実験にはMinikubeを使っています。初学者が調べながらまとめたものなので、誤りがあればご指摘ください。
25+
26+
# Pod起動までの流れの概要
27+
28+
Podが起動するまでの流れを紹介します。複数のKubernetesコンポーネントが登場しますが、この記事のメインテーマではないため、用語は簡単な説明に留めます。詳細が気になる方は、既存の解説記事や[公式ドキュメント](https://kubernetes.io/ja/docs/concepts/overview/components/)をご参照ください。
29+
30+
```txt
31+
kubectl run
32+
33+
API Server → etcd(Podデータ保存、ノード未割り当て)
34+
35+
Scheduler(最適なノードを割り当て)
36+
37+
kubelet(コンテナイメージのPull・起動)
38+
39+
podが起動
40+
```
41+
42+
- **etcd**:Kubernetesの全状態を保存するデータベース
43+
- **API Server**:全コンポーネントからのリクエスト受付と、状態変更の通知する窓口
44+
- **Scheduler**:Podをどのノードで動かすかを決定するスケジューラ
45+
- **kubelet**:各ノード上で実際にコンテナを起動・管理する実行エンジン
46+
47+
> 補足:`kubectl run`はPodを直接作成します。Deployment経由の場合は、Controller ManagerがDeploymentデータ → ReplicaSetデータ → Podデータ と各種データを作成するステップが加わります。この記事では、簡略化のため`kubectl run`による直接的な作成を追いかけます。
48+
49+
# 実験1:イベントログでPod作成の流れを確認する
50+
51+
最初の実験として、実際に`kubectl run`を実行し、出力されるイベントログを確認してみます。
52+
53+
```sh
54+
% kubectl run log-test --image=nginx
55+
pod/log-test created
56+
% kubectl get events -w
57+
LAST SEEN TYPE REASON OBJECT MESSAGE
58+
23m Normal Scheduled pod/log-test Successfully assigned default/log-test to minikube
59+
22m Normal Pulling pod/log-test Pulling image "nginx"
60+
22m Normal Pulled pod/log-test Successfully pulled image "nginx" in 1.481s (1.481s including waiting). Image size: 180545980 bytes.
61+
22m Normal Created pod/log-test Container created
62+
22m Normal Started pod/log-test Container started
63+
```
64+
65+
REASON列とMESSAGE列を見ると、まずScheduledでPodがノード(ここではminikube)に割り当てられ、その後にコンテナの立ち上げ処理が始まっていることがわかります。
66+
67+
さらに`kubectl get events -o yaml` とオプションを追加すると、各イベントがどのコンポーネントから出力されたのかを詳しく確認できます。
68+
69+
<details>
70+
<summary> kubectl get events -o yaml の出力</summary>
71+
72+
```yaml
73+
% kubectl get events -o yaml
74+
apiVersion: v1
75+
items:
76+
- action: Binding
77+
apiVersion: v1
78+
eventTime: "2026-03-17T08:45:27.742811Z"
79+
firstTimestamp: null
80+
involvedObject:
81+
apiVersion: v1
82+
kind: Pod
83+
name: log-test
84+
namespace: default
85+
resourceVersion: "62245"
86+
uid: c79a534d-9c74-45bf-acf4-5a9985c39c14
87+
kind: Event
88+
lastTimestamp: null
89+
message: Successfully assigned default/log-test to minikube
90+
metadata:
91+
creationTimestamp: "2026-03-17T08:45:27Z"
92+
name: log-test.189d9485200842b1
93+
namespace: default
94+
resourceVersion: "62247"
95+
uid: 822856bb-0e26-4942-8d78-ac1eff975cb0
96+
reason: Scheduled
97+
reportingComponent: default-scheduler
98+
reportingInstance: default-scheduler-minikube
99+
source: {}
100+
type: Normal
101+
- apiVersion: v1
102+
count: 1
103+
eventTime: null
104+
firstTimestamp: "2026-03-17T08:45:28Z"
105+
involvedObject:
106+
apiVersion: v1
107+
fieldPath: spec.containers{log-test}
108+
kind: Pod
109+
name: log-test
110+
namespace: default
111+
resourceVersion: "62246"
112+
uid: c79a534d-9c74-45bf-acf4-5a9985c39c14
113+
kind: Event
114+
lastTimestamp: "2026-03-17T08:45:28Z"
115+
message: Pulling image "nginx"
116+
metadata:
117+
creationTimestamp: "2026-03-17T08:45:28Z"
118+
name: log-test.189d94853f8ec93d
119+
namespace: default
120+
resourceVersion: "62249"
121+
uid: 212f6090-34de-46dd-82eb-d721ace1b32a
122+
reason: Pulling
123+
reportingComponent: kubelet
124+
reportingInstance: minikube
125+
source:
126+
component: kubelet
127+
host: minikube
128+
type: Normal
129+
- apiVersion: v1
130+
count: 1
131+
eventTime: null
132+
firstTimestamp: "2026-03-17T08:45:30Z"
133+
involvedObject:
134+
apiVersion: v1
135+
fieldPath: spec.containers{log-test}
136+
kind: Pod
137+
name: log-test
138+
namespace: default
139+
resourceVersion: "62246"
140+
uid: c79a534d-9c74-45bf-acf4-5a9985c39c14
141+
kind: Event
142+
lastTimestamp: "2026-03-17T08:45:30Z"
143+
message: 'Successfully pulled image "nginx" in 2.135s (2.135s including waiting).
144+
Image size: 180542930 bytes.'
145+
metadata:
146+
creationTimestamp: "2026-03-17T08:45:30Z"
147+
name: log-test.189d9485beda4a95
148+
namespace: default
149+
resourceVersion: "62252"
150+
uid: 3eb2fd61-7b0b-4f62-b7c3-e5a4be893399
151+
reason: Pulled
152+
reportingComponent: kubelet
153+
reportingInstance: minikube
154+
source:
155+
component: kubelet
156+
host: minikube
157+
type: Normal
158+
- apiVersion: v1
159+
count: 1
160+
eventTime: null
161+
firstTimestamp: "2026-03-17T08:45:30Z"
162+
involvedObject:
163+
apiVersion: v1
164+
fieldPath: spec.containers{log-test}
165+
kind: Pod
166+
name: log-test
167+
namespace: default
168+
resourceVersion: "62246"
169+
uid: c79a534d-9c74-45bf-acf4-5a9985c39c14
170+
kind: Event
171+
lastTimestamp: "2026-03-17T08:45:30Z"
172+
message: Container created
173+
metadata:
174+
creationTimestamp: "2026-03-17T08:45:30Z"
175+
name: log-test.189d9485c0e87bde
176+
namespace: default
177+
resourceVersion: "62253"
178+
uid: df68a670-7684-4573-beff-b92e5a6bbdec
179+
reason: Created
180+
reportingComponent: kubelet
181+
reportingInstance: minikube
182+
source:
183+
component: kubelet
184+
host: minikube
185+
type: Normal
186+
- apiVersion: v1
187+
count: 1
188+
eventTime: null
189+
firstTimestamp: "2026-03-17T08:45:30Z"
190+
involvedObject:
191+
apiVersion: v1
192+
fieldPath: spec.containers{log-test}
193+
kind: Pod
194+
name: log-test
195+
namespace: default
196+
resourceVersion: "62246"
197+
uid: c79a534d-9c74-45bf-acf4-5a9985c39c14
198+
kind: Event
199+
lastTimestamp: "2026-03-17T08:45:30Z"
200+
message: Container started
201+
metadata:
202+
creationTimestamp: "2026-03-17T08:45:30Z"
203+
name: log-test.189d9485c5d61a22
204+
namespace: default
205+
resourceVersion: "62254"
206+
uid: edf2dbb3-11e1-49e6-bb91-6dbca2d51edb
207+
reason: Started
208+
reportingComponent: kubelet
209+
reportingInstance: minikube
210+
source:
211+
component: kubelet
212+
host: minikube
213+
type: Normal
214+
kind: List
215+
metadata:
216+
resourceVersion: ""
217+
```
218+
219+
</details>
220+
221+
出力されたイベントログの `reportingComponent` フィールドを確認すると、各イベントの出力元は以下です。
222+
223+
- **Scheduled** → `reportingComponent: default-scheduler`
224+
- **Pulling / Pulled / Created / Started** → `reportingComponent: kubelet`
225+
226+
概要で確認した通り、まずSchedulerがノードを割り当てた後、そのノードのkubeletがコンテナの起動処理を行っていることが、実際のログからも確認できました。
227+
228+
# Static PodとMirror Pod
229+
230+
ここまででpodが起動するまでの流れはわかったのですが、ここで1つ疑問が湧きました。
231+
232+
先ほど確認した通り、Podが起動するにはSchedulerによるノード割り当てが必要です。しかし、そのScheduler自身もPodとして動いています。となると、Schedulerを起動するためのSchedulerが必要で、そのSchedulerを起動するためにさらにSchedulerが必要で、、、と無限ループに陥ってしまいます。最初のSchedulerは一体どのように起動されているのでしょうか?
233+
234+
結論から言うと、 SchedulerはAPI Serverや別のSchedulerを経由せず、kubeletがマニフェストファイルから直接起動していました。
235+
236+
実際のSchedulerのマニフェストファイルを確認すると、以下のようになっています。kindが `Deployment` ではなく `Pod` となっており、Podデータそのものが定義されていることがわかります。
237+
238+
```yaml
239+
% minikube ssh
240+
$ sudo cat /etc/kubernetes/manifests/kube-scheduler.yaml
241+
apiVersion: v1
242+
kind: Pod
243+
metadata:
244+
labels:
245+
component: kube-scheduler
246+
tier: control-plane
247+
name: kube-scheduler
248+
namespace: kube-system
249+
spec:
250+
containers:
251+
- command:
252+
- kube-scheduler
253+
...
254+
```
255+
256+
このように、特定のディレクトリにマニフェストを配置し、API Serverを経由せずにkubeletが直接起動・管理するPodを**Static Pod**と呼びます。Kubernetesの起動時には、コントロールプレーンのkubeletがこの仕組みを利用して、API Serverやetcd、Schedulerなどの主要なコンポーネントをStatic Podとして起動しています。
257+
258+
ただ、このStatic PodはAPI Serverを経由しないため、etcd上に実体がありません。そのままでは、`kubectl get pods`で確認できず、クラスタ全体の状態を一元管理する上で不便です。そこでkubeletは、**Mirror Pod**と呼ばれる読み取り専用のPodを作成します。`kubectl get pods -n kube-system`で見える`kube-scheduler-minikube`は、実はこのMirror Podでした。
259+
260+
# 実験2:Schedulerを止めてみる
261+
262+
ここまでの話が本当なら、以下が成り立つはずです。
263+
264+
- Mirror Podを削除しても、Schedulerは止まらない(本体はマニフェストファイルだから)
265+
- マニフェストファイルを削除(移動)すると、Schedulerが止まる
266+
- Schedulerが止まると、新しいPodはノードに割り当てられずPendingのままになる
267+
268+
というわけで、さっそく試してみましょう。
269+
270+
## 1. Mirror Podを削除する
271+
272+
`kubectl delete pod`で`kube-scheduler-minikube`を削除してみます。しかし、何度削除しても、即座に復活し、`kubectl get pods`の結果に出てきます。
273+
274+
```sh
275+
% kubectl delete pod kube-scheduler-minikube -n kube-system
276+
pod "kube-scheduler-minikube" deleted from kube-system namespace
277+
% kubectl get pods -n kube-system
278+
NAME READY STATUS RESTARTS AGE
279+
...
280+
kube-scheduler-minikube 1/1 Running 0 5s
281+
...
282+
```
283+
284+
## 2. マニフェストファイルを移動する
285+
286+
次に`minikube ssh`でMinikubeのノード内入り、マニフェストファイルを他の場所に動かしてみます(削除すると戻すのが面倒なので`mv`にしました)。
287+
288+
すると、あれだけ何度削除しても復活していた`kube-scheduler-minikube`が、`kubectl get pods`から消えました。
289+
290+
```sh
291+
% minikube ssh
292+
$ sudo mv /etc/kubernetes/manifests/kube-scheduler.yaml /tmp/
293+
$ exit
294+
% kubectl get pods -n kube-system
295+
NAME READY STATUS RESTARTS AGE
296+
coredns-7d764666f9-bbkvl 1/1 Running 0 11h
297+
etcd-minikube 1/1 Running 0 11h
298+
kube-apiserver-minikube 1/1 Running 0 11h
299+
kube-controller-manager-minikube 1/1 Running 0 11h
300+
kube-proxy-fj8zg 1/1 Running 0 11h
301+
storage-provisioner 1/1 Running 0 11h
302+
```
303+
304+
## 3. Schedulerなしで新しいPodを作成する
305+
306+
このSchedulerがいない状態で、新しいPodを作成してみます。Podデータ自体は作成できましたが、STATUSが`Pending`のまま動かず、NODEも`<none>`のままです(必要な情報を出力するため`custom-columns`オプションをつけています)。
307+
308+
```sh
309+
% kubectl run new-pod --image=nginx
310+
pod/new-pod created
311+
% kubectl get pods -o custom-columns=NAME:.metadata.name,STATUS:.status.phase,NODE:.spec.nodeName
312+
NAME STATUS NODE
313+
hello-node-64fc5894d-57r8s Running minikube
314+
hello-node-64fc5894d-7cqrw Running minikube
315+
hello-node-64fc5894d-jl4pb Running minikube
316+
new-pod Pending <none>
317+
```
318+
319+
どれだけ待っても`Running`になることはありませんでした。
320+
321+
Schedulerがいないと、Podデータは作成されても、ノードが割り当てられないため、冒頭で確認したフローのSchedulerのステップで処理が止まってしまうことが実際の挙動でも確認できました。
322+
323+
# おわりに
324+
325+
解説動画や記事を見ても、Kubernetesの各概念やその関係性がピンとこず、「デプロイするとPodが作れてその中にコンテナがいるらしい」くらいの認識でした。そんな状態だったので、当然`kubectl`コマンドの意味がわかるはずもなく、結果としてAIの指示通りにコマンド打つマンと化していました。
326+
327+
しかし今回、小さな実験を通して手を動かしてみたことで、裏側で各コンポーネントがどのように連携して動いているのかを具体的に追うことができ、一気に理解が深まりました。特にSchedulerは、こうして実験してみなければ存在すら知らなかった概念だったため、非常に学びになりました。
328+
329+
今回の実験で、何度も消されたりファイルを移動させられたりと散々な目に遭わせてしまったSchedulerには、この場を借りて感謝と謝罪を述べて締めくくりたいと思います。
19.7 KB
Loading
92.3 KB
Loading

0 commit comments

Comments
 (0)