1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Kubernetes Operator for Nutanix はじめの一歩

Last updated at Posted at 2021-12-02

はじめに

この記事はNutanix Advent Calender 2021の12/3分の記事です。Nutanixを制御対象とし、Kubernetes Operatormに入門してみましたのでその成果を記します。

Kubernets Operatorとは?

Kubernetesの機能を拡張し、独自のリソース定義とコントローラを実装することでKubernetesによる運用自律化の仕組みをあらゆるアプリケーション・ミドルウェア・インフラに応用出来るようにするものです。

Operatorを実装するためのフレームワーク

今回はさくっとOperatorの仕組みを押さえたい、とりあえずシンプルなユースケースとしてNutanix AHVの仮想マシン制御する実装を行いたいので1番手っ取り早く実装できそうなkopfを選択しました。Githubのスター数を見ているとRedhat社が推しているOperator Frameworkが主流のようです。

必要なパーツ

  • Kubernetesクラスタ: kubeadmを使用して1ノードクラスタを事前に作成しました。
  • CRD(Custom Resource Definition): 独自APIリソースのスキーマをyaml形式で定義します
  • カスタムコントローラ
    • ソースコード: ControllerのロジックをPythonで実装します。
    • Dockerfile: ソースコードとベースイメージを合わせてOperatorとして動作させるためのコンテナイメージを定義します

実装

1. CRDの準備

今回はカスタムリソースを利用してAHV VMを作成する所までやりたいので以下のahvvms.nutanix.comなるリソースを定義するCRDを準備しました。

1_crd.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: ahvvms.nutanix.com
spec:
  group: nutanix.com
  scope: Namespaced
  names:
    kind: AhvVm
    plural: ahvvms
    singular: ahvvm
    shortNames:
      - avm
  versions:
    - name: v1alpha1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                num_sockets:
                  minimum: 1
                  maximum: 4
                  type: integer
                num_vcpus_per_socket:
                  minimum: 1
                  maximum: 2
                  type: integer
                memory_size_mib:
                  minimum: 1024
                  maximum: 4096
                  type: integer
      additionalPrinterColumns:
        - name: Sockets
          type: integer
          jsonPath: .spec.num_sockets
          description: Number of sockets
        - name: vCPUsPerSocket
          type: integer
          jsonPath: .spec.num_vcpus_per_socket
          description: Number of vCPUs per socket
        - name: Memory
          type: integer
          jsonPath: .spec.memory_size_mib
          description: Memory Size in MiB

これを準備したKubernetesクラスタに適用すると、CRDとして適用されます。

[sho.uchida@C02Y41RDJHD3 kopf-nutanix] (main)$ kubectl get crds
NAME                 CREATED AT
ahvvms.nutanix.com   2021-11-11T14:40:16Z

2. カスタムコントローラの準備

kopfにおけるコントローラの書き方に沿ってPythonでカスタムコントローラを実装します。接続先のPrism Centralの情報はコントローラPodの環境変数から参照する実装としました。中身はいたってシンプルで、ahvvms.nutanix.comのカスタムリソースが作成された際にNutanixのv3APIを使用して仮想マシンを作成します。

2_cc.py
import kopf
import requests
import urllib3
import json
import os
import logging
import sys

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

logger = logging.getLogger(__name__)
sh = logging.StreamHandler(sys.stdout)
sh.setLevel(logging.DEBUG)
logger.addHandler(sh)

# Prism
PRISM_HOST = os.getenv('PRISM_HOST')
PRISM_USER = os.getenv('PRISM_USER')
PRISM_PASS = os.getenv('PRISM_PASS')
PRISM_PORT = os.getenv('PRISM_PORT', 9440)
PRISM_CLUSTERNAME = os.getenv('PRISM_CLUSTERNAME')
PRISM_CLUSTERUUID = os.getenv('PRISM_CLUSTERUUID')

@kopf.on.create('nutanix.com', 'v1alpha1', 'ahvvm')
def create_fn(spec, **kwargs):

    vm_name = kwargs["body"]["metadata"]["name"]
    num_sockets = spec.get('num_sockets', 1)
    num_vcpus_per_socket = spec.get('num_vcpus_per_socket', 1)
    memory_size_mib = spec.get('memory_size_mib', 1024)

    url = f'https://{PRISM_HOST}:{PRISM_PORT}/api/nutanix/v3/vms'
    payload = f'{{ \
                    "spec":{{ \
                        "name":"{vm_name}", \
                        "resources":{{ \
                            "num_sockets":{num_sockets}, \
                            "num_vcpus_per_socket":{num_vcpus_per_socket}, \
                            "memory_size_mib":{memory_size_mib} \
                        }}, \
                        "cluster_reference":{{ \
                            "kind":"cluster", \
                            "name":"{PRISM_CLUSTERNAME}", \
                            "uuid":"{PRISM_CLUSTERUUID}" \
                        }}\
                    }}, \
                    "metadata":{{ \
                        "kind":"vm" \
                    }} \
                }}'
    try:
        response = session.post(url, data=payload)
        if(response.ok):
            logger.debug("OK")
        else:
            logger.error(f'An error occurred while connecting to {PRISM_HOST}.')
            logger.error(response.text)
    except Exception as ex:
        logger.error(f'An {type(ex).__name__} exception occurred while connecting to {PRISM_HOST}.\nArgument: {ex.args}.')
      
session = requests.Session()
session.auth = (PRISM_USER, PRISM_PASS)
session.verify = False 
session.headers.update({"Content-Type": "application/json", "Accept": "application/json", "cache-control": "no-cache"})
logger.debug("Session is created.")

このソースコードを使用し、pythonのベースイメージを使ってコンテナイメージをビルドします。

Dockerfile
FROM python:3.7
RUN mkdir /src
ADD 2_cc.py /src
ADD requirements.txt .
RUN pip3 install -r requirements.txt
CMD kopf run /src/2_cc.py --verbose
[sho.uchida@C02Y41RDJHD3 kopf-nutanix] (main)$ docker build -t (Dockerレポジトリ名)/kopf-nutanix:latest .
[sho.uchida@C02Y41RDJHD3 kopf-nutanix] (main)$ docker push (Dockerレポジトリ名)/kopf-nutanix:latest

3. カスタムコントローラの展開

2でビルド・プッシュしたコントローラのコンテナイメージを元に、コントローラをKubernetesクラスタ上に展開します。

3_cc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ahvvm-operator
  namespace: kopf-system
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      application: ahvvm-operator
  template:
    metadata:
      labels:
        application: ahvvm-operator
    spec:
      serviceAccountName: ahvvm-operator
      containers:
      - name: ahvvms
        image: (Dockerレポジトリ名)/kopf-nutanix:latest
        imagePullPolicy: Always
        env:
        - name: PRISM_HOST
          value: "Prism Centralのホスト名"
        - name: PRISM_USER
          value: "Prism Centralのユーザ名"
        - name: PRISM_PASS
          value: "Prism Centralのパスワード"
        - name: PRISM_PORT
          value: "Prism Centralのポート番号、たぶん9440"
        - name: PRISM_CLUSTERNAME
          value: "クラスタ名"
        - name: PRISM_CLUSTERUUID
          value: "クラスタUUID"

カスタムコントローラが展開出来ていることを確認します。

[sho.uchida@C02Y41RDJHD3 kopf-nutanix] (main)$ kubectl get all -n kopf-system
NAME                                  READY   STATUS    RESTARTS   AGE
pod/ahvvm-operator-5d78c4b94f-cj9p9   1/1     Running   0          20d

NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ahvvm-operator   1/1     1            1           20d

NAME                                        DESIRED   CURRENT   READY   AGE
replicaset.apps/ahvvm-operator-5d78c4b94f   1         1         1       20d

4. カスタムリソースの適用

では、CRDとカスタムコントローラが展開されましたので、CRDで定義されたモデルに則ったカスタムリソースを作成します。

4_cr.yaml
apiVersion: nutanix.com/v1alpha1
kind: AhvVm
metadata:
  name: ahvvm-from-operator
spec:
  num_sockets: 2
  num_vcpus_per_socket: 1
  memory_size_mib: 2048

ahvvmのリソースを確認すると、新たに作成したahvvm-from-operatorのオブジェクトが出来ています。

[sho.uchida@C02Y41RDJHD3 kopf-nutanix] (main)$ kubectl get ahvvm
NAME                  SOCKETS   VCPUSPERSOCKET   MEMORY
ahvvm-from-operator   2         1                2048

コントローラのログ確認。

[sho.uchida@C02Y41RDJHD3 kopf-nutanix] (main)$ kubectl logs ahvvm-operator-5d78c4b94f-cj9p9 -n kopf-system
~~~略~~~
[2021-11-12 00:06:13,086] kopf.objects         [DEBUG   ] [default/ahvvm-from-operator] Creation is in progress: {'apiVersion': 'nutanix.com/v1alpha1', 'kind': 'AhvVm', 'metadata': {'annotations': {'kubectl.kubernetes.io/last-applied-configuration': '{"apiVersion":"nutanix.com/v1alpha1","kind":"AhvVm","metadata":{"annotations":{},"name":"ahvvm-from-operator","namespace":"default"},"spec":{"memory_size_mib":2048,"num_sockets":2,"num_vcpus_per_socket":1}}\n'}, 'creationTimestamp': '2021-11-12T00:06:12Z', 'generation': 1, 'managedFields': [{'apiVersion': 'nutanix.com/v1alpha1', 'fieldsType': 'FieldsV1', 'fieldsV1': {'f:metadata': {'f:annotations': {'.': {}, 'f:kubectl.kubernetes.io/last-applied-configuration': {}}}, 'f:spec': {'.': {}, 'f:memory_size_mib': {}, 'f:num_sockets': {}, 'f:num_vcpus_per_socket': {}}}, 'manager': 'kubectl', 'operation': 'Update', 'time': '2021-11-12T00:06:12Z'}], 'name': 'ahvvm-from-operator', 'namespace': 'default', 'resourceVersion': '636082', 'uid': '92cf13c0-c68f-4ef8-8d18-ade4aac450df'}, 'spec': {'memory_size_mib': 2048, 'num_sockets': 2, 'num_vcpus_per_socket': 1}}
[2021-11-12 00:06:13,087] kopf.objects         [DEBUG   ] [default/ahvvm-from-operator] Handler 'create_fn' is invoked.
[2021-11-12 00:06:13,090] urllib3.connectionpo [DEBUG   ] Resetting dropped connection: 10.42.9.39
[2021-11-12 00:06:13,806] urllib3.connectionpo [DEBUG   ] https://10.42.9.39:9440 "POST /api/nutanix/v3/vms HTTP/1.1" 202 None
[2021-11-12 00:06:13,807] __kopf_script_0__/sr [DEBUG   ] OK
OK
[2021-11-12 00:06:13,808] kopf.objects         [INFO    ] [default/ahvvm-from-operator] Handler 'create_fn' succeeded.
[2021-11-12 00:06:13,808] kopf.objects         [INFO    ] [default/ahvvm-from-operator] Creation is processed: 1 succeeded; 0 failed.
[2021-11-12 00:06:13,809] kopf.objects         [DEBUG   ] [default/ahvvm-from-operator] Patching with: {'metadata': {'annotations': {'kopf.zalando.org/last-handled-configuration': '{"spec":{"memory_size_mib":2048,"num_sockets":2,"num_vcpus_per_socket":1}}\n'}}}
[2021-11-12 00:06:13,939] kopf.objects         [DEBUG   ] [default/ahvvm-from-operator] Something has changed, but we are not interested (the essence is the same).
[2021-11-12 00:06:13,939] kopf.objects         [DEBUG   ] [default/ahvvm-from-operator] Handling cycle is finished, waiting for new changes.

そして肝心のAHV VMが作成されているかを確認してみると、めでたく指定されたスペックで仮想マシンが作成されています。
スクリーンショット 2021-12-02 23.58.44.png

終わりに

今回はVM作成という非常にシンプルなユースケースで手っ取り早く実装を行いましたのでOperatorがただのAPI変換マシーンになっています。今後、VMレプリカ数を制御し、障害時は新たな仮想マシンを構築する等、KubernetesのControllerが内部実行するReconciliation Loopの仕組みを利用したNutanixの運用自律化の構築に期待が出来る内容となっています。また、Operator Frameworkでの実装にもチャレンジしたいと思っています。以上!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?