도커,리눅스

쿠버네티스 첫걸음: 거대한 오케스트라 지휘하기

dig04214 2025. 5. 14. 21:58

 

쿠버네티스는 오케스트라에 비유할 수 있습니다. 수많은 악기가 지휘자의 지휘 아래 하모니를 이루는 것처럼, 쿠버네티스(줄여서 k8s)는 수많은 애플리케이션을 안정적으로 운영해 주는 컨테이너 오케스트레이션 플랫폼입니다.

이런 쿠버네티스의 특징은 선언적 구성자동화입니다.

 

선언적 구성 (Declarative Configuration) :

"이 앱은 이렇게 실행되어야 해" 라고 YAML 파일에 원하는 상태를 정의하면, 쿠버네티스는 이 YAML을 읽고 애플리케이션이 그 상태를 유지하도록 운영합니다. 예를 들어, '3개의 웹 서버가 항상 동작해야 하고, 업데이트 시에도 서비스가 정지되면 안돼'라고 정의하면, 쿠버네티스는 웹 서버 하나가 멈추면 자동으로 새로운 웹 서버를 띄우고, 업데이트 시에도 순차적으로 진행해 웹 서비스가 정지되지 않도록 관리합니다. 개발자는 설계 도면만 작성하면 되고, 실제 건물은 쿠버네티스가 건축하는 것과 비슷한 느낌입니다.

 

서비스 검색 및 로드 밸런싱 (Service Discovery & Load Balancing) :

여러 개의 컨테이너가 동작하고 있다면, 특정 컨테이너의 IP를 알고 있지 않다면, 그 컨테이너와 통신하는 것은 매우 어렵습니다. 특히, 원하는 컨테이너가 죽어서 새롭게 생성된다면, 새롭게 생성된 컨테이너는 IP가 바뀔테니, 더욱 어려워 집니다.

쿠버네티스는 서비스라는 것을 이용해 이를 해결합니다. 개발자가 특정 서비스에 연결되어야 하는 컨테이너 이름을 명시하면, 쿠버네티스는 해당 서비스에 웹 주소와 고정 IP를 부여하고, 컨테이너를 서비스를 통해 통신할 수 있도록 IP를 관리합니다. 또한 특정 서비스에 들어오는 요청들을 여러 개의 컨테이너에 분산해서 전달하는 기능도 제공합니다.

 

자동화된 롤아웃 및 롤백 (Automated Rollouts & Rollbacks) :

새 버전의 앱을 배포하면, 쿠버네티스는 점진적으로 새 버전을 배포합니다. 또한, 문제가 생기면 이전 버전으로 돌아가는 기능도 제공합니다.

 

자가 치유 (Self-healing) : 

실행 중인 컨테이너가 죽거나, 컨네이터가 실행되는 서버(노드)에 문제가 발생한다면 쿠버네티스는 이를 감지하고 컨테이너를 재시작하거나 다른 노드로 컨테이너를 옮깁니다. 따라서 문제가 발생해도 개발자의 개입 없이 알아서 복구되는 기적을 느낄 수 있습니다.

 

쿠버네티스의 구성 요소

쿠버네티스의 구성 요소는 정말 많이 있지만, 웹 서비스를 제공하기 위해 꼭 필요한 구성 요소만 다룰 예정입니다.

이번에 다룰 요소는 컨테이너, 파드, 노드, 클러스터, 디플로이먼트, 서비스, 인그레스 입니다.

 

컨테이너 (Container) : 악기의 핵심 부품

컨테이너는 애플리케이션을 실행하는 데 필요한 모든 것을 담고 있는 독립적인 실행 환경입니다. '독립'적이기 때문에, 운영체제에 상관없이 컨테이너만 있으면 실행이 가능하다는 장점이 있습니다. 또한 가상 머신에 비해 가볍기 때문에 효율적으로 관리할 수 있습니다. 쿠버네티스는 도커 외에도 CRI(Container Runtime Interface) 표준을 따르는 다양한 컨테이너 런타임을 지원합니다.

파드 (Pod) : 악기

쿠버네티스에서 가장 기본적인 배포 단위입니다.(= 컨테이너으로만 배포할 수 없음) 하나 이상의 컨테이너 그룹과 공유 스토리지, 리소스 등을 포함하고 있습니다. 마치 현, 활, 몸통이 모여 멋진 바이올린이 만들어지는 것과 같습니다.  파드 내의 컨테이너들은 항상 같은 노드(워커 머신)에 함께 배치되고 함께 스케줄링됩니다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
  - name: nginx-container
    image: nginx:1.14.2 
    ports:
    - containerPort: 80

노드 (Node) : 악기를 연주하는 단원

파드가 실제로 실행되는 물리적 또는 가상 머신입니다. 오케스트라의 악기 연주자들이 각자의 악기(파드)를 연주하는 것처럼, 노드는 애플리케이션의 특정 기능을 담당합니다.

모든 노드에는 kubelet, kube-proxy, Container Runtime이 구성되어 있습니다.

kubelet : 각 노드에서 실행되는 에이전트입니다.. 마스터로부터 지시를 받아 파드 내의 컨테이너들이 의도한 대로 잘 실행되고 있는지 확인하고 관리합니다.

kube-proxy : 각 노드에서 실행되는 네트워크 프록시입니다. 쿠버네티스 서비스(Service) 개념을 구현하는 역할입니다. 노드의 네트워크 규칙을 관리하고, 파드들 간의 통신이나 외부와의 통신을 가능하게 합니다.

Container Runtime : 실제로 컨테이너를 실행하는 소프트웨어입니다.

클러스터 (Cluster) : 오케스트라

여러 노드의 집합으로, 컨테이너화된 애플리케이션을 실행하기 위한 전체 시스템입니다. 지휘자(컨트롤 플레인)의 지휘 아래 여러 단원(노드)들이 각자의 악기(파드)를 연주하며 하나의 멋진 공연(애플리케이션)을 만들어내는 오케스트라 전체같은 느낌입니다. 마스터 노드와 워커 노드들로 구성됩니다.

컨트롤 플레인 (Control Plane, 마스터 노드) : 클러스터의 두뇌 역할. 클러스터 전체를 관리하고 조율하는 핵심 구성 요소

  • kube-apiserver: 쿠버네티스 API를 노출하는 프론트엔드. 사용자는 kubectl이라는 CLI 도구를 통해 API 서버와 통신하며 클러스터를 제어합니다.
  • etcd: 모든 클러스터 데이터(설정 정보, 상태 정보 등)를 저장하는 일관성 있고 고가용성을 지원하는 키-값 저장소.
  • kube-scheduler: 새로 생성된 파드를 어떤 노드에 배치할지 결정합니다. 노드의 리소스 상태, 제약 조건 등을 고려해서 최적의 노드에 파드를 배치합니다.
  • kube-controller-manager: 다양한 컨트롤러(노드 컨트롤러, 레플리케이션 컨트롤러 등)를 실행해서 클러스터의 상태를 원하는 상태로 유지합니다.
  • cloud-controller-manager (선택 사항): 클라우드 환경에서 쿠버네티스를 사용할 경우, 특정 클라우드 제공업체의 API와 연동하여 로드 밸런서, 스토리지 등을 관리합니다.

워커 노드 (Worker Nodes) : 실제 애플리케이션 파드들이 실행되는 노드들을 말합니다.

디플로이먼트 (Deployment) : 빵집 주문서

애플리케이션의 복제본(파드) 수를 관리하고, 업데이트와 롤백을 쉽게 할 수 해줍니다. 마치 빵집에 빵이 팔리거나(파드 실패) 레시피가 바뀌면(업데이트) 알아서 새 빵을 구워 채워 넣는 것과 같습니다. 디플로이먼트는 파드를 직접 관리하지는 않고, 레플리카셋(ReplicaSet)을 이용해서 관리합니다.

이러한 선언적 상태 관리는 개발자가 "어떻게"가 아니라 "무엇을" 원하는지에 집중하게 합니다. 마치 "내 앱 버전 2를 5개 복제본으로 실행하고 싶어"라고 목표 상태만 명시하면, 쿠버네티스 디플로이먼트 컨트롤러가 현재 상태를 지속적으로 관찰하며 목표 상태로 나아가기 위한 조치를 취합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx # 디플로이먼트 자체에 대한 라벨
spec:
  replicas: 3 # 복사본 3
  selector:
    matchLabels:
      app: nginx # 디플로이먼트가 어떤 파드를 관리할지 알려주는 부분
  template: # 파드들의 설계도
    metadata:
      labels:
        app: nginx # 생성될 파드들이 가질 라벨
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

 

서비스 (Service) : 빵집 직원

파드는 언제든 새로 생기거나 없어질 수 있어서 IP 주소가 계속 바뀔 수 있습니다. 서비스는 이렇게 변덕스러운 파드들에게 고정된 IP 주소와 DNS 이름을 부여해서, 다른 애플리케이션이나 사용자가 안정적으로 접근할 수 있도록 해주는 역할을 합니다. 빵집의 특정 빵(파드)을 찾는 손님에게 "저쪽 코너로 가시면 됩니다!" 하고 안내해 주는 친절한 직원과 같습니다.

서비스 타입 범위 노출 방식 사례 특징
Cluster IP 내부 내부 IP 할당 클러스터 내 DB 기본 값
NodePort 외부 각 노드 IP의 정적 포트에 노출 테스트 노드의 포트를 노출할 수 있음

 

apiVersion: v1
kind: Service
metadata:
  name: nginx-clusterip-service
spec:
  selector:
    app: nginx # 파드의 라벨과 일치해야 함
  ports:
    - protocol: TCP
      port: 80         # 서비스가 노출하는 포트 (클러스터 내부)
      targetPort: 80   # 컨테이너(파드 안의)가 리스닝하는 포트
  type: ClusterIP      # 기본값이므로 생략 가능
apiVersion: v1
kind: Service
metadata:
  name: nginx-nodeport-service
spec:
  type: NodePort # 타입을 명시적으로 설정
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80         # ClusterIP 접근을 위한 포트 (여전히 생성됨)
      targetPort: 80   # 컨테이너 포트
      nodePort: 30080  # 각 노드 IP에 열릴 포트 (예: 30000-32767)

인그레스 (Ingress) : 구역 안내판

클러스터 외부에서 내부 서비스로 들어오는 HTTP(S) 요청을 관리합니다. 마치 쇼핑몰의 정문에 있는 구역 안내판처럼, "A 매장으로 가려면 이쪽, B 매장으로 가려면 저쪽" 하고 외부 손님(트래픽)을 올바른 서비스로 안내해 줍니다. SSL/TLS 암호화 처리나 특정 경로에 따른 라우팅도 담당합니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: / # Nginx 인그레스 컨트롤러에만 해당
spec:
  ingressClassName: nginx # 여러 인그레스 컨트롤러가 있을 경우 지정
  rules:
  - host: myapp.yourcsdomain.com # 선택 사항: 설정하지 않으면 모든 호스트에 적용
    http:
      paths:
      - path: /api         # myapp.yourcsdomain.com/api/... 로의 요청
        pathType: Prefix   # /api, /api/, /api/users 등과 일치
        backend:
          service:
            name: my-api-service # 백엔드 API 서비스의 이름
            port:
              number: 8080       # 해당 서비스의 포트
      - path: /                  # myapp.yourcsdomain.com/ 로의 요청
        pathType: Prefix
        backend:
          service:
            name: my-frontend-service
            port:
              number: 80
   tls: # 선택 사항: HTTPS용
    - hosts:
       - myapp.yourcsdomain.com
         secretName: myapp-tls-secret # TLS 인증서와 키를 포함하는 쿠버네티스 시크릿