다중 스케줄러 설정

쿠버네티스는 여기에서 설명한 스케줄러를 기본 스케줄러로 사용한다. 만일 기본 스케줄러가 사용자의 필요를 만족시키지 못한다면 직접 스케줄러를 구현하여 사용할 수 있다. 이에 더해, 기본 스케줄러와 함께 여러 스케줄러를 동시에 사용하여 쿠버네티스가 각 파드에 대해 어떤 스케줄러를 적용할지에 대한 설정도 할 수 있다. 예제와 함께 쿠버네티스에서 다중 스케줄러를 사용하는 방법에 대해 배워보도록 하자.

스케줄러를 구현하는 방법에 대한 자세한 설명은 해당 문서에서 다루지 않는다. kube-scheduler 구현을 다루는 공식 예시는 쿠버네티스 소스 디렉토리에 있는 pkg/scheduler 를 참고한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

스케줄러 패키징

스케줄러 바이너리를 컨테이너 이미지로 패키징한다. 해당 예제를 통해 기본 스케줄러 (kube-scheduler)를 두 번째 스케줄러로 사용할 수 있다. GitHub 쿠버네티스 소스코드를 클론하고 소스를 빌드하자.

git clone https://github.com/kubernetes/kubernetes.git
cd kubernetes
make

kube-scheduler 바이너리를 담은 컨테이너 이미지를 생성하자. 이미지를 빌드 하기 위한 Dockerfile은 다음과 같다.

FROM busybox
ADD ./_output/local/bin/linux/amd64/kube-scheduler /usr/local/bin/kube-scheduler

파일을 Dockerfile로 저장하고 이미지를 빌드한 후 레지스트리로 푸시하자. 해당 예제에서는 이미지를 Google Container Registry (GCR)로 푸시하고 있다. 이에 대한 자세한 내용은 GCR 문서를 참고하자.

docker build -t gcr.io/my-gcp-project/my-kube-scheduler:1.0 .
gcloud docker -- push gcr.io/my-gcp-project/my-kube-scheduler:1.0

스케줄러에서 사용할 쿠버네티스 디플로이먼트 정의하기

이제 스케줄러 컨테이너 이미지가 있으니, 해당 이미지를 포함하는 파드 구성을 생성하고 쿠버네티스 클러스터 내에서 실행해보자. 해당 예제에서는, 클러스터 내에 직접 파드를 생성하는 대신에 디플로이먼트를 사용해도 된다. 디플로이먼트레플리카 셋을 관리하며, 이는 또 파드를 관리하기 때문에 스케줄러에 대한 회복 탄력성을 제공한다. 다음은 디플로이먼트에 대한 구성 파일이다. 이 파일을 my-scheduler.yaml으로 저장한다.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-scheduler
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: my-scheduler-as-kube-scheduler
subjects:
- kind: ServiceAccount
  name: my-scheduler
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: system:kube-scheduler
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: my-scheduler-as-volume-scheduler
subjects:
- kind: ServiceAccount
  name: my-scheduler
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: system:volume-scheduler
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-scheduler-config
  namespace: kube-system
data:
  my-scheduler-config.yaml: |
    apiVersion: kubescheduler.config.k8s.io/v1beta2
    kind: KubeSchedulerConfiguration
    profiles:
      - schedulerName: my-scheduler
    leaderElection:
      leaderElect: false    
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    component: scheduler
    tier: control-plane
  name: my-scheduler
  namespace: kube-system
spec:
  selector:
    matchLabels:
      component: scheduler
      tier: control-plane
  replicas: 1
  template:
    metadata:
      labels:
        component: scheduler
        tier: control-plane
        version: second
    spec:
      serviceAccountName: my-scheduler
      containers:
      - command:
        - /usr/local/bin/kube-scheduler
        - --config=/etc/kubernetes/my-scheduler/my-scheduler-config.yaml
        image: gcr.io/my-gcp-project/my-kube-scheduler:1.0
        livenessProbe:
          httpGet:
            path: /healthz
            port: 10259
            scheme: HTTPS
          initialDelaySeconds: 15
        name: kube-second-scheduler
        readinessProbe:
          httpGet:
            path: /healthz
            port: 10259
            scheme: HTTPS
        resources:
          requests:
            cpu: '0.1'
        securityContext:
          privileged: false
        volumeMounts:
          - name: config-volume
            mountPath: /etc/kubernetes/my-scheduler
      hostNetwork: false
      hostPID: false
      volumes:
        - name: config-volume
          configMap:
            name: my-scheduler-config

해당 매니페스트에서는 KubeSchedulerConfiguration을 사용하여 구현할 스케줄러의 특성을 정의한다. 이러한 설정은 초기화 과정에서 --config 옵션을 통해 kube-scheduler에게 전달된다. 해당 구성 파일은 my-scheduler-config 컨피그맵에 저장된다. my-scheduler 디플로이먼트의 파드에서는 my-scheduler-config 컨피그맵을 볼륨으로 마운트 시킨다.

앞서 언급한 스케줄러 구성에서는, 구현한 스케줄러가 KubeSchedulerProfile의 형식으로 나타나게 된다.

또한, kube-scheduler와 같은 권한을 부여받기 위해서는 전용 서비스 어카운트 my-scheduler를 생성하고 해당 서비스 어카운트를 클러스터롤 system:kube-scheduler와 바인딩해야 한다.

이외의 커맨드 라인 인자에 대한 자세한 설명은 kube-scheduler 문서에서 참고하고 이외의 사용자 정의 kube-scheduler 구성에 대한 자세한 설명은 스케줄러 구성 레퍼런스 에서 참고한다.

두 번째 스케줄러를 클러스터에서 실행하기

쿠버네티스 클러스터에서 스케줄러를 실행하기 위해서, 위의 구성 파일에서 명시한 디플로이먼트를 쿠버네티스 클러스터 내에 생성한다.

kubectl create -f my-scheduler.yaml

스케줄러 파드가 실행되고 있는지 확인한다.

kubectl get pods --namespace=kube-system
NAME                                           READY     STATUS    RESTARTS   AGE
....
my-scheduler-lnf4s-4744f                       1/1       Running   0          2m
...

기본 kube-scheduler 파드와 더불어, my-scheduler 파드가 실행("Running")되고 있다는 것을 목록에서 볼 수 있을 것이다.

리더 선출 활성화

리더 선출이 활성화된 상태로 다중 스케줄러를 실행하기 위해서는 다음과 같은 작업을 수행해야 한다.

my-scheduler-config 컨피그맵의 YAML 파일에서 KubeSchedulerConfiguration의 다음과 같은 필드들을 갱신한다.

  • leaderElection.leaderElecttrue
  • leaderElection.resourceNamespace<lock-object-namespace>
  • leaderElection.resourceName<lock-object-name> 으로

클러스터 내에 RBAC가 활성화되어 있는 상태라면, system:kube-scheduler 클러스터롤을 업데이트 해야 한다. 다음 예시와 같이, 구현한 스케줄러의 이름을 endpointsleases 리소스에 적용되는 룰의 resourceNames에 추가하자.

kubectl edit clusterrole system:kube-scheduler
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-scheduler
rules:
  - apiGroups:
      - coordination.k8s.io
    resources:
      - leases
    verbs:
      - create
  - apiGroups:
      - coordination.k8s.io
    resourceNames:
      - kube-scheduler
      - my-scheduler
    resources:
      - leases
    verbs:
      - get
      - update
  - apiGroups:
      - ""
    resourceNames:
      - kube-scheduler
      - my-scheduler
    resources:
      - endpoints
    verbs:
      - delete
      - get
      - patch
      - update

파드의 스케줄러를 지정하기

이제 두 번째 스케줄러가 실행되고 있으니, 파드를 몇 개 생성하여 기본 스케줄러 또는 새로 배치한 스케줄러에 의해 스케줄링이 되도록 설정해 보자. 특정 스케줄러를 이용하여 파드를 스케줄링하기 위해서는 해당 파드의 명세에 해당 스케줄러의 이름을 명시해야 한다. 세 가지 예시를 참고해 보자.

  • 스케줄러 이름을 명시하지 않은 파드 명세

    apiVersion: v1
      kind: Pod
      metadata:
        name: no-annotation
        labels:
          name: multischeduler-example
      spec:
        containers:
        - name: pod-with-no-annotation-container
          image: registry.k8s.io/pause:2.0

    스케줄러 이름을 제공받지 못했다면, 파드는 자동으로 기본 스케줄러에 의해 스케줄링이 수행된다.

    해당 파일을 pod1.yaml로 저장하고 쿠버네티스 클러스터에 제출해 보자.

    kubectl create -f pod1.yaml
    
  • default-scheduler를 명시한 파드 명세

    apiVersion: v1
      kind: Pod
      metadata:
        name: annotation-default-scheduler
        labels:
          name: multischeduler-example
      spec:
        schedulerName: default-scheduler
        containers:
        - name: pod-with-default-annotation-container
          image: registry.k8s.io/pause:2.0
      

    spec.schedulerName의 값으로 스케줄러 이름을 제공함으로써 스케줄러가 정해진다. 이와 같은 경우에서는, 기본 스케줄러의 이름인 default-scheduler를 명시하고 있다.

    해당 파일을 pod2.yaml로 저장하고 쿠버네티스 클러스터에 제출해 보자.

    kubectl create -f pod2.yaml
    
  • my-scheduler를 명시한 파드 명세

    apiVersion: v1
      kind: Pod
      metadata:
        name: annotation-second-scheduler
        labels:
          name: multischeduler-example
      spec:
        schedulerName: my-scheduler
        containers:
        - name: pod-with-second-annotation-container
          image: registry.k8s.io/pause:2.0
      

    이와 같은 경우에서는, 직접 배치한 스케줄러 - my-scheduler를 통해 해당 파드의 스케줄링이 수행되어야 한다는 것을 명시하고 있다. spec.schedulerName의 값은 KubeSchedulerProfile 매핑의 schedulerName 필드와 일치해야 한다.

    해당 파일을 pod3.yaml로 저장하고 쿠버네티스 클러스터에 제출해 보자.

    kubectl create -f pod3.yaml
    

    세 개의 파드가 모두 실행되고 있는지 확인해 보자.

    kubectl get pods
    

파드가 원하는 스케줄러에 의해 스케줄링 되었는지 확인해보기

이번 예제들을 수월하게 진행하기 위해, 파드가 실제로 원하는 스케줄러에 의해 스케줄링되고 있는지 확인해 보지 않았다. 해당 사항은 파드와 디플로이먼트 구성 파일의 제출 순서를 바꿔보면 확인해 볼 수 있다. 만일 스케줄러 디플로이먼트 구성 파일을 제출하기 전에 모든 파드의 구성 파일을 쿠버네티스 클러스터에 제출한다면, 다른 두 개의 파드는 스케줄링 되는 와중에 annotation-second-scheduler 파드는 무기한 "Pending" 상태에 머무르는 것을 관찰할 수 있다. 스케줄러 디플로이먼트 구성 파일을 제출하여 새로운 스케줄러가 실행되기 시작하면, annotation-second-scheduler 파드도 스케줄링 된다.

다른 방법으로는, 이벤트 로그에서 "Scheduled" 항목을 찾아 파드가 원하는 스케줄러에 의해 스케줄링 되었는지 확인해 볼 수 있다.

kubectl get events

또한, 관련된 컨트롤 플레인 노드들의 스태틱 파드 매니페스트를 수정하면 클러스터의 메인 스케줄러로 사용자 정의 스케줄러 구성 또는 사용자 정의 컨테이너 이미지를 사용할 수도 있다.

최종 수정 October 15, 2024 at 3:18 AM PST: Merge pull request #48346 from windsonsea/metricy (50a9341)