쿠버네티스 매니페스트(Kubernetes Manifest) 완벽 가이드: 기본 원리부터 고급 활용까지
매니페스트 심층 분석: 기본 원리부터 고급 활용까지
- 매니페스트 완벽 가이드
1. 매니페스트란 무엇인가? (YAML/JSON 형식, 선언적 구성)#
쿠버네티스에서 애플리케이션을 배포하고 관리하기 위한 핵심 구성 요소인 매니페스트는, 애플리케이션이 어떤 모습으로 동작해야 하는지를 정의하는 설계도와 같습니다. 이 설계도는 YAML 또는 JSON 형식으로 작성되며, 선언적 구성을 통해 쿠버네티스에게 원하는 상태를 알려줍니다.
1.1 매니페스트의 정의#
매니페스트(Manifest)는 쿠버네티스 클러스터에 배포할 애플리케이션의 리소스를 정의하는 파일입니다. 여기서 리소스란 Pod, Service, Deployment, ConfigMap, Secret 등 쿠버네티스에서 관리하는 모든 오브젝트를 의미합니다. 즉, 매니페스트는 쿠버네티스에게 "이러한 리소스를 이러한 설정으로 생성하고 유지해 주세요"라고 요청하는 역할을 합니다.
1.2 YAML/JSON 형식#
매니페스트는 YAML(YAML Ain't Markup Language) 또는 JSON(JavaScript Object Notation) 형식으로 작성됩니다. 일반적으로는 YAML 형식이 가독성이 뛰어나고 주석을 사용할 수 있어 많이 사용됩니다.
- YAML: 사람이 읽기 쉬운 데이터 직렬화 형식으로, 들여쓰기를 사용하여 데이터의 계층 구조를 표현합니다.
- JSON: 데이터 교환을 위한 경량화된 텍스트 기반 형식으로, 키-값 쌍으로 데이터를 표현합니다.
YAML 예시:
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: nginx:latest
ports:
- containerPort: 80JSON 예시 (동일한 내용):
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "my-app-pod",
"labels": {
"app": "my-app"
}
},
"spec": {
"containers": [
{
"name": "my-app-container",
"image": "nginx:latest",
"ports": [
{
"containerPort": 80
}
]
}
]
}
}두 형식 모두 동일한 정보를 담고 있지만, YAML 형식이 가독성이 더 뛰어나다는 것을 알 수 있습니다.
1.3 선언적 구성 (Declarative Configuration)#
매니페스트는 선언적 구성 방식을 사용합니다. 이는 "어떻게(How)" 해야 하는지를 지시하는 것이 아니라, "무엇(What)"을 원하는지를 선언하는 방식입니다. 즉, 매니페스트에는 원하는 리소스의 상태(desired state)를 정의하고, 쿠버네티스는 이를 자동으로 맞춰줍니다.
- 명령형 구성 (Imperative Configuration): "어떻게" 해야 하는지를 순서대로 지시합니다. 예를 들어, "Pod를 생성하고, Service를 생성하고, Deployment를 생성하라"와 같이 구체적인 단계를 명시합니다.
- 선언적 구성 (Declarative Configuration): "무엇"을 원하는지를 선언합니다. 예를 들어, "3개의 Pod 인스턴스를 가진 Deployment를 실행하고, 외부에서 접근 가능한 Service를 생성하라"와 같이 원하는 최종 상태를 선언합니다.
선언적 구성의 장점:
- 자동 복구: 쿠버네티스는 매니페스트에 정의된 desired state를 지속적으로 모니터링하고, 실제 상태가 desired state와 다를 경우 자동으로 복구합니다. 예를 들어, Pod가 죽으면 자동으로 새로운 Pod를 생성합니다.
- 간편한 관리: 사용자는 원하는 상태만 정의하면 되므로, 복잡한 관리 작업을 쿠버네티스에게 위임할 수 있습니다.
- 멱등성 (Idempotency): 동일한 매니페스트를 여러 번 적용해도 결과는 항상 같습니다. 이는 쿠버네티스가 이미 desired state와 일치하는 리소스에 대해서는 아무런 작업을 수행하지 않기 때문입니다.
예시:
# Deployment 매니페스트 (선언적 구성)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-deployment
spec:
replicas: 3 # 원하는 Pod 인스턴스 수: 3개
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: my-app:1.0.0위 매니페스트는 "my-app" 레이블을 가진 Pod 인스턴스 3개를 유지하는 Deployment를 실행하라는 desired state를 선언합니다. 쿠버네티스는 이 선언에 따라 Pod 인스턴스를 생성하고, 인스턴스 수가 부족하면 자동으로 추가하고, 과도하면 제거합니다.
1.4 매니페스트의 구성 요소#
매니페스트는 일반적으로 다음과 같은 구성 요소를 포함합니다.
- apiVersion: 쿠버네티스 API의 버전을 지정합니다.
- kind: 생성할 리소스의 종류를 지정합니다. (Pod, Service, Deployment 등)
- metadata: 리소스의 이름, 레이블, 어노테이션 등 메타데이터를 지정합니다.
- spec: 리소스의 desired state를 정의합니다.
2. 쿠버네티스 리소스 종류 및 매니페스트 작성법#
쿠버네티스에서 애플리케이션을 구성하는 다양한 리소스들을 이해하고, 각 리소스에 대한 매니페스트를 작성하는 방법을 배우는 것은 쿠버네티스 활용의 핵심입니다. 이 섹션에서는 주요 쿠버네티스 리소스 종류와 각 리소스의 매니페스트 작성법, 필수/옵션 필드, 레이블, 셀렉터, 어노테이션 활용법에 대해 상세히 설명합니다.
2.1 Pod#
- 정의: 쿠버네티스에서 배포 및 관리의 가장 기본적인 단위입니다. 하나 이상의 컨테이너를 포함하며, 스토리지, 네트워크, 컨테이너 실행 방법을 명시합니다.
- 매니페스트 예시:
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: nginx:latest
ports:
- containerPort: 80- 필수 필드:
- apiVersion: API 버전 (예: v1)
- kind: 리소스 종류 (예: Pod)
- metadata.name: Pod 이름
- spec.containers: 컨테이너 목록
- spec.containers[].name: 컨테이너 이름
- spec.containers[].image: 컨테이너 이미지
- 옵션 필드:
- metadata.labels: Pod에 레이블을 지정합니다.
- spec.containers[].ports: 컨테이너가 노출하는 포트 목록
- spec.containers[].env: 컨테이너에 전달할 환경 변수 목록
- spec.containers[].resources: 컨테이너의 자원 요청 및 제한 (CPU, 메모리)
- spec.restartPolicy: Pod의 재시작 정책 (Always, OnFailure, Never)
- spec.livenessProbe: 컨테이너의 상태를 확인하는 프로브 (헬스 체크)
- spec.readinessProbe: 컨테이너가 트래픽을 처리할 준비가 되었는지 확인하는 프로브
- 레이블, 셀렉터 활용: Pod에 레이블을 지정하고, 다른 리소스(Service, Deployment)에서 셀렉터를 사용하여 해당 Pod를 선택할 수 있습니다.
2.2 Service#
- 정의: Pod 집합에 대한 네트워크 접근을 제공하는 추상화 계층입니다. Pod의 IP 주소는 변경될 수 있으므로, Service를 통해 안정적인 접근점을 제공합니다.
- 매니페스트 예시:
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app # Pod 레이블 셀렉터
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer # 외부 접근을 위한 LoadBalancer 타입- 필수 필드:
- apiVersion: API 버전 (예: v1)
- kind: 리소스 종류 (예: Service)
- metadata.name: Service 이름
- spec.selector: Service가 연결할 Pod의 레이블 셀렉터
- spec.ports: 포트 목록
- spec.ports[].port: Service 포트
- spec.ports[].targetPort: Pod 포트
- 옵션 필드:
- spec.type: Service 타입 (ClusterIP, NodePort, LoadBalancer, ExternalName)
- spec.ports[].protocol: 프로토콜 (TCP, UDP, SCTP)
- spec.loadBalancerIP: LoadBalancer 타입 Service의 고정 IP 주소
- spec.externalIPs: 외부 IP 주소 목록
- 레이블, 셀렉터 활용: Service는 spec.selector를 사용하여 Pod를 선택합니다. Pod에 지정된 레이블과 Service의 셀렉터가 일치하는 Pod에 트래픽이 전달됩니다.
2.3 Deployment#
- 정의: Pod의 desired state를 정의하고, Pod를 배포하고 업데이트하는 데 사용됩니다. Deployment는 ReplicaSet을 관리하여 지정된 수의 Pod 인스턴스를 유지합니다.
- 매니페스트 예시:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-deployment
spec:
replicas: 3 # Pod 인스턴스 수
selector:
matchLabels:
app: my-app # Pod 레이블 셀렉터
template:
metadata:
labels:
app: my-app # Pod 레이블
spec:
containers:
- name: my-app-container
image: nginx:latest
ports:
- containerPort: 80- 필수 필드:
- apiVersion: API 버전 (예: apps/v1)
- kind: 리소스 종류 (예: Deployment)
- metadata.name: Deployment 이름
- spec.replicas: Pod 인스턴스 수
- spec.selector.matchLabels: Deployment가 관리할 Pod의 레이블 셀렉터
- spec.template: Pod 템플릿
- spec.template.metadata.labels: Pod에 적용할 레이블
- spec.template.spec.containers: 컨테이너 목록
- spec.template.spec.containers[].name: 컨테이너 이름
- spec.template.spec.containers[].image: 컨테이너 이미지
- 옵션 필드:
- spec.strategy: 배포 전략 (RollingUpdate, Recreate)
- spec.minReadySeconds: Pod가 준비 상태로 간주되기까지의 최소 시간 (초)
- spec.revisionHistoryLimit: 롤백을 위해 유지할 ReplicaSet의 수
- 레이블, 셀렉터 활용: Deployment는 spec.selector.matchLabels를 사용하여 관리할 Pod를 선택합니다. Deployment는 spec.template.metadata.labels에 정의된 레이블을 Pod에 적용합니다.
2.4 ConfigMap#
- 정의: 설정 파일, 환경 변수, 명령줄 인수 등 설정 데이터를 키-값 쌍으로 저장하는 리소스입니다. Pod에서 ConfigMap의 데이터를 사용할 수 있습니다.
- 매니페스트 예시:
apiVersion: v1
kind: ConfigMap
metadata:
name: my-app-config
data:
database_url: "jdbc:mysql://mydb:3306/mydb"
log_level: "INFO"- 필수 필드:
- apiVersion: API 버전 (예: v1)
- kind: 리소스 종류 (예: ConfigMap)
- metadata.name: ConfigMap 이름
- data: 설정 데이터 (키-값 쌍)
- 옵션 필드:
- binaryData: 바이너리 데이터 (base64 인코딩)
2.5 Secret#
- 정의: 비밀번호, API 키, 인증서 등 민감한 정보를 안전하게 저장하는 리소스입니다. Pod에서 Secret의 데이터를 사용할 수 있습니다. Secret은 base64로 인코딩되어 저장되지만, 암호화되지는 않습니다. 따라서 추가적인 보안 조치가 필요합니다.
- 매니페스트 예시:
apiVersion: v1
kind: Secret
metadata:
name: my-app-secret
type: Opaque # Secret 타입 (Opaque, kubernetes.io/tls, kubernetes.io/dockerconfigjson)
data:
database_password: "c2VjcmV0LXBhc3N3b3JkCg==" # base64 인코딩된 비밀번호- 필수 필드:
- apiVersion: API 버전 (예: v1)
- kind: 리소스 종류 (예: Secret)
- metadata.name: Secret 이름
- type: Secret 타입 (Opaque, kubernetes.io/tls, kubernetes.io/dockerconfigjson)
- data: 비밀 데이터 (키-값 쌍, base64 인코딩)
- 옵션 필드:
- stringData: 문자열 데이터 (base64 인코딩 없이 직접 문자열 입력 가능)
2.6 Ingress#
- 정의: 클러스터 외부에서 Service에 접근할 수 있도록 HTTP/HTTPS 트래픽을 라우팅하는 규칙을 정의하는 리소스입니다. Ingress Controller가 필요합니다.
- 매니페스트 예시:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: / # Nginx Ingress Controller 어노테이션
spec:
rules:
- host: myapp.example.com # 호스트 이름
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-service # 연결할 Service 이름
port:
number: 80 # Service 포트- 필수 필드:
- apiVersion: API 버전 (예: networking.k8s.io/v1)
- kind: 리소스 종류 (예: Ingress)
- metadata.name: Ingress 이름
- spec.rules: 규칙 목록
- spec.rules[].host: 호스트 이름
- spec.rules[].http.paths: 경로 목록
- spec.rules[].http.paths[].path: 경로
- spec.rules[].http.paths[].pathType: 경로 타입 (Prefix, Exact, ImplementationSpecific)
- spec.rules[].http.paths[].backend.service.name: 연결할 Service 이름
- spec.rules[].http.paths[].backend.service.port.number: 연결할 Service 포트
- 옵션 필드:
- metadata.annotations: Ingress Controller 설정을 위한 어노테이션
- spec.tls: TLS 설정 (HTTPS)
2.7 레이블, 셀렉터, 어노테이션 활용법#
- 레이블 (Labels): 쿠버네티스 오브젝트에 키-값 쌍으로 메타데이터를 추가합니다. 오브젝트를 식별하고 그룹화하는 데 사용됩니다.
- 예: app: my-app, environment: production
- 셀렉터 (Selectors): 레이블을 기반으로 오브젝트를 선택하는 데 사용됩니다. Service, Deployment 등이 특정 레이블을 가진 Pod를 선택하는 데 사용됩니다.
- 예: matchLabels: {app: my-app}
- 어노테이션 (Annotations): 쿠버네티스 오브젝트에 메타데이터를 추가하지만, 레이블과 달리 오브젝트를 식별하거나 그룹화하는 데 사용되지 않습니다. 주로 도구나 라이브러리에서 사용하기 위한 정보를 저장하는 데 사용됩니다.
- Pod, Service, Deployment, ConfigMap, Secret, Ingress 등
- 각 리소스의 필수 필드 및 옵션 필드 상세 설명
- 레이블, 셀렉터, 어노테이션 활용법
3. 매니페스트 배포 및 관리#
쿠버네티스에서 매니페스트를 작성하는 것만큼 중요한 것이 매니페스트를 효과적으로 배포하고 관리하는 것입니다. kubectl 명령어를 사용하여 매니페스트를 배포, 조회, 업데이트, 삭제하는 방법과 다양한 매니페스트 업데이트 전략에 대해 알아보겠습니다.
3.1 kubectl 명령어 활용#
kubectl은 쿠버네티스 클러스터와 상호 작용하기 위한 커맨드 라인 도구입니다. kubectl을 사용하여 매니페스트를 배포하고 관리하는 기본적인 명령어는 다음과 같습니다.
- kubectl apply: 매니페스트 파일을 기반으로 쿠버네티스 리소스를 생성하거나 업데이트합니다.
kubectl apply -f my-app-deployment.yaml # 파일 기반 배포
kubectl apply -k kustomize/overlay/prod # Kustomize 디렉토리 기반 배포
kubectl apply -f - <<EOF # 인라인 매니페스트 배포
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
spec:
containers:
- name: my-app-container
image: nginx:latest
EOF- -f: 매니페스트 파일 또는 디렉토리를 지정합니다.
- -k: Kustomize 디렉토리를 지정합니다.
- <<EOF: 인라인 매니페스트를 지정합니다.
- kubectl get: 쿠버네티스 리소스의 정보를 조회합니다.
kubectl get pods # 모든 Pod 조회
kubectl get pod my-app-pod # 특정 Pod 조회
kubectl get pods -n my-namespace # 특정 네임스페이스의 Pod 조회
kubectl get deployments # 모든 Deployment 조회
kubectl get svc # 모든 Service 조회
kubectl get pods -o wide # Pod의 상세 정보 조회 (노드 정보 포함)
kubectl get pods -o yaml # Pod의 YAML 형식 매니페스트 조회- pods, pod, deployments, svc 등: 조회할 리소스 종류를 지정합니다.
- -n: 네임스페이스를 지정합니다.
- -o wide: 넓은 형식으로 상세 정보를 출력합니다.
- -o yaml: YAML 형식으로 매니페스트를 출력합니다.
- kubectl describe: 쿠버네티스 리소스의 상세 정보를 조회합니다. 이벤트, 상태, 설정 정보 등을 포함합니다.
kubectl describe pod my-app-pod # 특정 Pod 상세 정보 조회
kubectl describe deployment my-app-deployment # 특정 Deployment 상세 정보 조회
kubectl describe svc my-app-service # 특정 Service 상세 정보 조회- pods, pod, deployments, svc 등: 조회할 리소스 종류를 지정합니다.
- kubectl delete: 쿠버네티스 리소스를 삭제합니다.
kubectl delete pod my-app-pod # 특정 Pod 삭제
kubectl delete -f my-app-deployment.yaml # 매니페스트 파일 기반 삭제
kubectl delete deployment my-app-deployment # 특정 Deployment 삭제
kubectl delete all --all # 모든 리소스 삭제 (주의!)- -f: 매니페스트 파일 또는 디렉토리를 지정합니다.
- all: 모든 리소스를 삭제합니다.
- --all: 모든 네임스페이스의 모든 리소스를 삭제합니다.
3.2 매니페스트 업데이트 전략#
애플리케이션을 업데이트하는 것은 쿠버네티스에서 흔히 발생하는 작업입니다. 쿠버네티스는 다양한 업데이트 전략을 제공하며, 프로젝트의 요구사항에 맞는 전략을 선택하는 것이 중요합니다.
- 롤링 업데이트 (RollingUpdate): 가장 일반적인 업데이트 전략으로, 기존 Pod를 점진적으로 새로운 버전의 Pod로 교체합니다. 다운타임을 최소화하며 업데이트를 수행할 수 있습니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-deployment
spec:
strategy:
type: RollingUpdate # 롤링 업데이트 전략
rollingUpdate:
maxSurge: 25% # 최대 25%까지 추가 파드 생성 가능
maxUnavailable: 25% # 최대 25%까지 파드 사용 불가능
# ... (나머지 설정)- maxSurge: 업데이트 중에 기존 Pod 수보다 얼마나 많은 Pod를 추가적으로 생성할 수 있는지 지정합니다. (백분율 또는 절대값)
- maxUnavailable: 업데이트 중에 사용 불가능한 Pod의 최대 수를 지정합니다. (백분율 또는 절대값)
롤링 업데이트 과정:
Deployment는 기존 ReplicaSet을 유지하면서 새로운 ReplicaSet을 생성합니다.
새로운 ReplicaSet은 maxSurge에 지정된 수만큼 Pod를 점진적으로 생성합니다.
기존 ReplicaSet은 maxUnavailable에 지정된 수만큼 Pod를 점진적으로 삭제합니다.
이 과정을 반복하여 모든 Pod가 새로운 버전으로 교체될 때까지 진행합니다.
카나리 배포 (Canary Deployment): 새로운 버전의 애플리케이션을 일부 사용자에게 먼저 배포하여 테스트하는 전략입니다. 새로운 버전의 안정성을 검증하고 문제 발생 시 빠르게 롤백할 수 있습니다.
카나리 배포 과정:
- 기존 버전의 Deployment와 Service를 유지합니다.
- 새로운 버전의 Deployment를 생성하고, 일부 트래픽을 새로운 버전으로 라우팅합니다. (Service 셀렉터, Ingress 규칙 등을 활용)
- 새로운 버전의 성능, 오류율 등을 모니터링합니다.
- 새로운 버전이 안정적인 것으로 판단되면, 전체 트래픽을 새로운 버전으로 점진적으로 이동합니다.
- 기존 버전의 Deployment를 삭제합니다.
예시:
# 기존 Deployment (버전 1.0.0)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-deployment
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: "1.0.0" # 버전 레이블
template:
metadata:
labels:
app: my-app
version: "1.0.0"
spec:
containers:
- name: my-app-container
image: my-app:1.0.0
# 새로운 Deployment (버전 2.0.0, 카나리)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-deployment-canary
spec:
replicas: 1 # 일부 트래픽만 처리
selector:
matchLabels:
app: my-app
version: "2.0.0"
template:
metadata:
labels:
app: my-app
version: "2.0.0"
spec:
containers:
- name: my-app-container
image: my-app:2.0.0
# Service (기존 버전과 카나리 버전을 모두 선택)
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app # 모든 Pod 선택 (버전 무관)
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer위 예시에서 my-app-deployment-canary는 새로운 버전(2.0.0)의 Pod를 1개만 배포합니다. my-app-service는 app: my-app 레이블을 가진 모든 Pod (버전 1.0.0과 2.0.0 모두)를 선택하므로, 일부 트래픽이 새로운 버전으로 라우팅됩니다.
블루-그린 배포 (Blue-Green Deployment): 새로운 버전의 애플리케이션을 완전히 새로운 환경에 배포하고, 트래픽을 한 번에 전환하는 전략입니다. 롤백이 빠르지만, 일시적인 다운타임이 발생할 수 있습니다.
블루-그린 배포 과정:
- 기존 환경 (Blue)과 동일한 새로운 환경 (Green)을 구축합니다.
- Green 환경에 새로운 버전의 애플리케이션을 배포합니다.
- Green 환경을 테스트하고 검증합니다.
- 트래픽을 Blue 환경에서 Green 환경으로 한 번에 전환합니다. (Service 업데이트, DNS 변경 등)
- Blue 환경을 대기 상태로 유지하거나 삭제합니다. (롤백 대비)
Recreate: 기존 Pod를 모두 삭제하고 새로운 Pod를 생성하는 전략입니다. 다운타임이 발생할 수 있으므로, 일반적으로 사용하지 않습니다.
3.3 매니페스트 관리 전략#
- GitOps: Git 저장소를 사용하여 쿠버네티스 리소스의 desired state를 관리하는 방식입니다. Git 저장소의 변경 사항을 자동으로 쿠버네티스 클러스터에 반영합니다.
- Flux, Argo CD 등의 도구를 활용할 수 있습니다.
- Kustomize, Helm: 매니페스트를 템플릿화하고 환경에 따라 커스터마이징하여 관리합니다.
- kubectl apply, kubectl get, kubectl describe, kubectl delete 명령어 활용
- 매니페스트 업데이트 전략 (롤링 업데이트, 카나리 배포 등)
4. 고급 매니페스트 활용 기법#
쿠버네티스 매니페스트를 더욱 효과적으로 활용하기 위해, 이 섹션에서는 Init Container, Liveness Probe, Readiness Probe, Resource Quota, LimitRange와 같은 고급 기법들을 소개하고 자세히 설명합니다. 이러한 기법들을 통해 애플리케이션의 안정성, 가용성, 자원 효율성을 향상시킬 수 있습니다.
4.1 Init Container#
- 정의: 애플리케이션 컨테이너가 시작되기 전에 실행되는 특수한 컨테이너입니다. 초기화 작업 (예: 데이터베이스 스키마 생성, 설정 파일 다운로드)을 수행하는 데 사용됩니다.
- 특징:
- Init Container는 순차적으로 실행됩니다. 이전 Init Container가 성공적으로 완료되어야 다음 Init Container가 실행됩니다.
- Init Container가 모두 성공적으로 완료되어야 애플리케이션 컨테이너가 시작됩니다.
- Init Container는 애플리케이션 컨테이너와 동일한 Pod 내에서 실행되며, 네트워크와 스토리지를 공유할 수 있습니다.
- 활용 예시:
- 데이터베이스 연결을 위한 초기 설정
- 외부 서비스에 대한 의존성 확인
- 애플리케이션 설정 파일 생성
- 매니페스트 예시:
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
spec:
initContainers:
- name: init-db
image: busybox:latest
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;'] # mydb 서비스가 시작될 때까지 대기
containers:
- name: my-app-container
image: my-app:latest
ports:
- containerPort: 80
env:
- name: DATABASE_URL
value: "jdbc:mysql://mydb:3306/mydb"위 예시에서 init-db Init Container는 mydb 서비스가 시작될 때까지 대기합니다. 애플리케이션 컨테이너는 init-db가 성공적으로 완료된 후에 시작되며, 데이터베이스 URL을 환경 변수를 통해 전달받습니다.
4.2 Liveness Probe#
- 정의: 애플리케이션 컨테이너가 정상적으로 실행 중인지 주기적으로 확인하는 프로브입니다. Liveness Probe가 실패하면 쿠버네티스는 컨테이너를 재시작합니다.
- 목적: 애플리케이션이 데드락 상태에 빠지거나 응답하지 않는 경우, 컨테이너를 재시작하여 문제를 해결합니다.
- 타입:
- httpGet: HTTP GET 요청을 보내서 응답 코드를 확인합니다.
- tcpSocket: 특정 포트에 TCP 연결을 시도합니다.
- exec: 컨테이너 내에서 명령어를 실행하고 종료 코드를 확인합니다.
- 매니페스트 예시:
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
spec:
containers:
- name: my-app-container
image: my-app:latest
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /healthz # 헬스 체크 엔드포인트
port: 8080
initialDelaySeconds: 30 # 컨테이너 시작 후 30초 후에 첫 번째 프로브 실행
periodSeconds: 10 # 10초마다 프로브 실행
timeoutSeconds: 5 # 5초 이내에 응답해야 성공으로 간주
failureThreshold: 3 # 3번 연속 실패하면 컨테이너 재시작위 예시에서 Liveness Probe는 /healthz 엔드포인트에 HTTP GET 요청을 보내서 응답 코드를 확인합니다. 응답 코드가 200-399 범위에 속하면 컨테이너는 정상적으로 실행 중인 것으로 간주됩니다.
4.3 Readiness Probe#
- 정의: 애플리케이션 컨테이너가 트래픽을 처리할 준비가 되었는지 주기적으로 확인하는 프로브입니다. Readiness Probe가 실패하면 쿠버네티스는 해당 컨테이너로 트래픽을 라우팅하지 않습니다.
- 목적: 애플리케이션이 시작 중이거나 일시적으로 과부하 상태인 경우, 트래픽을 받지 않도록 하여 안정적인 서비스 제공을 보장합니다.
- 타입: Liveness Probe와 동일 (httpGet, tcpSocket, exec)
- 매니페스트 예시:
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
spec:
containers:
- name: my-app-container
image: my-app:latest
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /readyz # 준비 상태 확인 엔드포인트
port: 8080
initialDelaySeconds: 60 # 컨테이너 시작 후 60초 후에 첫 번째 프로브 실행
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1 # 1번 성공하면 준비 상태로 간주위 예시에서 Readiness Probe는 /readyz 엔드포인트에 HTTP GET 요청을 보내서 응답 코드를 확인합니다. 컨테이너가 준비 상태가 되기까지 충분한 시간을 주기 위해 initialDelaySeconds를 60초로 설정했습니다.
- Liveness Probe와 Readiness Probe의 차이점:
- Liveness Probe는 컨테이너의 생존 여부를 확인하고, 실패하면 컨테이너를 재시작합니다.
- Readiness Probe는 컨테이너가 트래픽을 처리할 준비가 되었는지 확인하고, 실패하면 컨테이너로 트래픽을 라우팅하지 않습니다.
4.4 Resource Quota#
- 정의: 네임스페이스에 할당할 수 있는 총 자원량 (CPU, 메모리, Pod 수 등)을 제한하는 리소스입니다.
- 목적: 특정 네임스페이스가 클러스터의 자원을 과도하게 사용하는 것을 방지하고, 자원 부족으로 인한 다른 애플리케이션의 장애를 예방합니다.
- 매니페스트 예시:
apiVersion: v1
kind: ResourceQuota
metadata:
name: my-namespace-quota
namespace: my-namespace # 적용할 네임스페이스
spec:
hard:
pods: "10" # 최대 Pod 수: 10개
requests.cpu: "2" # CPU 요청량 합계: 2 코어
requests.memory: "4Gi" # 메모리 요청량 합계: 4Gi
limits.cpu: "4" # CPU 제한량 합계: 4 코어
limits.memory: "8Gi" # 메모리 제한량 합계: 8Gi위 예시에서 my-namespace 네임스페이스는 최대 10개의 Pod를 생성할 수 있으며, Pod들의 CPU 요청량 합계는 2코어, 메모리 요청량 합계는 4Gi를 넘을 수 없습니다. 또한, CPU 제한량 합계는 4코어, 메모리 제한량 합계는 8Gi를 넘을 수 없습니다.
4.5 LimitRange#
- 정의: 네임스페이스 내의 각 컨테이너 또는 Pod에 대한 자원 요청 및 제한의 기본값과 최대/최소값을 설정하는 리소스입니다.
- 목적: 네임스페이스 내의 모든 컨테이너 또는 Pod가 적절한 자원을 요청하고 제한하도록 강제하고, 자원 요청/제한을 설정하지 않은 컨테이너 또는 Pod에 대한 기본값을 제공합니다.
- 매니페스트 예시:
apiVersion: v1
kind: LimitRange
metadata:
name: my-namespace-limitrange
namespace: my-namespace # 적용할 네임스페이스
spec:
limits:
- type: Container # 컨테이너에 대한 제한
default:
cpu: "500m" # 기본 CPU 요청량: 0.5 코어
memory: "512Mi" # 기본 메모리 요청량: 512Mi
defaultRequest:
cpu: "250m" # 기본 CPU 제한량: 0.25 코어
memory: "256Mi" # 기본 메모리 제한량: 256Mi
max:
cpu: "1" # 최대 CPU 요청량: 1 코어
memory: "1Gi" # 최대 메모리 요청량: 1Gi
min:
cpu: "100m" # 최소 CPU 요청량: 0.1 코어
memory: "100Mi" # 최소 메모리 요청량: 100Mi위 예시에서 my-namespace 네임스페이스 내의 모든 컨테이너는 기본적으로 CPU 0.25코어, 메모리 256Mi를 요청하고, CPU 0.5코어, 메모리 512Mi로 제한됩니다. 또한, 컨테이너는 최소 CPU 0.1코어, 메모리 100Mi 이상을 요청해야 하며, 최대 CPU 1코어, 메모리 1Gi를 초과할 수 없습니다.
- Init Container, Liveness Probe, Readiness Probe
- Resource Quota, LimitRange
5. 매니페스트 작성 실습: 간단한 웹 애플리케이션 배포#
이 섹션에서는 실제 웹 애플리케이션 배포 예제를 통해 매니페스트 작성 능력을 향상시키는 실습을 진행합니다. Nginx 웹 서버와 간단한 API 서버를 쿠버네티스에 배포하고, 로드 밸런서 설정을 통해 외부 접근을 허용하는 방법을 단계별로 설명합니다.
5.1 Nginx 웹 서버 배포#
Nginx는 널리 사용되는 웹 서버로, 간단한 HTML 파일을 호스팅하거나 리버스 프록시 역할을 수행할 수 있습니다.
1단계: Deployment 매니페스트 작성 (nginx-deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 2 # 2개의 Pod 인스턴스 유지
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:latest # 최신 Nginx 이미지 사용
ports:
- containerPort: 80 # 80번 포트 노출
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 30
periodSeconds: 10- replicas: Nginx Pod 인스턴스 수를 2개로 설정합니다.
- resources: 각 Nginx Pod에 대한 자원 요청 및 제한을 설정합니다.
- livenessProbe, readinessProbe: Nginx 서버의 상태를 확인하는 프로브를 설정합니다.
2단계: Service 매니페스트 작성 (nginx-service.yaml)
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx # Nginx Pod를 선택
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer # LoadBalancer 타입으로 외부 접근 허용- selector: app: nginx 레이블을 가진 Pod를 선택합니다.
- type: LoadBalancer: 클라우드 환경에서 로드 밸런서를 프로비저닝하여 외부에서 Nginx 서비스에 접근할 수 있도록 합니다. (minikube에서는 minikube service nginx-service 명령어를 사용하여 접근 가능)
3단계: 매니페스트 배포
kubectl apply -f nginx-deployment.yaml
kubectl apply -f nginx-service.yaml4단계: 서비스 확인
kubectl get svc nginx-serviceEXTERNAL-IP 주소를 확인하여 웹 브라우저에서 해당 주소로 접속하면 Nginx 기본 페이지가 표시됩니다.
5.2 간단한 API 서버 배포#
간단한 API 서버를 Go 언어로 작성하고, 쿠버네티스에 배포하는 예제를 진행합니다.
1단계: API 서버 코드 작성 (main.go)
package main
import (
"fmt"
"net/http"
"os"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from my API server! Version: %s\n", os.Getenv("VERSION"))
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("API server listening on port 8080")
http.ListenAndServe(":8080", nil)
}- 이 코드는 / 경로로 HTTP 요청이 들어오면 "Hello from my API server!" 메시지를 응답으로 반환합니다.
- VERSION 환경 변수를 사용하여 API 서버의 버전을 표시합니다.
2단계: Dockerfile 작성 (Dockerfile)
FROM golang:1.18-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o main .
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/main .
ENV VERSION=1.0.0
EXPOSE 8080
CMD ["./main"]- 이 Dockerfile은 Go 코드를 빌드하고 실행 가능한 바이너리를 생성합니다.
- VERSION 환경 변수를 설정합니다.
3단계: Docker 이미지 빌드 및 푸시
docker build -t <your-dockerhub-username>/my-api-server:1.0.0 .
docker push <your-dockerhub-username>/my-api-server:1.0.04단계: Deployment 매니페스트 작성 (api-deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
labels:
app: api
spec:
replicas: 2 # 2개의 Pod 인스턴스 유지
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api-container
image: <your-dockerhub-username>/my-api-server:1.0.0 # Docker 이미지 사용
ports:
- containerPort: 8080 # 8080번 포트 노출
env:
- name: VERSION
value: "1.0.0" # API 서버 버전 설정
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 30
periodSeconds: 10- env: API 서버에 VERSION 환경 변수를 전달합니다.
5단계: Service 매니페스트 작성 (api-service.yaml)
apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
selector:
app: api # API Pod를 선택
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer # LoadBalancer 타입으로 외부 접근 허용- targetPort: API 서버가 실제로 리스닝하는 포트인 8080으로 설정합니다.
6단계: 매니페스트 배포
kubectl apply -f api-deployment.yaml
kubectl apply -f api-service.yaml7단계: 서비스 확인
kubectl get svc api-serviceEXTERNAL-IP 주소를 확인하여 웹 브라우저에서 해당 주소로 접속하면 "Hello from my API server!" 메시지가 표시됩니다.
2.5.3 Ingress를 통한 외부 접근 허용 (선택 사항)#
여러 서비스를 하나의 IP 주소로 노출하려면 Ingress를 사용할 수 있습니다.
1단계: Ingress Controller 설치
- minikube 사용자는 minikube addons enable ingress 명령어를 사용하여 Ingress Controller를 활성화할 수 있습니다.
- 클라우드 환경에서는 해당 클라우드에서 제공하는 Ingress Controller를 설치해야 합니다.
2단계: Ingress 매니페스트 작성 (ingress.yaml)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: / # Nginx Ingress Controller 어노테이션
spec:
rules:
- host: myapp.example.com # 호스트 이름 (DNS 설정 필요)
http:
paths:
- path: /nginx
pathType: Prefix
backend:
service:
name: nginx-service # Nginx Service 연결
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: api-service # API Service 연결
port:
number: 80- host: Ingress에 접근하기 위한 호스트 이름을 지정합니다. (DNS 설정을 통해 Ingress Controller의 IP 주소로 연결해야 함)
- paths: 경로에 따라 다른 Service로 트래픽을 라우팅하는 규칙을 정의합니다.
- /nginx 경로로 접속하면 nginx-service로 라우팅됩니다.
- /api 경로로 접속하면 api-service로 라우팅됩니다.
3단계: Ingress 배포
kubectl apply -f ingress.yaml4단계: Ingress 정보 확인
kubectl get ingress my-ingressIngress Controller의 IP 주소를 확인하고, DNS 설정을 통해 myapp.example.com을 해당 IP 주소로 연결합니다. 이제 웹 브라우저에서 myapp.example.com/nginx 또는 myapp.example.com/api로 접속하여 Nginx 웹 서버 또는 API 서버에 접근할 수 있습니다.