개발일지/서버,인프라

쿠버네티스, 기본기를 다뤄보자.

recording or reCoding 2025. 5. 24. 13:48

우연히 `유튜브JSCODE 박재성`님의 유뷰트 영상을 보게 되었고, 앉은자리에서 쿠버네티스관련 영상을 완독하게 되었습니다. 

그과정에서 알게된 가장 기본적인 개념에 대해서 소개해보겠습니다.

쿠버네티스란?

컨테이너화된 애플리케이션을 자동으로 배포, 확장, 관리해주는 오픈소스 시스템입니다. 컨테이너 기술은 도커의 개념 입니다. 요새는 이러한 컨테이너를 통해 관리되고 있습니다.  하지만, 컨테이너가 수백 수천개가 된다면 문제가 되기 때문에 `어떤 컨테이너를 어떤 서버(노드)에 실행시킬지 알아서 결정하고 배포 해주는 역할이 쿠버네티스 입니다. 또한, 사용자가 몰리면 컨테이너 수를 자동으로 늘려주기도 하고,줄어들면 다시 주열서 자원 효율을 관리합니다. 이를 오토 스케일링이라 부릅니다. 오류가 발생하면 자동으로 재시작 하는 방안으로 중단되지 않도록도와주기도 합니다.

핵심 요약:

  • 대상: 컨테이너 (주로 도커)
  • 하는 일: 자동화된 배포, 확장, 운영 관리
  • 목표: 개발자와 운영자가 복잡한 인프라 관리 대신 애플리케이션 개발과 서비스에 더 집중할 수 있도록 돕는 것

쿠버네티스 Pod 개념

Pod는 쿠버네티스에서 생성하고 관리할 수 있는 가장 작은 배포 단위입니다.

하나의 Pod안에 컨테이너들이 들어 있습니다. Pod는 컨테이너를 직접 실행하는 것이 아니라, 컨테이너를 실행하기 위한 논리적인 호스트 또는 환경을 제공합니다.Pod 안에 컨테이너들은 네트워크,스토리지(볼륨) 등의 정보를 공유합니다. 이렇게 된다면 함께 실행되어야 하는 컨테이너들이 서로 강하게 결합하여 효율적이게 됩니다.

 

쿠버네티스를 공부하기전에 도커를 알아야 합니다. 도커를 공부하다보면 '도커 컴포즈`의 개념과 여러 컨테이너를 함께 관리한다는 유사성을 가지고 있지만, 다른 개념 입니다.

특징 Docker Compose (docker-compose.yml 전체) Kubernetes Pod
주요 목적 로컬 개발 환경, 단일 호스트에서의 다중 컨테이너 애플리케이션 실행 및 관리 쿠버네티스 클러스터 내 배포의 최소 단위, 긴밀하게 결합된 컨테이너 그룹 실행
범위 보통 하나의 애플리케이션 전체 (웹 서버, 데이터베이스, 캐시 등)를 정의 애플리케이션의 한 부분 또는 인스턴스. 여러 Pod가 모여 하나의 애플리케이션을 구성
네트워킹 각 서비스는 별도의 IP를 가질 수 있으며, 서비스 이름으로 통신 (Docker 네트워크) Pod 내 모든 컨테이너는 동일한 IP와 포트 공간을 공유 (localhost로 통신 가능)
생명 주기 docker-compose up/down으로 전체 서비스 그룹 관리 Pod 내 컨테이너들은 함께 시작되고 중지됨. Pod 자체가 생명 주기를 가짐.
확장성/관리 수동 확장. 주로 단일 호스트. 쿠버네티스 컨트롤러(Deployment 등)에 의해 자동 확장, 자가 치유, 롤링 업데이트 등 관리
컨테이너 관계 서비스들은 서로 연관되어 있지만, 각자 독립적인 프로세스로 실행될 수 있음 Pod 내 컨테이너들은 반드시 함께 실행되어야 하며, 서로에게 매우 의존적 (예: 사이드카)

 

결론 :

  • Pod는 쿠버네티스에서 가장 작은 배포 단위입니다.

쿠버네티스에서는 yml 이라는 파일로 설정을 진행합니다. 프로젝트를 구성하고 Dokerfile을 설정하여 이미지를 만들어보겠습니다.

- Dokerfile은 도커 이미지를 어떻게 만들지 정의하는 설계도 같은 개념입니다.

FROM openjdk:17-jdk

COPY build/libs/*SNAPSHOT.jar app.jar

ENTRYPOINT["java", "-jar","/app-jar"]
  • > docker build -t spring-server . 명령어를 통해 이미지를 만듭니다.
  • -t spring-server: -t 옵션은 이미지에 이름(태그)을 붙이는 옵션입니다. 이미지 이름을 spring-server로 지정합니다. (정확히는 spring-server:latest가 됩니다.)

 

 

매니페스트파일 작성( yaml 파일)

Pod는 실제로 어떻게 만들 수 있을까요? 바로 매니페스트(Manifest) 파일을 통해서입니다. 쿠버네티스는 대부분의 리소스(Pod, Service, Deployment 등)를 YAML 또는 JSON 형식의 파일로 정의하고 관리합니다. 이번에는 우리가 직접 만든 자바 스프링 부트 애플리케이션 이미지를 사용하여 간단한 Pod를 생성하는 매니페스트 파일을 작성해 보겠습니다.

apiVersion: v1
kind: Pod

#리소스에 대한 부가 정보(데이터에 대한 데이터)를 정의하는 부분입니다. 
#여기에는 이름, 레이블, 어노테이션 등이 포함될 수 있습니다.
metadata: 
  name: spring-pod

spec:
  containers:
    - name: spring-container  # 컨테이너의 이름 (Pod 내에서 고유해야 함)
      image: spring-server    # 사용할 도커 이미지 (이전에 빌드한 이미지)
      imagePullPolicy : IfNotPresent # 이미지 정책 관련 설정
      ports:
        - containerPort: 8080 # 컨테이너가 내부적으로 사용하는 포트 (문서화 목적)

 

 

kubectl cluster-info 명령어를 통해서  쿠버네티스 동작 확인 할 수 있으며, kubectl apply -f .\\spring-pod.yaml  명령어를 통해서 yaml 파일 기준 pod 생성합니다. 생성 후에는 kubectl get pods 명령어를 통해 확인합니다.

 

진행하다가. ImagePullBackOff 오류가 발생하였습니다. 이는 이미지 풀 정책 오류입니다. yml 파일에 설정을 통해서 원하는 정책에 맞게 설정 할 수 있습니다.

imagePullPolicy  설명 로컬 이미지 사용 여부 원격 레지스트리(Docker Hub 등) 사용 여부
Always 항상 원격 레지스트리에서 이미지를 새로 다운로드합니다. 로컬 캐시는 무시합니다. X O
IfNotPresent 로컬에 이미지가 있으면 해당 이미지를 사용하고, 없으면 원격 레지스트리에서 다운로드합니다. O O (로컬에 없을 시)
Never 로컬에 있는 이미지만 사용합니다. 로컬에 이미지가 없으면 Pod 생성이 실패합니다. O X
(명시하지 않음) 아래 규칙에 따라 기본 정책이 적용됩니다. - -
↳ 이미지 태그가 latest인 경우 Always 정책으로 동작합니다. X O
↳ 이미지 태그가 latest가 아닌 경우 (예: myimage:v1.0)  

 

이렇게 하나의 Pod에 spring-server라는 이미지에 대해서 spring-container 라는 컨테이너를 생성해보았습니다.

여러의 Pod에 이 container를 구성하고 싶다면 yml 로 만들었던 내용을 복사하여 --- 구분하고 밑에 yml을 설정하면 됩니다.

하지만 필요할때마다 이렇게 yml 내에서 소스를 복사해서 쓴다면 굉장히 효율적이지 못합니다. 

하나의 yml에 3개의 내용 적용.하여 확인 해본 결과.

이러한 보완을 위해 나온것이 리플리카라는 개념입니다.

 

쿠버네티스의 디플로이먼트 , 리플리카

pod를 묶어서 관리하는 기능을 디플로이 먼트라고 합니다. pod를 지정하는 대로 여러개 생성 할수 있으며, 생성된 파드를 한번에 중지시킵니다. 디플로이먼트는 레플리카셋을 관리합니다.(레프릴카셋 = 복제본의 묶음)

 

spec: 부분에 replicas로 몇개의 파드를 관리할것인지 설정 할 수 있습니다.

# 공식문서에 나와 있는 Deployment 설정
## apps/v1 : /v1을 꼭 명시해야함.
apiVersion : apps/v1
kind: Deployment

metadata:
  name: spring-deployment
# Deployment 세부 정보
  ## backend-app 의 labels을 가진 pod를 배포
spec:
  replicas: 3 #3개를 사용할거야 라고 정의 
  selector:
    matchLabels:
      app: backend-app

  # 배포할 Pod 정의
  template:
    metadata:
      labels: # 카테고리 => app: backend-app 이라는 카테고리로 정의 
        app: backend-app
    spec:
      containers:
        - name:  spring-container
          image: spring-server
          imagePullPolicy : IfNotPresent
          ports:
            - containerPort : 8080

 

위를 이용한다면 만약 파드 1이 어떤 이유로든 (예: 노드 장애, 컨테이너 내부 오류 등) 죽거나 응답하지 않게 되었을때 항상 지정한 개수만큼 유지하게 되고, 따라서 특정 파드가 죽더라도 살아있는 파드 2는 계속해서 서비스 요청을 처리 할수 있게 됩니다.

 

또한, 쿠버네티스 서비스(Service)가 진정한 의미의 로드 밸런싱(Load Balancing)을 수행하려면, 해당 서비스가 가리키는 여러 개의 파드(Pod)가 필요합니다. 이를 위해서도 필요한 개념입니다. 그럼 이제 쿠버네티스에서 말하는 `서비스`의 개념에 대해서 확인해보 겠습니다.

쿠버네티스의 서비스 개념

위에서 만들어진 pod 들은 서비스 요청을 하게 된다고 하였습니다. 서비스 요청이 들어왔을때 Pod에 대해서 균등하게 요청을 분배해주는 역할을 하는 것이 서비스 입니다.

서비스 타입 (Service Type) 주요 특징 및 용도 외부 접근 가능 여부 내부 접근 IP/포트 외부 접근 방식 (예시) 사용 환경/상황 예시
ClusterIP 클러스터 내부에서만 접근 가능한 고유 IP 주소를 할당합니다. (기본 타입) X 클러스터 내부 IP (<ClusterIP>:<Port>) 해당 없음 클러스터 내부의 다른 애플리케이션(파드)들 간의 통신 (예: 프론트엔드에서 백엔드 API 호출)
NodePort 각 노드(워커 노드)의 특정 포트를 열어 외부에서 해당 포트로 서비스에 접근할 수 있도록 합니다. O 클러스터 내부 IP (<ClusterIP>:<Port>) <br> 각 노드 IP (<NodeIP>:<NodePort>) 모든 노드의 IP와 지정된 포트 (<NodeIP>:<NodePort>) 개발/테스트 환경, 또는 외부 로드밸런서가 아직 없는 상황에서 간단하게 외부 노출이 필요할 때, 온프레미스 환경에서 직접 로드밸런서 구성 시
LoadBalancer 클라우드 플랫폼(AWS, GCP, Azure 등)에서 제공하는 외부 로드밸런서를 자동으로 프로비저닝하고 서비스에 연결합니다. O 클러스터 내부 IP (<ClusterIP>:<Port>) <br> 외부 로드밸런서 IP (<External-IP>:<Port>) 프로비저닝된 외부 로드밸런서의 IP 주소 (<External-IP>:<Port>) 클라우드 환경에서 프로덕션 서비스를 외부에 안정적으로 노출시키고 싶을 때 (가장 일반적인 외부 노출 방식)

 

위에서는 pod.yml 을 만들었다면, 서비스를 위한 yml 을 만들어서 적용 할 수 있습니다. spec의 type을 위 표에 내용을 참고하여 원하는 type을 지정합니다. 디플로이먼트 yml 에서 labels 로 적용한 이름을 적용하면 됩니다.

apiVersion: v1
kind : Service

metadata:
  name: spring-service

spec:
  type: NodePort #쿠버네티스 외부에서 내부에 요청 보내기 위해 설정
  selector: # deployment에 적었던 카테고리(labels)적용
    app: backend-app
  ports:
    - protocol: TCP
      port : 8080 # 쿠버네티스 내부에서 Service에 접속하기 위한 포트
      targetPort: 8080 # 매핑하기 위한 파드의 포트 번호(deployment의 포트)
      nodePort: 30000 # 외부에서 사용자들이 접근하게 될 포트

 

실제로 kill해보고 pod가 지정한개수만큼 유지되는지 확인.

'개발일지 > 서버,인프라' 카테고리의 다른 글

원격저장소, git 그리고 Gitea  (0) 2025.05.27