🎯 TL;DR

Pod 생성 ν”Œλ‘œμš° μš”μ•½ (정상 μΌ€μ΄μŠ€)

kubectl apply β†’ API Server β†’ ETCD β†’ Scheduler β†’ Kubelet β†’ Running
  0.1초        0.1초       0.5초     0.1초      5-30초

κ°€μž₯ 였래 κ±Έλ¦¬λŠ” 단계: 이미지 λ‹€μš΄λ‘œλ“œ (5-30초)
전체 μ†Œμš” μ‹œκ°„: 7-35초 (이미지 크기에 따라)

이 κΈ€μ—μ„œ λ°°μš°λŠ” 것:


πŸ’‘ μ™œ 이 글을 μ“°κ²Œ λ˜μ—ˆλ‚˜?

k3s둜 개인 ν”„λ‘œμ νŠΈλ₯Ό μš΄μ˜ν•˜λ©΄μ„œ 생성이 κ°œμΈν”„λ‘œμ νŠΈμ΄κ³  μ†Œκ·œλͺ¨μΈλ° 속도가 생각보닀 많이 κ±Έλ ΈλŠ”λ° 이 이유λ₯Ό μ°Ύκ³ , k8s ν™˜κ²½μ—μ„œ 각 μˆœμ„œμ—μ„œ μ–Όλ§ˆμ •λ„κ°€ 걸릴까 확인을 ν•˜λ €κ³  ν–ˆμŠ΅λ‹ˆλ‹€.

kubectl get pods --watch
NAME    READY   STATUS              AGE
nginx   0/1     ContainerCreating   30s  # ← μ™œ μ΄λ ‡κ²Œ 였래 κ±Έλ €?

간단 κ²°λ‘ Β Β» 이미지 λ‹€μš΄λ‘œλ“œμ˜ μ‹œκ°„μ΄ λŒ€λΆ€λΆ„μ΄λ‹€. (이미지λ₯Ό μ΅œμ ν™”ν•˜μž.)

이 κΈ€μ—μ„œλŠ”:


πŸ“Š 전체 흐름도

kubectl apply -f pod.yaml
    ↓ (1) 0.1초
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  API Server                          β”‚
β”‚  - 인증/인가 확인                    β”‚
β”‚  - YAML 검증                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               ↓ (2) 0.05초
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ETCD                                β”‚
β”‚  - Pod 정보 μ €μž₯                     β”‚
β”‚  - status: "Pending"                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               ↓ (3) 0.5-2초
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Scheduler                           β”‚
β”‚  - μ ν•©ν•œ λ…Έλ“œ μ°ΎκΈ°                  β”‚
β”‚  - nodeName κ²°μ •                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               ↓ (4) 0.1초
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Kubelet (μ›Œμ»€ λ…Έλ“œ)                 β”‚
β”‚  - 이미지 λ‹€μš΄λ‘œλ“œ ⭐ (κ°€μž₯ 였래!)   β”‚
β”‚  - μ»¨ν…Œμ΄λ„ˆ 생성                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               ↓ (5) 5-30초
        Pod Running βœ…

πŸ” 단계별 상세 뢄석

[단계 1] kubectl β†’ API Server (0.1초)

kubectl λͺ…λ ΉΒ Β» API Server둜 HTTPS μš”μ²­

# ~/.kube/config νŒŒμΌμ—μ„œ ν΄λŸ¬μŠ€ν„° 정보 읽기
cat ~/.kube/config

좜λ ₯:

clusters:
- cluster:
    server: https://192.168.1.100:6443  # ← API Server μ£Όμ†Œ

API Serverκ°€ ν•˜λŠ” 일:

1. 인증 (Authentication)

2. 인가 (Authorization - RBAC)

3. 검증 (Validation)

μ‹€μ œ μΈ‘μ •:

# time λͺ…λ ΉμœΌλ‘œ μΈ‘μ •
time kubectl apply -f simple-pod.yaml

# 좜λ ₯:
real    0m0.152s  # ← API Server 응닡 μ‹œκ°„

[단계 2] API Server β†’ ETCD (0.05초)

API ServerλŠ” 검증이 λλ‚˜λ©΄ ETCD에 μ €μž₯ν•œλ‹€.

ETCD에 μ €μž₯λ˜λŠ” λ‚΄μš© (λ‹¨μˆœν™”):

Key: /registry/pods/default/nginx
Value: {
  "metadata": {
    "name": "nginx",
    "namespace": "default"
  },
  "spec": {
    "containers": [...]
  },
  "status": {
    "phase": "Pending"  # ← 초기 μƒνƒœ
  }
}

싀무 포인트:

**직접 ETCD ν™•μΈν•˜κΈ° **

ETCDCTL_API=3 etcdctl get /registry/pods/default/nginx \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

β†’ μ‹€μ œ μ €μž₯된 데이터λ₯Ό λ³Ό 수 있음 (디버깅 μ‹œ 유용)


[단계 3] Scheduler μž‘λ™ (0.5-2초) ⭐⭐⭐

이 단계가 K8s의 핡심이닀. Schedulerκ°€ μ–΄λŠ λ…Έλ“œμ— Podλ₯Ό λ°°μΉ˜ν• μ§€ κ²°μ •ν•œλ‹€.

Scheduler의 2단계 μ•Œκ³ λ¦¬μ¦˜:

1단계: Filtering (λΆˆκ°€λŠ₯ν•œ λ…Έλ“œ μ œμ™Έ)

λͺ¨λ“  λ…Έλ“œ 검사:
β”œβ”€ CPU λΆ€μ‘± λ…Έλ“œ μ œμ™Έ
β”œβ”€ Memory λΆ€μ‘± λ…Έλ“œ μ œμ™Έ
β”œβ”€ NodeSelector 뢈일치 μ œμ™Έ
β”œβ”€ Taint/Toleration 뢈일치 μ œμ™Έ
└─ κ²°κ³Ό: κ°€λŠ₯ν•œ λ…Έλ“œ 리슀트

2단계: Scoring (점수 λ§€κΈ°κΈ°)

각 λ…Έλ“œμ— 점수 λΆ€μ—¬:
β”œβ”€ λ¦¬μ†ŒμŠ€ μ—¬μœ λ„ (κ°€μ€‘μΉ˜ 1)
β”œβ”€ Pod 뢄산도 (κ°€μ€‘μΉ˜ 1)
β”œβ”€ Affinity κ·œμΉ™ (κ°€μ€‘μΉ˜ 2)
└─ κ²°κ³Ό: 졜고 점수 λ…Έλ“œ 선택

μ‹€μ œ μ˜ˆμ‹œ:

초기 μƒνƒœ:
- node1: CPU 90%, Memory 80%
- node2: CPU 50%, Memory 60%  ← 점수 λ†’μŒ!
- node3: CPU 70%, Memory 85%

Scheduler κ²°μ •: node2 선택 βœ…

μ‹€μ œ μΈ‘μ •:

# Scheduler κ²°μ • μ‹œκ°„ 확인
kubectl get events --sort-by='.lastTimestamp' | grep Scheduled

# 좜λ ₯:
0s   Normal  Scheduled  pod/nginx  Successfully assigned default/nginx to node2

디버깅 팁:

# μ™œ 이 λ…Έλ“œμ— λ°°μ •λλŠ”μ§€ 확인
kubectl describe pod nginx

# Events μ„Ήμ…˜:
Events:
  Type    Reason     Message
  ----    ------     -------
  Normal  Scheduled  Successfully assigned to node2

[단계 4] Kubelet 감지 + 이미지 λ‹€μš΄λ‘œλ“œ (5-30초) ⭐⭐⭐

κ°€μž₯ 였래 κ±Έλ¦¬λŠ” 단계!

4-1. Kubelet이 감지 (0.1초)

Schedulerκ°€ nodeName을 node2둜 μ„€μ •ν•˜λ©΄, node2의 Kubelet이 Watch둜 κ°μ§€ν•œλ‹€.

Kubelet (node2):
"μ–΄? ETCD에 λ‚΄ λ…Έλ“œ 이름이 좔가됐넀!"
"이 Podλ₯Ό μ‹€ν–‰ν•΄μ•Όκ² λ‹€!"

4-2. 이미지 λ‹€μš΄λ‘œλ“œ (5-30초) ⭐⭐⭐

μ‹œκ°„μ΄ 였래 κ±Έλ¦¬λŠ” μž‘μ—…μ΄μ˜€λ‹€.

nginx 이미지 μ˜ˆμ‹œ:

docker images nginx
REPOSITORY   TAG      SIZE
nginx        latest   187MB  # ← μ•½ 6초 μ†Œμš” (λ‚΄ ν™˜κ²½)

이미지 Pull κ³Όμ •:

1. λ‘œμ»¬μ— 이미지 μžˆλ‚˜? 확인 (0.1초)
   └─ 있으면 β†’ μ¦‰μ‹œ μ»¨ν…Œμ΄λ„ˆ 생성 (1-2초)
   └─ μ—†μœΌλ©΄ β†’ λ‹€μš΄λ‘œλ“œ μ‹œμž‘

2. Docker Hubμ—μ„œ λ‹€μš΄λ‘œλ“œ
   nginx:latest (187MB)
   β”œβ”€ Layer 1: 50MB (2초)
   β”œβ”€ Layer 2: 37MB (1초)
   └─ Layer 3: 100MB (3초)
   합계: 6초

3. μ••μΆ• ν•΄μ œ & 검증 (1초)

4. μ»¨ν…Œμ΄λ„ˆ 생성 (1초)

μ‹€μ œ μΈ‘μ •:

# λ‘œμ»¬μ— 이미지 μ—†λŠ” μƒνƒœλ‘œ ν…ŒμŠ€νŠΈ
docker rmi nginx:latest

# Pod 생성 μ‹œκ°„ μΈ‘μ •
kubectl delete pod nginx
time kubectl apply -f nginx-pod.yaml && \
  kubectl wait --for=condition=ready pod/nginx --timeout=60s

# 좜λ ₯:
real    0m8.234s  # ← 8초 μ†Œμš” (이미지 λ‹€μš΄ 포함)
# 이미지 μžˆλŠ” μƒνƒœλ‘œ μž¬μ‹œλ„
kubectl delete pod nginx
time kubectl apply -f nginx-pod.yaml && \
  kubectl wait --for=condition=ready pod/nginx --timeout=60s

# 좜λ ₯:
real    0m2.145s  # ← 2초! (이미지 μΊμ‹œ μ‚¬μš©)

β†’ μš©λŸ‰ 차이도 많이 λ‚˜κ³ , 속도도 차이가 많이 λ‚œλ‹€.


[단계 5] μ»¨ν…Œμ΄λ„ˆ μ‹€ν–‰ (1초)

Container Runtime (containerd):
1. μ»¨ν…Œμ΄λ„ˆ 생성 (0.5초)
   └─ λ„€μž„μŠ€νŽ˜μ΄μŠ€, Cgroup μ„€μ •
   
2. λ„€νŠΈμ›Œν¬ μ„€μ • (0.3초)
   └─ CNI ν”ŒλŸ¬κ·ΈμΈ 호좜
   └─ IP ν• λ‹Ή (예: 10.244.1.5)
   
3. λ³Όλ₯¨ 마운트 (0.2초)
   └─ ConfigMap, Secret λ“±
   
4. ENTRYPOINT μ‹€ν–‰ (0.1초)
   └─ nginx ν”„λ‘œμ„ΈμŠ€ μ‹œμž‘

Kubelet이 API Server에 μƒνƒœ 보고:

ETCD μ—…λ°μ΄νŠΈ:
{
  "status": {
    "phase": "Running",  # ← Pendingμ—μ„œ λ³€κ²½!
    "containerStatuses": [{
      "ready": true,
      "restartCount": 0,
      "state": {
        "running": {
          "startedAt": "2025-10-13T10:05:30Z"
        }
      }
    }]
  }
}

⏱️ 단계별 μ†Œμš” μ‹œκ°„ 정리

단계 μ»΄ν¬λ„ŒνŠΈ μ†Œμš” μ‹œκ°„ λΉ„κ³ 
1 API Server 50-150ms kubectl 응닡
2 ETCD 10-50ms μ €μž₯
3 Scheduler 100-500ms λ…Έλ“œ 선택
4-1 Kubelet 100ms 감지
4-2 Runtime 5-8초 이미지 λ‹€μš΄ ⭐
4-3 Runtime 1-2초 μ»¨ν…Œμ΄λ„ˆ 생성
합계 Β  7-12초 이미지 없을 λ•Œ
합계 Β  2-3초 이미지 μžˆμ„ λ•Œ βœ…

이미지 크기별 비ꡐ (μ‹€μΈ‘):

이미지 크기 λ‹€μš΄λ‘œλ“œ μ‹œκ°„ 전체 μ‹œκ°„
nginx:alpine 42MB 2초 4초 ⭐
nginx:latest 187MB 6초 8초
python:3.9 915MB 30초 32초

🎬 μ‹€μ‹œκ°„ κ΄€μ°°ν•˜κΈ° ⭐⭐⭐

3개 ν„°λ―Έλ„λ‘œ μ „ κ³Όμ • κ΄€μ°° (싀무 ν•„μˆ˜ μŠ€ν‚¬!)

# 터미널 1: Pod μƒνƒœ μ‹€μ‹œκ°„ 보기
kubectl get pods --watch

# 터미널 2: 이벀트 μ‹€μ‹œκ°„ 보기 (κ°€μž₯ μ€‘μš”!)
kubectl get events --watch

# 터미널 3: Pod 생성
kubectl apply -f nginx-pod.yaml

μ‹€μ œ 좜λ ₯:

터미널 1 (Pod μƒνƒœ):

NAME    READY   STATUS    AGE
nginx   0/1     Pending   0s
nginx   0/1     Pending   0s      # ← Scheduler μž‘λ™ μ „
nginx   0/1     ContainerCreating  2s  # ← λ…Έλ“œ 배정됨
nginx   1/1     Running   8s      # ← μ™„λ£Œ!

터미널 2 (이벀트):

LAST SEEN   TYPE    REASON      MESSAGE
0s          Normal  Scheduled   Successfully assigned default/nginx to node1
2s          Normal  Pulling     Pulling image "nginx:latest"
7s          Normal  Pulled      Successfully pulled image
8s          Normal  Created     Created container nginx
8s          Normal  Started     Started container nginx

β†’ μ΄λŸ°μ‹μœΌλ‘œ μ–΄λ–€κ²Œ μ˜€λž˜κ±Έλ¦¬λŠ”μ§€ λͺ¨λ‹ˆν„°λ§ν•  수 μžˆλ‹€.

ν•œ 쀄 λͺ…λ ΉμœΌλ‘œ 보기:

kubectl get events --sort-by='.lastTimestamp' | tail -20

πŸ’‘ 이 κ³Όμ •μ—μ„œ 배운 점

1. imagePullPolicy 섀정은 ν•„μˆ˜! ⭐⭐⭐

Before (κΈ°λ³Έκ°’):

spec:
  containers:
  - name: nginx
    image: nginx:latest
    # imagePullPolicy μ—†μŒ β†’ Always둜 λ™μž‘

β†’ 맀번 이미지 확인, μ•½ 8초 μ†Œμš”

After (μ΅œμ ν™”):

spec:
  containers:
  - name: nginx
    image: nginx:latest
    imagePullPolicy: IfNotPresent  # 둜컬 μš°μ„ !

β†’ 둜컬 이미지 μ‚¬μš©, μ•½ 2초 μ†Œμš” βœ…

싀무 적용:


2. alpine 이미지λ₯Ό μ“°μž ⭐⭐⭐

Before:

nginx:latest  # 187MB β†’ 6초

After:

nginx:alpine  # 42MB β†’ 2초  ⭐

μ‹€μ œ 비ꡐ:

# nginx:latest ν…ŒμŠ€νŠΈ
time kubectl apply -f nginx-pod.yaml && \
  kubectl wait --for=condition=ready pod/nginx --timeout=60s
# real    0m8.234s

# nginx:alpine ν…ŒμŠ€νŠΈ
time kubectl apply -f nginx-alpine-pod.yaml && \
  kubectl wait --for=condition=ready pod/nginx-alpine --timeout=60s
# real    0m4.156s

β†’ 거의 절반!


3. μ΄λ²€νŠΈλŠ” λ””λ²„κΉ…μ˜ 보물창고 ⭐⭐⭐

# λ­”κ°€ μ΄μƒν•˜λ©΄ 무쑰건 이것뢀터!
kubectl get events --sort-by='.lastTimestamp'

사둀 μ˜ˆμ‹œ :

Podκ°€ 30초째 ContainerCreating인데 원인을 λͺ¨λ₯Ό λ•Œ:

kubectl describe pod my-app

# Events:
Events:
  Type    Reason     Message
  Normal  Scheduled  Successfully assigned to node1
  Normal  Pulling    Pulling image...
  Warning Failed     Failed to pull image: timeout  # ← 원인!

β†’ λ„€νŠΈμ›Œν¬ νƒ€μž„μ•„μ›ƒ! Registry μ£Όμ†Œ ν™•μΈν•˜λ‹ˆ μ˜€νƒ€μ˜€μŒ

디버깅 체크리슀트:

1. kubectl get pods  # μƒνƒœ 확인
2. kubectl describe pod <name>  # Events 확인
3. kubectl logs <name>  # μ•± 둜그 확인

β†’ 이 μˆœμ„œλ‘œ 90% 문제 ν•΄κ²° κ°€λŠ₯!


πŸš€ 배포 속도 μ΅œμ ν™” μš”μ•½

λ‚΄κ°€ μ μš©ν•œ 3κ°€μ§€:

  1. alpine 이미지 μ‚¬μš©
    nginx:latest β†’ nginx:alpine
    187MB β†’ 42MB
    
  2. imagePullPolicy μ„€μ •
    imagePullPolicy: IfNotPresent
    
  3. 둜컬 Registry ꡬ성 (선택)
    Harbor μ„€μΉ˜ β†’ λ‚΄λΆ€λ§μ—μ„œ 3초 μ•ˆμ— λ‹€μš΄
    

κ²°κ³Ό:


κ΄€λ ¨ λ©΄μ ‘ μ˜ˆμƒ 질문

Q: β€œPod 생성이 느린데 μ–΄λ””λ₯Ό ν™•μΈν•˜μ‹œκ² μ–΄μš”?”

A: β€œλ¨Όμ € kubectl get events --watch둜 μ–΄λŠ λ‹¨κ³„μ—μ„œ μ‹œκ°„μ΄ κ±Έλ¦¬λŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. λŒ€λΆ€λΆ„ 이미지 λ‹€μš΄λ‘œλ“œκ°€ 원인이라 imagePullPolicyλ₯Ό IfNotPresent둜 λ°”κΎΈκ±°λ‚˜ alpine 이미지λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€. μ‹€μ œλ‘œ 제 ν”„λ‘œμ νŠΈμ—μ„œ 이 λ°©λ²•μœΌλ‘œ 배포 μ‹œκ°„μ„ 35μ΄ˆμ—μ„œ 8초둜 μ€„μ˜€μŠ΅λ‹ˆλ‹€.”

Q: β€œK8sμ—μ„œ κ°€μž₯ μ€‘μš”ν•œ μ»΄ν¬λ„ŒνŠΈλŠ” λ­”κ°€μš”?”

A: β€œETCDμž…λ‹ˆλ‹€. μœ μΌν•œ λ°μ΄ν„°λ² μ΄μŠ€λΌμ„œ ETCD μ—†μ΄λŠ” 아무것도 μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ 제 k3s ν™˜κ²½μ—μ„œλŠ” 맀일 μžλ™μœΌλ‘œ ETCD 백업을 S3에 μ €μž₯ν•˜λ„λ‘ μ„€μ •ν–ˆμŠ΅λ‹ˆλ‹€.”


πŸ“š μ°Έκ³  자료


μž‘μ„±μΌ: 2025-10-13
ν™˜κ²½: k3s v1.28, 둜컬 3-node cluster