kubernetes와 LVM의 결합

6685 단어
본고는 주로 k8s와 LVM을 결합하여 사용하는 장면을 소개한다. 원생 k8s에서 로컬 저장소에 hostPath와emptyDir 두 가지volme를 제공했다. hostPath는 직접 파일을 로컬 호스트에 저장하는데 존재하는 문제점은quota 관리를 할 수 없기 때문에 한 사용자가 있는 디스크를 가득 채울 수 있다.kubernetes 1.10local ephemeral storage에서 베타 버전이 되면 emptyDir에 대해 디스크 제한을 할 수 있습니다. 그러나 이곳의 디스크 할당량은 사용자가 epmtyDir에 쓴 데이터뿐만 아니라 writable layer와logs도 포함됩니다. 또한 emptyDir는pod가 삭제됨에 따라 삭제되고 라이프 사이클을 사용자 정의할 수 없습니다. 만약pod가 종료된 후에 이 데이터를 보존해야 한다면 emptyDir는 감당할 수 없습니다.k8s원생volume의 상기 특징이 복잡한 업무 수요를 충족시키지 못함을 감안하여 우리는 LVM에 따라 로컬 저장소의volume 방안을 제공할 수 있다.본고는 LVM과 k8s의 결합 실현 방식만 소개하고 구체적인 LVM과 k8s의 지식은 관련 자료를 직접 찾아보십시오.
Out-of-Tree Volume Plugins
k8s는 추가 저장소에 좋은 지원을 제공한다. Out-of-Tree Volume Plugins를 참고하면 두 가지 방식으로volume plugin: CSI와 Flex Volume을 실현한다. 첫 번째는 현재 정부에서 추천하는 방식이지만 두 번째 정부에서 계속 지원을 제공하고 사용하기가 더욱 간단하다. 정부에서 Felx Volme LVM 셸 버전의 간단한 실현을 제공했다. 정부에서는github 주소를 참고하면 사실은 하나의binary나 스크립트로 특정한 호출을 제공한다.LVM의 경우 기본 mount과 umount subcommand를 실현하여kubelet 리셋을 제공해야 한다.mount subcommand LVM 만들기 단계는 다음과 같습니다.
  • 호출lvcreate LVM 장치를 만듭니다. 크기는yaml 파일에서 지정한 디스크 크기이고kubelet에서plugin을 호출할 때 매개 변수로 전달됩니다
  • mkfs 포맷 LVM을 특정 파일 형식으로 호출
  • 마운트 지점 디렉터리를 만듭니다. 이 매개 변수는 보통plugin이 매개 변수로 전달합니다. 보통은 $KUBELETROOT_DIR/pods/$POD_UID/volumes/...디렉토리
  • 마운트 지점에 mount LVM 장치 연결
  • LVM VG의 사용 상황을 보고한다. 뒤에 Pod를 스케줄링할 때 이 정보를 사용해야 하기 때문이다
  • 상기한 것은kubelet이plugin mount subcommand를 리셋하는 작업입니다. umount subcommand는 사용자 정의 삭제 정책이 필요하기 때문에 우리는 LVM을 직접 삭제할 수 없지만 마운트 지점 디렉터리는 삭제해야 합니다. 그렇지 않으면 kubelet은 이volume가 정확하게 제거되지 않았다고 생각하고pod는terminating에 있어서 삭제할 수 없습니다. 이때 Tricky 방식이 바로 모브 마운트 지점 디렉터리입니다. 이렇게 하면 LVM이 마운트 지점에 정상적으로 마운트됩니다.pods도 정상적으로 삭제될 수 있으며, 후속 사용자 정의 삭제 정책은 모브 이후의 마운트 장치를 직접 조작합니다.주의해야 할 것은 프로그램이pod를 종료한 LVM의 데이터를 보려면 이동한 후의 마운트 지점 디렉터리에서 보아야 한다는 것이다.
    LVM GC policy
    앞에서 k8s에서 LVM 지원을 어떻게 하는지 알았고umount할 때 LVM을 삭제하지 않고 사용자 정의 삭제 정책을 지원한다. 사용자 정의 삭제 정책은 옆길 청소 프로그램을 써야 한다. 데몬이나cron의 형식으로 실행할 수 있고 디스크 용량이나pod 종료 시간을 바탕으로 GC를 진행할 수 있다.비교적 좋은 방법은 LVM을 직접 정리하지 않고 먼저 lvreduce LVM을 축소하여 사용하지 않은 공간을 VG에 돌려주는 것이다. 그러면 디스크 사용률을 높이고 사용자 데이터를 보존하며 적당한 시기에 삭제할 수 있다.이 축소란 LVM과 파일 시스템의 양면이며 사용자의 데이터를 손상시킬 수 없다는 것을 전제로 합니다.매번 GC 이후에 VG 정보를 신속하게 보고하여 향후 스케줄링에 정보를 제공해야 한다.
    schedule pod based on LVM
    위의 실현은 집단 중의 모든 node에 추가 자원(LVM) 지원을 제공했다. node급의 자원이기 때문에pod를 스케줄링할 때 적당한 node assign을 어떻게 선택하는지 고려해야 한다. 사용자 정의 스케줄링 정책이 필요하다. 위에서 언급한 모든 LVM에 대한 작업에서 VG 정보를 신속하게 보고해야 한다. 바로 스케줄링할 때 이런 정보를 사용해야 한다.공식적으로 node resource에 대한 스케줄링 지원은 Advertise Extended Resources for a Node를 참조하지만 이런 방식은pod의 생명주기가 끝나면 끝나는 자원, 예를 들어emptyDir,memroy,cpu 등에만 적합하다.또 하나의 확장은 device plugin이다. LVM을 디스크 device로 사용하는 것도 고려할 수 있다. 이런 방식도 적합하지 않다. 왜냐하면 device 장치에 대한 신청은 입도가 있기 때문이다. 예를 들어 디스크 장치를 신청한다고 할 수 있지만 1G를 신청하는 공간은 말할 수 없기 때문이다.상기 두 가지 성명noderesource를 통해 내장된 스케줄링 정책을 사용할 수 없습니다. 우리는 스스로 스케줄링 정책을 실현할 수 있을 뿐입니다. k8s는 스케줄링의 확장에 대한 지원도 좋습니다. scheduler extender가 httpwebhook을 제공하는 것을 정의할 수 있습니다.scheudler extender를 실현하려면 우선kube-scheduler의 스케줄링 과정을 알아야 한다. 세 단계로 나뉘는데 먼저 선별(predict) 또는 Filter라고 부른다. 스케줄링할 수 없는 node를 필터한다.그 다음은 정렬(priorities)으로 스케줄링 가능한 노드를 점수를 매기고 스케줄링에 가장 적합한 노드를 결정한다.마지막으로 바인딩 (bind) 입니다. 바인딩 동작은 이pod를 node에 분배합니다.scheduler-extender는 상기 단계의'보충 프로토콜'에 해당한다. 상기 단계에 자신의 스케줄링 논리를 보충할 수 있다. Pod를 만들 때 스케줄링이 필요할 때 먼저 내장된 스케줄링 정책을 실행한 다음에 내장된 스케줄러의 결과를 extender 서버에 전달하고 스케줄링 논리가 완성된 후에 결과를 kube-scheduler에게 되돌려 다음 과정을 진행한다.kube-scheduler의 스케줄링 절차를 제외하고 kube-scheduler는 스케줄링 성능을 향상시키기 위해'자원 점용'이라는 개념을 가지고 있다. kube-scheduler는 로컬에 모든node 정보를 저장하는데 이cache는api-server와 동기화되고pod의 정보는 직접cache에서 얻는다. pod가 모든node로 스케줄링된 후에cache에서 이node의 자원을'선점용'하고 자원이pod에 소모되었다고 가정한다.사실pod는 이 노드 위에 스케줄링을 했을 뿐 아직 시작하지 않았고 자원을 진정으로 차지하지 않았다. 스케줄링을 통해pod가 이 자원을 점용했다고 가정하면 뒤에 스케줄링된pod에 변화된 정보를 제공한다. 만약에 지정한timeout에서pod가 아직 시작되지 않았다면pod가 미리 점용한 자원을 이node에게 되돌려준다. 이런 비동기적인 최종 일치성 방안을 통해pod의 스케줄링 효율을 효과적으로 높일 수 있다.또한 고가용성 방안에서 여러 개의kube-scheduler를 설정해야 한다. 각kube-scheduler의cache가 동기화되지 않기 때문에 여러 개의kube-scheduler가 동시에 대외적으로 서비스를 제공할 수 없기 때문에 이 때 주 프로토콜을 선택하여 그 중 하나를 선택하여 처리 요청을 할 수 있다.kube-scheduler의 실현을 이해한 후에 kube-scheduler를 본떠서 우리의 scheduler extender를 실현하고 extender의 httpserver 위치는 kube-scheduler의 --policy-config-file 또는 --policy-configmap를 본떠서 설정합니다. 전자는 config file를 지정하고 후자는 configmap을 통해 설정합니다. 필자는 configmap으로 관리하는 것을 권장합니다. 왜냐하면 업데이트 설정은 여러 kube-scheduler에 대해 한 부분만 변경할 수 있기 때문입니다.또한 위의 3단계 중 한 단계를 개별적으로 정의할 수 있습니다.
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: lvm-scheduler-extender
      namespace: kube-system
    data:
     policy.cfg : |
      {
      "kind" : "Policy",
      "apiVersion" : "v1",
      "extenders" : [
        {
              "urlPrefix": "http://localhost:8384/lvm-scheduler",
              "apiVersion": "v1alpha1",
              "filterVerb": "predicates/lvm_resource",
              "bindVerb": "bind",
              "prioritizeVerb": "",
              "weight": 1,
              "enableHttps": false,
              "nodeCacheCapable": false
        }
          ],
      "hardPodAffinitySymmetricWeight" : 10
      }

    위의 설정configmap은 extender webhook의 위치를 지정하고 Filter와bind 두 동작을 정의합니다. 이 두 동작을 정의하는 이유는 우리가 어떤 node가 스케줄링할 수 있는지에만 관심을 가지기 때문입니다. LVM의 남은 공간은pod를 만드는 node를 필터할 수 없기 때문입니다. 그리고 마지막으로 scheduler extender의cache에서 이 node의 자원을 차지하기 위해 그 node로 스케줄링됩니다.주의해야 할 것은 시스템에 bind 작업을 수행하는 binder가 하나만 있을 수 있다는 것입니다. extender에 binder가 지정되어 있으면 kube-scheduler에 내장된 것은 효력이 발생하지 않습니다. k8s client-go를 통해 귀속해야 합니다.
    clientset.CoreV1().Pods(podNamespace).Bind(&v1.Binding{
            ObjectMeta: metav1.ObjectMeta{Namespace: podNamespace, Name: podName, UID: podUID},
            Target: v1.ObjectReference{
                Kind: "Node",
                Name: nodeName,
            }})

    앞에서 plugin과 GC를 소개할 때 매번 LVM을 조작할 때마다 LVM 정보를 신속하게 보고하여 extender에게 스케줄링 결정을 제공해야 한다. 그러면 이 VG 정보를 어디에 보고해야 합니까?어떻게 보고합니까?이 문제는 서로 다른 장면에 따라 서로 다른 해결 방안이 있기 때문에 먼저 extender에 직접 보고할 수 없다. 고가용 집단에는 exteder 예시가 하나밖에 없기 때문에 분리된 통일된 백엔드 저장소를 제공해야 한다. 모든 extender는 백엔드 저장소에서 직접 가져와 Mysql나redis에 저장할 수 있다. Tricky를 비교하는 방식은 k8s의 etcd에 직접 저장하는 것이다.이러한 데이터 양은 비교적 적고 공간을 많이 차지하지 않으며 추가 조작 데이터베이스도 필요하지 않다. 그러나 etcd를 직접 조작하는 것은 부적절하다. CRD 자원을 정의할 수 있다. 모든 node에 crd 자원 실체를 만들어서 이 node가 사용할 수 있는 LVM 공간의 크기를 나타낸다. 그러면 다른 자원처럼 조회하기 편리하고 LVM 자원에 변화가 발생한 후에 이crd 대상에 대해 patch를 할 수 있다.extender에서watch 이crd 자원을 사용하면 됩니다.또한cache를 제공하여 자원의 사전 점용을 하고 메인 프로토콜의 실현을 제공해야 한다. 이상은kube-scheduler의 실현을 참고할 수 있다.
    summary
    참새는 작지만 오장은 모두 상기한다. LVM과 같은 node resource 지원을 실현함으로써 각각 storage plugin,scheduelr extender,CRD 등과 관련된다. 내용은 많지만 실제 인코딩은 많지 않다. 쿠베르넷이 유연한 확장을 제공해 주셔서 감사합니다. 관심 있는 사람은 스스로 실현할 수 있다.

    좋은 웹페이지 즐겨찾기