konchangakita

KPSを一番楽しんでいたブログ 会社の看板を背負いません 転載はご自由にどうぞ

【Nutanix Karbon】Kubernetes 上に Node-Red を作ってみる

Xi IoT でkubernetes Apps にチャレンジする前にもう少し Karbon で遊んでみます
Xi IoT でもよく使う Node-Red をKarbon 上に実装し、Xi IoT と繋いでみる実験にチャレンジです。

f:id:konchangakita:20200531195317p:plain

Node-RedのPod/Serviceを作る

いまだに Kubernetes のまにゅふぇすと(?)yaml は慣れないですが
まずは見よう見まねで pod と service を作って、動作確認してみます

nodered_sample.yaml

apiVersion: v1
kind: Service
metadata:
  name: node-red
  namespace: kon-ns
spec:
  type: NodePort
  selector:
    app: node-red
  ports:
  - name: node-red
    port: 1880
    nodePort: 31880
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-red
  namespace: kon-ns
spec:
  replicas: 1
  selector:
    matchLabels:
      app: node-red
  template:
    metadata:
      name: node-red
      labels:
        app: node-red
    spec:
      containers:
      - name: node-red
        image: nodered/node-red:1.0.6-2
        ports:
        - containerPort: 1880


即、作ってみる(なんていうの?)

PS > kubectl apply -f .\nodered.yaml
service/node-red created
deployment.apps/node-red created


案外サクッと、見覚えのあるページにつながりました
http://<KarbonクラスタIP>:31880/
f:id:konchangakita:20200530153455p:plain


Node-RedのStatefulSetを作る

StatefulSet のことはまだよく理解していないのですが、これで作っておいた方が良いと聞いたのでトライしてみます
pod を作り直してもデータが残っている永続ボリューム(PV/PVC)と言われる外付けストレージを持てるのが、うれしいことのようです

ハマったポイント

きっと k8s初心者だからなのですが、まずこの PVC を使うことによってハマったポイントから
StatefulSet で単純にボリュームをマウントしてみると、pod 作成で失敗してしまいます

PS > kubectl get all
NAME                             READY   STATUS              RESTARTS   AGE
pod/nodered-stateful-0           0/1     CrashLoopBackOff   3          78s


pod のログを確認してみますと、何やら "permission denied" と出ています
コンテナを作って、マウントした PVC へをコピーする時の権限周りで怒られているようです

> kubectl logs pod/nodered-stateful-0

> node-red-docker@1.0.6 start /usr/src/node-red
> node $NODE_OPTIONS node_modules/node-red/red.js $FLOWS "--userDir" "/data"

fs.js:114
    throw err;
    ^

Error: EACCES: permission denied, copyfile '/usr/src/node-red/node_modules/node-red/settings.js' -> '/data/settings.js'
    at Object.copyFileSync (fs.js:1728:3)
    at copyFile (/usr/src/node-red/node_modules/fs-extra/lib/copy-sync/copy-sync.js:68:8)
    at onFile (/usr/src/node-red/node_modules/fs-extra/lib/copy-sync/copy-sync.js:53:25)
    at getStats (/usr/src/node-red/node_modules/fs-extra/lib/copy-sync/copy-sync.js:48:44)
    at startCopy (/usr/src/node-red/node_modules/fs-extra/lib/copy-sync/copy-sync.js:38:10)
    at handleFilterAndCopy (/usr/src/node-red/node_modules/fs-extra/lib/copy-sync/copy-sync.js:33:10)
    at Object.copySync (/usr/src/node-red/node_modules/fs-extra/lib/copy-sync/copy-sync.js:26:10)
    at Object.<anonymous> (/usr/src/node-red/node_modules/node-red/red.js:108:20)
    at Module._compile (internal/modules/cjs/loader.js:778:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! node-red-docker@1.0.6 start: `node $NODE_OPTIONS node_modules/node-red/red.js $FLOWS "--userDir" "/data"`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the node-red-docker@1.0.6 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.


Node-Red のユーザサイトを確認してみると
Dockerで実行する : Node-RED日本ユーザ会
「 コンテナ内のnode-redユーザ(デフォルトではuid=1000)」との記載があり、なんとなく id を 1000 にすればよさそうなので

悩んだ末にこれを追記してみます

      securityContext:
        fsGroup: 1000

(これが Karbon の仕様であるのか、k8s ってそういうもんなのかは知らないです。。。)


完成した StatefulSet の yaml

とりあえずこれで、無事 pod が作れました

apiVersion: v1
kind: Service
metadata:
  name: nodered-stateful
spec:
  type: NodePort
  selector:
    app: nodered-stateful
  ports:
  - name: nodered-http
    port: 1880
    nodePort: 31880
    protocol: TCP
---
apiVersion: "apps/v1"
kind: "StatefulSet"
metadata:
  name: nodered-stateful
spec:
  serviceName: nodered-stateful
  selector:
    matchLabels:
      app: nodered-stateful
  replicas: 1
  template:
    metadata:
      name: nodered-stateful
      labels:
        app: nodered-stateful
    spec:
      securityContext:
        fsGroup: 1000
      terminationGracePeriodSeconds: 10
      containers:
      - name: node-red
        image: nodered/node-red:1.0.6-2
        ports:
        - containerPort: 1880
        volumeMounts:
        - name: nodered-data
          mountPath: /data
  volumeClaimTemplates:
  - metadata:
      name: nodered-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 5Gi


これでpod を作り直してもユーザデータが残せるようになりました!
f:id:konchangakita:20200530162713p:plain


PVCの確認

ちなみに StatefulSet でつくられた PVC は、Karbon の GUI で確認できます
f:id:konchangakita:20200530164709p:plain


Karbon 上で MQTT の通信テスト

Node-Red の GUI につながったので、せっかくなので MQTT のテスト行ってみます
(将来のXi IoTの為に)

Node-Red と繋ぐ MQTTブローカーを用意してやる必要があります
f:id:konchangakita:20200531160238p:plain

お手軽 MQTTブローカーとして mosquitto というのがあるので、これを使ってみます
Eclipse Mosquitto


Karbon 上でMQTTするパターン

f:id:konchangakita:20200531162746p:plain

早速 Karbon 上に mosquitto を作ってみます

apiVersion: v1
kind: Service
metadata:
  name: mosquitto
spec:
  selector:
    app: mosquitto
  ports:
  - name: mosquitto-mqtt
    port: 1883
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mosquitto
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mosquitto
  template:
    metadata:
      name: mosquitto
      labels:
        app: mosquitto
    spec:
      containers:
      - name: mosquitto
        image: eclipse-mosquitto
        ports:
        - containerPort: 1883


mosquitto のクラスタIPを確認し Node-Red からMQTT接続してみます

PS > kubectl get svc
NAME               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
grafana            NodePort    192.199.36.69    <none>        3000:30300/TCP   8d
mosquitto          ClusterIP   192.199.93.240   <none>        1883/TCP         5h25m
nodered-stateful   NodePort    192.199.18.30    <none>        1880:31880/TCP   79m


Node-Red側で、mqttノードに mosquitto のIPとポートを登録
これで MQTT で一人 Pub/Sub できました(マッチポンプ
f:id:konchangakita:20200531164805p:plain

ちなみに作ったフローを書き出しておきます

[{"id":"1d0bc5c2.18629a","type":"tab","label":"フロー 1","disabled":false,"info":""},{"id":"533670bc.25016","type":"mqtt in","z":"1d0bc5c2.18629a","name":"","topic":"test","qos":"2","datatype":"auto","broker":"7ad11645.de9188","x":130,"y":200,"wires":[["93a262b5.62409"]]},{"id":"93a262b5.62409","type":"debug","z":"1d0bc5c2.18629a","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":350,"y":220,"wires":[]},{"id":"84f0acab.6cbda","type":"mqtt out","z":"1d0bc5c2.18629a","name":"","topic":"test","qos":"","retain":"","broker":"7ad11645.de9188","x":330,"y":120,"wires":[]},{"id":"81714c3d.fe9a","type":"inject","z":"1d0bc5c2.18629a","name":"time","topic":"data/time","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":100,"wires":[["84f0acab.6cbda"]]},{"id":"7ad11645.de9188","type":"mqtt-broker","z":"","name":"","broker":"192.199.93.240","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]


Karbon 外部とMQTTするパターン

f:id:konchangakita:20200531190619p:plain


VM上で mosquitto のコンテナを作って、ポート 31883 を公開してみます
(あえてポート変えてみた)

docker run -it -p 31883:1883 --name mybroker eclipse-mosquitto

Node-Red の MQTTノードも、VMIPアドレスと 31883 に変更してやればすんなり Pub/Sub できました
f:id:konchangakita:20200531193717p:plain

ハマった時は

ちなみにうまくいかなかった時には、Node-Red(pod or serviceどちらでもOK)へシェルで入って確認するとよいですね

PS > kubectl exec -it pod/nodered-stateful-0 sh
~ $ nc -zv 172.16.101.126 31883
172.16.101.126 (172.16.101.126:31883) open

(ここでかなりつまづきました。。。。)

で、Xi IoTへ繋げたのか?

Xi IoTへMQTTを飛ばそうと試みたのですが、なんかポートがふさがっててうまく繋げなかったので、Xi IoT側の仕組みをもうちょっと勉強してみたいと思います


Nutanix Karbon公式ページこちら
www.nutanix.com