[IT-방법] 쿠버네티스 서비스 실전 예제 따라하기 – 실무 클러스터 배포와 동작 검증 단계별 실습

서비스 관련 쿠버네티스 구조를 설명하는 대표 이미지

끊임없이 변하는 파드 IP, 어떻게 관리하고 계신가요?

운영 중인 애플리케이션이 갑자기 통신 오류를 일으키며 멈춰버린 경험이 있으신가요? 로그를 살펴보니 방금 전까지 멀쩡하던 데이터베이스 파드의 IP 주소가 바뀌어 버려서 발생한 문제였어요. 쿠버네티스 환경에서 파드는 언제든 사라지고 다시 생성될 수 있는 휘발성 존재예요. 파드가 재시작될 때마다 새로운 IP를 할당받는다면, 이를 참조하는 다른 애플리케이션들은 매번 설정 파일을 수정해야 하는 재앙을 맞이하게 돼요.

이런 혼란을 막기 위해 등장한 것이 바로 쿠버네티스 서비스(Service)예요. 서비스는 고정된 가상 IP를 제공하여, 내부 파드들이 어떻게 변하더라도 변하지 않는 단일 진입점을 제공해 줘요. 테크리드라면 단순히 명령어를 외우는 것을 넘어, 서비스가 어떻게 트래픽을 목적지 파드로 전달하는지 그 메커니즘을 정확히 파악하고 있어야 해요.

오늘 이 글에서는 이론적인 설명은 뒤로하고, 실제 운영 환경에서 바로 사용할 수 있는 수준의 서비스 실전 예제를 단계별로 다뤄볼 거예요. 직접 매니페스트를 작성하고 클러스터에 배포하며, 트래픽이 흐르는 경로를 눈으로 확인하는 과정을 거치게 돼요. 실습을 마치고 나면 서비스의 유형을 상황에 맞게 선택하고 안정적으로 운영할 수 있는 자신감을 얻으실 거예요.

💡 알아두기
쿠버네티스 서비스는 파드들의 집합에 접근할 수 있는 고정된 주소를 제공하는 추상화된 계층이에요. 이를 통해 파드의 생명주기와 상관없이 안정적인 네트워크 통신이 가능해져요.

이번 실습을 통해 구체적으로 배우게 될 내용은 다음과 같아요.

  • 애플리케이션 배포를 위한 디플로이먼트 구성 방법
  • 상황별로 적합한 서비스 타입(ClusterIP, NodePort) 설정법
  • 매니페스트 파일을 활용한 선언적 리소스 관리
  • 실제 네트워크 연결을 확인하는 검증 절차
  • 실무에서 자주 발생하는 네트워크 오류 해결법

실습 시작 전 반드시 체크해야 할 기본 요건

본격적인 실습에 들어가기에 앞서, 여러분의 환경이 준비되었는지 확인이 필요해요. 준비 없이 명령어를 따라 하다 보면 환경 차이로 인해 오류를 마주하게 되고, 이는 학습 효율을 크게 떨어뜨리기 때문이에요. 안정적인 실습을 위해 다음의 환경과 개념을 미리 갖춰두시길 권장해요.

필수 준비물 및 환경 설정

가장 먼저 로컬 환경에서 쿠버네티스를 실행할 수 있는 도구가 필요해요. MinikubeKind 같은 경량화된 도구가 가장 적합해요. 만약 클라우드 환경을 선호하신다면 AWS의 EKS나 Google의 GKE를 활용하셔도 무방해요. 또한, 클러스터와 통신하기 위한 kubectl 명령줄 도구가 반드시 설치되어 있어야 해요.

이미 클러스터가 준비되었다면, 다음 명령어를 통해 노드와 파드의 상태를 확인해 보세요. 모든 노드가 Ready 상태여야 원활한 실습이 가능해요.

💡 알아두기
실습 도중 리소스를 과도하게 사용하게 되면 로컬 PC의 자원이 부족해질 수 있어요. 가급적 CPU 2코어, 메모리 4GB 이상의 여유 공간을 확보해 주세요.

서비스 타입 결정하기

쿠버네티스 서비스는 용도에 따라 크게 세 가지 타입으로 나뉘어요. 모든 상황에 만능인 타입은 없으므로, 아래 비교 표를 통해 현재 프로젝트에 어떤 타입이 필요한지 판단하는 기준을 세워보세요.

서비스 타입 주요 특징 주요 사용 사례
ClusterIP 클러스터 내부에서만 접근 가능한 가상 IP 제공 DB, 캐시 등 내부 통신용 리소스
NodePort 모든 노드의 특정 포트를 통해 외부 접근 허용 테스트 환경 또는 간단한 외부 노출
LoadBalancer 클라우드 제공업체의 로드밸런서를 통해 외부 노출 실제 운영 서비스의 외부 진입점

대부분의 마이크로서비스 아키텍처(MSA)에서는 내부 통신은 ClusterIP로 처리하고, 사용자 접점은 LoadBalancer를 사용하는 구조를 기본으로 채택해요. NodePort는 보안상 외부로 직접 노출되는 포트가 생기기 때문에 실무 운영 환경에서는 신중하게 사용해야 해요.

핵심 용어 정리

실습 중 반복해서 등장할 용어들을 미리 눈에 익혀두면 진행 속도가 훨씬 빨라져요. 특히 SelectorTargetPort의 차이를 명확히 이해하는 것이 이번 서비스 실습의 핵심이에요. Selector는 서비스가 어떤 파드를 찾아갈지 결정하는 라벨(Label) 필터이고, TargetPort는 파드 내부에서 실제로 애플리케이션이 리스닝하고 있는 포트 번호를 의미해요.

실전! 서비스 구축부터 외부 노출까지 5단계 실습

이제 준비가 끝났다면 직접 명령어를 입력하며 실습을 시작해 볼게요. 이번 시나리오는 웹 서버인 Nginx를 배포하고, 이를 클러스터 내부와 외부에서 각각 접근할 수 있도록 서비스를 구성하는 과정을 담고 있어요. 모든 과정은 선언적인 방식인 YAML 매니페스트 파일을 기반으로 진행할 거예요.

STEP 1. 테스트용 웹 서버 디플로이먼트 생성하기

서비스를 만들기 전에 먼저 트래픽을 받아줄 대상인 파드들이 필요해요. 파드들을 관리하기 위해 Deployment 리소스를 먼저 생성할게요. 아래의 YAML 내용을 `nginx-deployment.yaml`이라는 파일로 저장해 주세요.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-webserver
  labels:
    app: my-web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-web
  template:
    metadata:
      labels:
        app: my-web
    spec:
      containers:
      - name: nginx
        image: nginx:1.25
        ports:
        - containerPort: 80

이 설정의 핵심은 labels 부분이에요. `app: my-web`이라는 라벨을 파드에 부여했어요. 이 라벨은 나중에 서비스가 어떤 파드에 트래픽을 보내야 할지 결정하는 중요한 표식이 될 거예요. 파일을 저장했다면 다음 명령어를 입력하여 배포를 시작하세요.

kubectl apply -f nginx-deployment.yaml

배포가 완료되면 `kubectl get pods` 명령어로 3개의 Nginx 파드가 정상적으로 실행 중인지 확인해 보세요. 파드들이 모두 Running 상태라면 다음 단계로 넘어갈 준비가 된 거예요.

STEP 2. 클러스터 내부 통신을 위한 ClusterIP 서비스 생성

이제 생성한 파드들을 하나의 고정된 주소로 묶어줄 차례예요. 클러스터 내부의 다른 서비스들이 이 웹 서버에 접근할 수 있도록 ClusterIP 타입을 사용해 볼게요. `nginx-service-clusterip.yaml` 파일을 생성하고 아래 내용을 작성하세요.

apiVersion: v1
kind: Service
metadata:
  name: nginx-internal-svc
spec:
  type: ClusterIP
  selector:
    app: my-web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

여기서 주의 깊게 봐야 할 곳은 selector예요. 앞서 디플로이먼트에서 설정한 `app: my-web`과 정확히 일치해야 서비스가 파드를 찾아낼 수 있어요. 만약 오타가 발생한다면 서비스는 생성되지만 트래픽을 전달할 목적지를 찾지 못하게 돼요. 또한, port는 서비스 자체의 포트이고, targetPort는 실제 파드 내부의 포트임을 명심하세요.

명령어를 실행하여 서비스를 배포해 주세요.

kubectl apply -f nginx-service-clusterip.yaml

STEP 3. 서비스 동작 및 엔드포인트 검증하기

서비스가 제대로 생성되었는지 확인하는 과정은 매우 중요해요. 단순히 리소스가 존재한다고 해서 통신이 되는 것은 아니기 때문이죠. 먼저 서비스 목록을 확인해 보세요.

kubectl get svc nginx-internal-svc

명령어를 치면 `CLUSTER-IP` 항목에 고정된 IP 주소가 나타날 거예요. 이 주소가 바로 클러스터 내부에서 웹 서버로 접근할 때 사용하는 통로예요. 하지만 더 확실한 검증을 위해 Endpoints 객체를 확인해야 해요. 서비스는 Selector를 통해 찾은 파드들의 IP 목록을 Endpoints라는 객체에 자동으로 관리하거든요.

kubectl get endpoints nginx-internal-svc

결과값에 방금 생성한 3개 파드의 IP 주소들이 나열되어 있다면, 서비스와 파드 사이의 연결이 완벽하게 이루어진 상태예요. 만약 ENDPOINTS가 로 나온다면, 서비스의 Selector와 파드의 Label이 일치하지 않는다는 강력한 신호이니 즉시 설정을 재검토해야 해요.

STEP 4. 내부 네트워크 통신 테스트 수행

연결이 확인되었다면, 실제로 트래픽이 흐르는지 테스트해 볼 차례예요. 클러스터 내의 다른 임시 파드를 하나 띄워서 Nginx 서비스로 요청을 보내볼게요. 다음 명령어를 입력하여 테스트용 파드를 실행하세요.

kubectl run curl-test --image=curlimages/curl -i --tty --rm -- /bin/sh

테스트용 파드 안의 셸에 접속되었다면, 아래 명령어를 통해 서비스 이름으로 HTTP 요청을 보내보세요. IP 주소 대신 서비스 이름을 사용하는 것이 쿠버네티스 DNS의 강력한 기능이에요.

curl http://nginx-internal-svc

정상적으로 설정되었다면 Nginx의 HTML 소스코드가 화면에 출력될 거예요. 만약 연결 거부(Connection Refused)나 타임아웃(Timeout)이 발생한다면 네트워크 정책(Network Policy)이나 포트 설정 문제를 의심해 봐야 해요. 이처럼 서비스 실습 예제를 통해 직접 확인하는 과정은 실제 장애 상황에서 문제를 해결하는 능력을 키워준답니다.

STEP 5. 외부 노출을 위한 NodePort 서비스 확장

마지막으로, 클러스터 외부(여러분의 로컬 브라우저)에서 이 웹 서버에 접속할 수 있도록 서비스를 확장해 볼게요. 이번에는 NodePort 타입을 사용하여 모든 노드의 특정 포트를 열어줄 거예요. `nginx-service-nodeport.yaml` 파일을 만드세요.

apiVersion: v1
kind: Service
metadata:
  name: nginx-external-svc
spec:
  type: NodePort
  selector:
    app: my-web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30080

여기서 nodePort: 30080은 외부에서 접속할 포트 번호예요. 쿠버네티스 기본 범위인 30000~32767 사이의 숫자를 사용해야 해요. 배포 후에는 다음과 같이 접속할 수 있어요.

http://[노드-IP-주소]:30080

Minikube를 사용 중이라면 minikube service nginx-external-svc --url 명령어를 통해 쉽게 접속 가능한 주소를 얻을 수 있어요. 이제 외부에서도 우리의 Nginx 웹 서버에 안전하게 접근할 수 있게 되었어요!

💡 알아두기
실무 환경에서는 보안을 위해 NodePort를 직접 노출하기보다는, Ingress(인그레스)를 사용하여 L7 계층에서 경로 기반 라우팅을 처리하는 것이 일반적이에요.

자주 하는 실수와 해결법 + FAQ

실습 과정이나 실제 운영 환경에서 직면하게 될 수 있는 문제들을 정리했어요. 문제를 미리 알고 있으면 장애 대응 시간이 획기적으로 줄어들 거예요.

자주 하는 실수와 해결법

실수: 서비스는 생성되었는데 Endpoints가 비어 있음
왜 발생하는가: 서비스의 selector에 적힌 라벨과 파드의 labels가 단 한 글자라도 다르기 때문이에요.
해결법: kubectl get pods --show-labels 명령어로 파드의 라벨을 정확히 확인하고, 서비스 매니페스트의 selector와 일치시키세요.

실수: 서비스 IP로 접속하면 Connection Refused가 발생함
왜 발생하는가: 서비스의 targetPort가 파드 내부 애플리케이션이 실제로 사용하는 포트와 다르기 때문이에요.
해결법: 파드 내부의 컨테이너가 몇 번 포트에서 리스닝 중인지 확인하고, targetPort를 그 번호로 수정하세요.

실수: NodePort로 접속이 안 됨
왜 발생하는가: 클라우드 환경(AWS, GCP 등)을 사용 중이라면, 클라우드 자체 방화벽(Security Group)에서 해당 포트(30000-32767)를 막아두었을 가능성이 높아요.
해결법: 클라우드 콘솔에 접속하여 인바운드 규칙에 해당 포트 범위를 추가하세요.

실수: 서비스 이름으로 DNS 해석이 안 됨
왜 발생하는가: 클러스터 내부의 DNS 서비스(CoreDNS)에 문제가 있거나, 네임스페이스(Namespace)를 지정하지 않았기 때문이에요.
해결법: 다른 네임스페이스에서 접근한다면 서비스이름.네임스페이스.svc.cluster.local 형식을 사용하세요.

실수: 매니페스트 수정 후 적용했는데 변화가 없음
왜 발생하는가: kubectl apply 명령어를 실행하지 않았거나, 수정 사항이 서비스의 속성(Immutable field)을 변경하려 했기 때문이에요.
해결법: 변경 후 반드시 apply를 실행하고, 변경 불가능한 필드는 서비스를 삭제 후 다시 생성해야 해요.

자주 묻는 질문

Q. ClusterIP와 NodePort의 가장 큰 차이점은 무엇인가요?

A. 가장 큰 차이는 접근 범위예요. ClusterIP는 클러스터 내부의 구성 요소들끼리만 통신할 때 사용하고, NodePort는 클러스터 외부의 사용자에게 서비스를 노출할 때 사용해요. 내부 통신용 서비스에 NodePort를 쓰는 것은 보안상 권장되지 않아요.

Q. 서비스의 port와 targetPort는 어떻게 구분하나요?
A. port는 클러스터 내부의 다른 파드들이 서비스를 호출할 때 사용하는 포트예요. 반면 targetPort는 트래픽이 최종적으로 도달하여 실제 애플리케이션이 기다리고 있는 파드 내부의 포트예요. 즉, 클라이언트 → 서비스(port) → 파드(targetPort) 순으로 흐른다고 이해하시면 돼요.

Q. Headless 서비스는 언제 사용하나요?
A. 서비스의 타입을 ClusterIP로 설정하되, clusterIP: None으로 지정하면 Headless 서비스가 돼요. 이는 서비스의 가상 IP를 할당하지 않고, DNS 쿼리 시 연결된 파드들의 개별 IP를 직접 반환하게 해요. 데이터베이스 클러스터처럼 파드 각각의 개별 IP를 직접 알아야 하는 경우에 아주 유용해요.

Q. LoadBalancer 타입은 모든 환경에서 작동하나요?
A. 아니요. 클라우드 제공업체(AWS, Azure, GCP 등)처럼 로드밸런서 인프라를 제공하는 환경에서만 자동으로 작동해요. 일반적인 온프레미스(On-premise) 환경이나 로컬 Minikube 환경에서는 별도의 설정(MetalLB 등) 없이는 Pending 상태에 머물게 돼요.

실습 마무리 및 다음 단계로의 도약

오늘 우리는 쿠버네티스 서비스의 핵심 개념부터 실제 배포, 그리고 외부 노출까지의 전 과정을 실습해 보았어요. 서비스는 단순히 IP를 고정하는 도구가 아니라, 복잡한 마이크로서비스 환경에서 트래픽의 흐름을 제어하는 핵심 신경망과도 같아요. 이번 서비스 실습을 통해 익힌 기술은 앞으로 여러분이 다루게 될 더 큰 규모의 시스템에서도 가장 튼튼한 기초가 될 거예요.

✅ 핵심 요약

  • 파드는 휘발성이므로, 고정된 진입점인 서비스가 반드시 필요해요.
  • Selector와 Label은 서비스와 파드를 연결하는 생명선이에요.
  • ClusterIP는 내부 통신용, NodePort와 LoadBalancer는 외부 노출용이에요.
  • 서비스가 정상인지 확인하려면 반드시 Endpoints 객체를 체크하세요.
  • TargetPort는 실제 파드 내부의 포트 번호를 의미해요.

실습을 성공적으로 마쳤다면, 이제 다음 단계로 나아갈 준비가 된 거예요. 오늘 바로 실행할 수 있는 로드맵을 제안해 드릴게요.

  • 오늘 할 일: 오늘 작성한 매니페스트 파일을 깃허브(GitHub) 저장소에 올려서 나만의 코드 스니펫으로 관리하세요.
  • 이번 주 할 일: 서비스의 확장판인 Ingress(인그레스)를 공부하여, HTTP 경로 기반 라우팅을 직접 구현해 보세요.
  • 실행 직전 할 할 일: 실제 운영 환경에 적용하기 전, 서비스에 네트워크 정책(Network Policy)을 적용하여 보안을 강화하는 방법을 찾아보세요.

실습 과정에서 예상치 못한 오류를 만났거나, 동작 원리에 대해 더 궁금한 점이 있다면 언제든 댓글로 질문을 남겨 주세요. 여러분의 성장을 진심으로 응원해요!

관련된 내용을 더 깊이 알고 싶다면 아래 글들도 함께 읽어보시길 권장해요.
– 쿠버네티스 서비스 기본 개념 총정리
– 안정적인 클러스터 구축을 위한 입문 가이드

댓글 남기기