쿠버네티스 설치 및 기본 개념 (pod, replicaset, deployment)
쿠버네티스 설치
쿠버네티스 설치부분은 이전에 포스팅한 글로 대체하겠습니다.
kubeadm을 이용한 설치 - 온 프레미스
https://wath1457.tistory.com/23
우분투 쿠버네티스 1.31 클러스터 생성하기
OS : Ubuntu 22.04 kubeadm을 이용하여 쿠버네티스 클러스터를 구축해 보겠습니다. 공통) 모든 노드에서 진행 1. swap 비활성화# swap 활성화 되어있는지 확인free -h# 해당 파일을 수정하여 swap 부분을
wath1457.tistory.com
kind (kubernetes in docker) - 로컬 환경에서 쉽게 쿠버네티스 환경 구성하기
해당 방법은 로컬 컴퓨터에 미리 정의한 yaml파일을 가지고 쿠버네티스의 한 노드가 하나의 컨테이너로 띄워지는 환경입니다.
쿠버네티스 구성이 어려우신 분들 혹은 로컬에서 여러 노드의 쿠버네티스 환경을 체험해보고 싶으신 분들에게 추천드립니다.
https://wath1457.tistory.com/20
Ubuntu 22.04 환경에서 kind 클러스터 구축하기
Ubuntu 환경에서 kind를 이용하여 쿠버네티스 클러스터를 구축해 보겠습니다. kind는 Kubernetes In Docker의 약자이며, 쿠버네티스 노드 = container 와 같은 개념입니다. 따라서, 사용자는 container를 사용
wath1457.tistory.com
쿠버네티스 구성 컴포넌트
kube-apiserver
- 클러스터의 control plane 컴포넌트 중 핵심.
- 모든 클라이언트(예: kubectl) 및 내부 control loop의 요청을 처리하는 RESTful API 서버.
- 인증(Authentication), 인가(Authorization), 데이터 유효성 검사, Admission Control, etcd 연동 등을 담당
kube-controller-manager
- 여러 컨트롤러들을 실행하는 프로세스
- 주요 컨트롤러:
- Node Controller: 노드 상태 감지 및 반응
- ReplicaSet Controller: 파드 수를 원하는 상태로 유지
- Deployment Controller: 롤링 업데이트 및 롤백 관리
- ServiceAccount Controller 등.
- 클러스터 상태를 지속적으로 관찰하고, Desired state와 Actual state의 차이를 조정
kube-scheduler
- 스케줄링되지 않은 파드들을 감지하고, 적절한 노드에 할당
- 고려 요소: 리소스 가용성, affinity/anti-affinity, taint/toleration, topology 등
- Scheduling은 binding 단위로 처리되며, 실제 파드 실행은 kubelet이 수행
CoreDNS
- 클러스터 내 DNS 서비스 제공
- 서비스/파드 이름을 클러스터 내부 IP로 변환
- DNS 기반 서비스 디스커버리 구현
kube-proxy
- 서비스 오브젝트를 위한 네트워크 프록시 및 로드 밸런서 역할
- iptables 또는 IPVS를 이용해 클러스터 내 트래픽을 각 파드로 라우팅
- 각 노드에 데몬셋 형태로 실행됨
CNI (Container Network Interface) 플러그인
- 쿠버네티스는 네트워크 구현을 CNI에 위임함
- 파드 간 통신, 고유 IP 부여, 네임스페이스 간 정책 적용 등을 처리
- 대표적인 플러그인: Calico, Flannel, Cilium, Weave 등
- CNI는 파드 생성 시 kubelet이 호출하여 네트워크를 구성
kubelet
Kubelet은 각 워커 노드에 상주하는 에이전트로, 노드에서 파드가 정상적으로 동작하도록 관리하는 핵심 컴포넌트이다
kubelet 주요 기능
- 파드 상태 관리
- API Server로부터 전달된 파드 스펙(PodSpec)을 수신하고, 해당 파드가 정의된 상태로 실행되도록 보장.
- 컨테이너 런타임(docker, containerd 등)을 통해 파드 내 컨테이너를 생성, 모니터링, 재시작
- 컨테이너 상태 보고
- 파드 및 컨테이너 상태를 주기적으로 kube-apiserver에 보고 (NodeStatus, PodStatus 등)
- 헬스 체크 수행
- Liveness/Readiness Probe 등을 기반으로 파드 상태 판단
- 비정상 컨테이너를 재시작하거나 종료
- 로컬 리소스 관리
- cgroups, 네트워크, 볼륨 마운트 등을 사용해 파드 실행 환경을 세팅
- CNI, CSI 연동
CNI를 통해 네트워크 설정, CSI를 통해 스토리지 연결 설정 수행
동작 흐름 요약
- kube-apiserver → kubelet으로 파드 정의 전달
- kubelet → 컨테이너 런타임 통해 파드 실행
- kubelet → 상태 모니터링 및 주기적 보고
pod
파드는 하나 이상의 컨테이너 그룹으로, 공유 스토리지 및 네트워크 리소스와 컨테이너를 실행하는 방법에 대한 사양이 있습니다. 파드의 콘텐츠는 항상 공동 위치 및 공동 스케줄링되며 공유 컨텍스트에서 실행됩니다.
파드는 애플리케이션별 “논리적 호스트”를 모델링하며, 비교적 긴밀하게 결합된 하나 이상의 애플리케이션 컨테이너를 포함한다.
- 단일 컨테이너를 실행하는 Pod . "Pod당 컨테이너 1개" 모델은 가장 일반적인 Kubernetes 사용 사례입니다. 이 경우 Pod를 단일 컨테이너를 감싸는 래퍼로 생각할 수 있습니다. Kubernetes는 컨테이너를 직접 관리하는 대신 Pod를 관리합니다.
- 함께 작동해야 하는 여러 컨테이너를 실행하는 Pod . Pod는 밀접하게 결합되어 리소스를 공유해야 하는 여러 개의 공동 배치 컨테이너 로 구성된 애플리케이션을 캡슐화할 수 있습니다 . 이러한 공동 배치 컨테이너는 단일 응집 단위를 형성합니다.복제를 제공하기 위해 여러 컨테이너를 실행할 필요는 없습니다(복원성 또는 용량을 위해)
- 여러 개의 공동 배치 및 공동 관리 컨테이너를 단일 Pod에 그룹화하는 것은 비교적 고급 사용 사례입니다. 컨테이너가 밀접하게 결합된 특정 인스턴스에서만 이 패턴을 사용해야 합니다. / 해당 패턴은 아래에서 설명
Sidecar Pattern
보조 기능을 하는 컨테이너를 메인 컨테이너 옆에 배치하는 형태
예시:
로그 수집 컨테이너 (예: Fluentd)
프록시/서비스 메시 (예: Envoy, Istio)
SSL Termination
데이터 초기화/마이그레이션
✅ Adapter / Ambassador / Init Container 등의 고급 구성
Adapter: 주 컨테이너의 출력을 변환해 외부 시스템에 전달
Ambassador: 네트워크 프록시 역할로 외부와의 연결을 위임
Init Container: 본 컨테이너 실행 전에 설정 또는 준비 작업 수행
Pod 예시
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
pod template예시
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
template:
# This is the pod template
spec:
containers:
- name: hello
image: busybox:1.28
command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
- name: test-sidecar-container
image: testimage
command: ["tail"]
args: ["-f", "/dev/null"]
restartPolicy: OnFailure
# The pod template ends here
- command : docker container의 Entrypoint와 동일
- args : docker container의 Cmd와 동일
- 파드의 네트워크 네임스페이스는 Pause라는 이름의 컨테이너로부터 네트워크를 공유받아 사용됨 Pause 컨테이너는 네임스페이스를 공유하기 위해 파드별로 생성되는 컨테이너이며, 각 파드에 대해 자동으로 생성된다.
Replicaset
ReplicaSet의 목적은 주어진 시간에 실행되는 안정적인 복제 Pod 세트를 유지하는 것입니다. 따라서 지정된 수의 동일한 Pod의 가용성을 보장하는 데 자주 사용됩니다
파드의 한계?
- 여러 개의 파드를 직접 생성하는 것은 비효율적
- 파드에 장애가 발생하게 되었을 때, 파드의 자동복구가 일어나지 않음
따라서, 레플리카셋의 역할은 다음과 같다.
- 정해진 수의 동일한 파드가 항상 실행되도록 관리
- 노드 장애 등의 이유로 파드를 사용할 수 없다면 다른 노드에서 파드를 다시 생성
레플리카셋 예시
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
# 레플리카셋 정의 시작
# modify replicas according to your case
replicas: 3
selector:
matchLabels:
tier: frontend
# 레플리카셋 정의 끝
# 파드 정의 시작
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: php-redis
image: us-docker.pkg.dev/google-samples/containers/gke/gb-frontend:v5
# 파드 정의 끝
- label selector ( .metadata.labels / .spec.template.metadata.labels )를 이용한 느슨한 연결
- 위의 예시에선 tier: frontend 라벨을 가진 파드의 개수가 replicas의 개수인 3개와 일치하지 않는다면, podspec에 정의된 replicas와 pod의 수를 맞춰준다.
- 만약 replicaset이 생성한 파드의 라벨을 제거한다면 해당 파드는 더 이상 레플리케삿에 의해 관리되지 않으며, 직접 수동으로 생성한 파드와 동리한 상태가 된다.
ReplicaSet만 삭제
kubectl delete 옵션 을 사용하여 Pod에 영향을 미치지 않고 ReplicaSet을 삭제할 수 있습니다
원본이 삭제되면 새 ReplicaSet을 만들어 대체할 수 있습니다. 이전과 새 .spec.selector것이 동일하다면 새 ReplicaSet은 이전 Pod를 채택합니다.
그러나 기존 Pod를 새롭고 다른 Pod 템플릿과 일치시키려는 노력은 하지 않습니다. 제어된 방식으로 Pod를 새 사양으로 업데이트하려면 ReplicaSet이 롤링 업데이트를 직접 지원하지 않으므로 Deployment를 사용합니다
Replicaset 축소 알고리즘
축소할 때 ReplicaSet 컨트롤러는 다음과 같은 일반 알고리즘을 기반으로 사용 가능한 Pod를 정렬하여 축소할 Pod의 우선 순위를 정하여 삭제할 Pod를 선택합니다.
- 보류 중인(예약 불가능한) Pod가 먼저 축소됩니다.
- 주석이 설정된 경우 controller.kubernetes.io/pod-deletion-cost, 값이 낮은 Pod가 먼저 나옵니다. (미설정시 0으로 간주)
- 복제본이 많은 노드의 Pod는 복제본이 적은 노드의 Pod보다 먼저 배치됩니다.
- 포드의 생성 시간이 다르면 최근에 생성된 포드가 이전 포드보다 앞에 옵니다(생성 시간은 정수 로그 스케일로 분류됩니다).
위의 모든 사항이 일치하면 선택은 무작위로 이루어집니다.
Deployment
Deployment는 쿠버네티스에서 파드(Pod)를 선언적 방식으로 관리할 수 있게 해주는 상위 수준의 오브젝트이다. 내부적으로는 ReplicaSet을 생성하고 관리하는 역할을 한다.
즉, Deployment는 직접 파드를 생성하는 게 아니라, ReplicaSet을 생성하고, 그 ReplicaSet이 파드를 생성하게 한다.
Deployment 왜 사용할까
- 기본적으로, 애플리케이션의 업데이트와 배포를 쉽게 하기 위해서 사용
- 애플리케이션을 업데이트할 때 레플리카셋의 변경 사항을 감지하는 리비전(revision)을 남겨 롤백을 가능하게 해주며, 무중단 서비스를 위해 롤링 업데이트 전략 적용 가능
- Deployment는 파드의 정보를 업데이트 하면서, 새로운 레플리카셋과 파드를 생성했음에도, 이전 버전의 레플리카셋을 삭제하지 않고 남겨둔다.
Deployment UseCases
- 디플로이먼트를 생성하여 레플리카셋을 롤아웃한다. 레플리카셋은 백그라운드에서 파드를 생성한다. 롤아웃의 상태를 확인하여 성공 여부를 확인한다.
- 디플로이먼트의 파드템플릿스펙을 업데이트하여 파드의 새로운 상태를 선언한다. 새 레플리카셋이 생성되고 디플로이먼트는 제어된 속도로 이전 레플리카셋에서 새 레플리카셋으로 파드를 이동하는 것을 관리한다. 각각의 새 레플리카셋은 디플로이먼트의 리비전을 업데이트한다.
- 디플로이먼트의 현재 상태가 안정적이지 않은 경우 이전 디플로이먼트 리비전으로 롤백한다. 롤백할 때마다 디플로이먼트의 리비전이 업데이트됩니다.
- 배포를 확장하여 더 많은 부하를 처리할 수 있도록 합니다.
- 디플로이먼트의 롤아웃을 일시 중지하여 해당 파드템플릿스펙에 여러 수정 사항을 적용한 다음 다시 시작하여 새 롤아웃을 시작한다.
- 디플로이먼트의 상태를 롤아웃이 중단되었음을 나타내는 지표로 사용한다.
- 더 이상 필요하지 않은 오래된 레플리카셋 정리하기
Deployment 예시
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
쿠버네티스에서 롤링 업데이트(Rolling Update) 시에 파드 수를 다음과 같이 유지해야 한다.
replicas - maxUnavailable ≤ 사용 가능한 파드 수 ≤ replicas + maxSurge
- maxUnavailable(업데이트 중에 동시에 비정상 상태여도 허용되는 파드 수)
.spec.strategy.rollingUpdate.maxUnavailable은 업데이트 프로세스 중에 사용할 수 없는 최대 파드 수를 지정하는 선택적 필드이다.
값은 절대 숫자(예: 5)이거나 원하는 파드의 백분율(예: 10%)일 수 있다. 절대 숫자는 백분율에서 반내림하여 계산한다.
.spec.strategy.rollingUpdate.maxSurge가 0인 경우 값은 0이 될 수 없다. 기본값은 25%이다.
예를 들어, 이 값을 30%로 설정하면 롤링 업데이트가 시작될 때 즉시 이전 레플리카셋을 원하는 파드의 70%로 축소할 수 있다.
새 파드가 준비되면 이전 레플리카셋을 더 축소한 다음 새 레플리카셋을 확장하여 업데이트 중에 항상 사용 가능한 총 파드 수가 원하는 파드의 70% 이상이 되도록 할 수 있다.
- maxSurge(업데이트 중에 기존 replica 수보다 더 많이 임시로 생성할 수 있는 파드 수)
.spec.strategy.rollingUpdate.maxSurge는 원하는 파드 수에 대해 생성할 수 있는 최대 파드 수를 지정하는 선택적 필드이다.
값은 절대 숫자(예: 5)이거나 원하는 파드의 백분율(예: 10%)일 수 있다. 최대 사용 불가능이 0인 경우 값은 0이 될 수 없다. 절대값은 백분율에서 반올림하여 계산된다. 기본값은 25%이다.
예를 들어, 이 값을 30%로 설정하면 롤링 업데이트가 시작될 때 새 레플리카셋을 즉시 확장하여 이전 및 새 파드의 총 수가 원하는 파드의 130%를 초과하지 않도록 할 수 있다. 이전 파드가 종료되면, 새 레플리카셋을 더 확장하여 업데이트 중 언제든지 실행되는 총 파드 수가 원하는 파드의 최대 130%를 넘지 않도록 할 수 있다.
maxSurge, maxUnavailable 사용 예시
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
Rollover
롤링 업데이트 중에 또 업데이트가 발생하면, 이전에 생성 중이던 ReplicaSet은 중단되고, 새로운 ReplicaSet이 생성되어 롤아웃이 이어지는 것
1. 최초 Deployment 생성
nginx:1.14.2,replicas:5
- 이러면 Deployment 컨트롤러가 새로운 ReplicaSet(RS-A)을 만들어서 nginx:1.14.2 파드 5개를 띄움.
2. 업데이트가 중간에 들어옴
롤링 업데이트로 3개만 띄웠을 때,
nginx:1.16.1,replicas: 5
- 다시 새롭게 업데이트 됨
3. 쿠버네티스의 반응
- 기존에 생성 중이던 RS-A (nginx:1.14.2)는 "구버전 ReplicaSet"으로 간주.
- 이제 새로운 ReplicaSet (RS-B, nginx:1.16.1)을 만들고 얘를 스케일 업하기 시작함.
- RS-A는 더 이상 마저 5개 만들지 않고, 기존에 띄운 3개도 점점 죽이면서 RS-B로 대체함.
즉, 쿠버네티스는 중간에 생성 중이던 것도 버리고 바로 새 버전으로 넘어감.
4. 주의 사항
- 롤링 업데이트 중에 다시 apply하면 계속 새로운 ReplicaSet이 생기면서 "롤오버 지옥" 가능
- 그래서 CD 파이프라인에서는 롤아웃 완료 확인 (kubectl rollout status) 후 다음 업데이트 진행이 중요
- revisionHistoryLimit 설정을 통해 오래된 RS들이 쌓이는 걸 방지할 수 있다.
Proportional scaling (비례적 스케일링)
상황 가정
- 현재 롤링 업데이트 중임 (nginx:1.14.2 → nginx:1.16.1)
- 구버전 ReplicaSet (RS-A)에서 3개
- 신버전 ReplicaSet (RS-B)에서 2개
- 총 5개 (replicas=5)
이때 autoscaler가 작동하거나 사용자가 replicas 수를 늘려서
replicas:10 으로 동작하는 상황을 가정.
Deployment의 행동
쿠버네티스는 이렇게 하지 않음:
- ❌ 구버전 3개 + 신버전 7개 (무작정 신버전에만 몰빵)
대신 이렇게 나눠서 배분함:
- ✅ 구버전 RS-A: 6개
- ✅ 신버전 RS-B: 4개
즉, 현재 각 ReplicaSet이 차지하고 있는 비율에 따라 새로 추가된 파드를 비례해서 분배하는 것
왜 이렇게 동작할까?
- 업데이트가 아직 완료되지 않았기 때문에, 둘 다 실질적으로 서비스에 사용 중일 수 있음
- 신버전이 불안정한 상황에서 갑자기 신버전에만 트래픽이 쏠리면 위험함
- 그래서 리스크를 분산하기 위해 비율대로 스케일링하는 것
Deployment 배포 실패
다음과 같은 요인들로 인해 배포에 실패할 수 있다.
- Insufficient quota (할당 리소스 부족 - 서버의 리소스 부족)
- Readiness probe failures (준비 프로브 실패)
- Image pull errors (imagePullSecret 관련 오류)
- Insufficient permissions (부족한 권한)
- Limit ranges (리소스 제한 범위 - 사용자 의도)
- Application runtime misconfiguration (애플리케이션 런타임 구성 오류)
progress deadline seconds
progressDeadlineSeconds는 Deployment가 정상적으로 "진행 중(progressing)" 상태로 전환되기까지 기다릴 최대 시간(초)
- 기본값은 600초 (10분)
- 이 시간 안에 Deployment가 새로운 ReplicaSet으로 제대로 롤아웃되지 않으면,
Progressing: False, reason: ProgressDeadlineExceeded 라는 상태로 실패로 간주됨 - progressDeadlineSeconds 값은 반드시 .spec.minReadySeconds보다 커야 함
- minReadySeconds는 새 파드가 준비 완료 상태로 유지돼야 하는 최소 시간이므로
그보다 먼저 실패를 판단할 수 없다.
- minReadySeconds는 새 파드가 준비 완료 상태로 유지돼야 하는 최소 시간이므로
Revision History Limit
revisionHistoryLimit은 Deployment가 보관할 이전 ReplicaSet의 개수를 지정하는 필드
- 쿠버네티스는 Deployment가 생성하거나 업데이트할 때마다 새로운 ReplicaSet을 만든다.
- 그 ReplicaSet에는 해당 시점의 Deployment 설정(revision)이 담겨 있음.
- 이걸 보관해두면 나중에 문제가 생겼을 때 롤백(undo) 할 수 있다
✅ 기본 동작
- 기본값은 10
→ 이전 10개의 ReplicaSet은 유지되고, 그 이전 건 자동으로 삭제됨 - 이전 ReplicaSet은 etcd에 저장되기 때문에 너무 많으면 리소스 낭비, kubectl get rs 결과도 복잡해짐
🔥 중요 포인트
- 이 필드를 0으로 설정하면, 이전에 사용되었던 replicas가 0인 모든 ReplicaSet은 삭제됨
- 이 경우에는 롤백이 불가능해짐!
Paused
.spec.paused는 Deployment의 롤아웃(배포 진행)을 일시 중지하거나 재개할 수 있는 옵션 필드
값은 true 또는 false이고, 기본값은 false (즉, 기본은 실행 상태)
- .spec.paused: true로 설정하면
- PodTemplateSpec (즉, 이미지 버전 등 파드 템플릿)을 수정해도
- 새로운 롤아웃이 시작되지 않음
- 반대로, 다시 paused: false로 바꾸면
- 그동안 쌓아둔 변경 내용이 즉시 롤아웃됨
상황활용 예 | |
여러 설정을 한꺼번에 적용하고 싶을 때 | 롤아웃 막아두고 템플릿을 몇 번 수정한 뒤, 재개 시 한 번에 반영 |
수동으로 롤아웃 흐름을 제어하고 싶을 때 | 오토 롤아웃 막고, kubectl rollout resume으로 제어 |
테스트나 실험 환경에서 | 실수로 배포되지 않도록 잠깐 멈춰두는 용도 |