Kubernetes 정복기: 3노드 클러스터로 시작하는 첫걸음 (Day 1)

문한성·2025년 10월 29일
0

K8S 정복하기

목록 보기
1/9

들어가며

안녕하세요! 저는 최근 온프레미스 환경에서 Kubernetes 클러스터를 직접 구축하고 운영하면서 많은 것을 배우고 있습니다. 오늘은 그 여정의 첫 번째 날, "클러스터 기초와 아키텍처 이해"에 대해 이야기해보려 합니다.

처음 Kubernetes를 접할 때 가장 막막했던 부분이 "도대체 이 많은 컴포넌트들이 뭐하는 건데?"였습니다. Pod, Service, Deployment, etcd, CoreDNS... 용어만 들어도 머리가 복잡해지더라고요.

하지만 실제로 클러스터를 직접 만들고, 각 컴포넌트가 어떻게 동작하는지 하나씩 확인하면서 "아, 이래서 이렇게 설계했구나!" 하는 순간들이 있었습니다. 오늘 그 경험을 공유하고자 합니다.


목차

  1. 우리 클러스터 소개
  2. 클러스터의 심장: Control Plane
  3. Pod와 네임스페이스의 비밀
  4. 네트워킹의 마법: CoreDNS
  5. 배운 것과 다음 계획

1. 클러스터 소개

클러스터 구성

저는 총 3개의 노드로 클러스터를 구성했습니다:

$ kubectl get nodes -o wide
NAME   STATUS   ROLES           AGE   VERSION    INTERNAL-IP   OS-IMAGE             CONTAINER-RUNTIME
cpu1   Ready    control-plane   46h   v1.31.13   172.30.1.43   Ubuntu 22.04.5 LTS   containerd://1.7.28
cpu2   Ready    <none>          17h   v1.31.13   172.30.1.80   Ubuntu 22.04.5 LTS   containerd://1.7.28
gpu1   Ready    <none>          17h   v1.31.13   172.30.1.38   Ubuntu 22.04.5 LTS   containerd://1.7.28
노드역할스펙특징
cpu1Master + Worker12코어, 7.5GB마스터 노드지만 taint 제거로 워커로도 사용
cpu2Worker8코어, 16GB일반 워크로드 실행
gpu1Worker12코어, 16GBGPU 워크로드용 (향후 활용 예정)

여기서 포인트!

보통 Master 노드는 Control Plane 컴포넌트만 실행하고 일반 Pod는 실행하지 않습니다. 하지만 저는 리소스 활용을 위해 cpu1의 taint를 제거했습니다.

$ kubectl describe node cpu1 | grep Taints
Taints:             <none>

<none>이 보이시나요? 이제 cpu1에도 일반 애플리케이션 Pod를 배포할 수 있습니다!

클러스터 정보 확인

가장 먼저 해본 명령어는 이거였습니다:

$ kubectl cluster-info
Kubernetes control plane is running at https://172.30.1.43:6443
CoreDNS is running at https://172.30.1.43:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

간단하지만 중요한 정보:

  • API Server 주소: https://172.30.1.43:6443 - 모든 kubectl 명령어가 여기로 갑니다
  • CoreDNS: 클러스터 내부 DNS 서비스 - Pod들이 서로를 찾을 수 있게 해줍니다

2. 클러스터의 심장: Control Plane

Control Plane이 뭐길래?

Kubernetes 클러스터를 사람에 비유하면, Control Plane은 "뇌"입니다. 모든 결정이 여기서 이루어지죠.

Control Plane은 4개의 핵심 컴포넌트로 구성됩니다:

$ kubectl get pods -n kube-system -o wide | grep cpu1
etcd-cpu1                      1/1     Running   172.30.1.43   cpu1
kube-apiserver-cpu1            1/1     Running   172.30.1.43   cpu1
kube-controller-manager-cpu1   1/1     Running   172.30.1.43   cpu1
kube-scheduler-cpu1            1/1     Running   172.30.1.43   cpu1

모두 cpu1 (마스터 노드)에서 실행되고 있습니다. 각각의 역할을 알아봅시다:

1. etcd - "기억하는 자"

$ kubectl get componentstatuses
NAME                 STATUS    MESSAGE   ERROR
etcd-0               Healthy   ok
  • 역할: 클러스터의 모든 데이터를 저장하는 key-value 저장소
  • 저장하는 것: Pod 정보, Service 설정, ConfigMap, Secret 등 모든 것!
  • 중요성: etcd가 죽으면 클러스터 전체가 멈춥니다 😱

팁: 프로덕션 환경에서는 etcd를 반드시 백업하세요. 이게 바로 클러스터의 "두뇌 백업"입니다.

2. kube-apiserver - "중앙 통제소"

  • 역할: 모든 요청을 받아서 처리하는 REST API 서버
  • 포트: 6443 (HTTPS)
  • 특징:
    • kubectl 명령어가 통신하는 대상
    • 인증, 인가, Admission Control 수행
    • etcd와 직접 통신하는 유일한 컴포넌트

내가 깨달은 것:

kubectl get pods
      ↓
API Server (172.30.1.43:6443)
      ↓
etcd에서 Pod 정보 조회
      ↓
결과 반환

모든 작업이 API Server를 거친다는 게 핵심입니다!

3. kube-controller-manager - "자동화 담당자"

  • 역할: 클러스터의 "바라는 상태"를 유지
  • 예시:
    • Deployment가 "Pod 3개 실행"을 원하면 → 계속 3개 유지
    • Node가 죽으면 → 해당 노드의 Pod를 다른 노드로 재생성
    • Service Endpoint 자동 관리

실제로 본 예시:

제가 테스트로 Pod를 하나 삭제했을 때:

$ kubectl delete pod coredns-76b86bc878-5v86m -n kube-system
pod "coredns-76b86bc878-5v86m" deleted

$ kubectl get pods -n kube-system | grep coredns
coredns-76b86bc878-5v86m   1/1     Running   0          5s  <- 자동 재생성됨!
coredns-76b86bc878-pnpjq   1/1     Running   0          46h

5초 만에 새 Pod가 생성되었습니다. 이게 바로 Controller Manager의 마법입니다! ✨

4. kube-scheduler - "배치 전문가"

  • 역할: 새로운 Pod를 "어느 노드에 배치할지" 결정
  • 고려 사항:
    • 노드의 여유 리소스 (CPU, 메모리)
    • nodeSelector, affinity 규칙
    • Taints와 Tolerations

재미있는 발견:

$ kubectl get pods -A -o wide | grep -c cpu1
12

$ kubectl get pods -A -o wide | grep -c cpu2
3

cpu1에 Pod가 더 많은 이유? Control Plane Pod 4개 + CoreDNS 2개 + 기타 시스템 Pod들이 모두 cpu1에 있기 때문입니다!


3. Pod와 네임스페이스의 비밀

총 19개의 Pod가 돌아가는 중

$ kubectl get pods -A | wc -l
20  # 헤더 포함

처음엔 "19개나?"라고 놀랐습니다. 하지만 알고 보니 모두 필요한 시스템 Pod들이었어요.

Pod 종류별 분류

1. Control Plane Pod (4개) - Static Pod

etcd-cpu1
kube-apiserver-cpu1
kube-controller-manager-cpu1
kube-scheduler-cpu1

여기서 발견! 이들은 /etc/kubernetes/manifests/에 YAML 파일로 정의되어 있습니다:

$ ls /etc/kubernetes/manifests/
etcd.yaml  kube-apiserver.yaml  kube-controller-manager.yaml  kube-scheduler.yaml

kubelet이 이 디렉토리를 감시하다가 파일이 있으면 자동으로 Pod를 실행합니다. API Server가 없어도 실행된다는 점이 신기했어요!

2. DaemonSet Pod (9개) - 각 노드마다 1개씩

$ kubectl get daemonset -A
NAMESPACE       NAME              DESIRED   CURRENT   READY
calico-system   calico-node       3         3         3
calico-system   csi-node-driver   3         3         3
kube-system     kube-proxy        3         3         3

각 노드마다 정확히 1개씩! 이게 DaemonSet의 핵심입니다.

  • calico-node: 네트워크 에이전트
  • kube-proxy: 서비스 라우팅
  • csi-node-driver: 스토리지 드라이버

3. Deployment Pod (6개) - 복제 가능

$ kubectl get deployment -A
NAMESPACE         NAME                      READY   UP-TO-DATE   AVAILABLE
calico-system     calico-kube-controllers   1/1     1            1
calico-system     calico-typha              2/2     2            2
kube-system       coredns                   2/2     2            2
tigera-operator   tigera-operator           1/1     1            1

CoreDNS가 2개인 이유? 고가용성(HA)을 위해서입니다. 하나가 죽어도 다른 하나가 서비스를 계속합니다!

네임스페이스는 "방"

$ kubectl get namespaces
NAME              STATUS   AGE
default           Active   46h  <- 기본 네임스페이스
kube-system       Active   46h  <- Kubernetes 시스템
calico-system     Active   46h  <- Calico 네트워크
tigera-operator   Active   46h  <- Calico 운영자
kube-public       Active   46h  <- 공개 리소스
kube-node-lease   Active   46h  <- 노드 하트비트

네임스페이스를 "아파트의 각 집"으로 생각하면 이해하기 쉽습니다:

  • 논리적으로 격리됨
  • 각자 독립적인 리소스 관리
  • 하지만 필요하면 서로 통신 가능

실용 팁:

# 특정 네임스페이스 조회
$ kubectl get pods -n kube-system

# 모든 네임스페이스 조회
$ kubectl get pods -A

# 기본 네임스페이스 변경 (매번 -n 안 써도 됨)
$ kubectl config set-context --current --namespace=kube-system

4. 네트워킹의 마법: CoreDNS

3가지 네트워크가 공존한다

처음 Pod IP를 봤을 때 혼란스러웠습니다:

$ kubectl get pods -A -o wide
NAMESPACE     NAME                  IP               NODE
kube-system   coredns-...           10.244.184.81    cpu1   <- Pod IP
kube-system   kube-proxy-...        172.30.1.43      cpu1   <- Node IP

왜 IP가 다른가요?

우리 클러스터에는 3가지 네트워크가 있습니다:

1. Node Network: 172.30.1.0/24
   - 실제 서버들의 IP
   - 예: 172.30.1.43 (cpu1)

2. Pod Network: 10.244.0.0/16
   - Calico가 할당하는 Pod 전용 IP
   - 예: 10.244.184.81 (coredns)

3. Service Network: 10.96.0.0/12
   - 가상 IP (실제로는 존재하지 않음!)
   - 예: 10.96.0.10 (CoreDNS Service)

Calico 네트워크 확인:

$ kubectl get ippool
spec:
  cidr: 10.244.0.0/16
  blockSize: 26
  vxlanMode: CrossSubnet
  natOutgoing: true

핵심 설정:

  • vxlanMode: CrossSubnet - 같은 서브넷은 직접, 다른 서브넷은 VXLAN 터널 사용
  • blockSize: 26 - 각 노드에 /26 블록 할당 (62개 IP 사용 가능)

DNS 테스트 - 드디어 성공!

가장 신나는 순간이었습니다. DNS가 정말 작동하는지 직접 테스트했어요:

$ kubectl run test-dns --image=busybox:1.28 --rm -i --restart=Never -- sh -c "
  nslookup kubernetes.default &&
  nslookup google.com
"

결과:

Server:    10.96.0.10
Address 1: 10.96.0.10

Name:      kubernetes.default
Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local

Name:      google.com
Address 1: 142.250.206.238

성공!

무슨 일이 일어난 걸까요?

1. Pod 생성됨
   └─ /etc/resolv.conf에 nameserver 10.96.0.10 자동 설정

2. "kubernetes.default" 조회 요청
   └─ CoreDNS (10.96.0.10)가 받음
   └─ "kubernetes" Service를 찾아서 10.96.0.1 반환

3. "google.com" 조회 요청
   └─ CoreDNS가 받음
   └─ 클러스터 내부에 없으니 상위 DNS (168.126.63.1)로 포워딩
   └─ Google IP 반환

DNS Search Domain의 마법:

Pod의 /etc/resolv.conf를 보면:

nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local

이 설정 덕분에:

  • nginxnginx.default.svc.cluster.local 자동 확장
  • kube-dns.kube-systemkube-dns.kube-system.svc.cluster.local 자동 확장

실용 예시:

같은 네임스페이스의 서비스 호출:

curl nginx         # ✅ 작동
curl nginx:80      # ✅ 작동

다른 네임스페이스의 서비스 호출:

curl nginx.default              # ✅ 작동
curl nginx.default.svc          # ✅ 작동
curl nginx.default.svc.cluster.local  # ✅ 작동 (전체 FQDN)

5. 배운 것과 다음 계획

Day 1에서 배운 핵심

  1. Kubernetes는 선언적(Declarative) 시스템이다

    • "이렇게 되어야 한다"를 선언하면
    • Controller Manager가 알아서 그 상태를 유지
  2. 모든 것은 API Server를 거친다

    • kubectl, Controller, Scheduler 모두 API Server와 통신
    • etcd와 직접 통신하는 건 API Server뿐
  3. 네트워크는 3개 레이어로 분리

    • Node Network (물리)
    • Pod Network (Calico)
    • Service Network (가상)
  4. DNS는 Kubernetes의 핵심

    • 없으면 Pod들이 서로를 못 찾음
    • CoreDNS는 클러스터 + 외부 DNS 모두 처리

유용했던 명령어 Top 5

# 1. 전체 리소스 한눈에 보기
kubectl get all -A

# 2. 노드별 Pod 개수 확인
kubectl get pods -A -o wide | awk '{print $8}' | sort | uniq -c

# 3. 특정 Label을 가진 Pod만 조회
kubectl get pods -l app=nginx

# 4. 리소스 상세 정보 (문제 해결에 필수!)
kubectl describe pod <pod-name> -n <namespace>

# 5. 실시간 로그 확인
kubectl logs -f <pod-name> -n <namespace>

마치며

Kubernetes, 처음엔 정말 어려웠습니다. 용어도 생소하고, 개념도 복잡하고...

하지만 직접 클러스터를 만들고, 하나씩 확인하면서 점점 이해가 되기 시작했어요. 특히 DNS 테스트가 성공했을 때의 그 기쁨이란!

여러분도 처음엔 막막하실 수 있습니다. 하지만 포기하지 마세요. 하나씩 차근차근 따라가다 보면 어느새 "아, 이래서 Kubernetes를 쓰는구나!"하는 순간이 올 겁니다.

profile
기록하고 공유하려고 노력하는 DevOps 엔지니어

0개의 댓글