Workload API vol. 1: Pod

성윤·2026년 2월 23일

[☸️Kubernetes]

목록 보기
7/13
post-thumbnail

들어가며 👋

지난 포스팅에서는 파드의 상태를 모니터링하고 에러 발생 시 원인을 추적하는 필수 트러블슈팅 명령어(get, describe, logs 등)에 대해 알아보았습니다.

이제는 본격적으로 내 애플리케이션을 클러스터 위에 '어떤 방식으로 띄울지' 알아보겠습니다.

쿠버네티스에서는 '컨테이너를 이런 식으로 구동해 줘'라고 선언하는 상위 수준의 규칙들을 묶어 Workload API(워크로드 API) 라고 부르며, 여기에는 Pod, ReplicaSet, Deployment, DaemonSet 등 다양한 리소스가 존재합니다.

이번 포스팅에서는 그중에서도 쿠버네티스 아키텍처의 가장 근간이 되는 배포의 최소 단위, 파드(Pod)에 대해 정리해 보겠습니다. 🚀


1. 파드 (Pod): 쿠버네티스의 기본 단위 📦

파드(Pod)는 쿠버네티스에서 컨테이너를 배포하는 가장 작고 기본적인 단위입니다.

쿠버네티스는 컨테이너를 하나씩 낱개로 흩어놓지 않고, 하나의 서비스로 완성하기 위해 반드시 함께 움직여야 하는 컨테이너들을 일부러 '파드'라는 단위로 묶어서 배포 합니다.

상품 주문 처리와 배송 처리가 하나의 상품 구매 과정을 나타내듯이, 연관된 기능을 묶어서 배포, 효율적으로 관리하기 위한 목적이라고 생각하면 쉽습니다.


다음은 배포되는 애플리케이션에 대해 알아보겠습니다.

파드에 배포되는 애플리케이션은 성격에 따라 크게 두 가지 형태로 나뉩니다.

  • Stateless: 상태를 저장하지 않는 앱 (주로 웹 서버). 언제든 죽고 새로 살아나도 문제가 없습니다.

  • Stateful: 데이터베이스처럼 상태나 세션을 별도로 영구 저장(Volume)해야 하는 앱.

💡 알아두기: 쿠버네티스는 컨테이너를 직접 실행하지 않습니다.
쿠버네티스(Control Plane)는 파드가 어느 노드에 배치될지 스케줄링하고 관리할 뿐입니다. 실제 컨테이너의 생성과 실행은 배정받은 워커 노드에 설치된 컨테이너 런타임(containerd, docker 등)이 담당합니다.


2. 파드의 핵심 원리: 네트워크 공유와 포트 충돌 주의 🚨

파드에 대해 꼭 알아두어야 할 점이 있습니다.

파드 아키텍처에서 가장 중요한 특징은 바로 "같은 파드 안에 있는 컨테이너들은 네트워크 네임스페이스(IP)를 공유한다"는 점입니다.

쿠버네티스에서는 파드 하나당 하나의 IP가 할당됩니다. 따라서 같은 파드 내부의 컨테이너들은 네트워크가 격리되어 있지 않아 서로 localhost를 이용해 통신할 수 있습니다.

하지만 이 강력한 특징 때문에 반드시 주의해야 할 점이 있습니다. 바로 포트 충돌입니다. 도커 엔진에서는 컨테이너마다 80번 포트를 써도 포트 포워딩으로 해결할 수 있었지만, 파드 내부의 컨테이너들은 localhost를 공유하므로 똑같은 포트를 쓰는 컨테이너 두 개를 띄우면 충돌이 발생합니다.


실패하는 YAML 매니페스트(sample-2pod-fail.yaml)를 만들어 직접 확인해 보겠습니다.

apiVersion: v1
kind: Pod
metadata:
  name: sample-2pod-fail
spec:
  containers:
    - name: nginx-container-112
      image: nginx:1.17 # 여기서 80포트 점유
    - name: nginx-container-113
      image: nginx      # 똑같이 80포트를 쓰려고 하므로 충돌 발생!

$ kubectl apply -f sample-2pod-fail.yaml
pod/sample-2pod-fail created

$ kubectl get pods

명령어를 내리면 API Server가 문법을 검사하고 etcd에 먼저 저장한 뒤 비동기로 스케줄링을 진행합니다. 그래서 CLI 상으로는 처음엔 created라고 뜨지만, 막상 조회를 해보면 컨테이너 하나가 포트 충돌로 인해 생성에 실패하여 에러 상태에 빠진 것을 확인할 수 있습니다.


이럴 땐 직전 포스팅에서 배운 대로 실패한 컨테이너의 로그를 콕 집어 확인해야 합니다.

$ kubectl logs sample-2pod-fail -c nginx-container-113
2026/02/13 00:33:39 [emerg] 1#1: bind() to 0.0.0.0:80 failed (98: Address already in use)

명확하게 Address already in use (포트가 이미 사용 중) 에러가 발생한 것을 확인할 수 있습니다.


3. 네트워크 공유를 활용한 멀티 컨테이너 디자인 패턴 🧩

포트 충돌이라는 단점도 있지만, localhost를 공유한다는 점은 여러 컨테이너를 유기적으로 결합할 때 엄청난 장점이 됩니다.


보통 메인 애플리케이션 컨테이너 하나와, 이를 보조하는 서브 컨테이너를 함께 배치하며 다음과 같은 패턴들이 자주 사용됩니다.

  • 사이드카 패턴 (Sidecar): 메인 컨테이너 옆에 찰싹 붙어서 보조적인 기능을 수행합니다. (예: 깃 저장소 동기화, 애플리케이션 로그 파일을 수집해 스토리지로 전송)

  • 앰버서더 패턴 (Ambassador): 메인 컨테이너가 외부 시스템과 접속할 때 대리로 중계해 줍니다. 메인 컨테이너는 목적지를 localhost로 지정해 앰버서더로 접속합니다.

  • 어댑터 패턴 (Adapter): 서로 다른 데이터 형식을 변환해 줍니다. (예: 기존 미들웨어의 메트릭을 프로메테우스가 수집할 수 있는 형식으로 변환하여 반환)


4. 파드를 100% 활용하는 세부 설정 (Spec 옵션) ⚙️

단순히 이미지를 띄우고 묶는 것을 넘어, 파드의 명세서(spec)에 다양한 옵션을 주어 디테일하게 제어할 수 있습니다.

(참고: 파드 이름은 영문 소문자와 숫자, 기호는 -.만 허용되며 시작/끝은 무조건 소문자여야 합니다.)

① 시작 명령어 설정 (command, args)

Docker의 ENTRYPOINT는 K8s에서 command로, CMDargs로 부릅니다. 이를 지정하면 컨테이너 이미지가 가진 기본 시작 명령어를 덮어쓸 수 있습니다.

# sample-entrypoint.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sample-entrypoint
spec:
  containers:
  - name: nginx-container-112
    image: nginx:1.17
    command: ["/bin/sleep"] # ENTRYPOINT 덮어쓰기
    args: ["3600"]          # CMD 덮어쓰기

② 작업 디렉토리 설정 (workingDir)

Dockerfile의 WORKDIR 설정을 무시하고, 내가 원하는 디렉토리에서 프로세스를 시작하게 만듭니다. 특정 스크립트가 마운트된 폴더로 바로 이동해서 실행할 때 유용합니다.

# sample-workingdir.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sample-workingdir
spec:
  containers:
  - name: nginx-container
    image: nginx
    workingDir: /tmp # 작업 디렉토리 변경
  • 결과 확인: kubectl exec -it sample-workingdir -- pwd

/tmp가 출력되는 것을 확인할 수 있습니다.


③ 호스트 네트워크 사용 (hostNetwork: true)

기본적으로 파드는 외부에서 볼 수 없는 K8s 내부 IP(예: 10.244.x.x)를 가집니다.

하지만 이 옵션을 켜면 파드가 자신이 띄워진 워커 노드(호스트)의 실제 IP를 그대로 사용하게 됩니다. (포트 충돌 위험이 커서 Edge 환경이나 네트워크 감시 등 특수 목적에만 씁니다.)

# sample-hostnetwork.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sample-hostnetwork
spec:
  hostNetwork: true # 호스트 네트워크 활성화
  containers:
  - name: nginx-container
    image: nginx
  • 결과 확인:

1) 파드의 IP와 배포된 노드 확인하기: kubectl get pod sample-hostnetwork -o wide

2) 해당 노드(서버)의 실제 IP 확인하기: kubectl get node worker2 -o wide

파드가 배포된 노드가 worker2라서 worker2를 확인합니다.

일반 파드와 달리, 호스트네트워크 파드는 NODE의 IP(예: 10.0.2.103)와 파드의 IP가 똑같이 할당된 것을 확인할 수 있습니다.


④ DNS 정책 설정 (dnsPolicy)

파드가 도메인을 IP로 변환할 때 사용할 라우팅 정책입니다.

  • ClusterFirst (기본값): K8s 내부 DNS(kube-dns, 보통 10.96.0.10)를 먼저 조회하고, 못 찾으면 외부 업스트림 DNS로 질의합니다. 서비스 디스커버리에 사용됩니다.

  • None: 클러스터 내부 DNS를 무시합니다.

  • Default: K8s 노드의 DNS 설정을 그대로 상속받습니다.

  • ClusterFirstWithHostNet: hostNetwork: true를 쓴 파드가 클러스터 내부 DNS를 참조하고 싶을 때 반드시 함께 써야 합니다.


# 파드의 DNS 설정 확인 명령어
kubectl exec -it sample-pod -- cat /etc/resolv.conf

출력 결과 내부 DNS인 10.96.0.10을 바라보는 것을 확인해볼 수 있습니다.


⑤ 정적 호스트 이름 설정 (hostAliases)

리눅스의 /etc/hosts 파일에 특정 도메인과 IP를 수동으로 억지로 매핑해줄 때 사용합니다.

# sample-hostaliases.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sample-hostaliases
spec:
  containers:
  - name: nginx-container
    image: nginx
  hostAliases: # /etc/hosts 에 수동 주입
  - ip: 8.8.8.8
    hostnames:
    - google-dns
    - google-public-dns
  • 결과 확인: `kubectl exec -it sample-hostaliases -- cat /etc/hosts

# Entries added by HostAliases.
8.8.8.8 google-dns      google-public-dns

이렇게 파드 내부 hosts 파일 하단에 아래 내용이 추가됨을 확인해볼 수 있습니다.


5. 파드 내부 직접 접속하기 (exec) 💻

세부 설정을 마쳤거나, 앞서 살펴본 네트워크/설정 문제로 트러블슈팅이 필요할 때는 터미널 접속 상태로 파드 내부에 직접 들어갈 수도 있습니다.

  • 파드에 컨테이너가 1개일 때:

    kubectl exec -it sample-pod -- /bin/bash

  • 파드에 컨테이너가 여러 개일 때 (컨테이너 지정 필수):

    kubectl exec -it sample-2pod -c nginx-container -- /bin/bash

⚠️주의:
파드 안에 컨테이너가 2개 이상일 때 -c로 컨테이너를 명시하지 않으면 임의의 컨테이너로 접속됩니다.

중간의 -- 기호는 kubectl 명령어와 컨테이너 내부에서 실행할 명령어를 구분하는 역할입니다.


마치며 ✍️

오늘은 쿠버네티스의 가장 밑바탕이 되는 최소 단위인 파드(Pod)의 내부 동작 원리(네트워크 공유)와 디자인 패턴, 포트 충돌의 원인, 그리고 다양한 Spec 옵션들까지 깊게 살펴보았습니다.

하지만 실무 환경에서 파드를 단독으로 .yaml 파일로 만들어 배포하는 일은 거의 없습니다. 왜냐하면 파드는 한 번 에러가 나서 죽거나 노드가 다운되면, 누군가 수동으로 다시 띄워주기 전까지 영원히 사라져버리기 때문입니다.

서비스가 절대로 중단되지 않으려면 누군가 파드를 지속적으로 감시하고, 죽으면 다시 살려내야겠죠?

다음 포스팅에서는 이러한 '자동화된 복구(Self-Healing)'를 책임지고 파드의 개수를 든든하게 유지해 주는 레플리카셋(ReplicaSet) 과 무중단 배포의 핵심 디플로이먼트(Deployment) 에 대해 알아보도록 하겠습니다.

0개의 댓글