# 1.포드, 레플리카 셋, 디플로이먼트, 서비스
# 개요
- 컨테이너 오케스트레이션 도구
- 서버 자원 클러스터링, MSA 구조의 컨테이너 배포, 페일오버 처리 등
- 컨테이너 기반의 클라우드를 운영할 때 필요한 기능과 컴포넌트 커스터마이징 가능
- (e.g. PV, 장애 복구, 스케줄링, 오토 스케일링 등)
- 여러 개의 노드로 구성됨(마스터, 워커)
# 쿠버네티스 노드의 역할
- 마스터 : 쿠버네티스가 제대로 동작할 수 있게 클러스터 관리하는 역할
- 워커 : 애플리케이션 컨테이너 생성
# Pod
- 컨테이너 애플리케이션의 기본 단위
- 1개 이상의 컨테이너로 구성된 컨테이너의 집합
# nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: my-nginx-pod
spec:
containers:
- name: my-nginx-container
image: nginx:latest
ports:
- containerPort: 80
protocol: TCP
kubectl apply -f명령어로 쿠버네티스에 생성 가능kubectl describe명령어로 리소스 정보를 볼 수 있음
$ kubectl describe pods my-nginx-pod
Name: my-nginx-pod
Namespace: default
Priority: 0
Service Account: default
Node: rpi4b/192.168.0.7
Start Time: Fri, 10 Oct 2025 12:38:38 +0900
Labels: <none>
Annotations: <none>
Status: Running
IP: 10.42.0.129 # 클러스터 내부에서 접속가능한 IP
IPs:
IP: 10.42.0.129
Containers:
my-nginx-container:
...
kubectl logs로 로그 확인 가능kubectl delete -f명령어로 쿠버네티스 오브젝트 삭제 가능
# Pod vs Docker Container
- 쿠버네티스가 'Pod'라는 새로운 개념을 도입한 이유는 리눅스 네임스페이스를 공유하는 여러 컨테이너를 추상화된 집합으로 사용하기 위함
- 하나의 포드는 하나의 완전한 애플리케이션으로써 만들어져야 함
- nginx 컨테이너 1개 (o)
- nginx 컨테이너 2개 (x : nginx 컨테이너는 그 자체만으로 완전한 애플리케이션이기 때문)
- nginx 컨테이너 + 로그 수집 컨테이너 (o : 주 컨테이너에 대해 기능 확장을 위한 사이드카 컨테이너가 포함되어야 할 수 있음)
# 사이드카 컨테이너
- 포드에 정의된 부가적인 컨테이너
- 주 컨테이너에 대해 기능 확장을 위한 컨테이너
- 한 포드 내의 다른 컨테이너와 네트워크 환경 등을 공유
# Replica Set
- 일정 개수의 포드를 유지하는 컨트롤러
# 레플리카 셋을 사용하는 이유?
- 정해진 수의 동일한 포드가 항상 실행되도록 관리
- 포드만 사용한다면 원하는 컨테이너 개수만큼 직접 생성해줘야 함
- 노드 장애 등의 이유로 포드를 사용할 수 없다면 다른 노드에서 포드 다시 실행 * 포드만 사용한다면 사용 불가능한 상태로 남아있음 → 결국 동일한 포드를 안정적으로 여러 개 실행하고 장애가 생기더라도 정해진 포드 개수를 유지하기 위함
# ---레플리카 셋 정의---
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: replicaset-nginx
spec:
replicas: 3
selector:
matchLabels:
app: my-nginx-pods-label
# ---포드 정의---
template:
metadata:
name: my-nginx-pod
labels:
app: my-nginx-pods-label
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
- spec.selector.matchLabels를 통해 생성해야 하는 포드를 찾음
- 즉
app: my-nginx-pods-label라벨을 가지는 포드의 개수가 replicas 항목에 정의된 숫자와 일치하지 않으면 포드 템플릿으로 포드 생성
# Deployment
- 레플리카셋, 포드의 배포를 관리
- 실제 쿠버네티스 운영환경에서는 레플리카셋과 포드의 정보를 정의하는
Deployment라는 오브젝트를 정의해 사용 - 디플로이먼트는 레플리카셋의 상위 오브젝트 → 디플로이언트를 생성하면 해당 디플로이먼트에 대응하는 레플리카셋도 함께 생성됨
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: my-nginx
template:
metadata:
name: my-nginx-pod
labels:
app: my-nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
- 디플로이먼트, 레플리카 셋, 포드가 함께 생성되는 모습
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
my-nginx-deployment 3/3 3 3 21s
$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
my-nginx-deployment-9b459b4c5 3 3 3 26s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-nginx-deployment-9b459b4c5-cst47 1/1 Running 0 98s
my-nginx-deployment-9b459b4c5-gzwtc 1/1 Running 0 98s
my-nginx-deployment-9b459b4c5-lpklh 1/1 Running 0 98s
# 디플로이먼트를 사용하는 이유?
- 애플리케이션의 업데이트와 배포를 더욱 편하기 만들기 위함
- 애플리케이션 업데이트 시 레플리카 셋의 변경 사항을 관리하는 revision을 남겨 롤백을 가능하게 해줌
- 무중단 서비스를 위해 포드의 롤링 업데이트 전략을 지정할 수 있음
- ex :
--record옵션 사용
$ kubectl apply -f deployment-nginx.yaml --record # --record 옵션으로 이전 정보를 리비전으로써 보존
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/my-nginx-deployment created
$ kubectl set image deployment my-nginx-deployment nginx=nginx:1.11 --record # 이미지 변경
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/my-nginx-deployment image updated
$ kubectl get rs # 오류 발생
NAME DESIRED CURRENT READY AGE
my-nginx-deployment-759b76bcd5 2 2 0 60s
my-nginx-deployment-9b459b4c5 2 2 2 99s
$ kubectl get pods # 오류 발생
NAME READY STATUS RESTARTS AGE
my-nginx-deployment-759b76bcd5-4jccd 0/1 CrashLoopBackOff 4 (29s ago) 119s
my-nginx-deployment-759b76bcd5-vk9ck 0/1 CrashLoopBackOff 3 (48s ago) 97s
my-nginx-deployment-9b459b4c5-n29cz 1/1 Running 0 2m38s
my-nginx-deployment-9b459b4c5-v4sq8 1/1 Running 0 2m38s
$ kubectl rollout history deployment my-nginx-deployment # 리비전 확인
deployment.apps/my-nginx-deployment
REVISION CHANGE-CAUSE
1 kubectl apply --filename=deployment-nginx.yaml --record=true
2 kubectl set image deployment my-nginx-deployment nginx=nginx:1.11 --record=true
$ kubectl rollout undo deployment my-nginx-deployment --to-revision=1 # 리비전 1로 롤백
deployment.apps/my-nginx-deployment rolled back
$ kubectl get rs # 롤백 완료
NAME DESIRED CURRENT READY AGE
my-nginx-deployment-759b76bcd5 0 0 0 2m36s
my-nginx-deployment-9b459b4c5 3 3 3 3m15s
$ kubectl get pods # 롤백 완료
NAME READY STATUS RESTARTS AGE
my-nginx-deployment-9b459b4c5-7xcbx 1/1 Running 0 11s
my-nginx-deployment-9b459b4c5-n29cz 1/1 Running 0 3m21s
my-nginx-deployment-9b459b4c5-v4sq8 1/1 Running 0 3m21s
# Service
- 포드를 연결하고 외부에 노출하는 역할
- 기존에
kubectl describe명령어로 확인한 포드 내부 IP는 클러스터 내부에서만 접근 가능 + 영속적이지 않음 - 서비스는 포드에 접근하기 위한 규칙을 정의
- 여러 개의 포드에 쉽게 접근할 수 있도록 고유한 도메인 네임 부여
- 여러 개의 포드에 접근할 때, 요청을 분산하는 로드 밸런서 기능
- 포드를 외부로 노출
# 서비스 종류
- ClusterIP 타입 : 쿠버네티스 내부에서만 포드들에 접근할 때 사용. 외부로 포드를 노출하지 않고 쿠버네티스 클러스터 내부에서만 사용되는 포드에 적합
- NodePort 타입 : 포드에 접근할 수 있는 포트를 클러스터의 모든 노드에 동일하게 개방. 외부에서 포드에 접근 가능. 접근 포트는 랜덤으로 정해지지만 특정 포트로 접근하도록 설정도 가능
- LordBalancer 타입 : 클라우드 플랫폼(AWS, GCP 등)에서 제공하는 로드 밸런서를 동적으로 프로비저닝해 포드에 연결. NodePort 타입과 마찬가지로 외부에서 포드에 접근 가능. 일반적으로 클라우드 플랫폼 환경에서만 사용 가능
# ClusterIP
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: hostname-deployment
spec:
replicas: 3
selector:
matchLabels:
app: webserver
template:
metadata:
name: my-webserver
labels:
app: webserver
spec:
containers:
- name: my-webserver
image: alicek106/rr-test:echo-hostname
ports:
- containerPort: 80
# Service
apiVersion: v1
kind: Service
metadata:
name: hostname-svc-clusterip
spec:
ports:
- name: web-port
port: 8080
targetPort: 80
selector:
app: webserver
type: ClusterIP
webserver라는 라벨을 가진 포드들을 8080 → 80 으로 바인딩
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hostname-svc-clusterip ClusterIP 10.43.49.255 <none> 8080/TCP 2m54s
$ kubectl run -i --tty --rm debug --image=alicek106/ubuntu:curl --restart=Nev
er -- bash
...
root@debug:/# curl 10.43.49.255:8080 --silent | grep Hello
<p>Hello, hostname-deployment-96df6b9c4-t5lpr</p>
root@debug:/# curl hostname-svc-clusterip:8080 --silent | grep Hello
<p>Hello, hostname-deployment-96df6b9c4-w4pfb</p>
- 위와 같이 클러스터 내의 임시 포드를 만들어 요청을 전송해볼 수 있음
- IP 뿐만 아니라 서비스 이름 그 자체로도 접근 가능
- 여러 포드가 클러스터 내부에서 서로를 찾아 연결할 때는 서비스의 이름과 같은 도메인 이름을 사용하는 것이 일반적
root@debug:/# curl hostname-svc-clusterip:8080 --silent | grep Hello
<p>Hello, hostname-deployment-96df6b9c4-w4pfb</p>
root@debug:/# curl hostname-svc-clusterip:8080 --silent | grep Hello
<p>Hello, hostname-deployment-96df6b9c4-t5lpr</p>
root@debug:/# curl hostname-svc-clusterip:8080 --silent | grep Hello
<p>Hello, hostname-deployment-96df6b9c4-zf8q2</p>
- 서비스는 연결된 포드들에 대해 자동으로 로드 밸런싱 수행
# endpoint
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hostname-svc-clusterip ClusterIP 10.43.49.255 <none> 8080/TCP 11m
$ kubectl get endpoints
Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice
NAME ENDPOINTS AGE
hostname-svc-clusterip 10.42.0.144:80,10.42.0.145:80,10.42.0.146:80 11m
kubernetes 192.168.0.7:6443 25d
- 서비스의 라벨 셀렉터와 포드의 라벨이 매칭되어 연결되면 자동으로 엔드포인트라는 오브젝트가 생성됨
# NodePort
- 모든 노드의 특정 포트를 개방해 서비스에 접근하는 방식
apiVersion: v1
kind: Service
metadata:
name: hostname-svc-nodeport
spec:
ports:
- name: web-port
port: 8080
targetPort: 80
selector:
app: webserver
type: NodePort
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hostname-svc-nodeport NodePort 10.43.36.241 <none> 8080:31294/TCP 4s
- clusterip 때 처럼 CLUSTER-IP에 내부 IP가 할당됨 → NodePort 타입의 서비스가 ClusterIP의 기능을 포함하고 있기 때문
- 하지만 실제 운영 환경에서 NodePort로 서비스를 외부에 제공하는 경우는 많지 않음
- 포트 번호를 80, 443 으로 설정하기엔 적절하지 않음(기본적으로 NodePort)가 사용할 수 있는 포트 범위는 30000~32768
- tls 인증서 적용, 라우팅 등과 같은 복잡한 설정을 서비스에 적용하기 어렵
- 대신 인그레스라는 오브젝트를 통해 간접적으로 사용
# sessionAffinity
---
spec:
sessionAffinity: ClientIP
- sessinAffinity 설정으로 Sticky Session 기능 적용 가능
- 특정 클라이언트의 요청이 같은 Pod에서만 처리되게 할 수 있음
# externalTrafficPolicy
![]()
- NodePort, LoadBalancer 타입의 서비스를 사용하면 각 워커 노드로 들어온 요청이 포드 중 하나로 전달됨
- 하지만 위 그림과 같이 워커0으로 들어온 요청이 다시 워커1의 포드로 전달되는 경우
- 불필요한 네트워크 홉이 발생함
- 노드 간의 리다이렉트가 발생하게 되어 트래픽의 출발지 주소가 바뀜 → 클라이언트의 IP 주소가 보존되지 않음
- 이는
externalTrafficPolicy가Cluster로 설정되어 있기 때문
$ kubectl get svc hostname-svc-nodeport -o yaml
apiVersion: v1
kind: Service
...
spec:
externalTrafficPolicy: Cluster
...
- 이를 방지하기 위해 externalTrafficPolicy를
Local로 설정 가능 → 포드가 생성된 노드에서만 포드로 접근 가능 - 그럼 접근한 노드에 포드가 없으면?

- 요청 처리 실패함
결론 if 불필요한 네트워크 홉으로 인한 레이턴시나 클라이언트의 IP 보존이 중요하지 않음 → Cluster else → Local
# ExternalName
- ExternalName 타입을 사용해 서비스를 생성하면 서비스가 외부 도메인을 가리키도록 설정할 수 있음
apiVersion: v1
kind: Service
metadata:
name: externalname-svc
spec:
type: ExternalName
externalName: my.database.com
| 사용하는 도메인 이름 | 변환되는 주소 |
|---|---|
| externalname-svc | my.database.com |