나 JAVA 봐라

Kubernetes (쿠버네티스, K8s) 본문

DevOps, MLOps/K8s

Kubernetes (쿠버네티스, K8s)

cool_code 2024. 6. 17. 13:11

키잡이 로고

 

주변 사람들이 나만 빼고 쿠버네티스 얘기해서 소외감이 들었다. (놀 때는 기술 얘기 그만해...)

나만 빼고 CKA를 따? 나만 빼고 홈서버를 사? (사실 관심 없었음)

나만 빼고 해지뭴라고 ! 

 

때마침 인턴 업무도 쿠버네티스 관련 업무여서(ㅠㅠ), 쿠버네티스 공부를 시작하게 되었다. 

가보자고 

 

(계속 공부해가면서 해당 포스팅은 계속 업데이트 될 예정입니다.)


쿠버네티스의 등장

등장 순서

전통적인 배포 시대 (Traditional Deployment)

  • 한 물리 서버에 애플리케이션을 배포
  • (문제 상황) 한 물리 서버에서 여러 애플리케이션의 리소스 한계를 정의할 수 없는 리소스 할당 문제 -> (해결책) 서로 다른 물리 서버에서 각 애플리케이션을 실행 -> (또 다른 문제) 리소스를 충분히 활용하지 못하고, 여러 물리 서버를 유 지하는 비용증가


가상화된 배포 시대 (Virtualized Deployment)

  • 위에서 언급된 문제를 해결하기 위해 가상화 도입
  • 가상화를 통해 단일 물리 서버에 여러 가상 시스템(VM)을 실행하여 애플리케이션을 배포
  • VM 간에 애플리케이션을 격리하고 애플리케이션의 정보를 다른 애플리케이션에서 자유롭게 액세스할 수 없으므로, 일정 수준의 보안성 제공함.
  • 가상화를 통해 리소스를 더 효율적으로 활용할 수 있게 되었으며, 쉽게 애플리케이션을 추가, 업데이트하고 하드웨어 비용을 절감할 수 있게 됨. (확장성 제공)
  • 호스트 운영체제(Host OS) 위에 게스트 운영체제(Guest OS) 를 띄워 배포하므로 상당한 오버헤드 발생

 

컨테이너 개발 시대 (Container Deployment)

  • VM과 유사하지만 격리 속성을 완화하여 애플리케이션 간에 OS를 공유 -> 가볍다.
  • VM과 마찬가지로 컨테이너에는 자체 파일 시스템,CPU 점유율,메모리, 프로세스 공간 등이 있다.
  • 독립적인 환경에서 애플리케이션 배포 및 개발 가능
  • 압도적인 리소스 효율성 및 높은 이식성, 가시성


만약 컨테이너가 수십만개 라면? 프로덕션 레벨에서 가용성을 보장해야하는데, 컨테이너가 다운되면 어떻게 처리해야할 
까? -> 자동으로 관리해주는 시스템( = 컨테이너 오케스트레이션 시스템)이 필요하다 !

 

=> 쿠버네티스 가 나오게 되었다.

 

왜 쿠버네티스를 쓸까

  • 오픈소스이다.
  • self-healing : 자동으로 실패한 컨테이너를 다시 시작하거나 컨테이너를 교체한다.
  • 모든 동작을 사용자가 직접 작성하는 것이 아닌, 원하는 상태를 선언한다. (yaml 등으로 선언) -> 구체적인 동작은 쿠버네티스가 자동으로 실행하며 원하는 상태를 만들어준다.

 

쿠버네티스 클러스터 (Cluster)

 

쿠버네티스 클러스터 구성 요소

 

master에 여러 노드들이 연결되면 하나의 클러스터로 묶일 수 있다. 클러스터(cluster)는 컨테이너화된 애플리케이션을 실행하기 위한 노드(워커 머신)의 집합, 쉽게 말해 여러 컴퓨터가 협력하여 애플리케이션을 실행하도록 관리하는 시스템이다. 만약 클러스터의 전체 자원을 늘리고 싶으면 노드를 계속 추가하면 된다. 

 

이를 통해 컨테이너를 자동으로 배포, 확장, 관리할 수 있기에 개발자는 운영 관리에 대해서는 신경쓰지 않고 애플리케이션 개발에만 집중할 수 있다. 쿠버네티스 클러스터는 크게 두 영역으로 구성된다.

 

컨트롤 플레인 컴포넌트 (master node)

  • 머신 노드들을 관리, 컨테이너 배포, 스케일링, 네트워킹 등을 담당하는 제어 담당
    • 클러스터에 관한 전반적인 결정(예를 들어, 스케줄링)을 수행하고 클러스터 이벤트(예를 들어, 디플로이먼트의 replicas 필드에 대한 요구 조건이 충족되지 않을 경우 새로운 파드를 구동시키는 것)를 감지하고 반응
  • 일반적으로 3개 이상의 노드로 구성 ( = 여러 컴퓨터에 걸쳐 실행)
  • etcd, kube-scheduler, kube-apiserver, cloud-controller-manager 등 실행

노드 컴포넌트 (worker node)

  • 실제 컨테이너를 실행하는 워커 머신
  • 컨테이너 이미지를 다운로드하고, 컨테이너를 실행하고, 리소스 사용량을 모니터링하며, 컨테이너 상태를 보고
  • 각 노드에는 kubelet, kube-proxy, pod 등 실행

그렇다면 클러스터는 어떻게 작동될까?

  1. 개발자는 컨테이너 이미지를 만들고, 쿠버네티스 클러스터에 배포하도록 명령한다.
  2. 컨트롤 플레인은 컨테이너 이미지를 다운로드하고, 노드에 배포할 컨테이너의 개수를 결정하며, 각 노드에 컨테이너를 배포한다.
  3. 각 노드의 kubelet은 컨트롤 플레인으로부터 명령을 받아 컨테이너를 실행하고, 리소스 사용량을 모니터링하며, 컨테이너 상태를 보고한다.
  4. 컨트롤 플레인은 각 노드로부터 컨테이너 상태 정보를 수집하고, 필요에 따라 컨테이너를 추가하거나 제거하거나 다시 시작한다.
  5. 사용자는 쿠버네티스 API를 통해 컨테이너, 서비스, 네트워크 등을 관리할 수 있다. 

 

오브젝트와 컨트롤러

  • 오브젝트: 하나의 의도를 담은 레코드 (파드, 서비스 볼륨, 네임스페이스)
  • 컨트롤러: 사용자가 바라는 상태와 현재 상태가 일치하도록 오브젝트들을 생성/삭제 (Object의 Status와 Spec이 일치 하도록 제어)

네임스페이스

  • namespace : 클러스터를 논리적인 단위로 나눠서 사용할 수 있게 한다. (= 클러스터 내부의 여러 개의 네임 스페이스가 오브젝트들을 독립된 공간으로 분리되게 만들어준다.)
  • 하나의 네임 스페이스 안에는 여러 개의 서비스, 파드, 컨트롤러 등이 있을 수 있다. (+ 서로 다른 namespace에 있는 pod들과 연결 불가함.)
  • 노드, PV(Persistent Volume)과 같이 모든 네임스페이스가 공유하는 오브젝트를 제외하고는, 일반적으로 대부분의 자원은 네임스페이스 단위로 제어된다. 
  • 또한 네임스페이스에 ResourceQuota, LimitRange를 달아서 한 네임스페이스에서 사용할 수 있는 자원의 양을 환정하거나, 파드 개수를 제한하거나, cpu 메모리를 제한할 수 있다. 
  • ex) 한 네임스페이스는 쿠버네티스 관리자용 네임스페이스가 될 수 있고, 또 다른 네임스페이스는 모든 사용자가 접근할 수 있는 네임스페이스가 될 수 있다.

네임스페이스의 자원을 제한하는 기능

  • ResourceQuota : 네임스페이스 별로 사용 가능한 자원(CPU, 메모리, 스토리지 등)의 양을 제한한다. 
  • LimitRange : 네임스페이스 내 파드, 컨테이너가 사용할 수 있는 최대 리소스량을 제한한다.

 

파드 (Pod)

  • 쿠버네티스에서 생성, 관리할 수 있는 배포 가능한 가장 작은 단위. 독립적인 공간과 ip를 가지며, ip는 랜덤하게 지정 되어 restart 할 때마다 변한다. (IP 바뀌는게 AWS의 EC2 Instance와 비숫해 보임.)
    • 파드 안의 컨테이너들은 해당 IP를 공유한다.
    • 각 컨테이너는 포트 번호로 구별한다. (ex. 1.0.0.0:80, 1.0.0.0:81, 1.0.0.0:82)
  • 상호의존성이 높은 컨테이너들, 즉, 컨테이너들이 묶어 하나의 단위 수행을 할 수 있게끔 묶는다. (웹 서버 + 로그 수 
    집기 + 볼륨 컨테이너)
  • 따라서, 컨테이너들은 같은 목적으로 자원을 공유한다.
  • Pod 생성 시에는 컨테이너 안에 환경 변수 값을 넣거나, 파일을 마운팅할 수도 있다. 이는 ConfigMap이나 Secret을 통해 세팅할 수 있으며, 설정/민감 정보를 안전하게 관리할 수 있도록 한다. (배포할 때, github secret으로 유출되면 안되는 값 넣어줬던 거랑 비슷해보인다. )
    • ConfigMap : 키-값 쌍으로 구성된 데이터로, 애플리케이션 설정, 환경 변수, 구성 파일 등을 저장한다. 
    • Secret : 민감한 정보 (비밀번호, API 키, OAuth 토큰) 를 안전하게 저장한다. 

 

서비스 (Service)

쿠버네티스의 서비스는 Pod의 집합을 의미한다. 위에서 말했듯, Pod는 클러스터 내에서 유동적이므로 접속 정보가 일정하지 않다. (ex. IP가 계속 바뀜..)
Pod에 접속하기 위해 계속 접속 정보가 무엇인지 확인하는 것도 번거로울 것이다. 따라서 Pod 접속을 안정적으로 유지하기 위해 Service를 사용한다.

  • 서비스는 Pod를 위한 안정적인 엔드포인트를 제공하고, 라벨에 따라 파드를 감지한다.
  • 한줄로 요약하자면, 동적으로 변하는 파드에 고정적으로 접근하기 위해 서비스를 사용한다.

클러스터 내의 컨테이너를 네트워크상에서 노출하고, 컨테이너 간의 통신과 외부로부터의 액세스를 가능하게 한다. 즉, 여러 컨테이너로 구성된 애플리케이션을 하나의 서비스로 묶어 관리하고, 사용자나 다른 애플리케이션이 쉽게 접근할 수 있도록 도와주는 역할을 한다.

 

Service는 클러스터 외부에 서비스를 어떻게 노출(expose)하는지에 따라 크게 4가지로 구별한다. 

  • ClusterIP: 클러스터 내부에서만 서비스를 노출한다. 외부 네트워크에서는 직접 액세스할 수 없다. (비공개 주소)
  • NodePort: 외부에서 액세스 가능한 노드 IP 주소와 포트를 사용하여 액세스할 수 있다.
  • LoadBalancer: 클라우드 제공업체에서 제공하는 로드밸런서를 사용하여 서비스에서 유입되는 트래픽을 내부에 있는 노드로 전달한다.
  • ExternalIP: 외부에서 직접 할당된 IP 주소를 사용하여 서비스를 제공한다. 고정된 IP 주소가 필요한 경우 사용된다.

 

볼륨 (Volume)

 

쿠버네티스에서 파드는 끊임없이 생성되고 사라진다. 그렇기 때문에 파드의 디렉터리도 임시로 사용된다. 만약 파드가 사 라지더라도 디렉터리를 유지하고 싶다면, 볼륨 오브젝트를 사용할 수 있다.

(AWS 인스턴스 삭제되어도 데이터 유지하는 EBS 볼륨과 비슷해 보인다. )

 

Pod 안에는 여러 컨테이너가 있고, 보통 컨테이너 하나 당 하나의 앱이 동작한다. 즉, 하나의 파드 안에는 여러 앱이 동작하는데, 파드 안에 문제가 생겨서 재생성하게 되면 그 안에 있는 데이터가 날아간다. 그래서 볼륨(ex. NAS)을 만들어 파드에 mount 시키면 데이터를 해당 볼륨에 조성할 수 있기에, 파드가 재생성 되더라도 데이터가 유실되는 문제를 해결할 수 있다. 

 

또한, 쿠버네티스는 다양한 볼륨 유형을 제공한다.

 

emptyDir

  • 컨테이너가 실행되는 동안 지속적으로 존재하지만, 컨테이너가 종료되면 데이터가 삭제된다. 
  • 하나의 파드 내에서 여러 컨테이너들 간의 데이터를 공유하기 위해 사용된다.
  • 데이터가 삭제되기에, 일시적인 데이터를 저장할 때 적합하다. (ex. 로그, 캐시)

hostPath

  • 호스트 노드의 실제 파일 디렉토리를 볼륨으로 사용한다. 
  • emptyDir과 달리, 해당 볼륨을 여러 파드들이 마운트하여 사용하기 때문에 데이터 삭제의 위험이 없다. 
  • 하지만, 파드가 삭제된 후 원래 있던 노드에 재생성된다는 보장이 없기에, 원래 있었던 노드의 볼륨에 마운트할 수 없는 문제가 발생할 수 있다. -> 그렇기 때문에, 파드 자신이 할당된 노드의 데이터(시스템 파일, 설정 파일)를 읽어와야할 때 주로 사용한다. 

PV (Persistent Volume)

  • 클러스터 수준에서 관리되는 지속적인 저장공간으로, 파드에게 영속성 있는 볼륨을 제공한다. 
  • 컨테이너/파드가 종료되어도 PV의 데이터는 유지되며, 하나의 PV를 여러 파드에서 공유하여 사용할 수 있다. (= 파이프라인 간 데이터 공유)
  • 그리고 사용자가 특정 조건에 맞는 PV를 요청할 수 있다. (=PVC, Persistent Volume Claim)

 

컨트롤러 (Controller)

파드들을 관리하는 역할로, 특정 리소스 유형(파드, deployment, replicaset 등)을 관찰하고 원하는 상태를 유지하기 위해 지속적으로 시스템을 모니터링(실제 상태와 비교)하고 조치를 취한다.

 

대표적인 기능으로는 AutoHealing, AutoScaling, SW Update, Job 등이 있다.

  • AutoHealing : 파드, 노드, 서비스 등의 리소스가 예상치 못하게 종료, 오류 발생 시 자동으로 복구한다. (-> 새로운 파드를 자동으로 재생성)
  • AutoScaling: 클러스터의 리소스 사용량을 실시간으로 모니터링하고, 필요에 따라 파드의 개수를 자동으로 조정한다. 
  • SW Update: 애플리케이션의 새로운 버전을 컨트롤러를 통해 한번에 배포하며, 기존 버전을 관리한다.
  • Job : 한 번만 실행되는 작업을 관리하고, 작업이 완료되면 관련 리소스를 자동으로 삭제한다.

 

또한, 어떤 목적을 가지고 관리하느냐에 따라 다양한 컨트롤러가 사용될 수 있다.

 

ReplicaSet

  • 원하는 파드 복제본 개수를 유지하도록 한다.
  • 핵심 기능: 원하는 파드 개수가 유지되도록 하드가 죽으면 감지해서 다시 살리거나, 하드의 개수를 조절한다. (scale in-out)

Deployment

  • ReplicaSet을 기반으로 애플리케이션 배포 및 관리를 자동화한다.
  • ReplicaSet 의 모든 기능을 포함한다.
  • 핵심 기능:  Rolling Update, Recreate 등 다양한 파드 배포 전략을 지원하며, 롤백 기능을 제공한다. 

CronJob

  • Job은 어떤 특정 작업만 수행하고 종료시켜야할 때 사용하는데, 이러한 Job을 주기적으로 실행할 때 사용한다.
  • 핵심 기능: 주기적으로 작업을 실행하고, 실패하면 재시도, 작업 완료 후 리소스 삭제한다. 

DaemonSet

  • 클러스터 내 모든 노드에 파드 복제본을 실행하도록 한다.
  • 핵심 기능: 모든 노드에 파드를 배포하고, 새로운 노드가 클러스터에 포함되면 해당 노드에 파드를 생성하고, 탈퇴할 경우 해당 노드의 파드를 삭제한다. 또한 파드가 종료되면 자동으로 재실행한다. 

즉, 각 컨트롤러 별 사용 사례를 간단히 요약하자면 아래와 같다.  

 

  • 단순한 Pod 배포: ReplicaSet
  • 복잡한 애플리케이션 배포 및 관리: Deployment
  • 주기적 작업 자동화: CronJob
  • 고가용성 시스템 구축: DaemonSet

 

 

예시) Replicaset

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx-rs
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
  • apiVersion: 쿠버네티스 API 버전 지정
  • kind: 리소스 종류 지정. 예시에서는 ReplicaSet
  • metadata: 리소스의 이름, 레이블 지정
    • name: ReplicaSet 이름
  • spec: ReplicaSet의 사양 정의
    • replicas: 생성할 복제본의 수 지정. 예시에서는 3개
    • selector: 복제본이 선택되는 방식 정의
      • matchLabels: 복제본에 적용할 label 지정
    • template: 복제본 템플릿 정의
      • metadata: Pod의 레이블 지정
      • spec: Pod의 사양 정의
        • containers: 파드의 컨테이너 지정
          • name: 컨테이너의 이름
          • image: 컨테이너 이미지 지정
          • ports: 컨테이너 포트 지정

 

위 YAML 파일을 nginx-rs.yaml과 같은 파일로 저장한 후, 아래 명령을 실행하여 ReplicaSet을 생성한다. 

 kubectl apply -f nginx-rs.yaml

이렇게 하면 nginx 앱 레이블을 가진 파드 3개가 생성된다. 각 파드에는 nginx 이미지를 실행하는 컨테이너가 포함된다.

실제로 파드가 잘 생성되었는지 확인해볼 수도 있다. 

 

아래 명령어를 통해 app=nginx 레이블을 가진 Pod만 출력한다.

kubectl get pods -l app=nginx
NAME                        READY   STATUS    RESTARTS   AGE
nginx-rs-689775564-w8v9d   1/1     Running   0          10s
nginx-rs-689775564-l5v9b   1/1     Running   0          10s
nginx-rs-689775564-98j4r   1/1     Running   0          10s

 

위 출력과 같이 nginx-rs라는 ReplicaSet에 의해 생성된 3개의 파드를 확인할 수 있다. 각 파드는 READY 상태이고 RUNNING 상태다. RESTARTS는 파드가 재시작된 횟수를 나타내고, AGE는 파드가 생성된 시간을 나타낸다. 

 

ReplicaSet은 spec에 명시된 파드의 갯수만큼 파드들이 항상 실행될 수 있도록 파드의 수를 조절한다. -> 즉, 하나의 파드에 장애가 생겨 종료된다면, 새로운 파드를 재생성하여 개수를 맞춘다. 

 

배포 방식

쿠버네티스는 애플리케이션을 원하는 상태로 유지하기 위해 다양한 배포 방식을 제공하고 있어, 사용 목적에 따라 적합한 배포 방식을 채택할 수 있다. 

 

  • Recreate: 기존 Pod를 모두 종료하고 새 Pod를 새 이미지로 다시 생성하는 방식 
    • 장점: 간단, 빠른 배포, 쉬운 롤백
    • 단점: 서비스 중단, 데이터 손실 가능성, 롤링 업데이트 불가능
  • Rolling Update: 기존 Pod를 하나씩 새 Pod로 교체하면서 애플리케이션을 업데이트하는 방식
    • 장점: 무중단 배포, 데이터 손실 방지, 롤링 업데이트 지원
    • 단점: 복잡성, 배포 시간 증가
  • Blue-Green: 최신 버전의 애플리케이션을 새 환경에 구축한 후 트래픽을 전환하는 방식
    • 장점: 무중단 배포, 안전한 롤백, 실제 배포 전 테스트 가능
    • 단점: 복잡성, 더 많은 리소스 필요
  • Canary: 최신 버전의 애플리케이션을 일부 사용자에게 배포하고 점진적으로 확대하는 방식
    • 장점: 위험 감소, 문제 감지 용이, 점진적인 롤백
    • 단점: 복잡성, 추가적인 설정 필요

주요 오브젝트별 관련 키워드

Pod - Container, Label, NodeSchedule

Service - ClusterIP, NodePort, LoadBalacer

Volume - emptyDir, hostPath, PVC/PV

환경변수(Env, Mount) - ConfigMap, Secret

자원 - NameSpace, ResourceQuota, LimitRange

컨트롤러- Replication Controller, ReplicaSet

배포 - Recreate, RollingUpdate

컨트롤러 : DaemonSet, Job, CronJob 


Reference

https://kubernetes.io/ko/docs

 

'DevOps, MLOps > K8s' 카테고리의 다른 글

로컬 K8s에 Kubeflow 설치하기  (0) 2024.07.02