CRD를 통한 사용자 정의 컨트롤러의 예 소개

이 항목은 Z Lab Advent Calendar 2017 의 17일 항목입니다.
이 항목은 사용자 정의 리소스 정의(CRD)를 사용하여 사용자 정의 컨트롤러를 구현하는 예시를 설명합니다.실시 예로 당사 내에서 개발·이용하는 Etcd as a 서비스의 개요와 구조를 소개합니다.

Etcd as a Service


이 항목은 Etcd asa 서비스를 하나의 명령을 통해 etcd 집단을 구축하고 그 집단을 관리하는 서비스로 정의합니다.
당사가 개발하고 사용하는 Etcd as a 서비스는 다음과 같은 기능을 가지고 있습니다.
  • 클러스터 생성 및 제거
  • CRD 리소스를 생성하여 클러스터를 구축하고 삭제 작업을 수행하여 삭제
  • 스크롤 업데이트를 통한 업데이트
  • 한 대씩 구성원을 업데이트하여 다운타임 없이 클러스터 업데이트
  • 페일오버
  • 클러스터 구성원이 하드웨어 장애 등으로 인해 다운될 경우 해당 구성원을 삭제하고 새 구성원을 추가합니다
  • 범위 축소
  • 구성원 추가/삭제
  • 백업 서비스
  • 백업 정기적 확보 및 저장
  • 백업 데이터로부터 복구
  • 백업 서비스로 캡처한 백업 데이터 복구 클러스터
  • 구현 방법


    Etcd as a 서비스를 주로 처리하는 사용자 정의 자원과 사용자 정의 컨트롤러는 etcd와 Machine 두 가지로 나뉜다.
    etcd는 etcd 군집을 나타내는 자원입니다. etcd 자원을 만들면 etcd 컨트롤러가 etcd 군집을 만들고 관리합니다.
    Machine은 CloudProvider(예를 들어 OpenStack)가 제공하는 실례(예를 들어 VM)에 대한 추상적인 자원입니다. Machine 컨트롤러는 Machine 자원을 만든 후에 Machine 컨트롤러는 CloudProvider에서 실례를 만들고 관리합니다.

    개발한 사용자 정의 컨트롤러는 작년Kubernetes Meetup Tokyo#3에 소개된 Kubernetes as a 서비스 때의 실현 방법과 같이 다음과 같은 Kubernetes의 구조를 이용하여 실현한다.
  • CustomResourceDefinision(ThirdPartyResource)
  • InformerFramework
  • IndexedQueue
  • 각 구조는 위 슬라이드 에 기재되어 있으니 저쪽을 참조하십시오.
    etcd군집의 창설 처리 자체는 StatefullSets와 같은 일반적인 방식이 아니라 CoreOS의etcd-operator를 참고하여 컨트롤러 측면에서 etcd의memer의 추가 처리를 실시하는 등 전용 컨트롤러를 창설하여 군집을 만든다.Machine 컨트롤러는 Kubernetes as a Service를 직접 사용할 때 제작된 컨트롤러입니다.

    실행 예


    나는 문장만으로는 이해하기 어렵다고 생각해서 실제로 Etcd as a 서비스를 사용한다.
    먼저 클러스터를 생성합니다.클러스터를 만들려면 CRD 리소스 목록을 준비하십시오.이번 제작 선언은
    sample-etcd.yaml
    apiVersion: external.zlab.co.jp/v1alpha1
    kind: Etcd
    metadata:
      name: sample-etcd
    spec:
      version: 3.0.17
      replicas: 3
      backupPolicy:
        backupIntervalInSeconds: 3600
        maxBackups: 5
      machineFlavor: large
      machineImageName: container-linux-1465-8-0
      machinePoolName: development
    
    kubectl로 이 목록을 적용합니다.
    $ kubectl apply -f sample-etcd.yaml
    etcd "sample-etcd" created
    
    자원을 만들면 이 그룹에 배치된 사용자 정의 컨트롤러가 자원의 생성을 감지하고 etcd 그룹을 만들기 시작합니다.
    etcd 컨트롤러는 클러스터를 만들기 위해 인증서를 생성하거나 DNS를 등록한 다음 Machine 리소스를 생성합니다.
    클러스터를 생성하는 Machine 리소스의 상태를 확인합니다.
    $ kubectl get machine -o json | jq -c '.items[]|{"name": .metadata.name, "status": .status.phase, "ip": .status.machineState.hostIP }'
    {"name":"1-sample-etcd-default","status":"Creating","ip":"182.22.23.11"}
    
    이렇게 하면 etcd 컨트롤러는 Machine 자원을 군집을 만드는 처리로 사용합니다. 이것은 Machine를 만드는 것을 볼 수 있습니다.Phase가 런닝이면 Machine 제작이 완료됩니다.
    기계가 실행될 때, etcd 컨트롤러는 다음 기계를 만들 것입니다.
    $ kubectl get m -o json | jq -c '.items[]|{"name": .metadata.name, "status": .status.phase, "ip": .status.machineState.hostIP }'
    {"name":"1-sample-etcd-default","status":"Error","ip":"182.22.23.11"}
    {"name":"2-sample-etcd-default","status":"Creating","ip":"182.22.23.17"}
    
    두 번째 제작이 시작되었다.여기서 완성해야 할 1-sample-etcd-default의 Phase는 Error입니다. 이것은 etcd의 member를 추가했기 때문입니다. status의ready 구성원이 절반 이하를 초과하고 etcd의health는false를 되돌려줍니다.
    $ kubectl get m -o json | jq -c '.items[]|{"name": .metadata.name, "status": .status.phase, "ip": .status.machineState.hostIP }'
    {"name":"1-sample-etcd-default","status":"Running","ip":"182.22.23.11"}
    {"name":"2-sample-etcd-default","status":"Running","ip":"182.22.23.17"}
    {"name":"3-sample-etcd-default","status":"Creating","ip":"182.22.23.18"}
    
    위에서 말한 바와 같이 만약에 2-sample-etcd-default가 런닝이라면 1-sample-etcd-default도 런닝이다.
    replicas를 3으로 설정하기 때문에 3-sample-etcd-default가 실행되면 군집 생성이 완료되고 etcd 자원의 Phase가 실행됩니다.
    $ kubectl get e sample-etcd -o yaml
    apiVersion: external.zlab.co.jp/v1alpha1
    kind: Etcd
    metadata:
      ...
    spec:
      ...
    status:
      clientCertificate:
        secretName: sample-etcd-client-cert
      endpoints:
      - https://1.sample-etcd.default.etcd.sample.zlab.co.jp:2379
      - https://2.sample-etcd.default.etcd.sample.zlab.co.jp:2379
      - https://3.sample-etcd.default.etcd.sample.zlab.co.jp:2379
      lastBackupTime: 2017-12-06T16:26:12Z
      peerCertificate:
        secretName: sample-etcd-peer-cert
      phase: Running
      ready: true
      replicas: 3
      serverCertificate:
        secretName: sample-etcd-server-cert
      storedBackupObjects:
      - default/sample-etcd/2017-12-05T08:22:30Z
      - default/sample-etcd/2017-12-06T15:26:11Z
      - default/sample-etcd/2017-12-06T16:26:12Z
    
    그럼 집단이 생겼으니 방문해보세요.
    생성된 클러스터에 사용되는 각 인증서는 Secret 리소스로 저장되므로 클라이언트 인증서를 가져오고 연결합니다.
    $ kubectl get secret sample-etcd-client-cert -o json | jq -r '.data["certificate"]' | base64 -d > etcd-client.pem
    $ kubectl get secret sample-etcd-client-cert -o json | jq -r '.data["private-key"]' | base64 -d > etcd-client.key
    $ export ETCDCTL_API=3
    # status.endpoints にある値を endpoints として利用する
    $ export ETCD_ENDPOINTS=https://1.sample-etcd.default.etcd.sample.zlab.co.jp:2379,https://2.sample-etcd.default.etcd.sample.zlab.co.jp:2379,https://3.sample-etcd.default.etcd.sample.zlab.co.jp:2379
    $ etcdctl --cert etcd-client.pem --key etcd-client.key --endpoints $ETCD_ENDPOINTS endpoint  status
    https://1.sample-etcd.default.etcd.sample.zlab.co.jp:2379, 6f3636672576c0f2, 3.0.17, 33 kB, true, 19, 33351
    https://2.sample-etcd.default.etcd.sample.zlab.co.jp:2379, b9017e59944282a8, 3.0.17, 33 kB, false, 19, 33353
    https://3.sample-etcd.default.etcd.sample.zlab.co.jp:2379, ab5b60a8ad3eaa09, 3.0.17, 33 kB, false, 19, 33354
    
    값을 써보십시오.
    $ etcdctl --cert etcd-client.pem --key etcd-client.key --endpoints $ETCD_ENDPOINTS put /zlab "We are hiring!"
    OK
    $ etcdctl --cert etcd-client.pem --key etcd-client.key --endpoints $ETCD_ENDPOINTS get /zlab
    /zlab
    We are hiring!
    
    이렇게 하면 일반적으로 etcd로 사용할 수 있다.
    그리고 etcd 버전을 v3.2.11로 업그레이드해 보십시오.
    다음과 같이 목록을 수정합니다.
    sample-etcd.yaml
    spec:
    + version: 3.2.11
    - version: 3.0.17
      replicas: 3
    
    수정된 목록입니다.
    $ kubectl apply -f sample-etcd.yaml
    etcd "sample-etcd" configured
    
    응용 프로그램이 자원 변경을 감지하고 스크롤 업데이트를 시작합니다.
    스크롤 업데이트도 당연하지만 etcd는 정상적으로 사용할 수 있습니다.
    $ kubectl get m -o json | jq -c '.items[]|{"name": .metadata.name, "status": .status.phase, "ip": .status.machineState.hostIP }'
    {"name":"1-sample-etcd-default","status":"Creating","ip":"182.22.22.77"}
    {"name":"2-sample-etcd-default","status":"Running","ip":"182.22.22.44"}
    {"name":"3-sample-etcd-default","status":"Running","ip":"182.22.22.45"}
    $ etcdctl  --cert etcd-client.pem --key etcd-client.key --endpoints $ETCD_ENDPOINTS get /zlab
    /zlab
    We are hiring!
    
    업데이트가 완료되면 v3.2.11이 표시됩니다.
    $ etcdctl --cert etcd-client.pem --key etcd-client.key --endpoints  $ETCD_ENDPOINTS endpoint  status
    https://1.sample-etcd.default.etcd.sample.zlab.co.jp:2379, 6f3636672576c0f2, 3.2.11, 33 kB, true, 19, 33351
    https://2.sample-etcd.default.etcd.sample.zlab.co.jp:2379, b9017e59944282a8, 3.2.11, 33 kB, false, 19, 33353
    https://3.sample-etcd.default.etcd.sample.zlab.co.jp:2379, ab5b60a8ad3eaa09, 3.2.11, 33 kB, false, 19, 33354
    
    그런 다음 페일오버를 시도합니다.
    세 개의 Stack 열기 인스턴스 중 하나가 고장으로 인해 다운되었다고 가정합니다.
    $ kubectl get m -o json | jq -c '.items[]|{"name": .metadata.name, "status": .status.phase, "ip": .status.machineState.hostIP }'
    {"name":"1-sample-etcd-default","status":"Running","ip":"182.22.22.77"}
    {"name":"2-sample-etcd-default","status":"Error","ip":"182.22.22.44"}
    {"name":"3-sample-etcd-default","status":"Running","ip":"182.22.22.45"}
    
    그러면 해당 Machine 리소스의 Probe가 실패하므로 Phase가 Error가 됩니다.이 상태가 일정 시간 이상 지속되면 etcd 컨트롤러는 Error의 컴퓨터를 삭제하고 다시 생성합니다.
    kubectl get m -o json | jq -c '.items[]|{"name": .metadata.name, "status": .status.phase, "ip": .status.machineState.hostIP }'
    {"name":"1-sample-etcd-default","status":"Running","ip":"182.22.22.77"}
    {"name":"2-sample-etcd-default","status":"Creating","ip":"182.22.22.85"}
    {"name":"3-sample-etcd-default","status":"Running","ip":"182.22.22.45"}
    
    따라서 이렇게 자동으로 복구됩니다.
    마지막으로 배율을 조정합니다.나는 etcd가 축소를 잘 하지 않는다고 생각하지만, 이번에는 리플리카스를 3에서 5로 변경할 것이다.
    sample-etcd.yaml
    spec:
      version: 3.2.11
    + replicas: 5
    - replicas: 3
    
    $ kubectl apply -f sample-etcd.yaml
    etcd "sample-etcd" configured
    
    군집을 만들 때처럼 하나하나 추가하여 최종적으로 리플렉스 수량의 5개로 수렴합니다.
    $ kubectl get m -o json | jq -c '.items[]|{"name": .metadata.name, "status": .status.phase, "ip": .status.machineState.hostIP }'
    {"name":"1-sample-etcd-default","status":"Running","ip":"182.22.22.77"}
    {"name":"2-sample-etcd-default","status":"Running","ip":"182.22.22.85"}
    {"name":"3-sample-etcd-default","status":"Running","ip":"182.22.22.45"}
    {"name":"4-sample-etcd-default","status":"Running","ip":"182.22.22.81"}
    {"name":"5-sample-etcd-default","status":"Creating","ip":"182.22.22.94"}
    

    총결산


    이번에는 CRD를 이용한 사용자 정의 컨트롤러의 설치 사례 중 하나로 당사에서 개발·이용하는 Etcd as a 서비스를 소개합니다.자세한 내용이 다 기재되지 않으니 관심 있으시면 Kubernetes Meetup Tokyo 등 직접 물어보시면 됩니다.
    CRD와 사용자 정의 컨트롤러를 사용하면 Kubernetes를 확장할 수 있고 다양한 일을 할 수 있습니다.며칠 전 진행된 KubeCon 2017 에서도 CRD와 사용자 정의 컨트롤러(Operator)를 사용한 이야기가 몇 개 있었기 때문에 Z Lab Advent Calendar 2017 21일 항목에서 몇 개를 소개할 예정이다.가능하다면 저쪽을 보세요.

    참고 문헌

  • https://github.com/coreos/etcd-operator
  • http://events.linuxfoundation.org/events/kubecon-and-cloudnativecon-north-america
  • 좋은 웹페이지 즐겨찾기