Infra/Kubernetes

[Kubernetes] Pod, 리소스, Ingress 정리

minseoki 2026. 5. 10. 18:03

Pod란?

  • 한 개 이상의 컨테이너로 구성된 쿠버네티스의 기본 단위
  • 같은 Pod면 IP가 같다
  • 같은 Pod 내 컨테이너들은 포트로 구분한다

Pod도 결국 컨테이너로 구성되어 있으며, 이 컨테이너를 실행하기 위해 docker, containerd, CRI-O 같은 CRI(컨테이너 런타임)가 필요하다.

1. kube-apiserver

  • 내/외부의 모든 요청을 주고받는 서버. contoller나 scheduler,proxy의 주시대상.
  • 호텔지배인.

2. kube-scheduler

  • 생성될 리소스들을 어떤 노드에 '배치'할지 결정(스케쥴링)
  • 호텔 로비 직원

3. kube-controller

  • 다양한 리소스에 대한 여러가지 컨트롤러들이 존재한다.
  • 원하는 상태(Desired state)에 현재 상태(current state)가 수렴하도록 지속적으로 모니터링. 문제가 생기면 고치거나 리소스를 재생성.

ex) 하우스키퍼

4. etcd

  • 클러스터 및 모든 리소스에 대한 정보를 key:value 형태로 저장하는 일종의 데이터베이스.

ex) 장부


모든 노드에 존재하는 컴포넌트

1. kubelet

  • 노드 관리자. 실질적으로 각 노드에 존재하는 리소스 관리

2. kube-proxy

  • 노드 안과 밖을 넘나드는 수직트래픽을 관리.

매니페스트 (yml 파일)

매니페스트란 내가 원하는 상태를 적어둔 명세서다.

vi test-pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - image: public.ecr.aws/docker/library/httpd:latest
    name: test-con
kubectl apply -f test-pod.yml

kubectl apply -fdocker compose up, docker stack deploy와 비슷하다.
내가 원하는 상태(Desired State)가 미리 정의된 매니페스트 파일을 구성해놓고 apply해서 반영한다. 매니페스트 수정 후 다시 apply하면 바로 변경사항을 반영시킬 수 있다.

kubectl delete -f test-pod.yml

앞으로는 명령어로 직접 리소스를 생성하거나 지우지 말고 매니페스트 파일을 -f 옵션을 통해 apply하거나 delete하도록 하자.


label - 중요

vi labels.yml
apiVersion: v1
kind: Pod
metadata:
  name: test-label-pod
  labels:
    app: my-web
spec:
  containers:
  - image: public.ecr.aws/docker/library/httpd:alpine
    name: test-label-con

label은 여러 개를 쓸 수 있다.

kubectl apply -f labels.yml

생성.

kubectl describe pod test-label-pod

조회.

label은 리소스를 컨트롤(원하는 상태, 현재 상태)하고 찾아가기 위한 용도다.


쿠버네티스의 다양한 리소스들

1. pod

  • 한 개 이상의 컨테이너로 구성된 쿠버네티스의 기본 배포 단위.

2. replicaset

  • Pod의 복제본 수를 유지해주는 리소스.
  • 지정한 수만큼 Pod가 항상 실행되도록 보장한다.

3. Deployment

  • ReplicaSet을 관리하며 롤링 업데이트, 롤백 등을 지원하는 리소스.
  • 실무에서 가장 많이 사용한다.

4. namespace

  • 클러스터 내에서 리소스를 논리적으로 분리하는 단위.
  • 팀이나 프로젝트별로 격리된 환경을 만들 수 있다.

5. Service (너무 중요!)

  • 작은 로드밸런서라고도 할 수 있다.
  • Pod에 접근하기 위한 고정된 엔드포인트를 제공하는 리소스이다.
  • Pod는 재생성될 때마다 IP가 바뀌기 때문에 Service를 통해 안정적으로 접근한다.
  • 서비스라는 리소스를 생성 시 하나의 접속지점이 생성된다.
    이 서비스를 통해 모든 노드에 존재하는 pod에 트래픽을 인가할 수 있다.
    인가하는 기준은 labels 를 통해 해당 pod를 특정하면 된다.

서비스는 다양한 종류(type)이 존재한다.


5-1. ClusterIP 타입 (svc의 default 타입)

  • 클러스터 내부에서만 유효한 IP
  • 내부 테스트 용도, 외부로 배포를 안하는 경우. 내부에 존재하는 서비스들끼리만 통신할 때.
vi dep.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-dep
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-web
  template:
    metadata:
      labels:
        app: my-web
    spec:
      containers:
      - image: public.ecr.aws/docker/library/httpd:alpine
        name: my-web-con
kubectl apply -f dep.yml

svc를 연결할 deployment 생성.

vi dep-svc.yml

서비스 매니페스트 정의.

apiVersion: v1
kind: Service
metadata:
  name: svc-myweb
spec:
  selector:
    app: my-web
  ports:
  - port: 80
    targetPort: 80
kubectl apply -f dep-svc.yml

svc 생성.

kubectl describe svc svc-myweb

자세한 정보 확인.

Endpoint에 뜨는 pod들은 건강한 pod만 뜬다.


5-2. NodePort 타입

  • 노드의 포트
  • 서비스를 제공받는 사용자 입장에서 내부로 진입하여 pod에 접근하려면 노드 포트로 진입해야 한다.
mkdir svc
cd svc
vi svc-nodeport.yml
apiVersion: v1
kind: Service
metadata:
  name: svc-dep
spec:
  selector:
    app: mydep
  type: NodePort
  ports:
  - nodePort: 30001
    port: 80
    targetPort: 80

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mydep
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mydep
  template:
    metadata:
      labels:
        app: mydep
    spec:
      containers:
      - image: public.ecr.aws/docker/library/httpd:alpine
        name: dep-con
kubectl apply -f svc-nodeport.yml



kubectl describe svc svc-dep

3개의 포트(NodePort, Port, TargetPort)가 각각 어떤 대상인지 구분할 수 있어야 한다.

  • Port - 서비스 포트
  • TargetPort - pod
  • NodePort - Node

외부에서 노드로 통신만 된다면 노드 포트를 통해 pod로 접속 가능하다.

어떤 노드로 들어가는지는 중요하지 않고, NodePort로 접근하면 동일한 공간(오버레이 네트워크가 구성된 pod-network)으로 들어간다는 사실을 인지하자.

pod가 어떤 노드에 존재하는지는 신경 쓸 필요도 없고 중요하지도 않다.
pod가 worker1에 띄워져 있어도 worker2의 노드 포트를 통해 접근 가능하다.


5-3. LoadBalancer 타입

클러스터 관리자의 도움이 필요한 서비스 타입.

쿠버네티스 외부 네트워크 대역의 IP를 자동으로 할당하고 관리해 주는 사설 로드밸런서 관리자가 MetalLB다.

EKS 같은 쿠버네티스 클러스터의 경우 클라우드 서비스 제공자(AWS)가 LB를 제공해줄 수 있지만, 온프레미스에 구성한 클러스터는 그렇지 않다. 따라서 svc를 LoadBalancer 타입으로 만들었을 때 누군가는 LB를 생성하면서 노드 대역대의 IP를 할당해줘야 한다. 그 기능을 활성화하기 위해 MetalLB가 필요하다.

vi config-metal.yml

LB가 생성됐을 때 뿌려줄 IP 범위 설정.

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 211.183.3.200-211.183.3.240  # 안겹치게 수정. LB가 부여받을 IP 범위
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system
kubectl apply -f config-metal.yml
vi lb-tom.yml
apiVersion: v1
kind: Service
metadata:
  name: svc-tom
spec:
  selector:
    app: tom
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 8080

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dep-tom
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tom
  template:
    metadata:
      labels:
        app: tom
    spec:
      containers:
      - image: public.ecr.aws/docker/library/tomcat:10.1.40-jre11
        name: tom-con

svc의 타입으로 LoadBalancer 타입을 지정해준다. nodePort는 삭제한다.

kubectl apply -f lb-tom.yml

생성된 LB는 svc의 포트를 따라간다.


Ingress (제일 중요!)

  • path 기반 라우팅 (/board로 가면 board 앱으로, /login으로 가면 login 앱으로)
  • 일반적인 svc는 path 기반 라우팅이 불가능하다. (LoadBalancer, NodePort, ClusterIP)

서비스는 한 종류의 라벨만 품을 수 있기 때문에 이런 한계가 발생한다. 합칠 수 없다 = 같은 주소가 될 수 없다.

여러 개의 컨테이너에 각 기능들을 구현하면 path로 라우팅이 가능해야 한다. 일반적인 svc는 path 기반 라우팅이 불가능하기 때문에 Ingress라는 리소스가 필요하다.

Ingress를 구성하면 path 기반 라우팅이 가능하다.

ex) www.naver.com/board 로 오면 svc-board라는 svc로 보내줘.
ex) www.naver.com/login 으로 오면 svc-login이라는 svc로 보내줘.
→ 하나의 접속지점(www.naver.com)을 통해 여러 개의 svc를 구성할 수 있다.

Ingress를 구성하기 위해서는 Ingress Controller가 필요하다.
(AWS EKS에서는 Ingress Controller를 LoadBalancer Controller라고 부른다.)


Ingress Controller 설치

vi deploy.yaml

필요한 부분 수정.
(type 을 NodePort에서 LoadBalancer로 수정한다.)

kubectl apply -f deploy.yaml
kubectl get svc -n ingress-nginx


Ingress 매니페스트 작성

cp ../svc/ip.yml .
vi ip.yml
apiVersion: v1
kind: Service
metadata:
  name: svc-ipnginx
spec:
  selector:
    app: myipnginx
  ports:
  - port: 80
    targetPort: 80

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ip-dep
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myipnginx
  template:
    metadata:
      labels:
        app: myipnginx
    spec:
      containers:
      - image: public.ecr.aws/docker/library/httpd:alpine
        name: ip-con
kubectl apply -f ip.yml
kubectl describe svc svc-ipnginx

서비스가 정상인지 확인.

vi ing-ip.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ing-ip
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: "nginx"
  rules:
    - host: rapa.com
      http:
        paths:
        - path: /httpd
          pathType: Prefix
          backend:
            service:
              name: svc-ipnginx
              port:
                number: 80
  • annotation : 추가적인 정보. labels와 비슷하지만 주로 부가적인 기능 명시.
  • ingressClassName : ingress-controller의 종류 중에 nginx 방식을 사용.
  • rewrite-target: / → 비록 /httpd라는 경로로 들어왔더라도 실제 pod에서는 /라는 경로로 바꿔주는 기능.
  • host : 영문주소. IP는 안됨. DNS 기능이 필요하다. 인증서가 있다면 https 통신도 가능하다.
  • path : 한 종류의 앱. pathType: Prefix/httpd로 접근하는 애들 전부.
  • 서비스의 이름을 마치 주소처럼 사용하고 있다.
kubectl apply -f ing-ip.yml

ingress를 describe 했을 때 endpoint들(pod들)이 잘 떠있는 것만 봐도 ingress-svc-pod가 잘 연결되어 있는 걸 어느정도 확인할 수 있다.

원래는 DNS를 통해서 rapa.com에 해당하는 IP를 매핑시켜줘야 하지만, 미니 DNS인 /etc/hosts 파일을 사용하자.

vi /etc/hosts


Ingress 트래픽 흐름

쿠버네티스에서 외부 사용자가 도메인 이름(rapa.com)을 치고 들어와서 실제 앱(pod)까지 도달하는 ingress 트래픽 흐름.

rapa.com → ingress-controller의 svc External-IP(DNS) → ingress(path 기반 라우팅) → svc → pod