Kubernetes「で」構成管理
2019年12月18日 水曜日

CONTENTS
Twitterフォロー&条件付きツイートで「バリーくんぬいぐるみ」を抽選で20名にプレゼント!
応募期間は2019/11/29~2019/12/31まで。詳細はこちらをご覧ください。
今すぐツイートするならこちら→ フォローもお忘れなく!
【IIJ 2019 TECHアドベントカレンダー 12/18(水)の記事です】
Kubernetesは、アプリーケーションの開発・運用においてスケーラビリティと耐障害性を確保するのにとても便利な基盤です。しかしながら、このような用途でKubernetesを使うにはクラウドネイティブなアプリケーションが必要となったり、特にインフラの運用においては必ずしもout-of-the-boxなKubernetesがユースケースにはまらないことのほうが多いのではないでしょうか。
この記事では、KubernetesのCustom Resource Definition(CRD)とOperatorを使うことによってマルチテナントのvSphere環境から情報を取得し、あまり手間をかけずにオンプレミスなインフラ環境のアクセシビリティを改善することで、運用コストを低減できるかもしれない方法を紹介します。
インフラ寄り過ぎて、クラウドネイティブな流行りを傍から眺めている方でも、明日からKubernetesネイティブな生活をはじめられる…かもしれません。
オンプレミスなインフラ基盤の課題
具体的な実装の話に入る前に、まず解決したいインフラ基盤の運用における課題について少しまとめます。
構成管理について
インフラ基盤の運用において、構成管理は常に悩みの種ではないでしょうか。
- メンテナンスの対象を減らすためにデータベースの利用を避ける
- 検証環境時にテキストファイルから始めた管理が本番環境でも維持される
- UI/CLIを作りこむ暇がない
- Excel台帳
などなど、様々な理由によってオンプレミスなインフラ基盤では人によって構成管理がなされることが珍しくありません。このような構成管理では、構成管理のドキュメントは独立して管理されるため、運用に活用することも難しくなります。そして、構成管理はそれだけで独立しているため、リソースを投入してよりよい管理方法にするためのモチベーションも高くなくなってしまいます。
アクセシビリティについて
構成管理の作りこみが難しいとなると、構成の一覧性も低くなってしまいます。普段操作しているものとは異なる表示やフィルタをかけるためのUIが作られることもまれになります。作業者は管理されている情報をほかのツールや自分の脳内で変換することによって作業することも求められます。本来このような機能は構成管理に集約することで利便性を高めることができます。
概略:CRDとOperatorを組み合わせてKubernetesからvCenterの情報を取得する
上述の課題を解決するための選択肢として、Kubernetesを試しに使ってみることにします。
Kubernetesを採用する理由
- 構成管理ツールとしての実績があります。
- Kubernetesは、etcdとそれとうまく連携するAPI群、として捉えることができ、その本体はただのデータベースです。
- コマンドラインで管理が完結します。
- プライベートクラウドにおいて、コマンドラインで操作できれば、テナントユーザからはパブリッククラウドと同等の使い勝手になるはずです。構成管理の情報をKubernetesに入れることによって、自然とコマンドラインツールが利用できます。
- Kubernetesはそもそも運用を補助するために作られています。
- Kubernetesでは構成情報を格納するだけでなく、格納された情報を利用することで自動化まで行うことができます。
やること
- vCenterのテナントユーザの認証情報をKubernetesに入れることで仮想マシンの一覧が取得できるようにします。
完成図
ちょっとこれだけでは、何をやりたいのかがわかりにくいと思うので、kubectlで情報を一覧化できる完成図を先にお見せします。
以下のようなテナントユーザの情報を登録することで、このテナントが管理している仮想マシンの情報を取得できるようにします。
1 2 3 |
# kubectl get tenant NAME USERNAME TARGETNAMESPACE EXECDATE EXECFLAG AGE test ike-admin@vsphere.local test-tenant 4s false 4s |
以下のように仮想マシンの情報をユーザが手入力しなくても取得できるOperatorを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# kubectl get vm -n test-tenant NAME POWERSTATE NUMCPU MEMORYMB IPS UPDATEDATE AGE nwcd-ike-a-m1 poweredOn 2 2048 [10.141.86.60] 47s 47s nwcd-ike-a-w1 poweredOn 2 2048 [10.141.86.70] 47s 47s nwcd-ike-admin1500-stg poweredOn 2 8192 [10.141.86.11] 47s 46s nwcd-ike-centos poweredOn 2 8192 [10.141.86.23] 47s 46s nwcd-ike-clean poweredOff 2 8192 [] 47s 46s nwcd-ike-gate1500-stg poweredOn 1 1024 [10.141.86.10] 47s 47s nwcd-ike-router1500-stg poweredOn 1 512 [10.141.86.200] 47s 46s nwcd-ike-template1500-stg poweredOff 2 8192 [] 47s 46s nwcd-ike-ubuntu-base poweredOff 2 2048 [] 47s 46s nwcd-ike-ubuntu-template poweredOn 2 2048 [10.141.86.20] 47s 46s nwcd-ike-ubuntu-template-stg poweredOff 2 2048 [] 47s 46s |
Kubernetes流行のきざし
VMwareが今後vSphereにKubernetesを取り込んで展開していくことが今年話題になりました。オンプレミス環境でもKubernetesがはやり始めるのは時間の問題でしょう。
- [速報]KubernetesをvSphereに統合した「Project Pacific」を発表、vSphere上でKuberenetesクラスタなど運用可能に。VMWorld 2019 US(2019年8月27日)
- 「VMware Tanzu」発表、“エンタープライズKubernetes”加速を支援(2019年08月28日)
しかしながらVMwareからKubernetesが提供されるまでにはまだ少し時間がかかりそうです。vCenter 6.7U3が必要だったりNSX-Tが必要だったり、そもそもリリースされていないので試せない現状もありますが、リリースされてから基盤に採用されるまでにもラグがありそうです。
そこで、とりあえずミニマムで簡単にvCenterの情報をkubectlコマンドを用いて見られるようにしてみたいと思います。
参考
Operator作成に当たって、主に以下の記事・動画とライブラリのドキュメントを参考にしました。
- https://medium.com/flant-com/kubernetes-operator-in-python-451f2d2e33f3
- https://medium.com/swlh/building-a-kubernetes-operator-in-python-with-zalandos-kopf-37c311d8edff
- https://www.youtube.com/watch?v=rN_rQU92T5s
使うもの
- vCenter 6.5
- Kubernetes 1.16
- Python 3.7
- ライブラリ
- kopf==0.23.2
- kubernetes==10.0.1
- pyvmomi==6.7.3
CRDの作成
最初にKubernetesに登録するデータの構造を定義します。一般的なYAML形式での構成管理と同じですが、Kubernetesを用いることで、バリデーションや更新差分を簡単に見られるようになります。
次に、マルチテナントユーザの環境を仮定して、以下の情報を格納するためのCRDの定義を行います。
- テナントに関する情報
- 仮想マシンに関する情報
注釈
Custom Resource Definition(CRD)(https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/、https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/)はKubernetesに新しいリソースの定義を追加するものです。KubernetesにはPod/Deployment/Serviceなどのリソースタイプがありますが、これら以外に好きなリソースタイプを追加する方法がCRDです。CRDによって定義されたリソースタイプについてはget/set/describe/editなどができ、Operator(https://kubernetes.io/docs/concepts/extend-kubernetes/operator/)を作成することで、リソース作成・変更・削除などの際の振る舞いを制御することができます。
テナントCRD
まずはテナントユーザの認証情報を受け入れる箱を作ります。ここで必要な情報はユーザ名とパスワードだけです。パスワードを平文で扱うことを回避するためにSecret経由で与えるようにします。additionalPrinterColumnsを書いておくことで、getする際にきれいに表示できるようになります。(表示例は後述)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: tenants.test.local spec: group: test.local versions: - name: v1 served: true storage: true scope: Namespaced names: plural: tenants singular: tenant kind: Tenant shortNames: - tenant additionalPrinterColumns: - name: Username type: string JSONPath: .username description: Username for vCenter tenant account - name: TargetNamespace type: string JSONPath: .namespace - name: ExecDate type: date JSONPath: .execDate - name: ExecFlag type: boolean JSONPath: .execFlag - name: Age type: date JSONPath: .metadata.creationTimestamp validation: openAPIV3Schema: type: object properties: username: type: string passwordName: type: string namespace: type: string execFlag: type: boolean execDate: type: string |
仮想マシンCRD
次に、VM情報のCRDを作ります。ここではPowerCLIでGet-VMしたときによく表示される電源状態、CPU数、メモリ量のほかに運用でよく知りたい情報であるIPアドレスを格納できるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: vms.test.local spec: group: test.local versions: - name: v1 served: true storage: true scope: Namespaced names: plural: vms singular: vm kind: VM shortNames: - vm additionalPrinterColumns: - name: PowerState type: string JSONPath: .powerState - name: NumCpu type: number JSONPath: .numCpu - name: MemoryMB type: number JSONPath: .memoryMB - name: IPs type: string JSONPath: .ip - name: UpdateDate type: date JSONPath: .updateDate - name: Age type: date JSONPath: .metadata.creationTimestamp validation: openAPIV3Schema: type: object properties: vmname: type: string powerState: type: string numCpu: type: number memoryMB: type: number ip: type: array items: type: string updateDate: type: string |
Operatorの作成
次に入力されたテナントの情報から、そのテナントが管理している仮想マシンの情報を取得するためのOperatorを作成します。
新規の基盤で既存の環境がなければ、構成は宣言的に管理されるべきですが、既存の基盤上ですでに仮想マシンが動いているときに、テナントにその情報をすべて入力させるのは酷です。幸いにして、この取得は難しくありません。
OperatorはGo言語で書かれることが一般的だと思いますが、性能の要求が高くないので、Pythonのフレームワークを使って簡単に作成してしまいます。
全体の流れ
- CRDが作成・更新されるのをフックとします。
- 実行防止フラグが立っていなければ以下を実行します。
- Secretからパスワードを取得します。
- pyvmomiを使ってvCenterに接続して仮想マシンの情報を取得します。
- 取得した情報をKubernetesのDBへ書き込みます。
- 実行防止フラグを立てます。
Dockerfile
Operatorはコンテナで作成してKubernetesクラスタ内にデプロイすることで利用できるようになります。Pythonの公式コンテナイメージに必要なライブラリをインストールして、実行するソースコードを添えるだけです。
1 2 3 4 5 6 7 8 9 |
FROM python:3.7 RUN pip install kopf RUN pip install kubernetes RUN pip install pyvmomi COPY handlers.py /handlers.py CMD kopf run --standalone /handlers.py |
イベント応答
kopf(https://github.com/zalando-incubator/kopf)はPythonでKubernetesのOperatorを作るためのライブラリです。今回は作成時と更新時に同じ挙動をさせるためにon.createとon.updateに同じ関数を呼び出すようにして、common_fn内に処理を実装します。実装方法はexamples(https://github.com/zalando-incubator/kopf/tree/master/examples)を参照しました。
1 2 3 4 5 6 7 8 |
@kopf.on.create(CRD_GROUP, CRD_VERSION, CRD_PLURAL) def create_fn(body, **kwargs): common_fn(body, **kwargs) @kopf.on.update(CRD_GROUP, CRD_VERSION, CRD_PLURAL) def update_fn(body, **kwargs): common_fn(body, **kwargs) |
Kubernetesクラスタとの連携
kubernetes-clientはPythonでKubernetesのAPIを簡単に実行するためのライブラリです。APIを呼ぶためのドキュメントが充実していて、大変使いやすいです。(https://github.com/kubernetes-client/python/blob/master/kubernetes/README.md)
このライブラリを用いて、vCenterから取得した情報を仮想マシンのCRDに変換してKubernetesクラスタに投入します。
1 2 |
kapi = kubernetes.client.CoreV1Api() capi = kubernetes.client.CustomObjectsApi() |
参考:https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Secret.md
1 |
res = kapi.read_namespaced_secret(password_name, namespace, exact=True, export=True) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
info = { 'vmname': vmname, 'powerState': power_state, 'numCpu': num_cpu, 'memoryMB': memory_mb, 'ip': ip, } kopf.info(body, reason='vm-info', message=json.dumps(info)) rec = { 'apiVersion': f"{CRD_GROUP}/{CRD_VERSION}", 'kind': C_CRD_KIND, 'metadata': { "name": vmname, "namespace": target_namespace, }, 'updateDate': access_date, } rec.update(info) obj = { 'group': CRD_GROUP, 'version': CRD_VERSION, 'plural': C_CRD_PLURAL, 'namespace': target_namespace, 'body': rec, } try: capi.create_namespaced_custom_object(**obj) except Exception as e: kopf.info(body, reason='vm-info-creation-error', message=str(e)) obj['name'] = obj['body']['metadata']['name'] capi.patch_namespaced_custom_object(**obj) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
ubody = body ubody['execFlag'] = False ubody['execDate'] = access_date meta = { 'labels': ubody['metadata']['labels'], } ubody['metadata'] = meta obj = { 'group': CRD_GROUP, 'version': CRD_VERSION, 'plural': CRD_PLURAL, 'name': name, 'namespace': namespace, 'body': ubody, } kopf.info(body, reason='req-payload', message=json.dumps(obj)) capi.patch_namespaced_custom_object(**obj) |
vCenterとの接続
pyvmomi(https://github.com/vmware/pyvmomi)はPythonでvCenterとやり取りをするためのライブラリです。ユーザが入力したテナントの情報を用いてvCenterにログインし、仮想マシンなどの情報を取得します。
参考:https://github.com/vmware/pyvmomi/blob/master/sample/getallvms.py
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#--- vCenter connection si = SmartConnect(host=getenv('VCENTER_HOST'), user=username, pwd=password, ) if not si: kopf.warn(body, reason='auth', message="connection error") msg = f"VM info created by Tenant" return {'message': msg} atexit.register(Disconnect, si) #=== vCenter connection |
参考:https://github.com/vmware/pyvmomi-community-samples/blob/master/samples/vminfo_quick.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
dc = si.content.rootFolder.childEntity[0] folders = dc.vmFolder.childEntity for folder in folders: vms = folder.childEntity for vm in vms: summary = vm.summary vmname = vm.config.name template = vm.config.template ip = vm.guest.ipAddress if ip is None: ip = [] elif type(ip) is not list: ip = [ip] power_state = vm.runtime.powerState num_cpu = vm.config.hardware.numCPU memory_mb = vm.config.hardware.memoryMB |
その他
いたるところにある kopf.info(body, reason='name', message=name)
はprintデバッグをするための情報表示…でもありますが、これをしておくことによってdescribeするとログに残るようにしています。(ログはKubernetesクラスタの設定次第ですが、デフォルトだと1時間だけ保持されます)
ソースコード全文
以下、ソースコードをまとめた全文です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
import base64 import yaml import json import datetime import atexit from os import getenv import ssl ssl._create_default_https_context = ssl._create_unverified_context import kopf import kubernetes from pyVim.connect import SmartConnect, Disconnect from pyVmomi import vim CRD_GROUP = 'test.local' CRD_VERSION = 'v1' CRD_PLURAL = 'tenants' C_CRD_PLURAL = 'vms' C_CRD_KIND = 'VM' def decode_secret(s): return base64.b64decode(s).decode() @kopf.on.create(CRD_GROUP, CRD_VERSION, CRD_PLURAL) def create_fn(body, **kwargs): common_fn(body, **kwargs) @kopf.on.update(CRD_GROUP, CRD_VERSION, CRD_PLURAL) def update_fn(body, **kwargs): common_fn(body, **kwargs) def common_fn(body, **kwargs): kapi = kubernetes.client.CoreV1Api() capi = kubernetes.client.CustomObjectsApi() exec_flag = body.get('execFlag', True) if exec_flag is not False: name = body['metadata']['name'] kopf.info(body, reason='name', message=name) namespace = body['metadata']['namespace'] kopf.info(body, reason='namespace', message=namespace) target_namespace = body['namespace'] kopf.info(body, reason='target_namespace', message=target_namespace) username = body['username'] kopf.info(body, reason='username', message=username) password_name = body['passwordName'] res = kapi.read_namespaced_secret(password_name, namespace, exact=True, export=True) password = decode_secret(res.data['password']) #--- vCenter connection si = SmartConnect(host=getenv('VCENTER_HOST'), user=username, pwd=password, ) if not si: kopf.warn(body, reason='auth', message="connection error") msg = f"VM info created by Tenant" return {'message': msg} atexit.register(Disconnect, si) #=== vCenter connection access_date = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ') kopf.info(body, reason='access-date', message=access_date) dc = si.content.rootFolder.childEntity[0] folders = dc.vmFolder.childEntity kopf.info(body, reason='dc', message=dc.name) for folder in folders: kopf.info(body, reason='folder', message=folder.name) vms = folder.childEntity for vm in vms: kopf.info(body, reason='vm', message=vm.name) summary = vm.summary vmname = vm.config.name template = vm.config.template ip = vm.guest.ipAddress if ip is None: ip = [] elif type(ip) is not list: ip = [ip] power_state = vm.runtime.powerState num_cpu = vm.config.hardware.numCPU memory_mb = vm.config.hardware.memoryMB info = { 'vmname': vmname, 'powerState': power_state, 'numCpu': num_cpu, 'memoryMB': memory_mb, 'ip': ip, } kopf.info(body, reason='vm-info', message=json.dumps(info)) rec = { 'apiVersion': f"{CRD_GROUP}/{CRD_VERSION}", 'kind': C_CRD_KIND, 'metadata': { "name": vmname, "namespace": target_namespace, }, 'updateDate': access_date, } rec.update(info) obj = { 'group': CRD_GROUP, 'version': CRD_VERSION, 'plural': C_CRD_PLURAL, 'namespace': target_namespace, 'body': rec, } try: capi.create_namespaced_custom_object(**obj) except Exception as e: kopf.info(body, reason='vm-info-creation-error', message=str(e)) obj['name'] = obj['body']['metadata']['name'] capi.patch_namespaced_custom_object(**obj) ubody = body ubody['execFlag'] = False ubody['execDate'] = access_date meta = { 'labels': ubody['metadata']['labels'], } ubody['metadata'] = meta obj = { 'group': CRD_GROUP, 'version': CRD_VERSION, 'plural': CRD_PLURAL, 'name': name, 'namespace': namespace, 'body': ubody, } kopf.info(body, reason='req-payload', message=json.dumps(obj)) capi.patch_namespaced_custom_object(**obj) |
Operatorのデプロイ
Operatorにしかるべき権限を与えるために、サービスアカウントを作成しRBACに則ってロールを付加します。(https://kubernetes.io/docs/reference/access-authn-authz/rbac/)
作りこむ手間を省くため、cluster-adminをとりあえず与えます。
1 2 3 4 |
apiVersion: v1 kind: ServiceAccount metadata: name: tenant-operator |
1 2 3 4 5 6 7 8 9 10 11 12 |
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: tenant-operator roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: tenant-operator namespace: default |
- 背景補足
- vCenterのURLは環境変数経由で与えます。
- Kubernetesクラスタ内にコンテナレジストリがデプロイしてあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
apiVersion: apps/v1 kind: Deployment metadata: name: tenant-op spec: selector: matchLabels: app: tenant-op template: metadata: labels: app: tenant-op spec: serviceAccountName: tenant-operator containers: - image: localhost:30852/op-tenant:latest name: tenant-op env: - name: VCENTER_HOST value: nwcd-vmw-vcenter1500-stg |
結果
以上で、kubectlで仮想マシンのリストを見るための準備はできました。
構成要素
以下の2つがOperator関連の構成要素です。
1 2 3 4 |
# kubectl get serviceaccounts NAME SECRETS AGE default 1 51d tenant-operator 1 2d22h |
1 2 3 4 5 6 7 |
# kubectl get deploy tenant-op NAME READY UP-TO-DATE AVAILABLE AGE tenant-op 1/1 1 1 2d22h # kubectl get po -l app=tenant-op NAME READY STATUS RESTARTS AGE tenant-op-6864547fb4-s4tmj 1/1 Running 0 32m |
テナント情報
次にテナントの認証情報です。これらが投入されることをトリガーに、仮想マシンについての情報がKubernetesに収集されます。
1 2 3 |
# kubectl get secrets test-pass NAME TYPE DATA AGE test-pass Opaque 1 2d21h |
1 2 3 4 5 6 7 8 9 |
apiVersion: test.local/v1 kind: Tenant metadata: name: test labels: module: tenant namespace: test-tenant username: ike-admin@vsphere.local passwordName: test-pass |
テナントCRDを投入すると…
ike-admin@vsphere.localというテナントがtenantリソースに登録されていることが確認できます。
1 2 3 |
# kubectl get tenant NAME USERNAME TARGETNAMESPACE EXECDATE EXECFLAG AGE test ike-admin@vsphere.local test-tenant 4s false 4s |
そして、test-tenantのnamespaceにこのテナントから見える仮想マシンの情報が一覧化されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# kubectl get vm -n test-tenant NAME POWERSTATE NUMCPU MEMORYMB IPS UPDATEDATE AGE nwcd-ike-a-m1 poweredOn 2 2048 [10.141.86.60] 47s 47s nwcd-ike-a-w1 poweredOn 2 2048 [10.141.86.70] 47s 47s nwcd-ike-admin1500-stg poweredOn 2 8192 [10.141.86.11] 47s 46s nwcd-ike-centos poweredOn 2 8192 [10.141.86.23] 47s 46s nwcd-ike-clean poweredOff 2 8192 [] 47s 46s nwcd-ike-gate1500-stg poweredOn 1 1024 [10.141.86.10] 47s 47s nwcd-ike-router1500-stg poweredOn 1 512 [10.141.86.200] 47s 46s nwcd-ike-template1500-stg poweredOff 2 8192 [] 47s 46s nwcd-ike-ubuntu-base poweredOff 2 2048 [] 47s 46s nwcd-ike-ubuntu-template poweredOn 2 2048 [10.141.86.20] 47s 46s nwcd-ike-ubuntu-template-stg poweredOff 2 2048 [] 47s 46s |
例えばCPU数順に表示することも簡単です。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# kubectl get vm -n test-tenant --sort-by='.numCpu' NAME POWERSTATE NUMCPU MEMORYMB IPS UPDATEDATE AGE nwcd-ike-router1500-stg poweredOn 1 512 [10.141.86.200] 16d 16d nwcd-ike-gate1500-stg.hop.2iij.net poweredOn 1 1024 [10.141.86.10] 16d 16d nwcd-ike-a-w1 poweredOn 2 2048 [10.141.86.70] 16d 16d nwcd-ike-admin1500-stg poweredOn 2 8192 [10.141.86.11] 16d 16d nwcd-ike-centos poweredOn 2 8192 [10.141.86.23] 16d 16d nwcd-ike-clean poweredOff 2 8192 [] 16d 16d nwcd-ike-a-m1 poweredOn 2 2048 [10.141.86.60] 16d 16d nwcd-ike-template1500-stg poweredOff 2 8192 [] 16d 16d nwcd-ike-ubuntu-base poweredOff 2 2048 [] 16d 16d nwcd-ike-ubuntu-template poweredOn 2 2048 [10.141.86.20] 16d 16d nwcd-ike-ubuntu-template-stg.hop.2iij.net poweredOff 2 2048 [] 16d 16d |
起動している仮想マシンが7台と、起動していない仮想マシンが4台あることも確認できます。
1 2 3 4 5 |
# kubectl get vm -n test-tenant | grep poweredOn | wc -l 7 # kubectl get vm -n test-tenant | grep poweredOff | wc -l 4 |
その他便利機能及び拡張
Kubernetesの機能を用いることで、構成の変更内容を確認することができます。
例えば、nwcd-ike-a-m1のCPU数を変更してみると、以下のような差分を表示することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# cat mod-vm.yaml apiVersion: test.local/v1 kind: VM metadata: name: nwcd-ike-a-m1 namespace: test-tenant vmname: nwcd-ike-a-m1 numCpu: 4 memoryMB: 2048 powerState: poweredOn ip: - 10.141.86.60 # kubectl -n test-tenant diff -f mod-vm.yaml diff -u -N /tmp/LIVE-707611086/test.local.v1.VM.test-tenant.nwcd-ike-a-m1 /tmp/MERGED-244975829/test.local.v1.VM.test-tenant.nwcd-ike-a-m1 --- /tmp/LIVE-707611086/test.local.v1.VM.test-tenant.nwcd-ike-a-m1 2019-12-12 13:34:39.002426608 +0900 +++ /tmp/MERGED-244975829/test.local.v1.VM.test-tenant.nwcd-ike-a-m1 2019-12-12 13:34:39.017426607 +0900 @@ -5,13 +5,13 @@ memoryMB: 2048 metadata: creationTimestamp: "2019-11-25T13:54:09Z" - generation: 1 + generation: 2 name: nwcd-ike-a-m1 namespace: test-tenant resourceVersion: "8255884" selfLink: /apis/test.local/v1/namespaces/test-tenant/vms/nwcd-ike-a-m1 uid: 0aa83184-9f8c-4256-99c9-466a8b7dff18 -numCpu: 2 +numCpu: 4 powerState: poweredOn updateDate: "2019-11-25T13:54:09Z" vmname: nwcd-ike-a-m1 |
差分に問題がないことを確認してから投入すれば、想定外の構成変更を実行してしまうことを防ぐことができます。また、VMリソースの変更に対するOperatorを実装することで、Kubernetes側で管理しているデータベースの内容をvCenterに反映させることもできるようになります。
万が一、typoをしてメモリの0(ゼロ)をo(オー)と書いてしまっても、型チェックをしてくれます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# cat mod-vm.yaml apiVersion: test.local/v1 kind: VM metadata: name: nwcd-ike-a-m1 namespace: test-tenant vmname: nwcd-ike-a-m1 numCpu: 4 memoryMB: 2o48 powerState: poweredOn ip: - 10.141.86.60 # kubectl -n test-tenant diff -f mod-vm.yaml The VM "nwcd-ike-a-m1" is invalid: memoryMB: Invalid value: "string": memoryMB in body must be of type number: "string" |
他にも例えば、CronJobで定期的にtenantリソースのEXECFLAGをtrueにすることで、取得情報を同期させることができます。
まとめ
以上でvCenterにある情報を手軽にKubernetesに保管して構成管理する方法を紹介しました。
Kubernetesを使うことによって、
- スキーマが定義されるため、構成管理の内容についてバリデーションをかけることができます
- kubectlによる一覧・編集などの制御ができます
- Operatorの開発により、運用の自動化を進めることができます
本記事では高々150行ほどのコードで、テナントユーザの情報からそのテナントが管理している仮想マシンの情報を一覧化することができました。
※本当は構成管理側の設定内容を反映させることが構成管理になりますが、今回はすでに動いている既存のシステムの情報をインポートする部分をカバーしました。