
NHN Cloud 위에서 쿠버네티스 클러스터를 생성·관리할 수 있게 해주는 관리형(Managed) 서비스.
컨트롤 플레인은 NHN Cloud가 책임지고, 사용자는 노드·서비스·파드에 집중한다.
직접 쿠버네티스를 설치·운영하면(=kubeadm으로 self-managed) 해야 하는 일:
NKS는 이걸 전부 NHN Cloud가 처리한다. 고가용성도 보장된다.
소규모 팀·인턴 프로젝트에서 self-managed K8s를 굴리는 건 사실상 비추천.
| 영역 | 담당 |
|---|---|
| 컨트롤 플레인 (apiserver, etcd, scheduler, controller-manager) | NHN Cloud |
| 워커 노드 (인스턴스 사양·개수·스케일링) | 사용자 |
| 워크로드 (Pod, Deployment, Service, ConfigMap...) | 사용자 |
| 애플리케이션 코드·이미지 보안 | 사용자 |
관리형 K8s의 진짜 가치는 클라우드 리소스를 K8s 오브젝트로 다룰 수 있다는 점.
(a) 로드 밸런서를 이용한 서비스 공개
type: LoadBalancer Service를 만들면 → NHN Cloud 로드 밸런서가 자동 생성되고 → 외부 IP가 붙는다.
콘솔에서 LB를 수동으로 만들고 대상 서버를 등록하는 과정이 YAML 한 장으로 대체된다.
apiVersion: v1
kind: Service
metadata:
name: my-api-lb
spec:
type: LoadBalancer
selector:
app: my-api
ports:
- port: 80 # LB가 받는 포트
targetPort: 8080 # 컨테이너 포트
kubectl get svc my-api-lb
# NAME TYPE EXTERNAL-IP PORT(S)
# my-api-lb LoadBalancer 133.186.x.x 80:31234/TCP
LoadBalancer 타입은 Service 하나당 LB 하나를 만든다. 서비스가 10개면 LB 10개 = 비용 10배.
실무에서는 Ingress Controller(nginx 등)를 하나만 LoadBalancer로 노출하고,
그 뒤에서 경로 기반으로 여러 서비스에 분배하는 구조를 쓴다.
(b) 블록 스토리지 기반 퍼시스턴트 볼륨
PVC를 만들면 NHN Cloud 블록 스토리지가 동적으로 생성되어 파드에 붙는다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 20Gi
# storageClassName: 콘솔에서 제공되는 SC 이름 확인 후 지정
주의점:
Retain/Delete)에 따라 실제 볼륨이 남거나 사라진다. 운영 데이터는 Retain 권장노드 그룹(Node Group) 개념이 핵심이다.
같은 사양·같은 설정의 노드 묶음이며, 그룹 단위로 스케일링·업그레이드한다.
클러스터
├─ 노드 그룹 A (일반 워크로드용, 4vCPU/8GB × 3대)
├─ 노드 그룹 B (메모리 집약, 8vCPU/32GB × 2대)
└─ 노드 그룹 C (GPU, 1대)
특정 워크로드를 특정 노드 그룹에 보내려면 nodeSelector / affinity / taint-toleration을 쓴다.
spec:
nodeSelector:
workload-type: memory-intensive
| 항목 | 고려사항 |
|---|---|
| 리전 | 서비스 대상 사용자와 가까운 곳. 다른 NHN 리소스(DB 등)와 같은 리전 |
| VPC/서브넷 | 나중에 바꾸기 어렵다. IP 대역을 넉넉히(파드마다 IP 소모) |
| 노드 사양 | Spring Boot 파드는 최소 512Mi~1Gi. 노드에는 kubelet/kube-proxy/DaemonSet 몫도 필요하니 사양이 너무 작으면 실제 가용 자원이 얼마 안 남음 |
| 노드 개수 | 최소 3대 권장. 1대면 노드 장애 = 전체 장애, 롤링 업데이트 여유 공간도 없음 |
| K8s 버전 | 최신-1 정도가 안전. 쓰려는 도구(Helm 차트 등)의 호환 버전 확인 |
| 인터넷 게이트웨이 | 외부 노출·이미지 pull 필요 여부. 없으면 NCR Private URI 사용 |
# 1. kubectl 설치 확인
kubectl version --client
# 2. 콘솔에서 kubeconfig 다운로드 → 배치
mkdir -p ~/.kube
mv ~/Downloads/kubeconfig.yaml ~/.kube/config
chmod 600 ~/.kube/config
# 3. 연결 확인
kubectl cluster-info
kubectl get nodes -o wide
여러 클러스터를 쓴다면
kubectx/kubens설치를 강력 추천.
실수로 prod 컨텍스트에서delete치는 사고를 줄여준다.
kubectl create deployment nginx --image=nginx:1.25
kubectl expose deployment nginx --type=LoadBalancer --port=80
kubectl get svc nginx -w # EXTERNAL-IP 할당될 때까지 대기
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-api
labels: { app: my-api }
spec:
replicas: 3
selector:
matchLabels: { app: my-api }
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels: { app: my-api }
spec:
imagePullSecrets:
- name: ncr-secret # NCR 인증 (03 문서 참고)
containers:
- name: app
image: <레지스트리주소>/my-api:1.0.3 # latest 쓰지 말 것
ports:
- containerPort: 8080
envFrom:
- configMapRef: { name: my-api-config }
- secretRef: { name: my-api-secret }
env:
- name: JAVA_OPTS
value: "-XX:MaxRAMPercentage=75.0"
resources:
requests: { cpu: "300m", memory: "512Mi" }
limits: { cpu: "1000m", memory: "1Gi" }
startupProbe:
httpGet: { path: /actuator/health, port: 8080 }
failureThreshold: 30
periodSeconds: 5
readinessProbe:
httpGet: { path: /actuator/health/readiness, port: 8080 }
periodSeconds: 5
livenessProbe:
httpGet: { path: /actuator/health/liveness, port: 8080 }
periodSeconds: 10
terminationGracePeriodSeconds: 45
---
apiVersion: v1
kind: Service
metadata:
name: my-api
spec:
type: ClusterIP # 내부 통신용. 외부 노출은 Ingress로
selector: { app: my-api }
ports:
- port: 8080
targetPort: 8080
무중단 배포를 위한 추가 설정 (Spring Boot 측)
# application.yml
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
management:
endpoint:
health:
probes:
enabled: true
health:
livenessstate:
enabled: true
readinessstate:
enabled: true
K8s가 SIGTERM을 보내면 → Spring이 새 요청을 안 받고 진행 중 요청만 마무리 → 정상 종료.
이게 없으면 배포 때마다 처리 중이던 요청이 끊긴다.
| 증상 | 원인 후보 | 확인 명령 |
|---|---|---|
ImagePullBackOff | 이미지 경로 오타 / NCR 인증 실패 / IGW 없음 | kubectl describe pod |
Pending (스케줄 안 됨) | 노드 자원 부족 / PVC 미바인딩 / nodeSelector 불일치 | kubectl describe pod 하단 Events |
CrashLoopBackOff | 앱 기동 실패 (DB 접속 실패 등) | kubectl logs <pod> --previous |
LB EXTERNAL-IP가 <pending> | 서브넷에 인터넷 게이트웨이 미연결 / 쿼터 초과 | 콘솔 네트워크 설정 |
| 배포 중 502/504 | readinessProbe 미설정 / graceful shutdown 미설정 | Deployment strategy 재점검 |
| 파드가 계속 OOMKilled | JVM 힙이 컨테이너 limits 초과 | MaxRAMPercentage 조정 |
PVC Pending | StorageClass 이름 오류 / AZ 불일치 | kubectl describe pvc |
kubectl get nodes가 성공한다kubectl apply로 롤링 업데이트를 봤다kubectl rollout undo로 롤백해봤다