Helm을 활용한 NestJS 애플리케이션 Kubernetes 배포 실습
저번 실습에 이어서 이번에는 Helm을 활용하여 NestJS 애플리케이션을 Kubernetes 클러스터에 배포하는 실습을 진행했습니다. Helm Chart를 생성하고 ClusterIP, NodePort, LoadBalancer 세 가지 Service 유형으로 로컬 환경에 배포해본 경험을 정리합니다.
🔹 Helm이란?
Helm은 Kubernetes의 패키지 관리자입니다. Helm을 사용하면 여러 Kubernetes 리소스를 하나의 패키지로 묶어 관리할 수 있으며 이를 통해 재사용성과 배포 편의성을 크게 높일 수 있습니다.
🔹 Helm의 핵심 구성 요소
- Chart: Kubernetes 리소스가 정의된 패키지 단위 (Chart.yaml, values.yaml, templates 등 포함)
- Release: 클러스터에 설치된 차트의 인스턴스
- Repository: Chart 저장하고 공유하는 저장
🔹 Helm의 주요 명령 흐름
- helm install: 차트 기반 리소스를 설치하며 릴리스 생성
- helm upgrade: 기존 릴리스를 새 설정으로 갱신
- helm rollback: 문제 발생 시 이전 릴리스로 복구
- helm uninstall: 설치된 릴리스 및 관련 리소스 제거
🔹 Helm의 장점
- 패키지화로 관리 효율성 향상
- 버전 관리로 배포 이력 추적 가능
- 재사용성이 뛰어나 여러 환경에 쉽게 적용 가능
- 자동화된 배포 및 롤백 가능
🔹 설치 및 버전 확인
- Windows 환경에서 Chocolatey로 Helm 설치

- Helm 버전 확인

🔹Helm Chart 생성 및 구조
- Helm Chart를 생성하여 time-service 애플리케이션 패키징

- Chart 구조

실습 1: ClusterIP 타입으로 배포
📄values.yaml 설정
이 파일은 Helm 차트의 기본 설정을 정의합니다. 여기에는 Docker 이미지 정보, 레플리카 수, ClusterIP 타입의 서비스 설정이 포함됩니다. 또한, 자동 스케일링과 리소스 요청 및 제한 설정 그리고 Ingress와 노드 선택 조건 등 네트워킹 및 배포 관련 설정도 포함되어 있습니다.
replicaCount: 1
image:
repository: sj2012/time-service
pullPolicy: IfNotPresent
tag: latest
service:
type: ClusterIP
port: 80
serviceAccount:
create: true
name: ''
ingress:
enabled: false
annotations: {}
hosts:
- host: chart-example.local
paths: []
tls: []
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
resources: {}
nodeSelector: {}
tolerations: []
affinity: {}
📄 deployment.yaml 설정
Kubernetes Deployment 리소스를 정의하는 Helm 템플릿입니다. 이 템플릿은 애플리케이션의 복제본을 관리하고 배포하는 데 사용됩니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "time-service.fullname" . }}
labels:
{{- include "time-service.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "time-service.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "time-service.selectorLabels" . | nindent 8 }}
spec:
serviceAccountName: {{ include "time-service.serviceAccountName" . }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: 3000
resources:
{{- toYaml .Values.resources | nindent 12 }}
📄 service.yaml 설정
Helm 차트와 values.yaml 파일을 사용하여 Kubernetes 서비스 리소스를 동적으로 생성합니다.
apiVersion: v1
kind: Service
metadata:
name: {{ include "time-service.fullname" . }}
labels:
{{- include "time-service.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: 3000
selector:
{{- include "time-service.selectorLabels" . | nindent 4 }}
📄 chart.yaml 설정
Helm 차트의 메타데이터를 정의하는 파일입니다. 차트의 이름, 버전, 설명, 애플리케이션 버전 등을 포함하고 있으며 Helm CLI와 다른 도구들이 차트를 이해하고 사용할 수 있도록 합니다.
apiVersion: v2
name: time-service
description: A Helm chart for Kubernetes
type: application
version: 0.1.0
appVersion: '1.0'
📄 serviceaccount.yaml 설정
Helm 템플릿을 사용하여 Kubernetes 서비스 계정을 정의합니다.
{{- if .Values.serviceAccount.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "time-service.serviceAccountName" . }}
labels:
{{- include "time-service.labels" . | nindent 4 }}
{{- end }}
🔹 Helm 차트 배포
helm install 명령어를 사용하여 Helm 차트를 배포했습니다.

🔹 클러스터에 설치된 helm 릴리스
time-service 이름으로 Helm 릴리스가 default 네임스페이스에 설치되었습니다.

🔹 애플리케이션 상태 확인
kubectl get pods, kubectl get services 명령어를 사용하여 Kubernetes 클러스터에서 애플리케이션의 상태를 확인했습니다.


🔹 애플리케이션 로그 확인
kubectl logs <pod-name> 명령어를 사용하여 로그를 확인했습니다.

🔹 포트 포워딩
- ClusterIP 서비스 타입은 Kubernetes 클러스터 내부에서만 접근할 수 있도록 설정되어 포트 포워딩을 진행했습니다.
- Kubernetes 클러스터 내의 Pod에 접근하기 위해 로컬 포트 8080을 해당 Pod의 포트 3000으로 포워딩하도록 했습니다.

🔹HTTP 요청 확인
- 8080포트로 요청하여 200 성공 응답이 오고 content에 시간이 오는 것을 확인했습니다.

실습 2: NodePort 타입으로 배포
📄 values.yaml 설정
service:
type: NodePort
port: 80
🔹릴리스 업그레이드
Helm을 사용하여 기존 time-service 릴리스를 업그레이드합니다.

🔹 애플리케이션 상태 확인
- kubectl get pods, kubectl get services 명령어를 사용하여 Kubernetes 클러스터에서 애플리케이션의 상태를 확인했습니다.
- NodePort 서비스 타입은 포트포워딩 없이 Kubernetes 클러스터 외부에서 특정 포트를 통해 서비스에 접근할 수 있습니다.
- 외부에서 접근할 수 있는 NodePort는 31289로 나왔습니다.


🔹HTTP 요청 확인
- 위에서 확인한 31289 포트로 요청을 보냈습니다.
- 200 성공 응답이 오고 content에 시간이 오는 것을 확인했습니다.

🔹실습 3: LoadBalancer 타입으로 배포
📄 values.yaml 설정
service:
type: LoadBalancer
port: 80
🔹릴리스 업그레이드
Helm을 사용하여 기존 time-service 릴리스를 업그레이드합니다.

🔹 애플리케이션 상태 확인
- kubectl get pods, kubectl get services 명령어를 사용하여 Kubernetes 클러스터에서 애플리케이션의 상태를 확인했습니다.


🔹 포트 포워딩
- Kubernetes 클러스터 내의 Pod에 접근하기 위해 로컬 포트 8080을 해당 Pod의 포트 3000으로 포워딩하도록 했습니다.

🔹HTTP 요청 확인
- 8080포트로 요청하여 200 응답이 오고 content에 시간이 오는 것을 확인했습니다.

초기 서비스 배포 시 문제 및 해결
🔹 첫 시도: LoadBalancer 사용
- values.yaml에서 service.type: LoadBalancer 설정
- kubectl get svc로 확인 시 무한로딩


🔹 문제 원인 및 해결
- 문제 원인: LoadBalancer 서비스 타입은 일반적으로 클라우드 환경(AWS, GCP, Azure 등)에서 사용되며 외부에서 접근할 수 있도록 공인 IP를 자동으로 할당합니다. 그러나 로컬 Kubernetes 환경(Docker Desktop, Minikube 등)에서는 외부 IP가 자동으로 할당되지 않기 때문에 문제가 발생합니다.
- 해결 방법: 로컬 환경에서는 LoadBalancer로 외부 접근이 불가능하기 때문에 ClusterIP 서비스에서 사용하는 방식처럼 kubectl port-forward 명령어를 사용해 로컬 포트를 서비스 포트로 연결했습니다.
이를 통해 애플리케이션이 정상적으로 동작하는지 확인할 수 있었습니다.
kubectl port-forward svc/time-service 8080:80
Kubernetes 서비스 타입 비교
| 서비스 타입 | 설명 | 외부 접근 가능 여부 | 로컬 테스트 방식 |
| ClusterIP | 기본값. 클러스터 내부에서만 접근 가능 | ❌ 불가능 | kubectl port-forward 사용 |
| NodePort | 외부에서 노드 IP와 포트를 통해 접근 가능 | ✅ 가능 | 노드 IP + NodePort로 접근 |
| LoadBalancer | 클라우드에서 외부 IP 자동 할당. 로컬에서는 동작하지 않음 | ❌ 불가능 (로컬 기준) | kubectl port-forward 사용 |
틀린 내용이 있다면 언제든지 지적 부탁드립니다!
'인턴' 카테고리의 다른 글
| 포트원(아임포트), 부트페이, 토스페이먼츠(PG사) 특징 및 수수료 비교 (2) | 2025.06.05 |
|---|---|
| 인턴 1주차 - 쿠버네티스 실습(로컬에서 NestJS 애플리케이션 배포하기) (0) | 2025.05.03 |
| 인턴 1주차 - Kubernetes 기본 개념 정리 (0) | 2025.05.03 |