5.4.2 Service를 이용한 외부 노출 (NodePort)
이전 단계에서 우리는 Nginx 웹서버를 쿠버네티스 클러스터 내부에 성공적으로 배포하고, kubectl get pods 명령을 통해 Nginx 파드가 건강하게 실행 중인 것을 확인했습니다. 하지만 지금 상태로는 마치 외딴 섬에 홀로 서 있는 등대와 같습니다. 내부에서는 밝게 빛나고 있지만, 외부에서는 그 존재를 알거나 접근할 방법이 없는 것이죠. 우리의 Nginx 웹서버도 마찬가지입니다. 쿠버네티스 클러스터 내부 네트워크에서는 잘 작동하고 있지만, 외부 네트워크, 예를 들어 여러분의 개인 컴퓨터에 있는 웹 브라우저에서는 아직 이 Nginx 웹서버에 접속할 수 없습니다.
이번 절에서는 이 문제를 해결하고, 우리가 배포한 Nginx 웹서버를 클러스터 외부에서도 접근할 수 있도록 ‘세상에 공개’하는 방법을 배울 것입니다. 이를 위해 쿠버네티스의 핵심 네트워킹 개념인 서비스(Service) 객체를 활용하게 됩니다. 서비스는 논리적으로 하나 이상의 파드 그룹(우리의 경우 Nginx 파드)에 대해 안정적인 네트워크 엔드포인트(고정된 IP 주소와 포트)를 제공하고, 이 파드들 간의 로드 밸런싱 기능까지 수행할 수 있는 매우 중요한 리소스입니다.
서비스에는 여러 가지 타입(Type)이 있으며, 각 타입은 서로 다른 방식으로 파드에 대한 접근을 제공합니다. 이번 실습에서는 특히 로컬 개발 환경에서 간단하게 외부 노출을 테스트해 볼 수 있는 NodePort 타입을 사용해 보겠습니다. NodePort 서비스는 클러스터를 구성하는 각 노드(Node)의 특정 포트를 개방하고, 이 포트로 들어오는 외부 트래픽을 해당 서비스와 연결된 내부 파드로 전달해주는 역할을 합니다.
5.4.2.1 kubectl expose deployment 명령어 사용
앞서 디플로이먼트를 생성할 때처럼, 서비스 역시 YAML 매니페스트 파일을 작성하여 생성하는 것이 일반적이지만, 간단한 경우에는 kubectl의 간편 생성 명령어를 사용할 수 있습니다. 우리는 이전 절에서 생성했던 nginx-deployment 디플로이먼트를 외부로 노출시키는 서비스를 만들어 보겠습니다.
터미널을 열고 다음 명령어를 입력해 보세요.
이 명령어가 어떤 의미를 가지고 있는지 자세히 살펴보겠습니다.
- kubectl expose deployment: “기존에 존재하는 디플로이먼트를 노출시켜 새로운 서비스를 생성하라”는 명령어입니다. expose는 특정 리소스를 네트워크적으로 노출시킨다는 의미를 가지며, deployment는 노출시킬 대상 리소스의 종류를 지정합니다.
- nginx-deployment: 노출시킬 대상 디플로이먼트의 이름입니다. 이전 절에서 우리가 생성했던 바로 그 Nginx 디플로이먼트죠. 이 서비스는 nginx-deployment가 관리하는 파드들을 대상으로 트래픽을 전달하게 됩니다.
- -type=NodePort: 생성할 서비스의 타입을 NodePort로 지정하는 매우 중요한 옵션입니다. 이 옵션 덕분에 쿠버네티스는 각 노드의 특정 포트를 통해 이 서비스에 접근할 수 있도록 설정합니다. (만약 이 옵션을 생략하면 기본적으로 ClusterIP 타입의 서비스가 생성되는데, 이는 클러스터 내부에서만 접근 가능한 서비스입니다.)
- -port=80: 이 서비스가 내부적으로 어떤 포트(Service Port)를 통해 파드와 통신할 것인지 지정합니다. Nginx 웹서버는 기본적으로 80번 포트에서 HTTP 요청을 수신하므로, 여기에 80을 지정했습니다. 또한, 이 –port 옵션은 서비스가 내부적으로 사용할 포트를 지정함과 동시에, 만약 파드의 컨테이너가 이와 다른 포트(예: 8080)를 사용한다면 –target-port 옵션을 추가로 명시하여 매핑할 수도 있습니다. 하지만 Nginx 공식 이미지는 80번 포트를 사용하므로, 이 경우에는 –target-port=80이 자동으로 설정된 것과 같습니다.
이 명령어를 실행하면, kubectl은 쿠버네티스 API 서버에 nginx-deployment 디플로이먼트를 가리키는 NodePort 타입의 서비스를 생성해달라는 요청을 보냅니다. 요청이 성공적으로 전달되면, 터미널에는 다음과 같은 메시지가 출력될 것입니다. (서비스 이름은 기본적으로 디플로이먼트 이름과 동일하게 생성됩니다.)
“service/nginx-deployment가 노출되었습니다.”라는 이 메시지는 이제 우리의 Nginx 웹서버로 향하는 외부 통로가 마련되기 시작했음을 의미합니다. 쿠버네티스는 이 서비스 정의에 따라, 클러스터 노드들의 특정 포트(임의로 할당됨, 보통 30000-32767 범위)를 열고, 이 포트로 들어오는 요청을 Nginx 파드의 80번 포트로 전달하도록 네트워크 규칙을 설정하기 시작합니다.
5.4.2.2 Service 상태 확인
디플로이먼트와 마찬가지로, 서비스도 생성 요청 후 실제로 잘 만들어지고 원하는 대로 작동하는지 확인하는 과정이 필요합니다. kubectl get service (또는 kubectl get svc) 명령어를 사용하여 우리가 방금 생성한 서비스의 상태를 조회해 보겠습니다.
터미널에 다음 명령어를 입력합니다.
또는 특정 이름을 지정하지 않고 현재 네임스페이스의 모든 서비스 목록을 보려면 다음과 같이 실행할 수도 있습니다.
성공적으로 실행되었다면, 다음과 유사한 표 형태의 출력을 볼 수 있습니다. (CLUSTER-IP, EXTERNAL-IP, PORT(S), AGE 값은 환경에 따라 다를 수 있습니다.)
여기서 우리가 주목해야 할 부분은 nginx-deployment 서비스에 대한 정보입니다.
- NAME: 서비스의 이름입니다. 우리가 expose 명령으로 생성한 nginx-deployment가 보입니다. (참고: kubernetes라는 이름의 서비스는 쿠버네티스 자체 API 서버에 접근하기 위한 기본 서비스로 항상 존재합니다.)
- TYPE: 서비스의 타입입니다. 우리가 –type=NodePort로 지정했으므로 NodePort라고 표시됩니다.
- CLUSTER-IP: 클러스터 내부에서 이 서비스에 접근할 때 사용하는 가상 IP 주소입니다. 이 IP는 클러스터 외부에서는 직접 접근할 수 없습니다.
- EXTERNAL-IP: 이 서비스에 할당된 외부 IP 주소입니다. NodePort 타입의 경우, 기본적으로 <none> 또는 <pending>으로 표시될 수 있습니다. (LoadBalancer 타입의 서비스를 클라우드 환경에서 사용하면 여기에 실제 공인 IP가 할당됩니다.)
- PORT(S): 이 부분이 NodePort 서비스에서 가장 중요한 정보입니다! 80:31234/TCP와 같은 형태로 표시되는데, 이는 다음과 같은 의미를 가집니다.
- 80: 이 서비스가 내부적으로 Nginx 파드의 80번 포트와 연결되어 있음을 나타냅니다 (우리가 –port=80으로 지정한 값).
- 31234: 바로 이것이 NodePort입니다! 쿠버네티스가 클러스터의 각 노드에 임의로 할당한 외부 공개 포트 번호입니다. 이 포트 번호는 보통 30000에서 32767 사이의 범위에서 자동으로 선택됩니다. 우리는 이 NodePort 번호를 사용하여 외부에서 Nginx 웹서버에 접속하게 됩니다. 이 번호는 여러분의 환경마다 다를 수 있으므로, 반드시 여러분의 출력 결과에서 이 NodePort 번호를 확인해야 합니다.
- /TCP: 해당 포트가 TCP 프로토콜을 사용함을 나타냅니다.
PORT(S) 열에서 80:<NodePort번호>/TCP 형태의 정보를 확인했다면, 우리의 NodePort 서비스가 성공적으로 생성되고 외부로 통하는 문이 열렸음을 의미합니다. 이제 마지막 단계로, 이 NodePort를 통해 실제로 Nginx 웹서버에 접속해 보겠습니다.
5.4.2.3 웹 브라우저 또는 curl로 접속 확인: “Welcome to nginx!” 메시지를 만나다!
모든 준비는 끝났습니다! 이제 웹 브라우저를 열거나, 터미널에서 curl 명령어를 사용하여 우리가 배포하고 외부로 노출시킨 Nginx 웹서버에 접속해 볼 시간입니다.
접속 주소는 어떻게 될까요?
NodePort 서비스를 통해 접속할 때는 다음 두 가지 정보가 필요합니다.
- 노드(Node)의 IP 주소: 로컬 쿠버네티스 환경(Rancher Desktop, K3s 단일 노드 등)에서는 보통 여러분의 로컬 머신 IP 주소(127.0.0.1 또는 localhost)를 사용하면 됩니다. 만약 Kind나 K3d와 같이 다중 노드 클러스터를 구성하고 특정 워커 노드의 IP를 알아야 하는 경우라면 kubectl get nodes -o wide 명령을 통해 노드의 내부 IP를 확인할 수 있지만, 로컬 단일 노드 환경에서는 localhost로 충분합니다.
- NodePort 번호: 바로 이전 단계에서 kubectl get service nginx-deployment 명령을 실행했을 때 PORT(S) 열에 표시되었던 콜론(:) 뒤의 숫자입니다. (예: 31234) 반드시 여러분의 환경에서 확인된 NodePort 번호를 사용해야 합니다.
1. 웹 브라우저로 접속하기:
웹 브라우저를 열고 주소창에 다음과 같은 형식으로 입력한 후 엔터 키를 누르세요. (<NodePort번호>는 여러분이 확인한 실제 NodePort 번호로 대체해야 합니다.)
예를 들어, NodePort 번호가 31234라면 http://localhost:31234 를 입력하면 됩니다.
성공적으로 접속되었다면, 웹 브라우저 화면에 다음과 같은 Nginx의 기본 환영 페이지가 나타날 것입니다!
[ 이미지: “Welcome to nginx!” 라는 제목과 함께 간단한 안내 문구가 포함된 Nginx 기본 웹 페이지 스크린샷 ]
이 화면을 직접 보셨다면, 진심으로 축하드립니다! 여러분은 쿠버네티스에 애플리케이션을 배포하고, 서비스를 통해 외부로 노출시켜, 웹 브라우저를 통해 성공적으로 접속하는 전 과정을 완수한 것입니다.
2. curl 명령어로 접속하기 (터미널):
웹 브라우저 대신 터미널에서 curl 명령어를 사용하여 접속 상태를 확인할 수도 있습니다. curl은 HTTP 요청을 보내고 응답을 받아오는 강력한 명령줄 도구입니다. 터미널에 다음과 같이 입력해 보세요.
성공적으로 접속되면, 터미널 화면에 Nginx 환영 페이지의 HTML 소스 코드가 출력될 것입니다. HTML 태그들 사이에 “Welcome to nginx!”와 같은 텍스트가 포함되어 있는지 확인해 보세요.
curl 명령어의 결과로 위와 같은 HTML 내용을 확인했다면, 이 역시 Nginx 웹서버가 정상적으로 응답하고 있음을 의미합니다.
이로써 우리는 쿠버네티스 클러스터에 간단한 Nginx 웹서버를 배포하고, NodePort 타입의 서비스를 이용하여 외부에서 접속하는 것까지 성공적으로 완료했습니다. 이 과정은 매우 기본적인 예제였지만, 쿠버네티스에서 애플리케이션을 실행하고 네트워크를 통해 서비스하는 핵심적인 원리를 담고 있습니다. 디플로이먼트를 통해 애플리케이션의 상태를 관리하고, 서비스를 통해 안정적인 접근점을 제공하는 이 두 가지 개념은 앞으로 여러분이 마주하게 될 모든 쿠버네티스 기반 아키텍처의 근간이 될 것입니다.