개념
쿠버네티스 개념과 구조
Kubernetes(쿠버네티스)는 여러 개의 컨테이너를 자동으로 배포하고 관리하는 시스템으로 크게 두 영역으로 나뉜다.
┌──────────── Control Plane (두뇌) ────────────
│ kube-apiserver → 명령 수신 및 저장
│ scheduler → Pod을 어떤 노드에 배치할지 결정
│ controller-manager → 상태 감시 및 복구 (자동화 핵심)
│ etcd → 모든 상태 저장 (DB)
└──────────────────────────────────────────────
↓
┌──────────────────── Node (손발) ──────────────
│ kubelet → Pod 상태 보고 및 실행 요청
│ container runtime → 실제 컨테이너 실행 (Docker 등)
│ kube-proxy → 네트워크 연결 담당
└──────────────────────────────────────────────
Control Plane이 “두뇌”라면,
Node는 “작업자(Worker)”로 실제 컨테이너가 도는 곳이다.
쿠버네티스와 런타임의 관계
Kubernetes는 컨테이너를 직접 실행하는 프로그램이 아닌 여러 서버(노드) 위에서 컨테이너들이 잘 돌아가도록 명령을 내리고 상태를 관리하는 상위 제어 시스템이다. 그렇기에 지휘를 뜻하는 컨테이너 오케스트레이터 라고 불린다.
하지만 실제로 컨테이너를 띄우려면 컨테이너를 만들고 실행할 수 있는 프로그램이 필요한데 이를 Container Runtime이 담당한다.
[사용자 / kubectl]
↓
[Control Plane]
├── API Server
├── Scheduler
├── Controller Manager
└── etcd
↓
[Node (Worker)]
├── Kubelet
├── Kube Proxy
└── Container Runtime (containerd, CRI-O)
↓
[Operating System + Kernel (cgroups, namespace)]
- 사용자가 kubectl 명령을 내리면, 이 명령은 Control Plane의 API Server로 전달된다. (API Server는 클러스터의 모든 명령이 통과하는 관문 역할을 하고 클러스터의 현재 상태는 etcd라는 키-값 저장소에 저장된다.)
- Scheduler가 어떤 노드에 pod를 배치할지 결정한다.
- Controller Manager는 Pod가 사라지거나 실패했을 때, 다시 만들어주는 등 클러스터가 항상 목표 상태(desired state)를 유지하도록 돕는다.
- 노드의 Kubelet은 Control plane으로부터 받은 명령을 수행하고 컨테이너의 상태를 감시하며 결과를 다시 보고한다.
- Kube Proxy는 노드 내외부의 네트워크 통신을 담당하여 Pod 간의 연결과 서비스 접근을 가능하게 한다.
- 실제로 컨테이너를 띄우기 위해서는 Container Runtime이 필요하다. 이때, Kubelet은 CRI라는 표준 인터페이스를 사용해 Runtime에 요청을 보내며, Runtime은 내부적으로 runc등의 하위 실행 도구를 호출해 리눅스 커널 기능(namespace, cgroups)을 이용해 컨테이너를 생성한다. 쿠버네티스에서는 이러한 런타임으로 (Containerd, CRI-O, Docker) 등이 사용된다.
쿠버네티스 리소스 흐름
쿠버네티스에서 리소스(Resource)란 쿠버네티스 API에서 관리되는 객체(Object)로 클러스터의 구성요소를 YAML이나 JSON 형태로 정의한 것을 의미한다. 위에서 정리했던 Control plane과 Node가 협력해서 여러 리소스들을 실제로 동작시키는데 한 번 자세히 살펴보자.
먼저 Control Plane은 명령을 받고 상태를 저장하고, 실행을 지시하는 계층으로, 쿠버네티스의 모든 리소스들이 정의되고 관리되는 중심이다. Contrl plane이 다루는 리소스들은 다음과 같다.
| 리소스 | 역할 | 처리 위치 |
| Deployment | 애플리케이션의 “목표 상태(desired state)” 정의 (예: Pod 몇 개 유지) | Controller Manager 내부의 Deployment Controller가 관리 |
| ReplicaSet | Deployment가 만드는 하위 리소스. 동일한 Pod 여러 개 유지 | Controller Manager의 ReplicaSet Controller |
| Job / CronJob / StatefulSet / DaemonSet | Pod을 특정 방식으로 관리하는 고급 워크로드 | Controller Manager |
| Service | Pod 접근용 고정 IP 생성, 네트워크 규칙 설정 | Control Plane이 관리하고 Node의 Kube Proxy와 연동 |
| Ingress | HTTP/HTTPS 트래픽을 Service로 라우팅 | Control Plane에서 정의, Ingress Controller가 관리 |
| ConfigMap / Secret | 애플리케이션 환경 변수 및 비밀정보 저장 | Control Plane에서 관리 |
| Namespace | 리소스들을 논리적으로 분리 | Control Plane에서 관리 |
즉, Control Plane은 리소스 정의 + 상태 감시 + 실행 명령을 담당하고 여기서 정의된 내용들은 etcd에 저장되고 controller들이 이를 감시하여 실제 Node 쪽으로 명령을 내린다.
다음 Node는 Control plane의 명령을 받아서 실제로 컨테이너를 실행하는 서버로 다루는 리소스들은 다음과 같다.
| 리소스 | 역할 | 처리 위치 |
| Pod | 컨테이너가 실제로 실행되는 최소 단위 | Node에서 Kubelet이 관리 |
| Container | Pod 내부에서 실행되는 애플리케이션 | Container Runtime이 실행 |
| Service Endpoint | Service가 연결하는 실제 Pod의 IP | Kube Proxy가 Node에 설정 |
즉, Node는 Control Plane이 요청한대로 Pod를 생성하고 그 안에서 컨테이너를 실행하며, Service를 통해 네트워크로 연결되도록 설정한다.
전체 흐름
사용자 (kubectl apply -f deployment.yaml)
↓
[ Control Plane ]
├─ API Server → etcd에 리소스 저장
├─ Controller Manager (Deployment Controller)
│ ↓
│ ReplicaSet 리소스 생성
│ ↓
│ Pod 리소스 생성
├─ Scheduler → Pod을 실행할 Node 선택
↓
[ Node (Worker) ]
├─ Kubelet이 Pod 실행 명령 수신
├─ Container Runtime(containerd)이 컨테이너 생성
├─ Kube Proxy가 Service 네트워크 설정
↓
애플리케이션 실행 (예: Flask app.py)
오케스트레이션 핵심 기능
오케스트레이션은 여러 개의 컨테이너들을 자동으로 배포, 조율, 복제, 복구, 확장하는 자동 관리 체계를 의미한다
Docker 등의 컨테이너 기술은 개별 애플리케이션을 실행하는 데 장점이 있지만 다음과 같은 문제가 생길 수 있다.
| 문제 | 예시 |
| 파드(컨테이너) 수가 많음 | 수백 개 이상 배포·관리 어려움 |
| 장애 발생 시 복구 필요 | 수동 재시작 불가능 |
| 버전 업데이트 | 중단 없이 교체 어려움 |
| 트래픽 분산 | 부하가 많을 때 자동 확장 필요 |
| IP 변동 문제 | 파드 IP가 바뀌면 서비스 단절 |
쿠버네티스는 이 모든걸 자동으로 처리하는데 핵심 기능을 차례대로 살펴보자
1. 스케줄링 (Scheduling)
어떤 노드에 어떤 파드를 띄울지 자동 결정하는 과정으로 kube - scheduler 가 담당한다.
※ 내부 동작
- 사용자가 Deployment를 생성 -> API server가 요청을 받고 etcd에 새 파드 생성 요청을 기록 (논리적 생성)
- Scheduler 가 대기 중인 파드 목록 확인 -> 노드의 리소스 상태 비교 후 최적 노드 선정
- 해당 노드의 kubelet에게 파드 생성 명령을 전달 (물리적 생성)
※ 고려 요소
| CPU / 메모리 여유 | 자원 부족 노드는 제외 |
| 노드 라벨(Label) | 특정 앱은 특정 노드에만 배포 가능 |
| Affinity / Anti-Affinity | 특정 파드끼리 함께 혹은 떨어지게 배포 |
| Taint / Toleration | 특정 노드 접근 제한 조건 설정 가능 |
2. 복제 관리 (Replication / Self-healing)
파드 개수를 항상 일정하게 유지하는 기능으로 Replicaset이 담당한다.
※ 내부 동작
1. Deployment에서 replicas : 3 으로 설정
2. Replicaset이 3개의 파드 생성 -> 파드 하나가 죽으면 replicaset이 자동으로 하나 생성
3. 롤링 업데이트 (rollout, rollback)
애플리케이션 버전을 무중단으로 교체하는 기능으로 Deployment가 담당
※ 내부 동작
1. Deployment 업데이트 ( 기존 replicaset(v1) -> 새 버전 (v2))
2. Deployment가 새 Replicaset (v2) 생성
3. pod를 하나씩 교체 (v2 1개 생성 -> 정상 작동 확인 -> v1 1개 종료)
4. 모든 파드가 v2로 바뀌면 rollout 완료
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # 한 번에 중단 가능한 최대 파드 수
maxSurge: 1 # 한 번에 추가 생성 가능한 새 파드 수
무중단 배포가 특징이며, 버전 이력 관리, rollback (kubectl rollout undo) 가능
4. 셀프 힐링 (Self - healing)
장애 발생 시 자동 복구 기능
※ 동작 원리
- etcd에 저장된 클러스터의 이상적 상태(Desired state)와 현재 상태를 Controller manager가 비교
- 상태가 다르면 자동으로 조치 시작
(1) kubelet: 파드 상태 보고
↓
(2) API Server & etcd: 상태 저장
↓
(3) Controller Manager: Desired State와 비교
↓
(4) 새 파드 생성 요청 (API Server에 등록)
↓
(5) Scheduler: 새 노드 선택
↓
(6) kubelet: 새 파드 실행(containerd)
5. 서비스 디스커버리 ( Service Discovery)
IP가 바뀌는 파드들을 고정된 엔드포인트로 접근 가능하게 함
※ 동작 원리
- 파드의 IP는 재시작 마다 바뀜
- Service가 라벨을 통해 파드 그룹을 인식 후 고정된 ClusterIP + DNS 이름을 부여
- kube proxy가 패킷을 자동으로 살아있는 파드로 전달
Service: myapp (10.96.0.15)
├─ Pod-1 (10.1.1.20)
├─ Pod-2 (10.1.1.21)
└─ Pod-3 (10.1.1.22)
6. 로드 밸런싱 ( Load Balancing)
여러 파드로 트래픽을 균등하게 분산
※ 동작 방식
kube proxy는 각 노드에 iptables/IPVS 규칙을 설정
외부 요청이 들어오면 자동으로 여러 파드에 라운드로빈 분배
7. 자동 스케일링 (Auto Scaling)
부하 (cpu, 메모리)에 따라 파드 개수를 자동 조정
※ 동작 원리
1. Metrics Server가 각 파드의 CPU/ 메모리 사용률 측정
2. HAP가 평균 부하 계산
3. 설정 기준 초과 시 / replicas 증가
4. 기준 이하 시 / replicas 감소
8. 구성 및 보안 관리 (Config / secret / RBAC)
config map
- 환경 변수, 설정 파일 등 민감하지 않은 정보 관리
- 파드에서 envFrom 또는 Volume으로 주입
Secret
- 비밀번호, API Key 등 민감한 정보 저장 (Base64 인코딩)
RBAC (Role-Based Access Control)
- 사용자/서비스 계정별 권한 제어
- “누가 무엇을 할 수 있는가” 정의
Namespace
- 클러스터 내 리소스를 팀/환경 단위로 논리적으로 분리
실습
powershell 기준 flask를 이용한 간단한 minikube 실습
1. Flask 애플리케이션 작성 (Notepad로 직접)
PowerShell에서 실행:
notepad app.py
→ 엔터 치면 메모장이 열림. 아래 코드 붙여넣고 Ctrl+S로 저장 후 닫기.
📄 app.py
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/summary", methods=["GET"])
def get_summary():
return jsonify({"message": "요약 서비스 작동 중"})
@app.route("/summary", methods=["POST"])
def post_summary():
data = request.get_json()
text = data.get("text", "")
summary = text[:100] + "..."
return jsonify({"summary": summary})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
- Flask 앱은 /summary 경로에 두 가지 요청을 처리:
- GET 요청: 단순 서버 상태 확인 (“요약 서비스 작동 중”)
- POST 요청: JSON 형태의 본문({"text": ...})을 받아 간단히 요약문 생성
- host='0.0.0.0' → 컨테이너 외부에서도 접속 가능하게 함.
- port=5000 → Flask 기본 포트. 쿠버네티스에서 Pod의 containerPort와 연결됨.
즉, 이 파일이 Pod 안의 컨테이너에서 실제로 실행될 프로세스(app.py) 가 된다.
Dockerfile 작성 (requirements.txt 없이)
notepad Dockerfile
📄 Dockerfile
FROM python:3.9
WORKDIR /app
COPY app.py .
RUN pip install flask
CMD ["python", "app.py"]
- Flask만 간단히 pip로 직접 설치 (requirements.txt 없이).
- /app 디렉터리에 app.py 복사 후 실행.
- CMD가 컨테이너 시작 시 실행할 명령(python app.py) 지정.
Dockerfile은 Container Runtime(containerd 등) 이 이해할 수 있는 “환경 구성 스크립트”
즉, 런타임이 이 이미지를 받아 실제 Pod 안에서 Flask를 실행하게 된다.
Minikube와 Docker 환경 설정
PowerShell에서 순서대로 실행
# 1️⃣ Minikube 시작
minikube start --driver=docker
# 2️⃣ Docker 환경을 Minikube 내부로 설정
minikube docker-env | Invoke-Expression
# 3️⃣ Flask 앱 Docker 이미지 빌드
docker build -t news-summary-app .
# 4️⃣ 이미지 목록 확인
docker images
- Minikube는 내부에 Docker 데몬이 따로 존재
- minikube docker-env | Invoke-Expression 명령을 실행해야 PowerShell의 Docker 명령이 Minikube 내부 Docker와 연결
- docker build → Flask 애플리케이션을 컨테이너 이미지(news-summary-app)로 빌드.
이 시점에서 Minikube 안의 Docker 데몬에 이미지가 등록되어
kubectl apply 시 바로 사용할 수 있습니다 (Docker Hub에 push 필요 없음).
Deployment 리소스 생성 (Pod 자동 관리)
📄 deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: news-summary-deploy
spec:
replicas: 3
selector:
matchLabels:
app: news-summary
template:
metadata:
labels:
app: news-summary
spec:
containers:
- name: flask-container
image: news-summary-app:latest
ports:
- containerPort: 5000
적용:
kubectl apply -f deployment.yaml
- Deployment 리소스는 Control Plane의 Controller Manager가 관리하는 리소스
- 내부적으로 ReplicaSet → Pod을 생성
- replicas: 3 → 동일한 Flask Pod 3개를 유지 (부하 분산, 가용성 확보).
- labels.app: news-summary → Service가 Pod를 연결할 때 기준이 됨.
결국 Deployment는 “이 Flask 앱을 항상 3개 띄워줘”라는 쿠버네티스 명령이다.
Pod이 죽거나 노드가 재시작돼도 Controller가 자동으로 다시 띄우게 됨
Service 리소스 생성 (label 기반 Pod 연결)
📄 service.yaml
apiVersion: v1
kind: Service
metadata:
name: news-summary-service
spec:
selector:
app: news-summary
ports:
- port: 80
targetPort: 5000
type: NodePort
적용:
kubectl apply -f service.yaml
- selector.app: news-summary → Deployment의 Pod label(app: news-summary) 과 매칭됨.
- 즉, Service는 label을 기준으로 Pod들을 자동으로 찾고 연결
- port: 80 → 외부에서 접근할 포트
- targetPort: 5000 → Flask 앱이 컨테이너 내부에서 열고 있는 포트
- type: NodePort → Minikube 클러스터 외부(로컬 PC)에서도 접근 가능하게 설정.
Service는 쿠버네티스의 네트워크 리소스로,
Pod의 IP가 계속 바뀌더라도 label을 기준으로 자동 라우팅해준다.
내부적으로 Node의 Kube Proxy가 iptables 규칙을 수정해 트래픽을 분배
리소스 상태 확인
# 현재 배포된 리소스 확인
kubectl get all
# Pod 상세 확인
kubectl get pods -o wide
# Service 상세 확인
kubectl get svc
Minikube에서 Service 주소 확인 및 접근
Service URL 확인:
minikube service news-summary-service --url
예시 출력:
http://127.0.0.1:50324
실제 동작 테스트 (curl)
GET 요청 (상태 확인)
curl http://127.0.0.1:50324/summary
응답:
{"message": "요약 서비스 작동 중"}
POST 요청 (요약 기능)
curl -X POST http://127.0.0.1:50324/summary `
-H "Content-Type: application/json" `
-d '{"text":"오늘 주요 뉴스는 인공지능 기술이 빠르게 발전하고 있다."}'
응답:
{"summary": "오늘 주요 뉴스는 인공지능 기술이 빠르게 발전하고 있다...."}
- curl은 클라이언트(사용자) 역할.
- 요청 → Service (Cluster IP) → Kube Proxy (노드 네트워크 계층) → Pod 중 하나로 라우팅 → Flask app.py 처리 후 응답 반환.
- Pod가 여러 개일 경우, Service가 로드밸런싱 처리.
| 단계 | 구성 요소 | 역할 | 실행 계층 |
| 1 | app.py | Flask 애플리케이션 코드 (요약 API) | Pod 내부 컨테이너 |
| 2 | Dockerfile | 런타임 이미지 정의 | Container Runtime |
| 3 | Deployment | Pod 자동 생성·유지 | Control Plane |
| 4 | Service | label 기반 Pod 연결, 외부 접근 IP 제공 | Control Plane + Node |
| 5 | curl | 클라이언트 테스트 도구 | 사용자 측 |

