Gom3rye

Kubernetes- Job, CronJob, Service 본문

현대 오토에버 클라우드 스쿨

Kubernetes- Job, CronJob, Service

Gom3rye 2025. 7. 4. 17:56
728x90
반응형

apiVersion: v1pod

  • But, 파드를 직접 배포하는 경우는 거의 없다. (안 되는 것들이 많아서)

apiVersion: apps/v1 → 일반적으로 애플리케이션 배포를 하는 것:

  • replicaset
  • deployment
  • daemonset(로그 붙이는 것)
  • statefulset(상태 저장, 따라서 거의 대부분 volume을 연결시켜 놓는다. 데이터베이스, 파드를 중지시켰다 삭제했다 다시 만들어도 그 전 데이터를 가지고 올 수 있도록)

apiVersion: batch/v1 → Job

job vs cronjob

  • Job: 컨테이너를 사용하여 한 번만 실행되는 리소스
  • cronjob: 주기를 가지고 하는 것
    • batch: 모아서 하는 것, 백업할 때 batch 처리를 해야 한다. (실제 업무 처리에서는 자주 사용된다.)

Workload API

Job

  • 이전까지의 리소스들은 정상 수행되면 running으로 나오지만 job은 completed가 보이게 된다.
  • kubectl get pods로 조회 시 기본적으로는 Running 상태의 Pod만 보여주고 job은 일시적으로 "Completed" 상태로 남아 있다가, 일정 시간 후 Garbage Collection에 의해 삭제된다.

restartPolicy

  • spec.template.spec.restartPolicy에 OnFailure 또는 Never 중 하나를 지정해야 하는데 Never를 지정한 경우 파드에 장애가 발생하면 신규 파드가 생성되고 OnFailure를 설정하면 다시 동일한 파드를 사용해서 잡을 다시 시작한다.
# sample-job-neverrestart.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: errorjob-invalidcommand
spec:
  template:
    spec:
      containers:
        - name: errorjob-invalidcommand
          image: busybox
          commad: ["ls", "unvalid path"]
      restartPolicy: Never

# job 생성
kubectl apply -f sample-job-neverrestart.yaml
# pod가 계속해서 생성되는 것을 확인할 수 있다.

# sample-job-onfail.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: errorjob-invalidcommand
spec:
  template:
    spec:
      containers:
        - name: errorjob-invalidcommand
          image: busybox
          command: ["ls", "invalid path"]
      restartPolicy: OnFailure

# 리소스 생성
kubectl apply -f sample-job-onfail.yaml
# 이번에는 하나의 파드만 만들어진다.

태스크와 작업 큐 병렬 실행

  • 성공 횟수를 지정하는 completions와 병렬성을 지정하는 parallelism 설정들의 기본값은 1이기 때문에 명시적으로 지정하지 않아도 동작은 변하지 않는다.
  • 이 두 파라미터는 잡을 병렬화하여 실행할 때 사용하는 옵션이다.
  • backoffLimit은 실패를 허용하는 횟수

워크로드                                                                completions                             parallelism                        backoffLimit

1회만 실행하는 태스크 1 1 0
n개를 병렬로 실행하는 태스크 m n p
한 개씩 실행하는 작업 큐 미지정 1 p
n 개씩 병렬로 실행하는 작업 큐 미지정 n p
  • 한 개씩 실행하는 작업 큐

👉 작업 큐처럼 새 Job을 하나씩 순서대로 처리하는 워커 패턴

👉 Kafka consumer, queue worker 등에서 사용.

  • n 개씩 병렬로 실행하는 작업 큐

👉 동시에 여러 작업을 처리하는 병렬 워커 패턴

👉 예: batch job, 이미지 처리 큐 등을 병렬로 처리할 때.

  • 파라미터들 중에서 성공 횟수(completions)는 나중에 변경할 수 없으며 parallelism과 backoffLimit은 도중에 변경할 수 있는데 매니페스트를 수정하고 kubectl apply 명령어를 실행한다.
  • 일정 시간 후 잡 삭제
    • 잡 리소스는 한 번만 실행하는 태스크로 생성하는 경우가 많은데 잡은 종류 후에 삭제되지 않고 계속 남는다.
    • spec.ttlSecondsAfterFinished를 설정해서 잡이 종료한 후에 일정 시간(단위는 초)이 지난 후 삭제하도록 설정
  • 실습
# sample-job-ttl.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  ttlSecondsAfterFinished: 30
  template:
    spec:
      containers:
        - name: pi
          image: perl:5.34.0
          commad: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

# job 생성
kubectl apply -f sample-job-ttl.yaml

CronJob

다른 매니페스트와 다르게 schedule, command 부분이 추가된다.

  • 생성 및 실행
    • busybox 이미지를 pod로 생성하고 생성된 pod는 1분 단위로 date; echo Hello from the Kubernetes Cluster라는 문장을 출력하는 크론잡
    # sample-cronjob.yaml
    apiVersion: batch/v1
    kind: CronJob
    metadata:
      name: hello
    spec:
      schedule: "*/1 * * * *"
      jobTemplate:
        spec:
          template:
            spec:
              containers:
              - name: hello
                image: busybox
                imagePullPolicy: IfNotPresent
                command:
                - /bin/sh
                - -c
                - date; echo Hello from the Kubernetes Cluster
              restartPolicy: OnFailure
    
    # job 생성
    kubectl apply -f sample-cronjob.yaml
    # cronjob 확인: kubectl get cronjobs
    # 스케쥴 시간이 되지 않으면 화면이 보이지 않을 수 있다.(만들어지지 않을 수 있다.)
    # 파드 확인
    kubectl get pods
    # 스케쥴마다 파드를 생성
    
    # 일시 정지 suspend를 true로 바꾸기
    kubectl get cronjobs
    kubectl patch cronjob hello -p '{"spec":{"suspend":true}}' # hello: job 이름
    
    # 크론 잡을 잡으로 생성
    kubectl create job sample-job-from-cronjob --from cronjob/hello
    
  • 동시 실행 제어
    • 크론잡에서는 잡을 생성하는 특성 상 동시 실행에 대한 정책을 설정할 수 있다.
    • 잡 실행이 의도한 시간 간격 안에서 정상 종료할 때는 명시적으로 지정하지 않아도 되지만 스케쥴 시간 안에 종료되지 않은 경우 어떻게 할 것인지 설정할 수 있다.
    • spec.concurrencyPolicy에 설정
    • 정책
      • Allow(기본값): 동시 실행에 대한 제한을 하지 않는다.
      • Forbid: 이전 잡이 종료되지 않았을 경우 다음 잡은 실행하지 않는다.
      • Replace: 이전 잡을 취소하고 잡을 시작한다.
  • 실행 시작 기한 제어
    • 지연 시간을 설정할 수 있는데 spec.startingDeadlineSeconds를 이용해서 설정한다.
    • 이를 설정하지 않으면 시작 시간이 아무리 느려도 잡을 생성하게 된다.
# sample-cronjob2.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello2
spec:
  schedule: "*/1 * * * *"
  concurrencyPolicy: Allow
  startingDeadlineSeconds: 30 # 30초동안 실행이 안되면 안되는 것
  successfulJobsHistoryLimit: 5  # 성공한 Job의 기록을 최대 5개까지만 저장, 오래된 성공 Job들은 자동으로 삭제
  failedJobsHistoryLimit: 5
  suspend: false # CronJob을 정상적으로 실행하겠다. false로 바꾸면 스케줄이 중지되어, 시간이 돼도 실행되지 않는다.
  jobTemplate:
    spec:
      completions: 1
      parallelism: 1
      backoffLimit: 1
      template:
        spec:
		      containers:
	        - name: busybox-container
	          image: busybox
	          imagePullPolicy: IfNotPresent
	          command:
	          - /bin/sh
	          - -c
	          - date; echo Hello from the Kubernetes Cluster
		      restartPolicy: OnFailure 

# job 생성
kubectl apply -f sample-cronjob2.yaml
# cronjob 확인
kubectl get cronjobs
# 스케쥴 시간이 되지 않으면 화면이 보이지 않을 수 있다.(만들어지지 않을 수 있다.)
# job 확인
kubectl get jobs # 스케쥴링 된 시간이 안되면 job은 생성되지 않는다.
# 파드 확인
kubectl get pods # 스케쥴마다 파드를 생성

# 일시 정지
kubectl get cronjobs
kubectl patch cronjob hello -p '{"spec":{"suspend":true}}' # hello: cronjob 이름

# 크론 잡을 잡으로 생성
kubectl create job sample-job-from-cronjob --from cronjob/hello
  • 크론잡 이력
    • spec.successfulJobsHistoryLimit: 성공한 잡을 저장하는 개수
    • spec.failedJobsHistoryLimit: 실패한 잡을 저장하는 개수
    • 잡이 남아있다는 것은 파드도 Completed 또는 Error 상태로 남아있고 kubectl logs 명령으로 로그를 확인할 수 있다는 의미이다.
    • 실제 운영 환경에서는 컨테이너의 로그를 외부 로그 시스템을 이용해서 운영하는 것을 추천한다.
    • 로그를 별도의 로그 시스템으로 구현하면 kubectl 명령을 사용해서 로그를 확인할 필요가 없고 가용성이 높은 환경에 로그를 저장할 수 있다.
# 실패도 발생하는 잡을 저장
# sample-cronjob3.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: sample-cronjob
spec:
  schedule: "*/1 * * * *"
  concurrencyPolicy: Allow
  startingDeadlineSeconds: 30 # 30초동안 실행이 안되면 안되는 것
  successfulJobsHistoryLimit: 10  # 성공한 Job의 기록을 최대 5개까지만 저장, 오래된 성공 Job들은 자동으로 삭제
  failedJobsHistoryLimit: 5
  suspend: false # CronJob을 정상적으로 실행하겠다. false로 바꾸면 스케줄이 중지되어, 시간이 돼도 실행되지 않는다.
  jobTemplate:
    spec:
      completions: 1
      parallelism: 1
      backoffLimit: 1
      template:
        spec:
		      containers:
	        - name: tools-container
	          image: amsy810/random-exit:v2.0
		      restartPolicy: Never

# job 생성
kubectl apply -f sample-cronjob3.yaml
# 로그 확인
kubectl logs 크론잡이름
  • 스케쥴의 의미
              • : 분 시 일 월 요일
    • 분: 0~59, 시: 0~23, 일: 1~31, 월: 1~12, 요일: 0~6 (0이 일요일)
    • 각 항목은 공백 문자로 구분한다.
    • 항목의 값이 *이면 해당 항목의 모든 값이다.
      • 연산자를 이용하면 값의 범위를 지정할 수 있다. 요일에 1-5를 사용하면 월요일부터 금요일까지
    • , 연산자를 사용하면 값 목록을 정의하는 것이 가능하고 시간에 1,3,5를 지정하면 1시 3시 5시가 된다.
    • /를 이용하면 단계값 지정이 가능하고 분에 */1은 1분마다, 1-10/2가 있으면 1,3,5,7,9가 된다.
    • 하루에 특정 시간에 한 번 동작: 41 14 * * *
  • cronjob을 매니페스트를 이용하지 않고 생성
kubectl create cronjob sample-cronjob-by-cli --image=busybox --schedule="*/1 * * * *" -- date

Service API

  • Service API 카테고리로 분류된 리소스는 클러스터상의 컨테이너에 대한 EndPoint를 제공하거나 레이블과 일치하는 컨테이너의 디스커버리에 사용되는 리소스
  • 내부적으로 사용되는 리소스를 제외하고 사용자가 직접 사용하는 것은 L4 로드밸런싱을 제공하는 서비스 리소스와 L7 로드밸런싱을 제공하는 인그레스 리소스가 있다.
  • 서비스 리소스에는 제공 목적에 따라 클러스터 내부나 클러스터 외부에서 접속할 수 있는 접속 창구가 되는 가상 IP 등의 EndPoint를 제공한다.
  • 종류
    • Service
      • Cluster IP
      • ExternalIP (ClusterIP의 한 종류)
      • NodePort
      • LoadBalancer
      • Headless(None)
      • ExternalName
      • NoneSelector
    • Ingress

쿠버네티스 클러스터 네트워크와 서비스

파드 간 통신

  • 같은 파드 내에 있는 컨테이너는 동일한 IP 주소를 할당 받게 되므로 같은 파드의 컨테이너끼리 통신을 할 때는 localhost로 통신하고 다른 파드에 있는 컨테이너와 통신하려면 파드의 IP 주소로 통신이 가능하다.

도커와 쿠버네티스의 차이

  • 도커는 container 단위로 ip할당
  • 쿠버네티스는 pod 단위로 ip할당
    • pod 안의 container 들은 이름으로 구분하거나 port로 구분한다. (ip로 구분 안됨)

쿠버네티스 클러스터의 내부 네트워크

  • 쿠버네티스 클러스터는 클러스터를 생성하면 노드 상의 파드를 위한 내부 네트워크가 자동으로 구성된다.
  • 내부 네트워크 구성은 사용할 CNI 라는 플러그형 모듈 구현에 따라 다르지만 노드 별로 다른 네트워크 세그먼트를 구성하고 노드 간의 트래픽은 VXLAN이나 L2 Routing의 기술을 사용하여 전송해서 노드 간 통신이 가능하게 구성한다.
  • 노드 별 네트워크 세그먼트는 쿠버네티스 클러스터 전체에 할당된 네트워크 세그먼트를 자동으로 분할해서 할당하므로 사용자가 설정하지 않아도 된다.
  • 파드 간 통신은 서비스를 사용하지 않고도 가능하지만 서비스를 사용하는 이유
    • 파드에 대한 트래픽 로드밸런싱
    • 서비스 디스커버리와 클러스터 내부 DNS
  • 파드에 대한 로드밸런싱
    • 디플로이먼트를 사용하여 여러 파드를 기동할 수 있는데 파드는 기동될 때마다 각각 다른 IP 주소를 할당받기 때문에 로드밸런싱하는 구조를 자체적으로 구현하려면 각 파드의 IP주소를 매번 조회하거나 전송 대상의 목적지를 다시 설정해야 한다.
  • 서비스를 사용하면 여러 파드에 대한 로드 밸런싱을 자동으로 구성할 수 있으며 서비스는 로드밸런싱의 창구가 되는 EndPoint를 제공한다.
  • EndPoint는 외부 LoadBalancer가 할당하는 가상 IP 주소나 클러스터 내부에서만 사용 가능한 가상 IP 주소 등 여러 가지 종류를 제공한다.
  • pod의 ip는 동적으로 할당된다.
  • IP는 기억하기가 어렵기 때문에 Service Discovery를 이용해서 통신 하는 게 더 좋다.

클러스터 IP를 이용해서 파드 간 통신

  • ClusterIP는 클러스터 내부에서만 사용 가능한 가상 IP를 가진 EndPoint를 제공하는 LoadBalancer를 구성한다.
  • 서비스는 spec.selector에 정의할 셀렉터 조건에 따라 트래픽을 전송한다.
# sample-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:  # ← selector와 같은 레벨
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: amsy810/echo-nginx:v2.0
 
# pod 확인
kubectl get pods -o wide
# pod의 IP 확인
kubectl get pods -l app=sample-app -o custom-columns="NAME:{metadata.name}, IP:{status.podIP}"
# NAME                                  IP
sample-deployment-5d9fcfcbb6-k4lvs   10.244.1.55
sample-deployment-5d9fcfcbb6-pl8gg   10.244.2.54
sample-deployment-5d9fcfcbb6-w9txk   10.244.2.53

# 파드에 ClusterIP를 할당하는 서비스를 작성 ClusterIP 만들기
# sample-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-clusterip
spec:
  type: ClusterIP
  ports:
  - name: "http-port"
    protocol: "TCP"
    port: 8080 # 서비스가 노출할 포트 (가상 IP) 이게 외부에서 접속할 포트
    targetPort: 80 # 실제 파드의 컨테이너 포트
  selector:
    app: sample-app # 이 레이블을 가진 파드들과 연결됨
    
# 클러스터ip 서비스 확인
kubectl get service
kubectl describe service sample-clusterip # 더 자세히 보기
# 여기에 아까 pod들의 endpoint가 있다. -> 자기가 가야할 곳을 알고 있다.
# Endpoints:   10.244.1.55:80, 10.244.2.53:80, 10.244.2.54:80
# EndPoints에 아무런 IP도 보이지 않는다면 selector 부분 오류일 가능성이 높다.

# curl 명령을 서로 보내보기 -> cluster ip를 알아야 한다.
# cluster로 요청을 전송하는 pod를 생성
kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- curl -s <http://10.103.98.14:8080> # (clusterip:port)
# 자기가 알아서 분산해서 보내준다.
  • 하나의 서비스에서 여러 포트 할당
    • 하나의 서비스에 여러 포트를 할당할 수 있다.
    • http와 https에서 ClusterIP가 다르면 불편한 경우가 많다 → 이 경우 하나의 서비스에 여러 개의 포트를 가질 수 있도록 하자.
    • ClusterIP의 8080/TCP 포트로의 요청은 파드 80/TCP 포트로 로드밸런싱하고 ClusterIP의 8443/TCP 포트로의 요청은 파드 443/TCP 포트로 로드밸런싱
      • 현재는 Deployment의 443번 포트가 Listen 상태가 아니므로 https-port로는 통신할 수 없다.
# sample-clusterip-multi.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-clusterip-multi
spec:
  type: ClusterIP
  ports:
  - name: "http-port"
    protocol: "TCP"
    port: 8080 # 서비스가 노출할 포트 (가상 IP) 이게 외부에서 접속할 포트
    targetPort: 80 # 실제 파드의 컨테이너 포트
  - name: "http-port"
    protocol: "TCP"
    port: 8443
    targetPort: 443
  selector:
    app: sample-app # 이 레이블을 가진 파드들과 연결됨
    
# 포트 이름을 통해서 통신이 가능하다.
# 파드를 여러 개 생성해서 통신해보기 sample-named-port-pods.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: sample-named-port-pod-80
  labels:
    app: sample-app
spec:
  containers:
  - name: nginx-container
    image: amsy810/echo-nginx:v2.0
    ports:
    - name: http
      containerPort: 80 
--- # 2번째꺼
apiVersion: v1
kind: Pod
metadata:
  name: sample-named-port-pod-81
  labels:
    app: sample-app
spec:
  containers:
  - name: nginx-container
    image: amsy810/echo-nginx:v2.0
    env:
    - name: NGINX_PORT
      value: "81"
    ports:
    - name: http
      containerPort: 81
      
# port이름을 이용하는 Service 리소스 파일- sample-named-port-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-named-port-service
  type: ClusterIP
  ports:
  - name: "http-port"
    protocol: "TCP"
    port: 8080
    targetPort: http # 포트 이름을 이용해서 참조
  selector:
    app: sample-app 
    
# 리소스를 생성
kubectl apply -f sample-named-port-pods.yaml
# 2개 running 중인거 확인하고 서비스 만들기
kubectl apply -f sample-named-port-service.yaml
# 서비스를 확인
kubectl get service
kubectl describe service sample-named-port-service
# http를 이름으로 갖고 있는 파드 2개를 다 잘 연결했다.

 

pod는 replicas 지정 못함

🧠 쿠버네티스 Service API 쉽게 이해하기

쿠버네티스를 하나의 회사라고 생각해보세요.

이 회사에는 여러 **팀(파드)**이 있고, 각 팀에는 여러 **직원(컨테이너)**이 있습니다.


📦 파드(Pod)와 통신

  • *같은 팀(같은 파드 안)**에 있는 직원들끼리는 자리도 가깝고(같은 IP), 서로 **대화(통신)**하기 쉬워요 → localhost
  • *다른 팀(다른 파드)**에 있는 직원과 이야기하려면 **그 팀 사무실(파드 IP 주소)**을 알아야 해요.
  • 하지만 이 사무실 주소(파드 IP)는 매번 바뀌는 임시 건물이에요! 😨

❓그래서, 왜 서비스(Service)가 필요할까?

  • 매번 바뀌는 주소 대신, 대표 전화번호 하나 있으면 편하겠죠?
  • 이게 바로 Service예요!→ 그 번호가 알아서 각 파드로 연결해줘요. (= 로드밸런싱)
  • → 파드들에게 전화 걸고 싶은 사람이 있으면, 서비스 번호로 걸면 되고

🛠 서비스(Service)의 종류

서비스 타입 설명

ClusterIP 클러스터 내부에서만 접속 가능. 내부 직원 전용 내선 번호 같은 것
NodePort 클러스터 외부에서 접속 가능. 회사 출입문에 있는 인터폰 번호 같은 느낌
LoadBalancer 클라우드에서 제공하는 공용 IP로 연결되는 외부 전화번호
ExternalName DNS 이름으로 외부 서비스를 연결 (예: api.google.com)
Headless 직접 파드 주소로 연결 (로드밸런싱 없이 직접 통신 필요할 때)

🕵️‍♂️ 서비스 디스커버리란?

"이 서비스의 최신 주소가 뭐야?"

이걸 자동으로 찾아주는 게 서비스 디스커버리입니다.

→ 쿠버네티스는 DNS를 통해 "my-service"라는 이름만 알면,

자동으로 올바른 IP를 찾아줘요.


🌐 쿠버네티스 네트워크 구조 (간단 요약)

  • 도커: 컨테이너 단위로 IP 할당
  • 쿠버네티스: 파드 단위로 IP 할당
  • (한 파드 안의 컨테이너는 같은 IP 사용)
  • 파드들끼리는 직접 통신 가능하지만,
  • 더 효율적이고 안정적인 방법은 → Service를 사용하는 것

📌 정리

상황 해결 방법

파드끼리 통신해야 해요 파드 IP 직접 쓰거나 Service 사용
여러 파드로 요청을 분산하고 싶어요 Service가 로드밸런싱 해줌
외부에서 내 앱에 접속하고 싶어요 NodePort, LoadBalancer, Ingress 사용
파드 IP가 바뀌어서 못 찾겠어요 Service + DNS가 해결해줌
728x90
반응형