쿠버네티스 실습 – 로컬에서 NestJS 애플리케이션 배포하기
앞서 Kubernetes의 구조와 개념을 학습한 뒤, 간단한 NestJS 애플리케이션을 직접 컨테이너화하고 로컬 쿠버네티스 클러스터에 배포하는 실습 과제를 수행했다. 해당 애플리케이션은 응답 테스트를 위해 현재 시간을 반환하는 단일 API로 구성하라는 요구사항이 있었다. Docker 환경에서 Kubernetes를 활성화한 후 Deployment와 Service 리소스를 적용하는 과정을 실습하면서 실제 동작 흐름과 리소스 간의 관계를 이해할 수 있었다.
쿠버네티스 설치 및 초기 상태 확인
- Docker Desktop에서 쿠버네티스를 활성화하고 설치했다.

- kubectl version 명령어로 클라이언트와 서버 버전이 1.25.0으로 일치하는 것을 확인했다.

- 클러스터 정보 조회 시 컨트롤 플레인이 https://kubernetes.docker.internal:6443에서 실행되고 있었고, CoreDNS가 정상적으로 서비스 중이었다. (CoreDNS는 클러스터 내부에서 DNS 역할을 하며, 파드들이 서비스 이름으로 서로를 찾을 수 있도록 도와준다.)

- kubectl get nodes 명령어로 control-plane 역할을 가진 docker-desktop 노드가 Ready 상태임을 확인했다. (Ready 상태는 노드가 정상적으로 작동하고 있으며 파드를 스케줄링할 수 있는 준비가 완료되었음을 의미한다)

NestJS 애플리케이션 구성
NestJS 프로젝트 생성
간단하게 현재 시간을 반환하는 NestJS 애플리케이션을 작성했다.
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getCurrentTime(): string {
const now = new Date();
return now.toISOString();
}
}
Dockerfile 작성 및 이미지 생성
멀티 스테이지 빌드 방식으로 도커파일을 구성해 애플리케이션을 빌드하고 실행하는 이미지를 만들었다.
# Stage 1: Build the application
FROM node:14-alpine as builder
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Stage 2: Run the application
FROM node:14-alpine
WORKDIR /usr/app
COPY --from=builder /usr/src/app/dist ./dist
COPY package*.json ./
RUN npm install --only=production
CMD ["node", "dist/main"]
- Stage 1 (애플리케이션 빌드) : node:14-alpine을 기반으로 빌드 환경을 구성하고, 작업 디렉토리를 /usr/src/app으로 설정한 뒤 npm install로 개발용 종속성을 포함한 전체 의존성을 설치한다. 이후 소스 코드를 복사하고 npm run build로 애플리케이션을 빌드한다.
- Stage 2 (애플리케이션 실행) : 실제 실행 환경을 위한 이미지가 생성된다. 빌드된 결과물(dist/)만 복사하고, package.json을 기준으로 production 의존성만 설치한다. 마지막으로 CMD ["node", "dist/main"] 명령으로 애플리케이션을 실행한다.
쿠버네티스 리소스 정의
Deployment 리소스 (deployment.yaml)
- 동작: 이 YAML 파일은 쿠버네티스에 Deployment 리소스를 생성하도록 지시합니다. Deployment는 애플리케이션의 상태를 관리하며 지정된 수의 복제본(replicas)을 유지합니다. 이 파일에는 사용할 도커 이미지, 필요한 복제본의 수, 애플리케이션을 구동할 포트 등이 명시됩니다.
- 결과: 쿠버네티스 클러스터는 Deployment 설정에 따라 하나 이상의 파드(Pod)를 생성합니다. 각 파드는 도커 컨테이너를 실행하여 애플리케이션이 포함된 환경을 제공합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: time-service
spec:
replicas: 1
selector:
matchLabels:
app: time-service
template:
metadata:
labels:
app: time-service
spec:
containers:
- name: time-service
image: sj2012/time-service:latest
ports:
- containerPort: 3000
- apiVersion: 쿠버네티스 API의 버전 지정
- kind: 리소스의 종류. Deployment는 파드의 복제, 스케일링, 업데이트를 관리한다.
- metadata: 리소스 이름 등 메타데이터 정의. name: time-service로 리소스를 식별한다.
- spec (Specification)
- replicas:실행할 파드 인스턴스 수
- selector:어떤 레이블의 파드를 관리할지 지정. matchLabels를 사용하여 app: time-service 레이블이 붙은 파드를 대상으로 한다.
- template:실제 생성될 파드의 템플릿
- metadata.labels:파드에 붙는 레이블 지정
- spec.containers:파드에 포함될 컨테이너 정의
- name: 컨테이너의 이름을 time-service로 설정
- image:사용할 컨테이너 이미지
- ports: 컨테이너가 클러스터 내부 또는 외부로 열어야 할 포트 명시
Service 리소스 (service.yaml)
- 동작: 이 YAML 파일은 쿠버네티스에 Service 리소스를 생성하도록 지시합니다. Service는 파드 그룹에 안정적인 네트워크 주소를 제공합니다. 특히 외부로부터의 요청을 파드로 라우팅하는 역할을 합니다. 이 과정에서 LoadBalancer 유형의 서비스를 사용하면 외부 IP 주소가 할당되어 인터넷에서 접근 가능해집니다.
- 결과: 생성된 Service는 클러스터 내부 또는 외부의 요청을 적절한 파드로 전달합니다. 이를 통해 애플리케이션에 안정적으로 접근할 수 있습니다.
apiVersion: v1
kind: Service
metadata:
name: time-service
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 3000
selector:
app: time-service
- apiVersion: 리소스 정의에 사용할 API 버전
- kind: 리소스 종류 지정 (Service: 쿠버네티스 내부 또는 외부에서 파드 그룹에 접근할 수 있게 하는 추상적인 방법)
- metadata: 서비스 이름 (time-service) 등 메타데이터
- spec:
- type: 서비스의 타입 지정. (LoadBalancer로 외부 트래픽을 받을 수 있게 하고, 이를 파드에 자동으로 라우팅한다.)
- ports: 외부 포트 80 → 내부 포트 3000으로 포워딩
- selector: 어떤 파드로 트래픽을 보낼지 지정 (app: time-service)
배포 및 실행 과정
1. 도커 이미지 빌드

2. Docker Hub에 이미지 푸시

3. Deployment 리소스 적용
- kubectl apply -f deployment.yaml으로 클러스터에 리소스를 적용했다.
- 정상적으로 리소스가 생성되었음을 출력 메시지로 확인했다.

4. Service 리소스 적용
- 아래 명령어를 사용하여 service.yaml 파일에 정의된 서비스 설정을 쿠버네티스 클러스터에 적용했다.
- 이 과정을 통해 애플리케이션에 대한 네트워크 액세스가 설정된다.
PS D:\GitHub\time-service\k8s> kubectl apply -f service.yaml
service/time-service created
실행 확인
파드 상태 조회
- time-service 앱 레이블을 가진 파드의 상태 조회

파드 로그 확인
- NesjJS 애플리케이션이 제대로 실행된 것을 확인

서비스 정보 출력
- 외부에서 접근할 수 있는 IP주소는 localhost, 포트는 80인 것을 확인

현재 시간 조회 요청
- 200 응답 확인, Content에서 ISO 형식의 현재 시간(2024-07-04T06:0421.149Z)이 응답된 것을 확

전체 흐름 요약
- NestJS 애플리케이션 작성 및 Docker 이미지 생성
- Docker Hub에 이미지 푸시
- Deployment 및 Service 리소스 정의
- kubectl apply 명령으로 리소스 적용
- 클러스터에서 파드 생성 및 실행 확인
- Service를 통해 외부 요청 라우팅 확인
- kubectl을 활용한 상태 조회 및 로그 확인
'인턴' 카테고리의 다른 글
| 포트원(아임포트), 부트페이, 토스페이먼츠(PG사) 특징 및 수수료 비교 (2) | 2025.06.05 |
|---|---|
| 인턴 1주차 - Helm을 활용한 NestJS 애플리케이션 Kubernetes 배포 (1) | 2025.05.04 |
| 인턴 1주차 - Kubernetes 기본 개념 정리 (0) | 2025.05.03 |