본문으로 건너뛰기

K8s YAML 생성

KIOPS의 [서비스 관리] 페이지에서는 Kubernetes 배포 YAML을 직접 작성할 필요가 없습니다. 폼에서 옵션을 선택하면 표준 YAML이 자동 생성되며, 미리보기에서 확인하거나 직접 편집할 수 있습니다.

이 가이드는 가장 단순한 "Hello World" 배포부터 시작하여 5단계에 걸쳐 점진적으로 기능을 추가합니다. 각 단계는 이전 단계의 YAML을 그대로 유지한 채 새로운 리소스 1개씩만 더하는 방식이므로, 처음 Kubernetes를 접하는 분도 부담 없이 따라할 수 있습니다.

왜 이 가이드를 읽어야 하는가

KIOPS가 YAML을 자동 생성해 주더라도, 미리보기에 표시되는 내용을 이해해야 잘못된 설정을 배포 전에 발견할 수 있습니다. 또한 특수한 요구사항이 생기면 자동 생성된 YAML을 직접 편집해야 하는 경우도 있습니다. 이 가이드는 폼에서 선택한 옵션이 YAML의 어떤 부분으로 만들어지는지 매핑해 주는 학습 자료입니다.

YAML 생성 페이지 진입

  1. [서비스 관리] 페이지에서 서비스를 선택합니다.
  2. 파이프라인의 Deploy 단계를 클릭합니다.
  3. 배포 환경 설정에서 K8s 배포를 선택하면 YAML 생성 화면이 표시됩니다.
이 가이드를 따라가는 방법
  • Step 1~5는 처음부터 순서대로 읽으세요. 단순한 정적 사이트에서 시작해 데이터베이스와 일회성 작업까지 확장합니다.
  • 부록: Kind별 참고는 옵션 사전입니다. 워크스루 중간이나 이후에 필요한 항목만 찾아보세요.
  • 모든 YAML 예시에는 # [필수] 또는 # [선택] 주석이 있어 어떤 값을 꼭 채워야 하는지 한눈에 보입니다.

시작하기 전에: 알아둘 10가지 용어

본문에 들어가기 전에 자주 등장하는 용어를 한 줄씩 정리합니다. 각 Step에서 처음 등장하는 시점에 "(용어 설명: 시작하기 전에 참고)"로 다시 짚어 드립니다.

용어정의 + 일상 비유
컨테이너앱 하나를 격리해서 실행하는 격리 박스. 도시락 한 칸처럼, 안의 음식(앱)은 옆 칸과 섞이지 않습니다.
이미지컨테이너를 만들기 위한 스냅샷(틀). 붕어빵 틀과 같습니다. 같은 틀로 여러 개를 찍어낼 수 있습니다.
Pod컨테이너를 묶어 클러스터에서 실행하는 최소 실행 단위. 아파트 한 호(號)와 같습니다. 같은 호 안의 컨테이너들은 주소와 자원을 공유합니다.
네임스페이스같은 클러스터 안에서 리소스를 구분하는 칸막이(폴더 같은 개념). 회사 안의 부서처럼 같은 건물(클러스터)을 부서별로 나눠 씁니다.
클러스터여러 노드(서버)를 묶어 Kubernetes가 관리하는 한 덩어리. 여러 동의 빌딩(서버)을 묶은 단지(團地)와 비슷합니다.
YAML 들여쓰기들여쓰기는 공백 2칸, 탭(tab) 키 금지. 마치 글머리표가 어긋난 보고서처럼, 한 칸이라도 어긋나면 전체가 인식되지 않습니다.
레이블(Label)Pod/리소스에 붙이는 이름표. 예: app: hello-world. 이름표가 같은 Pod들을 한 묶음으로 다룰 때 사용합니다.
DeploymentPod를 정해진 개수만큼 살려두는 관리자. 빌딩 관리소처럼 비어 있는 호(Pod)가 생기면 새로 채워줍니다.
ServicePod의 변하지 않는 우편번호. Pod가 바뀌어도 같은 주소로 들어가면 살아있는 Pod에 자동 연결됩니다.
SecretConfigMap의 금고 버전. 비밀번호, API 키 같은 민감 정보를 담습니다.

Step 1: Hello World 배포 (Deployment + Service)

전제: 없음 (시작 단계)

이 단계에서 배우는 것: Pod를 띄우고 클러스터 내부에서 접근 가능한 가장 단순한 형태.

왜 필요한가

컨테이너를 직접 실행하면 장애 발생 시 자동 복구되지 않고, Pod가 재시작되면 IP가 바뀌어 다른 곳에서 접근할 수 없습니다.

  • Deployment가 Pod 개수를 유지하고 장애 시 자동 재시작합니다.
  • Service가 변하지 않는 고정 주소를 제공하여 Pod IP가 바뀌어도 접근 가능하게 합니다.

만약 Service 없이 Deployment만 만들면 같은 클러스터 안의 다른 Pod조차 이 Pod를 안정적으로 호출할 방법이 없습니다.

(컨테이너 / Pod / 네임스페이스 / 클러스터는 용어 사전 참고)

Step 1에서 새로 등장하는 옵션
  • 이름 (metadata.name): 리소스 식별자. Deployment와 Service 각각 필요합니다.
  • 이미지 (spec.containers[].image): 컨테이너 이미지 주소. 빌드 목록에서 선택하거나 직접 입력합니다.
  • 레이블 매칭 (selector.matchLabelstemplate.metadata.labels): labels는 Pod에 붙은 이름표, selector.matchLabels는 "이 이름표가 붙은 애들을 내가 관리할게"라는 선언입니다. 두 값이 다르면 관리자가 다른 사람을 찾고 있는 셈이라 Pod가 방치됩니다.
  • 포트 연결 (containerPort, Service의 port/targetPort): targetPort가 컨테이너의 containerPort와 같아야 트래픽이 도달합니다.

옵션을 더 알고 싶다면 → 부록: Deployment, 부록: Service

자주 하는 실수
  • selector와 labels 불일치: matchLabelsapp: hello-worldtemplate.metadata.labels의 값을 다르게 적으면, Pod는 떠 있는데 Service의 Endpoint가 비어서 502 에러나 연결 거부가 발생합니다. 두 값을 항상 같이 수정하세요.
  • port와 targetPort 혼동: port는 Service 외부에서 받는 번호, targetPort는 컨테이너 내부에서 듣는 번호입니다. 컨테이너가 8080을 듣는데 targetPort: 80으로 적으면 "Connection refused"가 떨어집니다. 컨테이너 이미지의 EXPOSE 포트를 확인하고 맞추세요.

KIOPS 설정 요약

항목YAML 필드
Deployment 이름hello-worldmetadata.name (Deployment)
네임스페이스my-appmetadata.namespace
레플리카1spec.replicas
이미지nginx:1.25spec.template.spec.containers[0].image
컨테이너 포트80containers[0].ports[0].containerPort
Service 이름hello-world-svcmetadata.name (Service)
Service 타입ClusterIPspec.type (Service)
생성되는 YAML 확인
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world # [필수] 이름 (위 핵심 옵션 참고)
namespace: my-app # [선택] 기본값: default
labels:
app: hello-world # [선택] 리소스 분류용 레이블
spec:
replicas: 1 # [선택] 기본값: 1, 동시에 띄울 Pod 복제본 수
selector:
matchLabels:
app: hello-world # [필수] 레이블 매칭 - template.metadata.labels와 일치해야 함
template:
metadata:
labels:
app: hello-world # [필수] 레이블 매칭 - selector.matchLabels와 매칭
spec:
containers:
- name: web # [필수] 컨테이너 이름
image: nginx:1.25 # [필수] 이미지 (위 핵심 옵션 참고)
ports:
- containerPort: 80 # [선택] 포트 연결 - 컨테이너가 수신하는 포트
---
apiVersion: v1
kind: Service
metadata:
name: hello-world-svc # [필수] Service 이름
namespace: my-app # [선택] 기본값: default
spec:
type: ClusterIP # [선택] 기본값: ClusterIP
selector:
app: hello-world # [필수] 레이블 매칭 - Deployment의 Pod 레이블과 일치
ports:
- name: http # [선택] 포트 식별자
protocol: TCP # [선택] 기본값: TCP
port: 80 # [필수] 포트 연결 - Service가 수신하는 포트
targetPort: 80 # [필수] 포트 연결 - 트래픽을 전달할 컨테이너 포트

다음 단계 미리보기

Step 1의 Service는 클러스터 내부에서만 접근 가능합니다. Step 2에서는 Ingress를 추가해 외부 도메인으로 접근하게 만듭니다.


Step 2: 외부에서 도메인으로 접근 (Ingress 추가)

전제: Step 1 YAML 완성

이 단계에서 배우는 것: app.example.com 같은 도메인으로 외부 사용자가 접근할 수 있게 하는 방법.

(클러스터, 네임스페이스는 용어 사전 참고)

왜 필요한가

Step 1의 ClusterIP Service는 클러스터 내부 전용입니다. 외부 사용자가 브라우저로 접속하려면 다음 중 하나가 필요합니다.

  • Ingress (이번 단계, 권장): 도메인 기반 라우팅과 TLS 인증서를 한곳에서 관리할 수 있습니다.
  • NodePort/LoadBalancer Service: 도메인 처리가 어렵고 HTTPS 설정이 번거롭습니다.

Ingress 없이 운영하면 도메인마다 다른 IP를 외워야 하고, HTTPS 인증서도 애플리케이션이 직접 처리해야 합니다.

Step 2에서 새로 등장하는 옵션
  • 호스트 (spec.rules[].host): 외부 사용자가 입력할 도메인. DNS가 클러스터의 Ingress Controller IP를 가리키도록 설정되어 있어야 합니다. 이 작업은 IT 담당자 또는 운영팀에 요청하세요.
  • 경로와 백엔드 Service (paths[].backend.service.name/port.number): 어떤 URL을 어떤 Service로 보낼지 결정합니다. Service 이름과 port 번호는 Step 1에서 만든 값과 정확히 일치해야 합니다.
  • pathType: Prefix로 설정하면 /api, /api/users, /api/v1/data 모두 매칭됩니다. Exact는 정확히 /api만 매칭. 대부분 Prefix를 씁니다.
  • Ingress Class (spec.ingressClassName): 어떤 Ingress Controller가 이 Ingress를 처리할지 지정합니다. KIOPS는 traefik을 기본 권장합니다.

옵션을 더 알고 싶다면 → 부록: Ingress

자주 하는 실수
  • 경로 순서: paths/를 먼저, /api를 나중에 쓰면 /api 요청도 /로 매칭됩니다. 더 구체적인 경로(/api)를 위에 두세요.
  • DNS 설정 누락: 도메인이 클러스터의 Ingress Controller IP를 가리키도록 DNS 설정이 필요합니다. 이 작업은 IT 담당자 또는 운영팀에 요청하세요.

KIOPS 설정 요약

Step 1 YAML은 그대로 유지하고 Ingress 1개를 추가합니다.

항목YAML 필드
Ingress 이름hello-world-ingressmetadata.name
네임스페이스my-appmetadata.namespace
Ingress Classtraefikspec.ingressClassName
호스트app.example.comspec.rules[0].host
경로/hello-world-svc:80spec.rules[0].http.paths[0].path + backend.service
추가되는 Ingress YAML 확인
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-world-ingress # [필수] Ingress 이름
namespace: my-app # [선택] 기본값: default
spec:
ingressClassName: traefik # [선택] Ingress Class (위 핵심 옵션 참고)
rules:
- host: app.example.com # [필수] 호스트 (위 핵심 옵션 참고)
http:
paths:
- path: / # [필수] 경로와 백엔드 Service - 매칭할 URL 경로
pathType: Prefix # [필수] Prefix(접두사 매칭) 또는 Exact
backend:
service:
name: hello-world-svc # [필수] 경로와 백엔드 Service - 트래픽을 전달할 Service 이름
port:
number: 80 # [필수] 경로와 백엔드 Service - Service의 port 값
HTTPS가 필요한 경우

HTTPS를 사용하려면 spec.tls 항목에 secretNamehosts를 추가하고, 해당 도메인의 인증서가 담긴 Secret을 미리 생성해야 합니다. 자세한 내용은 도메인/SSL 설정 가이드를 참고하세요.

다음 단계 미리보기

Step 2까지는 nginx 이미지를 그대로 사용했습니다. Step 3에서는 애플리케이션에 환경 변수를 주입하기 위해 ConfigMap을 추가합니다.


Step 3: 설정값 분리 (ConfigMap 추가)

전제: Step 2 YAML 완성

이 단계에서 배우는 것: 환경 변수를 ConfigMap으로 분리하여 YAML 본문에서 빼내는 방법.

(환경 변수 의미가 흐릿하다면 IT 담당자 또는 운영팀에 문의하세요. Secret은 용어 사전 참고)

왜 필요한가

환경 변수를 Deployment YAML에 직접 적으면 다음과 같은 문제가 생깁니다.

  • 개발/운영 환경마다 Deployment를 따로 관리해야 합니다.
  • 여러 Deployment가 같은 설정을 공유할 수 없습니다.
  • 설정만 바꾸려 해도 Deployment 전체를 수정해야 합니다.

ConfigMap으로 설정값을 분리하면 한 곳에서 관리할 수 있고, envFrom으로 일괄 주입하면 변수 이름을 일일이 매핑할 필요가 없습니다.

비밀번호는 Secret을 사용하세요

ConfigMap은 평문으로 저장됩니다. 비밀번호, API 키, 토큰 등 민감 정보는 ConfigMap이 아닌 Secret에 저장하고 동일한 방식(envFrom: secretRef)으로 주입하세요.

Step 3에서 새로 등장하는 옵션
  • ConfigMap 데이터 (data): key-value 형태. 값은 모두 문자열이며, 숫자나 boolean도 따옴표로 감싸야 합니다.
  • envFrom + configMapRef: ConfigMap의 모든 key가 환경 변수 이름이 되고 value가 환경 변수 값이 됩니다. 개별 키를 하나씩 매핑하지 않아도 됩니다.
  • 이름 일치: Deployment의 configMapRef.name은 ConfigMap의 metadata.name과 정확히 같아야 하며, 같은 네임스페이스에 있어야 합니다.

옵션을 더 알고 싶다면 → 부록: ConfigMap

자주 하는 실수
  • 네임스페이스 불일치: ConfigMap을 못 찾는다는 에러가 난다면 Deployment와 ConfigMap의 namespace가 같은지 확인하세요. 네임스페이스를 넘어선 참조는 불가능합니다.
  • 민감 정보 ConfigMap에 저장: 비밀번호나 API 키를 ConfigMap에 넣으면 YAML 파일에 평문으로 노출됩니다. 반드시 Secret을 사용하세요.

KIOPS 설정 요약

Step 2 YAML에 ConfigMap 1개를 추가하고, Deployment에 envFrom을 추가합니다.

항목YAML 필드
ConfigMap 이름app-configmetadata.name (ConfigMap)
데이터WELCOME_MESSAGE, LOG_LEVEL, FEATURE_FLAGdata
Deployment 변경envFrom으로 app-config 전체 주입containers[0].envFrom[0].configMapRef.name
추가/변경되는 YAML 확인
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config # [필수] ConfigMap 이름
namespace: my-app # [선택] 기본값: default
data: # [필수] ConfigMap 데이터 - 한 개 이상의 key-value 쌍
WELCOME_MESSAGE: "Hello from KIOPS"
LOG_LEVEL: info
FEATURE_FLAG: "true"

Deployment의 containers 부분에 다음을 추가합니다.

      containers:
- name: web
image: nginx:1.25
ports:
- containerPort: 80
envFrom: # [선택] envFrom + configMapRef - ConfigMap 전체를 환경 변수로 주입
- configMapRef:
name: app-config # [필수] 이름 일치 - 참조할 ConfigMap 이름

다음 단계 미리보기

Step 3에서는 설정을 분리했지만, 아직 데이터를 저장할 곳이 없습니다. Step 4에서는 PVC를 추가하여 Pod가 재시작되어도 데이터가 사라지지 않도록 합니다.


Step 4: 데이터 영구 저장 (PVC + 데이터베이스 추가)

전제: Step 3 YAML 완성

이 단계에서 배우는 것: 데이터베이스를 PVC와 함께 배포하여 Pod 재시작 시에도 데이터가 유지되도록 하는 방법.

(Secret 의미는 용어 사전 참고, Secret 생성을 미리 IT 담당자 또는 운영팀에 요청하세요)

왜 필요한가

Pod의 파일 시스템은 일시적입니다. Pod가 재시작되거나 다른 노드로 이동하면 컨테이너 내부의 모든 파일이 사라집니다. 이는 nginx 같은 정적 서버에는 문제가 없지만 데이터베이스에는 치명적입니다.

**PVC (PersistentVolumeClaim)**는 Pod와 별개로 존재하는 외부 스토리지를 요청하는 리소스입니다. Deployment에서 이 PVC를 마운트하면 데이터가 Pod 라이프사이클과 무관하게 보존됩니다.

또한 데이터베이스 Deployment는 다음 두 가지가 일반 애플리케이션과 다릅니다.

  • replicas: 1: 동시에 여러 Pod가 같은 볼륨에 쓰면 데이터가 손상될 수 있습니다.
  • strategy: Recreate: 새 Pod를 먼저 띄우는 기본 RollingUpdate는 두 Pod가 동시에 같은 PVC를 잡으려고 시도하므로 실패합니다. 기존 Pod를 먼저 종료한 뒤 새 Pod를 생성하는 Recreate를 사용합니다.
사전 준비

이 Step은 db-credentials라는 이름의 Secret이 미리 생성되어 있다고 가정합니다. Secret 생성은 IT 담당자 또는 운영팀에 요청하세요. 또한 이 Step에서는 PVC, Recreate 전략, volumeMounts/volumes 연결, Secret 참조(secretKeyRef) 등 4가지 새로운 개념이 한꺼번에 도입됩니다.

Step 4에서 새로 등장하는 옵션
  • PVC 용량 (spec.resources.requests.storage): 10Gi, 100Gi 등 크기 단위 포함. 생성 후 늘리는 것은 스토리지 클래스에 따라 제한됩니다.
  • 접근 모드 (accessModes): 대부분 ReadWriteOnce(한 서버에서만 쓰고 읽기, DB 대부분이 이걸 씁니다). 다중 노드 공유가 필요할 때만 ReadWriteMany를 사용합니다.
  • volumes ↔ volumeMounts 연결: volumes[].namevolumeMounts[].name이 같아야 하고, volumes[].persistentVolumeClaim.claimName은 PVC의 metadata.name과 같아야 합니다.
  • Recreate 전략: 단일 PVC를 사용하는 Pod 한 개짜리 DB는 Recreate로 설정해야 새 Pod 배포 시 충돌이 발생하지 않습니다.

옵션을 더 알고 싶다면 → 부록: PersistentVolumeClaim (PVC), 부록: Deployment의 볼륨 부분

자주 하는 실수
  • PVC 함부로 삭제: Deployment를 지우면 Pod는 사라지지만 PVC는 남습니다. 하지만 kubectl delete -f로 YAML 전체를 지우면 PVC도 같이 삭제되어 데이터가 영구 손실됩니다. 삭제 전 백업 + 의도적으로 kubectl delete deployment만 사용하는 습관을 들이세요.
  • Recreate 트레이드오프: Recreate는 기존 Pod를 완전히 종료한 뒤 새 Pod를 띄우므로 수십 초~수 분 다운타임이 발생합니다. DB처럼 한 Pod만 띄워야 하는 경우엔 어쩔 수 없지만, 일반 웹 앱에는 절대 적용하지 마세요(Step 1처럼 RollingUpdate 기본값 사용).

KIOPS 설정 요약

Step 3 YAML에 PVC 1개, Deployment(postgres) 1개, Service(postgres-svc) 1개를 추가합니다.

항목YAML 필드
PVC 이름postgres-datametadata.name (PVC)
용량10Gispec.resources.requests.storage
스토리지 클래스nfs-clientspec.storageClassName
접근 모드ReadWriteOncespec.accessModes[0]
DB Deployment 이름postgresmetadata.name (Deployment)
이미지postgres:15spec.template.spec.containers[0].image
전략Recreatespec.strategy.type
볼륨 마운트/var/lib/postgresql/datacontainers[0].volumeMounts[0].mountPath
추가되는 PVC YAML 확인
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data # [필수] PVC 이름
namespace: my-app # [선택] 기본값: default
spec:
accessModes:
- ReadWriteOnce # [필수] 접근 모드 (위 핵심 옵션 참고)
storageClassName: nfs-client # [선택] 클러스터 기본 스토리지 클래스 사용 시 생략 가능
resources:
requests:
storage: 10Gi # [필수] PVC 용량 (위 핵심 옵션 참고)
추가되는 Postgres Deployment + Service YAML 확인
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres # [필수] Deployment 이름
namespace: my-app # [선택] 기본값: default
spec:
replicas: 1 # [선택] DB는 1로 고정 권장
strategy:
type: Recreate # [선택] Recreate 전략 (위 핵심 옵션 참고)
selector:
matchLabels:
app: postgres # [필수]
template:
metadata:
labels:
app: postgres # [필수]
spec:
containers:
- name: postgres # [필수]
image: postgres:15 # [필수]
ports:
- containerPort: 5432
# env: 평범한 값(POSTGRES_DB)은 직접 적고, 민감한 값(POSTGRES_PASSWORD)은 valueFrom.secretKeyRef로 Secret에서 꺼냅니다.
env:
- name: POSTGRES_DB
value: myapp
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials # [필수] 사전에 생성된 Secret 이름 (사전 준비 참고)
key: password # [필수] Secret 내 key 이름
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: postgres-storage # [필수] volumes ↔ volumeMounts 연결 - 아래 volumes의 name과 일치
mountPath: /var/lib/postgresql/data # [필수] 컨테이너 내부 마운트 경로
volumes:
- name: postgres-storage # [필수] volumes ↔ volumeMounts 연결 - volumeMounts.name과 일치
persistentVolumeClaim:
claimName: postgres-data # [필수] volumes ↔ volumeMounts 연결 - PVC의 metadata.name과 일치
---
apiVersion: v1
kind: Service
metadata:
name: postgres-svc # [필수]
namespace: my-app # [선택] 기본값: default
spec:
type: ClusterIP
selector:
app: postgres # [필수] Postgres Pod 레이블과 일치
ports:
- port: 5432
targetPort: 5432
PVC 삭제 주의

PVC를 삭제하면 연결된 데이터가 영구 손실될 수 있습니다. 운영 환경에서 PVC 삭제 전에는 반드시 백업 여부를 확인하세요.

다음 단계 미리보기

Step 4까지는 모든 리소스가 계속 실행되는 형태였습니다. Step 5에서는 한 번만 실행하고 종료되는 Job을 추가하여 DB 마이그레이션을 자동화합니다.


Step 5: 일회성 작업 (Job 추가)

전제: Step 4 YAML 완성

이 단계에서 배우는 것: 배포 전 한 번만 실행되어야 하는 DB 마이그레이션을 Job으로 구성하는 방법.

왜 필요한가

데이터베이스 스키마 변경, 초기 데이터 삽입, 일회성 정리 스크립트 등은 다음과 같은 특징이 있습니다.

  • 한 번만 실행되면 끝납니다 (계속 떠 있을 필요 없음).
  • 실패하면 재시도해야 합니다 (DB 연결 일시 장애 등).
  • 성공 여부를 명확히 확인해야 합니다 (Pod의 종료 코드).

Deployment로 만들면 작업이 끝난 뒤에도 Pod가 계속 살아있으려 하고, 종료되면 자동으로 재시작됩니다. Job은 작업 완료 후 Pod를 종료된 상태로 남기고, 실패 시 정해진 횟수만큼 재시도합니다.

Step 5에서 새로 등장하는 옵션
  • command와 args: 이미지의 기본 ENTRYPOINT를 덮어쓰고 마이그레이션 명령만 실행합니다. command가 실행 파일, args가 그 인자입니다.
  • restartPolicy (OnFailure / Never): Job은 Always를 사용할 수 없습니다. OnFailure는 같은 Pod 안에서 컨테이너만 재시작하고, Never는 매번 새 Pod를 생성합니다.
  • backoffLimit: 재시도 한계. 초과하면 Job이 실패 상태로 기록됩니다. 너무 적으면(01) 일시 장애에 곧바로 실패 처리되고, 너무 많으면(10+) 코드 오류가 있을 때 무한히 재시도하면서 클러스터 리소스를 잡아먹습니다. 35가 적절합니다.
  • envFrom 재사용: Step 3에서 만든 app-config ConfigMap을 그대로 사용합니다. 동일한 DB 접속 정보를 마이그레이션과 애플리케이션이 공유합니다.

옵션을 더 알고 싶다면 → 부록: Job

자주 하는 실수
  • Job과 Deployment 동시 시작 주의: 마이그레이션 순서 보장이 필요합니다 - 아래 Job 실행 순서 박스 참고.
  • backoffLimit는 3~5 권장: 자세한 이유는 위 tip 박스 참고.

KIOPS 설정 요약

Step 4 YAML에 Job 1개를 추가합니다.

항목YAML 필드
Job 이름db-migratemetadata.name
이미지harbor.example.com/my-app/migrator:v1.0spec.template.spec.containers[0].image
command["python", "manage.py", "migrate"]containers[0].command + args
envFromapp-config + db-credentialscontainers[0].envFrom[]
backoffLimit3spec.backoffLimit
restartPolicyOnFailurespec.template.spec.restartPolicy
추가되는 Job YAML 확인
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate # [필수] Job 이름
namespace: my-app # [선택] 기본값: default
spec:
backoffLimit: 3 # [선택] backoffLimit (위 핵심 옵션 참고) - 기본값: 6
template:
spec:
restartPolicy: OnFailure # [필수] restartPolicy (위 핵심 옵션 참고)
containers:
- name: migrator # [필수] 컨테이너 이름
image: harbor.example.com/my-app/migrator:v1.0 # [필수] 이미지
command: # [선택] command와 args - ENTRYPOINT 덮어쓰기
- python
args: # [선택] command와 args - 명령어 인자
- manage.py
- migrate
envFrom: # [선택] envFrom 재사용 - ConfigMap/Secret 일괄 주입
- configMapRef:
name: app-config
- secretRef:
name: db-credentials
Job 실행 순서

Job은 Deployment와 동시에 생성되므로, 마이그레이션이 끝나기 전에 애플리케이션이 시작될 수 있습니다. 다음 중 하나로 대응하세요.

  • 애플리케이션에 DB 연결 재시도 로직을 둡니다.
  • 애플리케이션 Pod의 initContainers에서 마이그레이션 완료를 대기합니다.

다음 단계 미리보기

5단계를 마쳤습니다. 이제 정적 사이트, 외부 도메인, 설정 분리, 영구 데이터, 일회성 작업까지 갖춘 완전한 배포 구성을 이해할 수 있습니다. 더 깊이 들어가려면 아래 부록에서 각 Kind의 옵션을 자세히 살펴보세요.


부록: Kind별 옵션 참고

워크스루에서 다루지 않은 세부 옵션을 정리한 참고용 섹션입니다. 필요한 항목만 찾아보세요.

Deployment

(Step 1에서 처음 등장)

옵션필수/선택기본값설명
metadata.name필수-Deployment 리소스 이름
metadata.namespace선택default배포 대상 네임스페이스
spec.replicas선택1Pod 개수. 운영 환경은 3 이상 권장
spec.selector.matchLabels필수-관리 대상 Pod의 레이블
spec.template.metadata.labels필수-Pod에 부여되는 레이블
spec.template.spec.containers[].name필수-컨테이너 식별자
spec.template.spec.containers[].image필수-컨테이너 이미지
spec.template.spec.containers[].ports[].containerPort선택-컨테이너 수신 포트
spec.template.spec.containers[].imagePullPolicy선택AlwaysAlways, IfNotPresent, Never
spec.strategy.type선택RollingUpdateRollingUpdate 또는 Recreate
spec.strategy.rollingUpdate.maxSurge선택1추가로 생성할 수 있는 Pod 수
spec.strategy.rollingUpdate.maxUnavailable선택0동시에 종료 가능한 Pod 수

리소스 제한 (spec.template.spec.containers[].resources)

requests는 "최소 이만큼은 받아야 일할 수 있어요"(예약), limits는 "이만큼 넘으면 잘라주세요"(상한)입니다. 호텔 객실 최소 보장 평수 vs 최대 평수와 비슷합니다.

옵션필수/선택설명
requests.cpu / requests.memory선택최소 보장량. 스케줄링 기준 (예: 100m, 128Mi)
limits.cpu / limits.memory선택최대 상한. 초과 시 CPU는 쓰로틀, 메모리는 OOM 종료 (예: 500m, 512Mi)

헬스체크 (livenessProbe, readinessProbe)

옵션필수/선택설명
httpGet.path필수헬스체크 경로 (예: /health)
httpGet.port필수헬스체크 포트
initialDelaySeconds선택Pod 시작 후 첫 검사까지 대기 시간
periodSeconds선택검사 주기
timeoutSeconds선택응답 대기 시간
failureThreshold선택연속 실패 허용 횟수

배포 전략은 RollingUpdate(무중단 점진 교체)와 Recreate(전체 종료 후 재생성) 두 가지입니다. Blue-Green 전략은 KIOPS에서 직접 지원하지 않습니다.

노드 배치제어 (spec.template.spec)

특정 노드에만 Pod를 배치하고 싶을 때 사용합니다. KIOPS 배포 폼의 노드 배치 섹션에서 클러스터 노드의 Label/Taint를 자동 수집해 입력 후보로 제안하며, 선택한 값이 아래 YAML 필드로 생성됩니다.

옵션필수/선택설명
nodeSelector선택지정한 Label과 정확히 일치하는 노드에만 배치 (예: disktype: ssd)
affinity.nodeAffinity선택Label 조건식(In/NotIn/Exists/DoesNotExist)으로 배치. nodeSelector보다 유연
tolerations선택노드의 Taint를 견뎌 배치를 허용. key/operator/value/effect(NoSchedule/PreferNoSchedule/NoExecute)

Taint가 걸린 노드에 Pod를 배치하려면 그 Taint를 견디는 Toleration이 반드시 있어야 합니다. Toleration만으로는 해당 노드에 "배치할 수 있다"는 허용일 뿐, 강제 배치하려면 nodeSelector 또는 nodeAffinity를 함께 지정합니다.

Service

(Step 1에서 처음 등장)

옵션필수/선택기본값설명
metadata.name필수-Service 이름
spec.type선택ClusterIPClusterIP, NodePort, LoadBalancer
spec.selector필수-트래픽을 전달할 Pod 레이블
spec.ports[].port필수-Service가 수신하는 포트
spec.ports[].targetPort필수-트래픽을 전달할 컨테이너 포트
spec.ports[].protocol선택TCPTCP, UDP, SCTP
spec.ports[].nodePort선택자동할당NodePort 타입일 때 노드에서 열리는 포트

타입별 사용 시나리오

타입사용 시나리오
ClusterIP클러스터 내부 통신, Ingress와 함께 사용 (가장 일반적)
NodePort외부 리버스 프록시(외부 Nginx 등)에서 노드 포트로 접근
LoadBalancer클라우드 환경에서 외부 IP를 직접 할당받음

Ingress

(Step 2에서 처음 등장)

옵션필수/선택기본값설명
metadata.name필수-Ingress 이름
spec.ingressClassName선택클러스터 기본처리할 Ingress Controller 지정
spec.rules[].host필수-라우팅 대상 도메인
spec.rules[].http.paths[].path필수-매칭 경로
spec.rules[].http.paths[].pathType필수-Prefix 또는 Exact
spec.rules[].http.paths[].backend.service.name필수-대상 Service 이름
spec.rules[].http.paths[].backend.service.port.number필수-대상 Service 포트
spec.tls[].secretName선택-HTTPS 인증서를 담은 Secret 이름
spec.tls[].hosts선택-TLS가 적용될 도메인 목록

Ingress Class 선택지 (6종)

Class설명
traefik자동 인증서, 미들웨어 체인 지원. KIOPS 기본 권장
nginx커뮤니티 프로젝트. 2026년 3월 EOL 예정으로 마이그레이션 권장
haproxy고성능 로드밸런싱. 엔터프라이즈 환경에 적합
kongAPI Gateway 통합, 플러그인 확장 지원
contourEnvoy 프록시 기반 CNCF 프로젝트
albAWS Application Load Balancer. AWS 환경 전용

여러 경로가 매칭될 수 있을 때는 paths 배열에서 먼저 정의된 항목이 우선합니다. /api/보다 먼저 두면 API 요청이 정적 페이지로 가지 않습니다.

ConfigMap

(Step 3에서 처음 등장)

옵션필수/선택기본값설명
metadata.name필수-ConfigMap 이름
metadata.namespace선택default배포 대상 네임스페이스
data필수-key-value 쌍. 모든 값은 문자열

Deployment에서 참조하는 방법

  • envFrom: configMapRef: 모든 key를 환경 변수로 일괄 주입 (가장 간편)
  • env[].valueFrom.configMapKeyRef: 특정 key만 골라서 다른 이름의 환경 변수로 주입
  • volumes[].configMap: 파일로 마운트 (설정 파일 형태로 사용할 때)

민감 정보는 ConfigMap 대신 Secret을 사용하고, 사용 방식은 동일합니다 (secretRef, secretKeyRef).

PersistentVolumeClaim (PVC)

(Step 4에서 처음 등장)

옵션필수/선택기본값설명
metadata.name필수-PVC 이름
spec.accessModes필수-접근 모드 배열
spec.resources.requests.storage필수-요청 용량 (예: 10Gi)
spec.storageClassName선택클러스터 기본스토리지 프로비저너 지정

스토리지 클래스 예시: nfs-client, local-path, longhorn, ceph-rbd, default 등 클러스터에 설치된 것 중 선택합니다.

접근 모드 (3종)

모드설명사용 시나리오
ReadWriteOnce단일 노드에서 읽기/쓰기대부분의 DB와 일반 애플리케이션 (기본)
ReadWriteMany다중 노드에서 동시 읽기/쓰기NFS 등 공유 스토리지 필요
ReadOnlyMany다중 노드에서 읽기 전용정적 설정 파일 공유

Job

(Step 5에서 처음 등장)

옵션필수/선택기본값설명
metadata.name필수-Job 이름
spec.template.spec.containers[].image필수-실행할 이미지
spec.template.spec.containers[].command선택-ENTRYPOINT 덮어쓰기
spec.template.spec.containers[].args선택-command 인자
spec.template.spec.restartPolicy필수-OnFailure 또는 Never
spec.backoffLimit선택6실패 재시도 횟수
spec.activeDeadlineSeconds선택-Job 전체 최대 실행 시간(초)

환경 변수 주입 방식

방식설명
env (개별)변수 하나씩 이름/값 지정. ConfigMap/Secret 일부만 참조 가능
envFrom: configMapRefConfigMap의 모든 key를 환경 변수로 일괄 주입
envFrom: secretRefSecret의 모든 key를 환경 변수로 일괄 주입

Job은 DB 마이그레이션, 시드 데이터 삽입, 일회성 정리 스크립트 등 한 번 실행하고 끝나는 작업에 사용합니다. 주기적으로 실행되는 작업은 CronJob을 사용하세요.


다음에 할 일

가이드를 다 읽었다면 다음 순서로 진행하세요.

  1. 실제 배포 실행 → K8s 배포
  2. 트래픽 증가 시 자동 확장 → HPA 자동 확장
  3. HTTPS 활성화 → 도메인/SSL 설정
  4. 문제 발생 시 → 롤백

관련 가이드