🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

쿠버네티스 아키텍처 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 22. · 2 Views

쿠버네티스 아키텍처 완벽 가이드

초급 개발자를 위한 쿠버네티스 아키텍처 설명서입니다. 클러스터 구조부터 Control Plane, Worker Node, 파드, 네트워킹까지 실무 관점에서 쉽게 풀어냅니다. 점프 투 자바 스타일로 술술 읽히는 이북 형식으로 작성되었습니다.


목차

  1. 쿠버네티스란
  2. 클러스터_아키텍처
  3. Control_Plane_구성요소
  4. Worker_Node_구성요소
  5. 파드_Pod_개념
  6. 쿠버네티스_네트워킹

1. 쿠버네티스란

김개발 씨는 회사에서 도커 컨테이너로 애플리케이션을 배포하고 있었습니다. 그런데 서비스가 커지면서 관리해야 할 컨테이너가 수십 개로 늘어났고, 어느 날 서버 한 대가 다운되자 모든 서비스가 멈춰버렸습니다.

선배 박시니어 씨가 다가와서 말했습니다. "이제 쿠버네티스를 배워야 할 때가 온 것 같네요."

쿠버네티스는 컨테이너화된 애플리케이션을 자동으로 배포, 스케일링, 관리해주는 오픈소스 플랫폼입니다. 마치 대형 물류센터의 관리 시스템처럼, 수많은 컨테이너들을 효율적으로 배치하고 모니터링하며 문제가 생기면 자동으로 복구합니다.

구글이 내부에서 사용하던 Borg 시스템을 기반으로 만들어졌으며, 현재 클라우드 네이티브 애플리케이션의 사실상 표준이 되었습니다.

다음 코드를 살펴봅시다.

# 간단한 쿠버네티스 파드 정의 예제
apiVersion: v1
kind: Pod
metadata:
  name: my-app
  labels:
    app: web
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80
    # 컨테이너의 리소스 제한 설정
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"

김개발 씨는 입사 6개월 차 백엔드 개발자입니다. 처음에는 도커만 사용해도 충분했습니다.

컨테이너 몇 개를 띄우고 관리하는 것은 어렵지 않았으니까요. 하지만 서비스가 성장하면서 상황이 달라졌습니다.

월요일 아침, 갑자기 서버 한 대가 다운되었습니다. 그 서버에서 돌아가던 모든 컨테이너가 멈췄고, 사용자들은 서비스에 접속할 수 없었습니다.

김개발 씨는 급하게 다른 서버에 컨테이너를 다시 띄웠지만, 이런 일이 반복될 것을 생각하니 막막했습니다. 바로 그때 선배 박시니어 씨가 말했습니다.

"이제 쿠버네티스를 도입할 때가 된 것 같아요." 쿠버네티스란 무엇일까요? 쉽게 비유하자면, 쿠버네티스는 대형 물류센터의 자동화 시스템과 같습니다. 택배 상자를 적재적소에 배치하고, 어느 선반이 가득 찼는지 모니터링하며, 문제가 생긴 상자는 자동으로 다른 곳으로 옮기는 시스템 말입니다.

쿠버네티스도 마찬가지로 컨테이너라는 작은 단위를 여러 서버에 배치하고, 상태를 지속적으로 확인하며, 문제가 생기면 자동으로 복구합니다. 컨테이너만으로는 부족한 이유 도커 컨테이너만 사용하던 시절에는 어땠을까요?

개발자가 직접 각 서버에 접속해서 컨테이너를 실행해야 했습니다. 서버 10대에 같은 애플리케이션을 배포하려면 10번 반복해야 했죠.

더 큰 문제는 서버 하나가 죽으면 그 위의 모든 컨테이너도 함께 사라진다는 점이었습니다. 누군가 24시간 모니터링하다가 문제가 생기면 수동으로 복구해야 했습니다.

트래픽이 갑자기 증가하면 어떻게 할까요? 개발자가 직접 서버를 추가하고, 컨테이너를 더 띄우고, 로드밸런서 설정을 변경해야 했습니다.

이 모든 과정이 수동이었고, 시간도 오래 걸렸습니다. 쿠버네티스의 등장 바로 이런 문제를 해결하기 위해 쿠버네티스가 등장했습니다.

쿠버네티스를 사용하면 선언적 설정이 가능해집니다. "이 애플리케이션을 3개의 복제본으로 실행해주세요"라고 선언하면, 쿠버네티스가 알아서 적절한 서버를 찾아 컨테이너를 배치합니다.

또한 자동 복구 기능도 제공합니다. 컨테이너가 죽으면 자동으로 다시 띄워주고, 서버가 다운되면 다른 서버로 옮겨줍니다.

무엇보다 자동 스케일링이라는 큰 이점이 있습니다. CPU 사용률이 높아지면 자동으로 컨테이너를 추가하고, 트래픽이 줄어들면 다시 줄여줍니다.

개발자가 직접 개입할 필요가 없습니다. 쿠버네티스의 핵심 철학 쿠버네티스는 원하는 상태현재 상태라는 개념을 사용합니다.

개발자는 YAML 파일로 "원하는 상태"를 선언합니다. 예를 들어 "nginx 컨테이너 3개를 실행하고, 각각 80 포트를 열어주세요"라고 선언하는 것이죠.

그러면 쿠버네티스는 지속적으로 현재 상태를 확인하면서 원하는 상태와 일치하도록 만듭니다. 만약 컨테이너 하나가 죽어서 현재 상태가 "2개 실행 중"이 되면, 쿠버네티스는 즉시 이를 감지하고 새 컨테이너를 띄워서 다시 3개로 맞춥니다.

이 모든 과정이 자동으로 일어납니다. 실제 코드 살펴보기 위의 YAML 코드를 한 줄씩 살펴보겠습니다.

먼저 apiVersion: v1은 쿠버네티스 API의 버전을 지정합니다. kind: Pod는 이것이 파드라는 리소스임을 나타냅니다.

파드는 쿠버네티스에서 가장 작은 배포 단위로, 하나 이상의 컨테이너를 포함합니다. metadata 섹션에서는 이름과 레이블을 지정합니다.

레이블은 나중에 이 파드를 식별하고 그룹화하는 데 사용됩니다. spec 섹션이 핵심인데, 여기서 실제로 어떤 컨테이너를 실행할지 정의합니다.

containers 아래에 nginx 컨테이너를 정의했습니다. 도커 허브의 nginx:1.14.2 이미지를 사용하고, 80 포트를 엽니다.

resources 섹션에서는 이 컨테이너가 사용할 수 있는 최대 메모리와 CPU를 제한합니다. 실무 활용 사례 실제 현업에서는 어떻게 활용할까요?

예를 들어 쇼핑몰 서비스를 운영한다고 가정해봅시다. 평소에는 웹 서버 5개로 충분하지만, 블랙프라이데이 세일 때는 트래픽이 10배로 증가합니다.

쿠버네티스를 사용하면 자동 스케일링 정책을 설정해서 CPU 사용률이 70%를 넘으면 자동으로 서버를 추가하도록 할 수 있습니다. 또한 무중단 배포도 가능합니다.

새 버전을 배포할 때 기존 컨테이너를 유지하면서 새 컨테이너를 하나씩 추가하고, 문제가 없으면 기존 컨테이너를 제거하는 방식으로 진행됩니다. 사용자는 서비스 중단을 전혀 느끼지 못합니다.

주의사항 하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 모든 애플리케이션을 무조건 쿠버네티스로 옮기려는 것입니다.

쿠버네티스는 복잡한 시스템이기 때문에 학습 비용이 높고, 작은 프로젝트에는 오히려 과한 선택일 수 있습니다. 컨테이너가 몇 개 안 되는 간단한 서비스라면 도커 컴포즈로도 충분합니다.

또한 쿠버네티스는 인프라 리소스를 상당히 사용합니다. 마스터 노드만 해도 최소 2GB 메모리가 필요하고, 실제 운영 환경에서는 고가용성을 위해 마스터 노드를 3개 이상 구성해야 합니다.

쿠버네티스가 필요한 순간 다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨는 이렇게 설명했습니다.

"쿠버네티스는 컨테이너가 10개 이상이고, 자동 복구와 스케일링이 필요할 때 도입하는 게 좋아요." 김개발 씨의 회사는 현재 서비스가 3개 있고, 각 서비스마다 최소 5개의 컨테이너를 실행합니다. 총 15개 이상의 컨테이너를 관리해야 하고, 트래픽 변동도 심한 편입니다.

쿠버네티스를 도입하기에 적절한 시점이었습니다. 쿠버네티스를 제대로 이해하면 대규모 서비스를 안정적으로 운영할 수 있는 강력한 무기를 얻게 됩니다.

여러분도 지금부터 차근차근 배워보세요.

실전 팁

💡 - 처음에는 Minikube나 Kind로 로컬 환경에서 연습하세요. 실제 클러스터 없이도 쿠버네티스를 학습할 수 있습니다.

  • kubectl 명령어에 익숙해지는 것이 중요합니다. 자주 사용하는 명령어는 alias로 등록해두면 편리합니다.
  • 쿠버네티스 공식 문서의 튜토리얼을 따라해보세요. 실습하면서 배우는 것이 가장 빠릅니다.

2. 클러스터 아키텍처

쿠버네티스를 배우기로 마음먹은 김개발 씨는 설치부터 시작했습니다. 그런데 설치 가이드를 보니 "마스터 노드", "워커 노드", "클러스터"라는 생소한 용어들이 나왔습니다.

박시니어 씨가 화이트보드를 가져와서 그림을 그리기 시작했습니다. "쿠버네티스의 전체 구조부터 이해해야 해요."

쿠버네티스 클러스터는 컨테이너화된 애플리케이션을 실행하는 노드들의 집합입니다. 클러스터는 크게 Control Plane(관리 역할)과 Worker Node(실행 역할)로 나뉩니다.

Control Plane은 클러스터 전체를 관리하고 결정을 내리는 두뇌 역할을 하며, Worker Node는 실제로 컨테이너를 실행하는 일꾼 역할을 합니다. 이 둘이 협력해서 안정적인 컨테이너 환경을 제공합니다.

다음 코드를 살펴봅시다.

# kubectl로 클러스터 정보 확인
# 클러스터의 전체 구조를 파악하는 기본 명령어

# 클러스터 정보 확인
kubectl cluster-info

# 모든 노드 목록 조회
kubectl get nodes

# 특정 노드의 상세 정보 확인
kubectl describe node <node-name>

# 클러스터의 모든 리소스 확인
kubectl get all --all-namespaces

박시니어 씨는 화이트보드에 큰 원을 두 개 그렸습니다. 왼쪽에는 "Control Plane", 오른쪽에는 "Worker Nodes"라고 적었습니다.

"쿠버네티스 클러스터는 이렇게 두 부분으로 나뉘어요." 김개발 씨는 고개를 가우뚱했습니다. "왜 이렇게 나누는 건가요?

그냥 한곳에서 다 하면 안 되나요?" 클러스터 아키텍처의 필요성 쿠버네티스 클러스터를 회사 조직에 비유해보겠습니다. 회사에는 경영진과 실무진이 있습니다.

경영진은 전략을 세우고 의사결정을 하며, 실무진은 실제 업무를 수행합니다. 경영진이 "신제품을 개발하자"고 결정하면, 실무진이 실제로 제품을 만듭니다.

이렇게 역할을 나누면 효율적이고 확장 가능한 조직이 됩니다. 쿠버네티스도 마찬가지입니다.

Control Plane은 경영진처럼 전체 클러스터의 상태를 관리하고 결정을 내립니다. "이 파드를 저 노드에 배치하자", "컨테이너가 죽었으니 다시 띄우자" 같은 결정을 내리는 것이죠.

반면 Worker Node는 실무진처럼 실제로 컨테이너를 실행하고 네트워크를 처리합니다. 왜 이렇게 분리했을까요? 첫 번째 이유는 확장성입니다.

서비스가 커지면 컨테이너를 실행할 Worker Node만 추가하면 됩니다. Control Plane은 그대로 두고 Worker Node를 10대, 100대, 1000대로 늘릴 수 있습니다.

마치 회사가 커질 때 경영진은 그대로 두고 직원만 늘리는 것과 같습니다. 두 번째 이유는 안정성입니다.

Control Plane과 Worker Node를 분리하면, Worker Node에 문제가 생겨도 Control Plane은 안전합니다. Control Plane이 살아있으면 문제를 감지하고 다른 노드로 컨테이너를 옮길 수 있습니다.

세 번째 이유는 보안입니다. 중요한 관리 기능을 Control Plane에만 모아두면, Worker Node가 해킹당해도 클러스터 전체가 장악당하는 것을 막을 수 있습니다.

Control Plane의 역할 Control Plane은 클러스터의 두뇌입니다. 개발자가 "nginx 파드 3개를 실행해주세요"라는 요청을 보내면, Control Plane이 이를 받아서 처리합니다.

어느 Worker Node에 여유 리소스가 있는지 확인하고, 가장 적합한 노드를 선택해서 파드를 배치합니다. 또한 지속적으로 모든 파드의 상태를 모니터링합니다.

Control Plane은 여러 개의 컴포넌트로 구성되어 있습니다. API Server, Scheduler, Controller Manager, etcd 등이 협력해서 작동합니다.

각 컴포넌트는 다음 섹션에서 자세히 다루겠습니다. Worker Node의 역할 Worker Node는 실제 일꾼입니다.

Control Plane으로부터 "이 파드를 실행하세요"라는 지시를 받으면, 도커나 containerd 같은 컨테이너 런타임을 사용해서 컨테이너를 띄웁니다. 그리고 컨테이너가 정상적으로 작동하는지 지속적으로 확인하고, 상태를 Control Plane에 보고합니다.

Worker Node도 여러 컴포넌트로 구성됩니다. kubelet, kube-proxy, Container Runtime 등이 함께 작동합니다.

실제 명령어로 확인하기 위의 kubectl 명령어들을 실행해보면 클러스터 구조를 직접 확인할 수 있습니다. kubectl cluster-info를 실행하면 Control Plane의 주소와 DNS 서비스 위치를 알 수 있습니다.

kubectl get nodes는 클러스터에 연결된 모든 노드의 목록을 보여줍니다. 각 노드의 역할(master 또는 worker), 상태(Ready 또는 NotReady), 버전 정보를 한눈에 볼 수 있습니다.

kubectl describe node로 특정 노드의 상세 정보를 확인하면, CPU와 메모리 용량, 현재 실행 중인 파드 목록, 할당된 리소스 등을 볼 수 있습니다. 고가용성 클러스터 실제 운영 환경에서는 어떻게 구성할까요?

대부분의 기업은 고가용성(High Availability) 클러스터를 구성합니다. Control Plane을 3개 또는 5개로 복제해서, 하나가 죽어도 다른 것이 즉시 역할을 대신하도록 만듭니다.

Worker Node도 최소 3개 이상 구성해서, 한두 개가 다운되어도 서비스가 계속 돌아가도록 합니다. 네이버, 카카오 같은 대형 서비스는 쿠버네티스 클러스터를 수십 개에서 수백 개 규모로 운영합니다.

각 클러스터는 수십 대에서 수백 대의 Worker Node를 포함하고 있습니다. 주의사항 초보자들이 흔히 하는 실수는 Minikube나 Kind로 연습하면서 단일 노드에 익숙해지는 것입니다.

로컬 환경에서는 Control Plane과 Worker Node가 같은 머신에서 돌아가지만, 실제 운영 환경에서는 완전히 분리됩니다. Control Plane 노드에는 일반적으로 애플리케이션 파드를 배치하지 않습니다.

오직 관리 업무만 수행하도록 합니다. 또한 Control Plane의 etcd는 클러스터의 모든 상태 정보를 저장하는 중요한 데이터베이스입니다.

반드시 백업을 주기적으로 수행해야 하며, 가능하면 별도의 디스크에 저장하는 것이 좋습니다. 클러스터 구조의 아름다움 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, 그래서 역할을 나눈 거군요.

확장도 쉽고 안정성도 높아지네요." 쿠버네티스의 클러스터 아키텍처는 수년간의 구글 경험이 녹아있는 설계입니다. 간단해 보이지만 매우 효율적이고 확장 가능한 구조입니다.

이 기본 구조를 이해하면, 앞으로 배울 내용들이 훨씬 쉽게 느껴질 것입니다.

실전 팁

💡 - 운영 환경에서는 Control Plane을 홀수 개(3, 5, 7)로 구성하세요. 과반수 합의를 위해 홀수가 유리합니다.

  • Worker Node는 최소 3개 이상 구성하고, 각 노드의 리소스는 비슷하게 맞추는 것이 좋습니다.
  • kubectl get nodes -o wide 명령어로 노드의 IP 주소와 OS 정보도 함께 확인할 수 있습니다.

3. Control Plane 구성요소

클러스터 구조를 이해한 김개발 씨는 이제 궁금증이 생겼습니다. "Control Plane이 관리를 한다는 건 알겠는데, 구체적으로 어떤 부품들이 어떻게 작동하는 거죠?" 박시니어 씨는 미소를 지으며 화이트보드에 네 개의 박스를 그렸습니다.

"Control Plane은 네 가지 핵심 컴포넌트로 구성되어 있어요."

Control Plane은 네 가지 핵심 컴포넌트로 구성됩니다. API Server는 모든 요청을 받는 창구이자 중앙 허브 역할을 하고, etcd는 클러스터의 모든 상태 정보를 저장하는 데이터베이스입니다.

Scheduler는 새로운 파드를 어느 노드에 배치할지 결정하며, Controller Manager는 원하는 상태와 현재 상태를 지속적으로 비교하면서 문제를 자동으로 해결합니다. 이 네 가지가 협력해서 클러스터 전체를 관리합니다.

다음 코드를 살펴봅시다.

# Control Plane 컴포넌트 상태 확인
# kube-system 네임스페이스에서 실행 중

# Control Plane 파드 목록 확인
kubectl get pods -n kube-system

# API Server 로그 확인
kubectl logs -n kube-system kube-apiserver-<node-name>

# etcd 상태 확인
kubectl get pods -n kube-system -l component=etcd

# Scheduler 상태 확인
kubectl get pods -n kube-system -l component=kube-scheduler

# Controller Manager 상태 확인
kubectl get pods -n kube-system -l component=kube-controller-manager

박시니어 씨가 화이트보드에 그린 네 개의 박스에는 각각 "API Server", "etcd", "Scheduler", "Controller Manager"라고 적혀 있었습니다. 김개발 씨는 메모를 시작했습니다.

"이 네 가지가 어떻게 협력하는지 보면 쿠버네티스의 작동 원리를 이해할 수 있어요." 박시니어 씨가 설명을 시작했습니다. API Server - 모든 것의 중심 쿠버네티스에서 kube-apiserver는 가장 중요한 컴포넌트입니다.

마치 공항의 관제탑처럼, 모든 통신이 API Server를 거쳐갑니다. 개발자가 kubectl 명령어를 실행하면 API Server로 요청이 갑니다.

Worker Node의 kubelet이 상태를 보고할 때도 API Server로 보냅니다. 다른 Control Plane 컴포넌트들도 API Server를 통해서만 데이터를 주고받습니다.

API Server는 RESTful API를 제공합니다. HTTP 요청으로 모든 작업을 수행할 수 있습니다.

파드를 생성하려면 POST 요청을, 조회하려면 GET 요청을, 삭제하려면 DELETE 요청을 보내면 됩니다. kubectl도 내부적으로는 이런 HTTP 요청을 보내는 클라이언트일 뿐입니다.

또한 API Server는 인증과 권한 관리도 담당합니다. 요청이 들어오면 먼저 "누구인가?"를 확인하고, "이 작업을 할 권한이 있는가?"를 검사합니다.

통과한 요청만 실제로 처리됩니다. etcd - 클러스터의 기억 etcd는 분산 키-값 저장소입니다.

클러스터의 모든 정보가 여기에 저장됩니다. 어떤 파드가 어느 노드에서 실행 중인지, 각 서비스의 설정은 무엇인지, 현재 몇 개의 레플리카가 돌아가고 있는지 등 모든 상태 정보를 etcd가 기억합니다.

etcd는 일관성을 매우 중요하게 생각합니다. Raft 알고리즘을 사용해서 여러 etcd 인스턴스 간에 데이터를 동기화합니다.

하나의 etcd가 죽어도 다른 것들이 데이터를 가지고 있어서 안전합니다. 주의할 점은 오직 API Server만 etcd와 직접 통신한다는 것입니다.

다른 컴포넌트들은 etcd에 직접 접근하지 못하고, 반드시 API Server를 거쳐야 합니다. 이렇게 해서 데이터 접근을 중앙에서 관리하고 보안을 강화합니다.

Scheduler - 최적의 배치를 찾는 전략가 kube-scheduler는 새로운 파드를 어디에 배치할지 결정합니다. 개발자가 "nginx 파드를 실행해주세요"라고 요청하면, API Server가 이를 받아서 etcd에 저장합니다.

이때 파드의 상태는 "Pending"입니다. 아직 어느 노드에도 배치되지 않은 상태죠.

Scheduler는 지속적으로 API Server를 감시하다가 Pending 상태의 파드를 발견합니다. 그러면 즉시 최적의 노드를 찾기 시작합니다.

Scheduler는 여러 가지를 고려합니다. 각 노드의 남은 CPU와 메모리는 얼마인지, 파드가 특정 노드에 배치되어야 한다는 제약은 없는지, 같은 종류의 파드를 여러 노드에 분산시켜야 하는지 등을 계산합니다.

이 모든 조건을 만족하는 가장 적합한 노드를 선택해서 파드를 배치합니다. Controller Manager - 끊임없는 감시자 kube-controller-manager는 여러 개의 컨트롤러를 실행하는 컴포넌트입니다.

각 컨트롤러는 특정 리소스를 감시하면서 "원하는 상태"와 "현재 상태"를 비교합니다. 차이가 발견되면 즉시 조치를 취합니다.

예를 들어 Replication Controller는 파드의 복제본 개수를 관리합니다. "nginx 파드 3개"를 원하는데 현재 2개만 실행 중이면, 즉시 새 파드를 생성합니다.

반대로 4개가 실행 중이면 하나를 삭제합니다. Node Controller는 노드의 상태를 감시합니다.

노드가 40초 동안 응답이 없으면 "NotReady" 상태로 표시하고, 5분이 지나도 복구되지 않으면 그 노드의 파드들을 다른 노드로 옮깁니다. Deployment Controller, Service Controller, Endpoint Controller 등 수많은 컨트롤러가 각자 맡은 리소스를 감시하면서 자동으로 관리합니다.

네 컴포넌트의 협력 이 네 가지가 어떻게 협력하는지 예를 들어봅시다. 개발자가 kubectl create deployment nginx --replicas=3 명령을 실행합니다.

이 요청은 API Server로 갑니다. API Server는 인증과 권한을 확인한 후, etcd에 "nginx 디플로이먼트, 복제본 3개"라는 정보를 저장합니다.

Deployment Controller가 이를 감지하고 3개의 파드를 생성하라는 요청을 API Server에 보냅니다. API Server는 etcd에 3개의 파드 정보를 저장합니다.

이때 파드는 Pending 상태입니다. Scheduler가 Pending 파드 3개를 발견하고, 각각을 적절한 Worker Node에 배치합니다.

API Server를 통해 "파드 1은 노드 A, 파드 2는 노드 B, 파드 3은 노드 C"라고 etcd에 기록합니다. 각 Worker Node의 kubelet이 자신에게 할당된 파드를 발견하고 실제로 컨테이너를 실행합니다.

실제 확인하기 위의 kubectl 명령어로 Control Plane 컴포넌트를 직접 확인할 수 있습니다. kubectl get pods -n kube-system을 실행하면 kube-apiserver, etcd, kube-scheduler, kube-controller-manager가 파드 형태로 실행되는 것을 볼 수 있습니다.

각 컴포넌트의 로그를 확인하면 실시간으로 어떤 작업을 하는지 관찰할 수 있습니다. 고가용성 구성 실제 운영 환경에서는 Control Plane을 복제해서 고가용성을 확보합니다.

API Server는 여러 개를 띄우고 로드밸런서 뒤에 둡니다. 하나가 죽어도 다른 것이 요청을 처리합니다.

etcd는 3개 또는 5개로 클러스터를 구성해서 데이터를 복제합니다. Scheduler와 Controller Manager는 리더 선출 방식을 사용합니다.

여러 개가 실행되지만 한 번에 하나만 실제로 작동하고, 리더가 죽으면 다른 것이 즉시 리더가 됩니다. 클러스터의 두뇌 김개발 씨는 메모를 정리하면서 감탄했습니다.

"네 개의 컴포넌트가 이렇게 유기적으로 협력하는구나. 정말 잘 설계되었네요." Control Plane의 네 가지 컴포넌트를 이해하면 쿠버네티스가 어떻게 자동화를 실현하는지 알 수 있습니다.

각 컴포넌트는 단순하지만, 함께 작동하면 놀라운 자동화 능력을 발휘합니다.

실전 팁

💡 - API Server의 감사 로그를 활성화하면 모든 요청 기록을 남길 수 있어 보안 감사에 유용합니다.

  • etcd는 반드시 정기적으로 백업하세요. etcdctl snapshot save 명령어로 스냅샷을 만들 수 있습니다.
  • Scheduler의 결정 과정이 궁금하면 --v=5 옵션으로 상세 로그를 확인할 수 있습니다.

4. Worker Node 구성요소

Control Plane을 이해한 김개발 씨는 이제 Worker Node가 궁금해졌습니다. "실제로 컨테이너를 실행하는 Worker Node에는 어떤 컴포넌트들이 있나요?" 박시니어 씨는 다시 화이트보드를 가리키며 세 개의 박스를 그렸습니다.

"Worker Node는 세 가지 핵심 컴포넌트로 구성되어 있어요."

Worker Node는 실제로 컨테이너를 실행하는 노드로, 세 가지 핵심 컴포넌트로 구성됩니다. kubelet은 Control Plane과 통신하며 파드의 생명주기를 관리하고, kube-proxy는 네트워크 규칙을 설정해서 파드 간 통신을 가능하게 합니다.

Container Runtime은 도커나 containerd처럼 실제로 컨테이너를 실행하는 엔진입니다. 이 세 가지가 협력해서 파드를 안정적으로 실행합니다.

다음 코드를 살펴봅시다.

# Worker Node 컴포넌트 확인 및 관리

# 특정 노드의 kubelet 상태 확인 (노드에서 직접 실행)
systemctl status kubelet

# kubelet 로그 확인
journalctl -u kubelet -f

# kube-proxy 파드 확인
kubectl get pods -n kube-system -l k8s-app=kube-proxy

# 컨테이너 런타임 확인 (containerd 예시)
crictl ps

# 노드의 리소스 사용량 확인
kubectl top node <node-name>

박시니어 씨가 그린 세 개의 박스에는 "kubelet", "kube-proxy", "Container Runtime"이라고 적혀 있었습니다. "Worker Node는 Control Plane보다 구성이 단순해요.

하지만 실제 일을 하는 곳이라 매우 중요합니다." 김개발 씨는 고개를 끄덕이며 질문했습니다. "그럼 이 세 가지가 어떻게 협력하나요?" kubelet - 노드의 대리인 kubelet은 각 Worker Node에서 실행되는 에이전트입니다.

마치 현장 관리자처럼, Control Plane으로부터 받은 지시를 실제로 수행합니다. API Server와 지속적으로 통신하면서 "이 노드에서 실행해야 할 파드가 있나요?"를 확인합니다.

새로운 파드가 할당되면 즉시 Container Runtime에게 "이 컨테이너를 실행해주세요"라고 요청합니다. kubelet은 실행 중인 파드를 지속적으로 모니터링합니다.

컨테이너가 정상적으로 작동하는지, 헬스체크는 통과하는지, 리소스 사용량은 얼마인지를 확인합니다. 문제가 발견되면 컨테이너를 재시작하거나 API Server에 상태를 보고합니다.

kubelet은 PodSpec이라는 YAML 정의를 읽어서 파드를 생성합니다. PodSpec에는 어떤 컨테이너를 실행할지, 어떤 볼륨을 마운트할지, 환경변수는 무엇인지 등 모든 정보가 담겨 있습니다.

Container Runtime - 컨테이너의 실행자 Container Runtime은 실제로 컨테이너를 실행하는 소프트웨어입니다. 초기에는 도커가 주로 사용되었지만, 현재는 containerdCRI-O 같은 경량 런타임을 많이 사용합니다.

쿠버네티스는 CRI(Container Runtime Interface)라는 표준 인터페이스를 정의해서, 어떤 런타임이든 사용할 수 있도록 만들었습니다. Container Runtime은 kubelet의 요청을 받아서 이미지를 다운로드하고, 컨테이너를 생성하고, 실행합니다.

또한 컨테이너의 로그를 수집하고, 리소스 사용량을 측정하며, 컨테이너가 종료되면 정리 작업을 수행합니다. containerd를 사용하는 경우, crictl 명령어로 컨테이너를 관리할 수 있습니다.

도커의 docker ps와 비슷하게 crictl ps로 실행 중인 컨테이너를 확인할 수 있습니다. kube-proxy - 네트워킹의 조율자 kube-proxy는 Worker Node의 네트워크 규칙을 관리합니다.

쿠버네티스에서는 파드가 동적으로 생성되고 삭제됩니다. IP 주소도 계속 바뀝니다.

그런데 클라이언트는 항상 같은 주소로 서비스에 접근할 수 있어야 합니다. 이 문제를 kube-proxy가 해결합니다.

kube-proxy는 Service 리소스를 감시합니다. 새로운 서비스가 생성되면, 해당 서비스의 가상 IP로 들어오는 트래픽을 실제 파드들에게 분산시키는 규칙을 만듭니다.

리눅스의 iptablesIPVS를 사용해서 네트워크 규칙을 설정합니다. 예를 들어 "nginx-service"라는 서비스가 있고, 이 서비스 뒤에 3개의 nginx 파드가 있다고 가정해봅시다.

kube-proxy는 서비스 IP로 들어온 요청을 3개의 파드에게 라운드로빈 방식으로 분산시킵니다. 파드가 추가되거나 삭제되면 자동으로 규칙을 업데이트합니다.

세 컴포넌트의 협력 이 세 가지가 어떻게 협력하는지 예를 들어봅시다. Scheduler가 "파드를 노드 A에 배치한다"고 결정하면, API Server가 etcd에 이 정보를 저장합니다.

노드 A의 kubelet이 API Server를 주기적으로 확인하다가 자신에게 새 파드가 할당된 것을 발견합니다. kubelet은 PodSpec을 읽어서 "nginx:1.14.2 이미지를 사용하는 컨테이너를 실행해야 한다"는 것을 알아냅니다.

그리고 Container Runtime에게 "nginx:1.14.2 컨테이너를 실행해주세요"라고 요청합니다. Container Runtime은 이미지를 도커 허브에서 다운로드하고, 네트워크를 설정하고, 컨테이너를 시작합니다.

동시에 kube-proxy는 이 파드가 어떤 서비스에 속하는지 확인하고, 필요한 네트워크 규칙을 설정합니다. kubelet은 컨테이너가 정상적으로 시작되었는지 확인하고, 상태를 API Server에 보고합니다.

"파드가 Running 상태입니다"라고 말이죠. 실제 확인하기 Worker Node에 직접 접속하면 이 컴포넌트들을 확인할 수 있습니다.

systemctl status kubelet으로 kubelet의 실행 상태를 볼 수 있습니다. kubelet은 systemd 서비스로 실행되며, 노드가 부팅될 때 자동으로 시작됩니다.

journalctl -u kubelet -f로 kubelet의 로그를 실시간으로 확인할 수 있습니다. 파드가 생성되거나 삭제될 때, 헬스체크가 실패할 때 등 모든 이벤트가 로그에 기록됩니다.

kube-proxy는 파드 형태로 실행됩니다. kubectl get pods -n kube-system -l k8s-app=kube-proxy로 확인할 수 있습니다.

일반적으로 DaemonSet으로 배포되어서 모든 노드에 하나씩 실행됩니다. 리소스 모니터링 Worker Node의 리소스 사용량을 모니터링하는 것은 매우 중요합니다.

kubectl top node로 각 노드의 CPU와 메모리 사용량을 확인할 수 있습니다. 특정 노드의 사용량이 계속 높다면, 파드를 다른 노드로 옮기거나 노드를 추가해야 할 시점입니다.

또한 kubectl describe node로 노드의 할당 가능한 리소스와 실제 요청된 리소스를 비교할 수 있습니다. 노드에 16GB 메모리가 있더라도, 모든 파드의 메모리 요청 합계가 14GB라면 실제로는 2GB만 여유가 있는 것입니다.

장애 상황 대처 Worker Node에 문제가 생기면 어떻게 될까요? kubelet이 멈추면 새로운 파드를 실행할 수 없고, 기존 파드의 상태도 보고되지 않습니다.

Control Plane은 일정 시간 후 이 노드를 "NotReady" 상태로 표시하고, 파드를 다른 노드로 옮깁니다. Container Runtime이 문제가 생기면 컨테이너를 실행할 수 없습니다.

kubelet이 이를 감지하고 재시작을 시도하며, 계속 실패하면 파드를 "CrashLoopBackOff" 상태로 표시합니다. kube-proxy에 문제가 생기면 네트워크 연결이 끊어집니다.

파드는 실행 중이지만, 외부에서 접근할 수 없게 됩니다. 현장의 일꾼들 김개발 씨는 정리하며 말했습니다.

"Control Plane이 두뇌라면, Worker Node는 손과 발이네요. 실제로 일을 하는 곳이군요." 박시니어 씨가 고개를 끄덕였습니다.

"맞아요. Worker Node 없이는 아무것도 실행할 수 없어요.

그래서 Worker Node를 안정적으로 관리하는 것이 매우 중요합니다." Worker Node의 세 가지 컴포넌트를 이해하면, 파드가 어떻게 실행되고 관리되는지 알 수 있습니다. 문제가 생겼을 때 어디를 확인해야 하는지도 명확해집니다.

실전 팁

💡 - kubelet의 설정 파일은 /var/lib/kubelet/config.yaml에 있습니다. 헬스체크 주기나 타임아웃 설정을 조정할 수 있습니다.

  • kube-proxy는 기본적으로 iptables 모드를 사용하지만, 대규모 클러스터에서는 IPVS 모드가 성능이 더 좋습니다.
  • Container Runtime은 이미지 캐시를 유지하므로, 같은 이미지를 반복해서 사용하면 훨씬 빠르게 시작됩니다.

5. 파드 Pod 개념

쿠버네티스 구조를 어느 정도 이해한 김개발 씨는 실제 애플리케이션을 배포해보기로 했습니다. 그런데 설정 파일을 보니 "컨테이너"가 아니라 "파드"라는 단위로 정의되어 있었습니다.

"컨테이너와 파드는 뭐가 다른 거죠?" 박시니어 씨가 웃으며 대답했습니다. "파드는 쿠버네티스의 가장 핵심적인 개념이에요."

**파드(Pod)**는 쿠버네티스에서 배포할 수 있는 가장 작은 단위로, 하나 이상의 컨테이너를 포함합니다. 파드 내의 컨테이너들은 같은 네트워크 네임스페이스를 공유해서 localhost로 통신할 수 있고, 같은 볼륨을 공유할 수도 있습니다.

대부분의 파드는 단일 컨테이너를 포함하지만, 밀접하게 결합된 여러 컨테이너를 하나의 파드로 묶어서 관리할 수 있습니다. 파드는 쿠버네티스의 스케줄링, 복제, 네트워킹의 기본 단위입니다.

다음 코드를 살펴봅시다.

# 단일 컨테이너 파드 정의
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: web
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80
    # 헬스체크 설정
    livenessProbe:
      httpGet:
        path: /
        port: 80
      initialDelaySeconds: 3
      periodSeconds: 10
    # 리소스 제한
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

김개발 씨는 도커를 사용할 때는 "컨테이너"만 신경 쓰면 됐습니다. 그런데 쿠버네티스에는 "파드"라는 개념이 등장했습니다.

왜 굳이 새로운 개념이 필요했을까요? 박시니어 씨가 설명을 시작했습니다.

"도커는 컨테이너 하나하나를 관리하지만, 쿠버네티스는 좀 더 높은 수준에서 생각해요." 파드가 필요한 이유 쿠버네티스는 추상화 계층을 하나 더 추가했습니다. 실제 세상에서는 하나의 애플리케이션이 여러 프로세스로 구성되는 경우가 많습니다.

예를 들어 웹 서버와 로그 수집기가 항상 함께 배포되어야 한다고 가정해봅시다. 이 둘은 밀접하게 결합되어 있어서 같은 노드에서 실행되어야 하고, 로그 파일도 공유해야 합니다.

도커만 사용한다면 이 두 컨테이너를 별개로 관리해야 합니다. 웹 서버는 노드 A에, 로그 수집기는 노드 B에 배치될 수도 있습니다.

그러면 로그를 수집할 수 없죠. 파드는 이런 문제를 해결합니다.

웹 서버와 로그 수집기를 하나의 파드로 묶으면, 쿠버네티스는 이 둘을 항상 같은 노드에 배치합니다. 또한 같은 네트워크와 볼륨을 공유하도록 설정해줍니다.

파드의 특징 파드 내의 컨테이너들은 매우 밀접하게 연결됩니다. 첫째, 네트워크 네임스페이스를 공유합니다.

같은 파드의 컨테이너들은 같은 IP 주소를 갖습니다. localhost로 서로 통신할 수 있습니다.

nginx 컨테이너가 80 포트를 열면, 같은 파드의 다른 컨테이너는 localhost:80으로 접근할 수 있습니다. 둘째, 볼륨을 공유할 수 있습니다.

파드에 볼륨을 정의하면, 파드 내의 모든 컨테이너가 이 볼륨을 마운트할 수 있습니다. 웹 서버가 로그를 /var/log에 쓰면, 로그 수집기가 같은 경로를 읽어서 수집할 수 있습니다.

셋째, 생명주기를 공유합니다. 파드가 생성되면 모든 컨테이너가 함께 생성됩니다.

파드가 삭제되면 모든 컨테이너가 함께 삭제됩니다. 하나의 운명 공동체인 셈입니다.

단일 컨테이너 파드 그렇다면 모든 파드가 여러 컨테이너를 포함해야 할까요? 아닙니다.

오히려 대부분의 파드는 단일 컨테이너를 포함합니다. 위의 YAML 예제처럼 nginx 컨테이너 하나만 포함하는 파드가 일반적입니다.

왜 컨테이너 하나인데 파드로 감싸야 할까요? 이유는 일관성입니다.

쿠버네티스는 모든 작업을 파드 단위로 수행합니다. 스케줄링도, 복제도, 네트워킹도 모두 파드 단위입니다.

컨테이너를 직접 다루는 것이 아니라 항상 파드를 통해 간접적으로 다룹니다. 이렇게 하면 단일 컨테이너든 다중 컨테이너든 같은 방식으로 관리할 수 있습니다.

코드 분석 위의 YAML 코드를 자세히 살펴보겠습니다. metadata 섹션에서 파드의 이름을 "nginx-pod"로 지정했고, "app: web"이라는 레이블을 붙였습니다.

레이블은 나중에 이 파드를 선택할 때 사용됩니다. spec.containers에 컨테이너 정의가 들어갑니다.

배열 형태라서 여러 컨테이너를 정의할 수 있습니다. 지금은 nginx 컨테이너 하나만 정의했습니다.

livenessProbe는 헬스체크 설정입니다. 쿠버네티스가 주기적으로 HTTP GET 요청을 보내서 컨테이너가 살아있는지 확인합니다.

응답이 없으면 컨테이너를 재시작합니다. 이렇게 하면 애플리케이션이 멈춘 상태로 계속 실행되는 것을 방지할 수 있습니다.

resources 섹션은 리소스 요청과 제한을 설정합니다. requests는 "이 컨테이너는 최소 이만큼 필요합니다"를 의미하고, limits는 "이 이상 사용할 수 없습니다"를 의미합니다.

Scheduler는 requests를 보고 노드를 선택하며, kubelet은 limits를 넘으면 컨테이너를 종료합니다. 파드의 생명주기 파드는 여러 단계의 생명주기를 거칩니다.

처음 생성되면 Pending 상태입니다. Scheduler가 노드를 선택하고, kubelet이 이미지를 다운로드하는 중입니다.

모든 컨테이너가 시작되면 Running 상태가 됩니다. 컨테이너가 종료되면 Succeeded 또는 Failed 상태가 됩니다.

파드가 계속 실패하면 CrashLoopBackOff 상태가 됩니다. kubelet이 계속 재시작을 시도하지만 계속 실패하는 상황입니다.

이때는 kubectl logs 명령어로 로그를 확인해서 원인을 찾아야 합니다. 실무 활용 실제 현업에서는 파드를 직접 생성하는 경우가 거의 없습니다.

대신 Deployment, StatefulSet, DaemonSet 같은 상위 리소스를 사용합니다. 이들은 파드를 자동으로 생성하고 관리합니다.

예를 들어 Deployment를 만들면, Deployment가 ReplicaSet을 만들고, ReplicaSet이 파드를 만듭니다. 하지만 파드의 개념을 이해하는 것은 매우 중요합니다.

모든 것의 기본이니까요. Deployment를 만들어도 결국 파드가 생성되고, 문제가 생기면 파드를 확인해야 합니다.

멀티 컨테이너 파드 패턴 여러 컨테이너를 포함하는 파드는 주로 세 가지 패턴으로 사용됩니다. 사이드카 패턴은 메인 컨테이너를 보조하는 컨테이너를 추가하는 것입니다.

로그 수집기, 프록시, 모니터링 에이전트 등이 사이드카로 사용됩니다. 앰배서더 패턴은 외부 서비스와의 통신을 중개하는 컨테이너를 추가하는 것입니다.

메인 컨테이너는 localhost로만 통신하고, 앰배서더가 실제 외부 서비스로 연결합니다. 어댑터 패턴은 메인 컨테이너의 출력을 표준 형식으로 변환하는 컨테이너를 추가하는 것입니다.

각 애플리케이션이 다른 로그 형식을 사용해도, 어댑터가 통일된 형식으로 변환해줍니다. 파드의 핵심 김개발 씨는 정리하며 말했습니다.

"파드는 단순히 컨테이너를 감싸는 게 아니라, 밀접하게 결합된 컨테이너들을 하나의 단위로 관리하는 개념이군요." 박시니어 씨가 고개를 끄덕였습니다. "맞아요.

파드를 이해하면 쿠버네티스의 절반은 이해한 거예요." 파드는 쿠버네티스의 가장 기본적이면서도 가장 중요한 개념입니다. 이것을 제대로 이해해야 다음 단계로 나아갈 수 있습니다.

실전 팁

💡 - kubectl exec -it <pod-name> -- /bin/bash 명령어로 파드 내부 컨테이너에 접속해서 디버깅할 수 있습니다.

  • 멀티 컨테이너 파드에서는 -c 옵션으로 특정 컨테이너를 지정해야 합니다. kubectl logs <pod-name> -c <container-name>
  • 파드의 리소스 requests는 너무 낮게 설정하지 마세요. 스케줄러가 잘못된 노드에 배치할 수 있습니다.

6. 쿠버네티스 네트워킹

김개발 씨는 nginx 파드를 성공적으로 실행했지만, 브라우저에서 접속할 수 없었습니다. "파드가 Running 상태인데 왜 접속이 안 되는 거죠?" 박시니어 씨가 웃으며 말했습니다.

"쿠버네티스의 네트워킹을 이해해야 해요. 파드를 실행하는 것과 외부에서 접근할 수 있게 만드는 것은 별개예요."

쿠버네티스 네트워킹은 크게 세 가지 계층으로 구성됩니다. 파드 네트워크는 모든 파드에게 고유한 IP를 부여하고 파드 간 직접 통신을 가능하게 하며, Service는 동적으로 변하는 파드들을 하나의 안정적인 엔드포인트로 묶어줍니다.

Ingress는 외부 트래픽을 클러스터 내부로 라우팅하면서 도메인 기반 라우팅과 SSL 종료 같은 고급 기능을 제공합니다. 이 세 계층이 협력해서 복잡한 네트워킹을 자동화합니다.

다음 코드를 살펴봅시다.

# Service와 Ingress 정의 예제

# ClusterIP Service - 클러스터 내부에서만 접근 가능
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: web
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: ClusterIP

---
# Ingress - 외부에서 도메인으로 접근
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
spec:
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-service
            port:
              number: 80

김개발 씨는 kubectl get pods로 확인했을 때 파드가 Running 상태였습니다. IP 주소도 할당되어 있었습니다.

하지만 브라우저에 그 IP를 입력하면 연결할 수 없었습니다. 박시니어 씨가 설명했습니다.

"쿠버네티스의 네트워킹은 세 가지 계층으로 나뉘어요. 파드 네트워크, 서비스, 그리고 인그레스입니다." 파드 네트워크 - 모든 파드는 IP를 갖는다 쿠버네티스의 첫 번째 네트워킹 원칙은 모든 파드는 고유한 IP 주소를 가진다는 것입니다.

파드가 생성되면 클러스터 내부의 IP 주소가 자동으로 할당됩니다. 예를 들어 10.244.1.5 같은 IP 주소를 받습니다.

같은 클러스터의 다른 파드는 이 IP로 직접 통신할 수 있습니다. NAT 없이 직접 연결됩니다.

이것이 도커와의 큰 차이점입니다. 도커는 기본적으로 브리지 네트워크를 사용해서 각 컨테이너가 사설 IP를 받고, 외부와 통신하려면 포트 매핑이 필요합니다.

쿠버네티스는 모든 파드가 플랫 네트워크에 있어서 서로 직접 통신할 수 있습니다. 이런 네트워크를 구현하는 방법은 여러 가지입니다.

Calico, Flannel, Weave 같은 CNI(Container Network Interface) 플러그인이 실제 네트워킹을 담당합니다. 각 플러그인은 다른 방식으로 작동하지만, 결과는 같습니다.

모든 파드가 고유한 IP를 받고 직접 통신할 수 있습니다. 파드 IP의 문제점 그런데 파드 IP에는 큰 문제가 있습니다.

일시적이라는 점입니다. 파드는 언제든 삭제되고 재생성될 수 있습니다.

노드가 다운되면 다른 노드에서 새 파드가 생성됩니다. 이때 IP 주소가 바뀝니다.

10.244.1.5였던 파드가 재생성되면 10.244.2.8 같은 새로운 IP를 받습니다. 만약 클라이언트가 파드 IP를 직접 사용한다면 어떻게 될까요?

파드가 재생성될 때마다 새로운 IP를 찾아야 합니다. 이것은 매우 불편하고 에러가 발생하기 쉽습니다.

Service - 안정적인 엔드포인트 바로 이 문제를 Service가 해결합니다. Service는 파드들 앞에 안정적인 엔드포인트를 제공합니다.

Service는 고정된 IP 주소와 DNS 이름을 가지며, 이 주소로 들어온 요청을 뒤에 있는 파드들에게 분산시킵니다. 위의 YAML 예제를 봅시다.

selector: app: web라고 정의했습니다. 이것은 "app: web 레이블을 가진 모든 파드"를 선택한다는 의미입니다.

nginx 파드를 만들 때 이 레이블을 붙였으니, Service가 자동으로 그 파드를 찾아서 연결합니다. Service는 ClusterIP 타입으로 생성되었습니다.

이것은 클러스터 내부에서만 접근 가능한 IP를 의미합니다. 다른 파드들은 이 Service의 IP나 DNS 이름(nginx-service)으로 nginx에 접근할 수 있습니다.

Service의 동작 원리 Service가 어떻게 작동하는지 좀 더 자세히 봅시다. Service가 생성되면 쿠버네티스는 가상 IP 주소를 할당합니다.

예를 들어 10.96.0.10 같은 주소를 받습니다. 이 IP는 실제 네트워크 인터페이스에 존재하지 않습니다.

가상 IP입니다. 그럼 이 가상 IP로 보낸 패킷은 어떻게 파드에 도달할까요?

바로 kube-proxy가 처리합니다. 각 노드의 kube-proxy가 iptables나 IPVS 규칙을 설정해서, Service IP로 들어온 패킷을 실제 파드 IP로 변환합니다.

파드가 3개 있다면, kube-proxy는 라운드로빈 방식으로 3개에게 분산시킵니다. 파드가 추가되거나 삭제되면 자동으로 규칙을 업데이트합니다.

Service 타입들 Service에는 여러 타입이 있습니다. ClusterIP는 기본 타입으로, 클러스터 내부에서만 접근 가능합니다.

NodePort는 모든 노드의 특정 포트를 열어서 외부에서 접근할 수 있게 합니다. LoadBalancer는 클라우드 프로바이더의 로드밸런서를 자동으로 생성합니다.

대부분의 서비스는 ClusterIP로 시작합니다. 외부 접근이 필요한 서비스만 NodePort나 LoadBalancer를 사용합니다.

Ingress - 고급 라우팅 그럼 외부에서 웹 애플리케이션에 접근하려면 어떻게 해야 할까요? NodePort를 사용할 수도 있지만, 더 나은 방법은 Ingress를 사용하는 것입니다.

Ingress는 HTTP/HTTPS 라우팅을 제공하며, 도메인 이름 기반으로 여러 서비스를 하나의 IP로 제공할 수 있습니다. 위의 Ingress 예제를 봅시다.

host: myapp.example.com이라고 정의했습니다. 이것은 "myapp.example.com으로 들어온 요청을 nginx-service로 보내라"는 의미입니다.

여러 host 규칙을 정의해서 하나의 IP로 여러 서비스를 제공할 수 있습니다. Ingress는 단순한 라우팅 이상을 제공합니다.

SSL/TLS 종료도 처리할 수 있습니다. 인증서를 Ingress에 설정하면, HTTPS 트래픽을 받아서 복호화한 후 HTTP로 백엔드 서비스에 전달합니다.

각 서비스에서 SSL을 처리할 필요가 없어집니다. Ingress Controller Ingress는 리소스 정의일 뿐입니다.

실제로 작동하려면 Ingress Controller가 필요합니다. Ingress Controller는 Ingress 리소스를 감시하다가 규칙이 생성되면 실제 로드밸런서를 설정합니다.

NGINX Ingress Controller, Traefik, HAProxy 등 여러 구현체가 있습니다. 클라우드 환경에서는 클라우드 프로바이더가 제공하는 Ingress Controller를 사용할 수도 있습니다.

AWS ALB Ingress Controller, GCP Ingress 등이 있습니다. DNS와 Service Discovery 쿠버네티스는 내장 DNS 서버를 제공합니다.

모든 Service는 자동으로 DNS 이름을 받습니다. nginx-service라는 Service를 만들면, 같은 네임스페이스의 파드들은 "nginx-service"라는 이름으로 접근할 수 있습니다.

다른 네임스페이스에서는 "nginx-service.default.svc.cluster.local" 같은 전체 이름을 사용해야 합니다. 이것을 Service Discovery라고 합니다.

파드들이 서로를 찾을 때 하드코딩된 IP 주소 대신 DNS 이름을 사용할 수 있습니다. 훨씬 유연하고 관리하기 쉽습니다.

네트워크 정책 기본적으로 모든 파드는 서로 통신할 수 있습니다. 하지만 보안을 위해 제한이 필요할 수 있습니다.

Network Policy를 사용하면 파드 간 트래픽을 제어할 수 있습니다. 예를 들어 "프론트엔드 파드는 백엔드 파드에만 접근 가능하고, 데이터베이스 파드에는 접근 불가"라는 규칙을 만들 수 있습니다.

Network Policy를 사용하려면 CNI 플러그인이 지원해야 합니다. Calico, Cilium 같은 플러그인은 Network Policy를 완전히 지원합니다.

실무 활용 실제 운영 환경에서는 어떻게 구성할까요? 일반적으로 Ingress를 외부 진입점으로 사용하고, 내부 서비스들은 ClusterIP Service로 연결합니다.

예를 들어 사용자는 Ingress를 통해 프론트엔드에 접근하고, 프론트엔드는 ClusterIP Service를 통해 백엔드 API에 접근하며, 백엔드는 또 다른 ClusterIP Service를 통해 데이터베이스에 접근합니다. 이렇게 계층화하면 각 계층을 독립적으로 확장하고 관리할 수 있습니다.

네트워킹의 마법 김개발 씨는 감탄했습니다. "파드 네트워크, Service, Ingress가 계층적으로 작동하는군요.

복잡해 보이지만 논리적이네요." 박시니어 씨가 고개를 끄덕였습니다. "맞아요.

처음에는 복잡하지만, 이해하고 나면 매우 강력한 도구예요." 쿠버네티스 네트워킹을 이해하면 클러스터 내부와 외부의 모든 통신을 자동화할 수 있습니다. 이것이 쿠버네티스의 가장 큰 장점 중 하나입니다.

실전 팁

💡 - kubectl port-forward 명령어로 로컬 머신에서 파드나 서비스에 직접 접근할 수 있어 디버깅에 유용합니다.

  • Service의 sessionAffinity 옵션을 사용하면 같은 클라이언트를 항상 같은 파드로 라우팅할 수 있습니다.
  • Ingress에서 도메인 와일드카드(*.example.com)를 사용하면 여러 서브도메인을 하나의 규칙으로 처리할 수 있습니다.

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Kubernetes#Container#Pod#ControlPlane#WorkerNode

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.