Kubernetes

쿠버네티스 설치 및 기본 개념 (pod, replicaset, deployment)

wath1457 2025. 4. 5. 20:05

쿠버네티스 설치

 

쿠버네티스 설치부분은 이전에 포스팅한 글로 대체하겠습니다.

 

 

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 주요 기능

  1. 파드 상태 관리
    • API Server로부터 전달된 파드 스펙(PodSpec)을 수신하고, 해당 파드가 정의된 상태로 실행되도록 보장.
    • 컨테이너 런타임(docker, containerd 등)을 통해 파드 내 컨테이너를 생성, 모니터링, 재시작
  2. 컨테이너 상태 보고
    • 파드 및 컨테이너 상태를 주기적으로 kube-apiserver에 보고 (NodeStatus, PodStatus 등)
  3. 헬스 체크 수행
    • Liveness/Readiness Probe 등을 기반으로 파드 상태 판단
    • 비정상 컨테이너를 재시작하거나 종료
  4. 로컬 리소스 관리
    • cgroups, 네트워크, 볼륨 마운트 등을 사용해 파드 실행 환경을 세팅
  5. CNI, CSI 연동
CNI를 통해 네트워크 설정, CSI를 통해 스토리지 연결 설정 수행

 

동작 흐름 요약

  1. kube-apiserver → kubelet으로 파드 정의 전달
  2. kubelet → 컨테이너 런타임 통해 파드 실행
  3. 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를 선택합니다.

  1. 보류 중인(예약 불가능한) Pod가 먼저 축소됩니다.
  2. 주석이 설정된 경우 controller.kubernetes.io/pod-deletion-cost, 값이 낮은 Pod가 먼저 나옵니다. (미설정시 0으로 간주)
  3. 복제본이 많은 노드의 Pod는 복제본이 적은 노드의 Pod보다 먼저 배치됩니다.
  4. 포드의 생성 시간이 다르면 최근에 생성된 포드가 이전 포드보다 앞에 옵니다(생성 시간은 정수 로그 스케일로 분류됩니다).

위의 모든 사항이 일치하면 선택은 무작위로 이루어집니다.

 


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는 새 파드가 준비 완료 상태로 유지돼야 하는 최소 시간이므로
      그보다 먼저 실패를 판단할 수 없다.

 

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으로 제어
테스트나 실험 환경에서 실수로 배포되지 않도록 잠깐 멈춰두는 용도