[Kubernetes] kubectl 연습 - Deployment ReplicaSet
내가 쿠버네티스 클러스터를 다룰 때는 kubectl을 사용한다. (kube-control)

그냥 명령어임. 도커 데스크탑 말고 CLI로 쓸 때 열심히 치던 명령어와 비슷하다.
13months@13monthsBook ~ % kubectl create deployment my-web --image=nginx
deployment.apps/my-web created
13months@13monthsBook ~ %
Deployment로 간단한 웹서버를 하나 띄워보자.
Pod을 직접 만들지 않고 Deployment를 사용해서 띄운다.
13months@13monthsBook ~ % kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
my-web 1/1 1 1 2m4s
13months@13monthsBook ~ % kubectl get pods
NAME READY STATUS RESTARTS AGE
my-web-684f49ddf4-bm4gh 1/1 Running 0 3m36s
13months@13monthsBook ~ %
READY: 1/1 은 원하는 개수 중 준비된 개수를 의미하고 AVAILABLE은 지금 서비스 가능한게 1개 있다는 것.
만든건 deployment 인데 ReplicaSet이랑 Pod도 한개 같이 생겼다.
684f머시기는 ReplicaSet가 붙인 식별자이고, bm4gh는 Pod의 식별자를 의미한다. (라벨아님 라벨은 my-web)
Pod는 언제든 죽고 새로 띄워지기에 매번 새로운 이름을 할당받는다.
Pod 안에는 nginx 웹서버가 한개 떠서 동작하는 중.
13months@13monthsBook ~ % kubectl delete pod my-web-684f49ddf4-bm4gh
pod "my-web-684f49ddf4-bm4gh" deleted from default namespace
13months@13monthsBook ~ % kubectl get pods
NAME READY STATUS RESTARTS AGE
my-web-684f49ddf4-gj4bf 1/1 Running 0 4s
13months@13monthsBook ~ %
Pod을 지우니까 새로 생긴 Pod이 생성된다.
ReplicaSet 아이디까지는 같지만 Pod 아이디는 달라짐.
13months@13monthsBook ~ % kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-web-684f49ddf4-gj4bf 1/1 Running 0 104s 10.244.0.4 minikube <none> <none>
13months@13monthsBook ~ % kubectl delete pod my-web-684f49ddf4-gj4bf
pod "my-web-684f49ddf4-gj4bf" deleted from default namespace
13months@13monthsBook ~ % kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-web-684f49ddf4-ztrt6 1/1 Running 0 3s 10.244.0.5 minikube <none> <none>
13months@13monthsBook ~ %
kubectl get pods -o wide 명령어로 아이피도 같이 확인해보면 죽고 새로 뜰 때 마다 아이피가 변하는걸 확인할 수 있다.
근데 지금은 Pods를 직접 삭제했는데 어차피 앞으로는 Pod를 직접 다루지 않고 Deployment를 다룬다.
# 1. kubectl 자동완성 기능을 켜기 위한 설정을 셸 설정 파일에 추가
echo 'source <(kubectl completion zsh)' >> ~/.zshrc
# 2. k를 kubectl의 별칭으로 등록
echo 'alias k=kubectl' >> ~/.zshrc
# 3. k로도 자동완성이 되게 연결
echo 'complete -F __start_kubectl k' >> ~/.zshrc
# 4. 설정을 지금 터미널에 바로 반영
source ~/.zshrc
탭치면 자동완성 되긴 하고 kubectl 치는것도 귀찮으면 k로 쳐도 된다.
13months@13monthsBook ~ % k get po
NAME READY STATUS RESTARTS AGE
my-web-684f49ddf4-ztrt6 1/1 Running 0 3m55s
13months@13monthsBook ~ % kubectl expose deployment my-web --port=80
service/my-web exposed
13months@13monthsBook ~ % kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 53m
my-web ClusterIP 10.101.193.95 <none> 80/TCP 11s
13months@13monthsBook ~ % k delete pod my-web-684f49ddf4-ztrt6
pod "my-web-684f49ddf4-ztrt6" deleted from default namespace
13months@13monthsBook ~ % k get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-web-684f49ddf4-x9tlm 1/1 Running 0 7s 10.244.0.6 minikube <none> <none>
13months@13monthsBook ~ % k get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 57m
my-web ClusterIP 10.101.193.95 <none> 80/TCP 3m31s
13months@13monthsBook ~ %
kubectl expose 명령어로 서비스를 만들자. deployment 앞에 서비스를 배치할거임.
서비스를 만들면 그 서비스의 IP가 고정되니 Pod가 죽어도 서비스의 IP는 그대로 유지된다.
지금까지는 kubectl 명령어를 직접 쳤는데 이건 쿠버의 철학인 declarative와 거리가 좀 있다.
yaml을 정의해보자.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-web
spec:
replicas: 3
selector:
matchLabels:
app: my-web
template:
metadata:
labels:
app: my-web
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
스프링부트에서 항상 쓰던건데 이걸 처음부터 타이핑하지는 않음. AI로 yaml만들면 되긴 하는데 cka에서는 그게 안된다.
--dry-run=client -o yaml 옵션을 붙이자. 실제로 만들지는 않고, 그 결과를 yaml 형식으로 보여달라는 명령어임.
13months@13monthsBook k8s % k apply -f my-web2.yaml
deployment.apps/my-web2 created
13months@13monthsBook k8s % k get pods
NAME READY STATUS RESTARTS AGE
my-web-684f49ddf4-x9tlm 1/1 Running 0 19m
my-web2-6777b885f8-q6pnz 1/1 Running 0 95s
my-web2-6777b885f8-qd66d 1/1 Running 0 95s
my-web2-6777b885f8-rnjtk 1/1 Running 0 95s
13months@13monthsBook k8s % k get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
my-web 1/1 1 1 37m
my-web2 3/3 3 3 3m2s
13months@13monthsBook k8s % vim my-web2.yaml
13months@13monthsBook k8s % k apply -f my-web2.yaml
deployment.apps/my-web2 configured
13months@13monthsBook k8s % k get pods
NAME READY STATUS RESTARTS AGE
my-web-684f49ddf4-x9tlm 1/1 Running 0 21m
my-web2-6777b885f8-4h9gk 1/1 Running 0 6s
my-web2-6777b885f8-cr9z8 1/1 Running 0 6s
my-web2-6777b885f8-q6pnz 1/1 Running 0 3m44s
my-web2-6777b885f8-qd66d 1/1 Running 0 3m44s
my-web2-6777b885f8-rnjtk 1/1 Running 0 3m44s
13months@13monthsBook k8s %
replicas 개수를 1개 -> 3개 -> 5개로 바꿨음.
3개에서 5개로 변할 때는 기존 3개는 그대로 두고 새로 2개만 추가해준다.
yaml만 수정하면 알아서 deployment가 Pod를 만들어주니 내가 직접 Pod를 만들 필요가 없다.
13months@13monthsBook k8s % k delete deployment my-web my-web2
deployment.apps "my-web" deleted from default namespace
deployment.apps "my-web2" deleted from default namespace
13months@13monthsBook k8s % k delete service my-web
service "my-web" deleted from default namespace
13months@13monthsBook k8s % k get all
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 80m
13months@13monthsBook k8s %
deployment를 삭제하면 그 아래 ReplicaSet과 Pods도 함께 정리된다.
처음 apply 했을 때는 created로 나타나지만, 설정 파일 수정 후 다시 apply 하면 configured로 차이만 반영한다.
아예 파일이 없으면 만들고, 있는데 수정됐다면 차이만 반영하고, 변화가 없다면 아무것도 안함.
13months@13monthsBook k8s % k create deployment broken --image=nginx:존재하지않는버전999
deployment.apps/broken created
13months@13monthsBook k8s % k get pods
NAME READY STATUS RESTARTS AGE
broken-cbb5595d-5pwg5 0/1 InvalidImageName 0 6s
13months@13monthsBook k8s % k describe pod broken-cbb5595d-5pwg5
Name: broken-cbb5595d-5pwg5
Namespace: default
Priority: 0
Service Account: default
Node: minikube/192.168.49.2
Start Time: Tue, 21 Jul 2026 16:19:16 +0900
Labels: app=broken
pod-template-hash=cbb5595d
Annotations: <none>
Status: Pending
IP: 10.244.0.12
IPs:
IP: 10.244.0.12
Controlled By: ReplicaSet/broken-cbb5595d
Containers:
nginx:
Container ID:
Image: nginx:존재하지않는버전999
Image ID:
Port: <none>
Host Port: <none>
State: Waiting
Reason: InvalidImageName
Ready: False
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-mkq62 (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready False
ContainersReady False
PodScheduled True
Volumes:
kube-api-access-mkq62:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
Optional: false
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 16s default-scheduler Successfully assigned default/broken-cbb5595d-5pwg5 to minikube
Warning InspectFailed 1s (x3 over 15s) kubelet spec.containers{nginx}: Failed to apply default image tag "nginx:존재하지않는버전999": couldn't parse image name "nginx:존재하지않는버전999": invalid reference format
Warning Failed 1s (x3 over 15s) kubelet spec.containers{nginx}: Error: InvalidImageName
13months@13monthsBook k8s %
deployment를 만들 때 nginx 버전을 이상하게 치면 고장난다.
kubectl get pods 명령어와 kubectl describe pod이름 명령어로 에러 로그를 확인해보자.
STATUS를 보면 InvalidImageName으로 이미지 자체가 규칙에 안맞음을 확인할 수 있음.
이미지 태그에 한글이 들어가니 깨졌다.
Events부분을 확인해보면 에러 원인을 확인할 수 있다.
couldn't parse image name invalid reference format 이라니까 저걸 보고 고쳐주면 됨.
13months@13monthsBook k8s % k set image deployment/broken nginx=nginx:latest
deployment.apps/broken image updated
13months@13monthsBook k8s % k get pods
NAME READY STATUS RESTARTS AGE
broken-74897b87bc-cb7vv 1/1 Running 0 7s
kubectl set images는 실행 중인 Deployment 컨테이너 이미지를 수정하는 명령어.
이미지가 깨졌으니까 이미지를 바꿔야 고칠 수 있다.
set image를 하면 내부적으로는 무중단 업데이트가 진행된다.
이걸 확인해보자.
13months@13monthsBook k8s % kubectl scale deployment broken --replicas=3
deployment.apps/broken scaled
13months@13monthsBook k8s % k get pods
NAME READY STATUS RESTARTS AGE
broken-74897b87bc-49kxs 1/1 Running 0 3s
broken-74897b87bc-79rd2 1/1 Running 0 3s
broken-74897b87bc-cb7vv 1/1 Running 0 4m5s
13months@13monthsBook k8s % k set image deployments/broken nginx=nginx:1.27
deployment.apps/broken image updated
13months@13monthsBook k8s % k get pods
NAME READY STATUS RESTARTS AGE
broken-58cc58dd76-qmssq 0/1 ContainerCreating 0 3s
broken-74897b87bc-49kxs 1/1 Running 0 18s
broken-74897b87bc-79rd2 1/1 Running 0 18s
broken-74897b87bc-cb7vv 1/1 Running 0 4m20s
13months@13monthsBook k8s %
무중단 업데이트의 방법 중 하나인 롤링 업데이트가 실행되는 순간이다.
예전의 Pod을 먼저 다 죽이지 않고 점진적으로 Pod를 띄운다.
kubectl rollout status deployment/broken 명령어를 사용하면 deployment의 교체가 다 끝났는지 알 수 있다.
진행 중이라면 그 상황을 실시간으로 확인할 수 있음.
13months@13monthsBook k8s % k get pods
NAME READY STATUS RESTARTS AGE
broken-58cc58dd76-2qxf7 1/1 Running 0 2m52s
broken-58cc58dd76-dd7f6 1/1 Running 0 2m53s
broken-58cc58dd76-qmssq 1/1 Running 0 3m3s
13months@13monthsBook k8s % k rollout history deployment/broken
deployment.apps/broken
REVISION CHANGE-CAUSE
1 <none>
2 <none>
3 <none>
13months@13monthsBook k8s %
rollout history의 REVISION을 통해 Deployment가 몇 번 바뀌었는지를 확인할 수 있다.
이미지를 두 번 바꿨으니 REVISION 3까지 생겼다. 다만 왜 바꿨는지 이유를 적어놓지 않아서 나머지는 none으로 뜸.
이렇게 rollout history에 리비전이 남아있다는건 바로 롤백도 가능하다는 것.
13months@13monthsBook k8s % k rollout history deployment/broken
deployment.apps/broken
REVISION CHANGE-CAUSE
1 <none>
2 <none>
3 <none>
13months@13monthsBook k8s % k rollout undo deployment/broken
deployment.apps/broken rolled back
13months@13monthsBook k8s % k get pods
NAME READY STATUS RESTARTS AGE
broken-74897b87bc-9p9gq 1/1 Running 0 9s
broken-74897b87bc-mt2cr 1/1 Running 0 8s
broken-74897b87bc-nv5rn 1/1 Running 0 9s
13months@13monthsBook k8s % k rollout history deployment/broken
deployment.apps/broken
REVISION CHANGE-CAUSE
1 <none>
3 <none>
4 <none>
13months@13monthsBook k8s % k describe deployment broken | grep Image
Image: nginx:latest
13months@13monthsBook k8s %
ReplicaSet 해시가 다시 돌아왔고 nginx 버전도 돌아왔다.
rollout history를 보면 REVISION이 1 3 4 로 변했는데..
같은 내용의 리비전은 번호가 중복될 수 없고 항상 최신 번호를 받는다. 그러니 2번이 4번으로 식별자를 바꿨음.
describe로 잡을 수 있는 문제는 쿠버네티스 수준에서 해결할 수 있는 문제.
Pod이 안 뜨거나, 이미지를 못 받거나, 자원이 부족하거나.. 이럴 때는 describe의 Events를 확인한다.
컨테이너가 시작되긴 했지만 그 안에서 실행되는게 고장난 경우 앱이 출력하는 로그를 본다.
logs 명령어로 확인할 수 있다.
13months@13monthsBook k8s % k run crasher --image=busybox --command -- sh -c "echo '앱 시작...'; echo '에러: 설정 파일을 찾을 수 없음' >&2; exit 1"
pod/crasher created
13months@13monthsBook k8s % k get pods
NAME READY STATUS RESTARTS AGE
broken-74897b87bc-9p9gq 1/1 Running 0 6m13s
broken-74897b87bc-mt2cr 1/1 Running 0 6m12s
broken-74897b87bc-nv5rn 1/1 Running 0 6m13s
crasher 0/1 CrashLoopBackOff 1 (16s ago) 23s
13months@13monthsBook k8s % k logs crasher
앱 시작...
에러: 설정 파일을 찾을 수 없음
13months@13monthsBook k8s %
kubectl run 명령어로 pod를 바로 띄울 수 있다.
arg로 exit 1; 호출하고 죽어버리니 컨테이너는 잘 뜨지만 앱이 오류를 뱉고 죽는다. 이 때 logs로 디버깅하면 됨.
이건 못고친다 그냥 logs로 읽을 수 있다만 확인하고 넘어가자.
계속 죽는 애플리케이션은 현재 로그가 비어있을 수 있으니 kube ctl logs --previous로 죽기 직전까지의 로그를 봐야 함.
'DevOps > Docker && Kubernetes' 카테고리의 다른 글
| [Kubernetes] kubectl 연습 - RoleBinding (0) | 2026.07.26 |
|---|---|
| [Kubernetes] Kubernetes Concept (0) | 2026.07.21 |
| [k8s] 쿠버네티스 도입 (0) | 2023.12.17 |
| [Docker] 네트워킹과 Docker Compose (1) | 2023.12.10 |
| [Docker] Dockerfile 프로젝트 배포하기 (3) | 2023.12.06 |
댓글
이 글 공유하기
다른 글
-
[Kubernetes] kubectl 연습 - RoleBinding
[Kubernetes] kubectl 연습 - RoleBinding
2026.07.26 -
[Kubernetes] Kubernetes Concept
[Kubernetes] Kubernetes Concept
2026.07.21 -
[k8s] 쿠버네티스 도입
[k8s] 쿠버네티스 도입
2023.12.17 -
[Docker] 네트워킹과 Docker Compose
[Docker] 네트워킹과 Docker Compose
2023.12.10