このセクションには、Things Cloud APIおよびその他の第三者サービスを使用するマイクロサービスを開発するための一連のチュートリアルを紹介します。ここで使用されるサンプルコードはGitHubリポジトリで見つけることができます。

Things Cloudプラットフォームでは、マイクロサービスのホスティングはDockerコンテナの上に構築されています。これにより、技術に依存しないため、開発者は任意の技術スタックでアプリケーションを作成することができます。

Pythonマイクロサービス

このチュートリアルでは、Python で記述されたマイクロサービスを作成して実行する方法を学びます。

  1. 必要な前提条件をインストールします。
  2. PythonとFlaskフレームワークを使用してRESTエンドポイントを公開するサンプルアプリケーションを作成します。
  3. アプリケーションをビルドしてDockerイメージとして保存するためのDockerfileを作成します。
  4. Things Cloudアプリケーションマニフェストを作成します。
  5. Dockerイメージとアプリケーションマニフェストをビルドして、Things Cloud にアップロードできるマイクロサービスZIPファイルにパッケージ化します。
  6. 新しいマイクロサービスZIPファイルをアップロードし、サブスクライブして実行します。

前提条件

最新バージョンのDockerがインストールされていることを確認してください。例えば、お使いのオペレーティングシステムに合ったDocker Desktopをインストールできます。

Things Cloudはlinux/amd64 Dockerコンテナをホストしています。例えば、Appleシリコンを搭載した最近のMacを使用している場合、Dockerを設定してlinux/amd64コンテナをビルドする必要があります。

$ export DOCKER_DEFAULT_PLATFORM=linux/amd64

Python Webアプリケーションの作成

この例では、Python 3、WebフレームワークFlask、HTTPサーバーWaitressを使用します。まず、以下の内容の application.py スクリプトを作成します。

#!flask/bin/python
from flask import Flask, jsonify
import os

app = Flask(__name__)

# Hello world endpoint
@app.route('/')
def hello():
    return 'Hello world!'

# Verify the status of the microservice
@app.route('/health')
def health():
    return '{ "status" : "UP" }'

# Get environment details
@app.route('/environment')
def environment():
    environment_data = {
        'platformUrl': os.getenv('C8Y_BASEURL'),
        'mqttPlatformUrl': os.getenv('C8Y_BASEURL_MQTT'),
        'tenant': os.getenv('C8Y_BOOTSTRAP_TENANT'),
        'user': os.getenv('C8Y_BOOTSTRAP_USER'),
        'password': os.getenv('C8Y_BOOTSTRAP_PASSWORD'),
        'microserviceIsolation': os.getenv('C8Y_MICROSERVICE_ISOLATION')
    }
    return jsonify(environment_data)

if __name__ == '__main__':
    import logging
    logging.basicConfig(level=logging.INFO)
    from waitress import serve
    serve(app, host="0.0.0.0", port=80)

アプリケーションは 3 つのエンドポイントを公開します。

  • / は「Hello world!」メッセージを返します。
  • /health はマイクロサービスが正常に稼働しているかを確認するための一般的なエンドポイントです。高可用性を確保するために、すべての本番マイクロサービスに含めるべきです。
  • /environment は、マイクロサービスのインストール中にプラットフォームによって環境に提供される標準変数を読み取り、その値をJSON形式で返します。

これはポート80でHTTPサーバーを実行します。これはすべてのマイクロサービスに必要です。

ログは「INFO」レベルに設定されており、管理アプリケーションでのいくつかのログ情報を表示します。ログレベルの設定を削除すると、警告のみが記録されます。

Dockerfileの作成

アプリケーションを含む実行可能なDockerイメージをビルドするために、application.py スクリプトと同じディレクトリに「Dockerfile」を作成し、次の内容を追加します。

FROM python:alpine

COPY application.py /
RUN pip install flask waitress

ENTRYPOINT ["python"]
CMD ["-u", "application.py"]

Dockerfileは次のことを行います。

  • Alpine LinuxとPythonに基づく非常に小さなDockerディストリビューションを使用します。
  • application.py ファイルをイメージにコピーします。
  • Web フレームワークと Web サーバー (Flask と Waitress) をイメージにインストールします。
  • Docker に、application.py を引数として Python を実行するように指示します。

アプリケーションマニフェストの作成

Dockerイメージに加えて、Things Cloud はDockerイメージを正しく実行するためにいくつかの追加情報を必要とします。これはアプリケーションマニフェストで提供されます。他のファイルと同じフォルダに cumulocity.json ファイルを作成し、以下の内容を追加してください。

{
    "apiVersion": "2",
    "version": "1.0.0",
    "provider": {
        "name": "Things Cloud"
    },
    "isolation": "MULTI_TENANT",
    "replicas": 2,
    "livenessProbe": {
        "httpGet": {
            "path": "/health"
        },
        "initialDelaySeconds": 10
    },
    "readinessProbe": {
        "httpGet": {
            "path": "/health"
        },
        "initialDelaySeconds": 10
    },
    "requiredRoles": [ ],
    "roles": [ ]
}

マイクロサービス:

  • マルチテナントマイクロサービスであるため、多くの顧客がサブスクライブしていても、一度しか実行されません。
  • 高可用性の本番環境マイクロサービスに必要な2つのレプリカがあります。注: 高可用性が不要な開発目的では、レプリカを1つだけに設定できます。
  • Things Cloud がマイクロサービスの健全性と実行可能性を確認するために使用する、いわゆる liveness プローブと readiness プローブがあります。
  • ロールは不要で、ロールも提供しません。情報を出力するだけです。

アプリケーションをビルドする

次のDockerコマンドを実行してDockerイメージをビルドし、image.tar として保存します。

$ docker build -t hello-python-microservice .
$ docker save hello-python-microservice > "image.tar"

次に、image.tar とマニフェスト cumulocity.json をZIPファイルにパッケージ化します。

$ zip hello-microservice cumulocity.json image.tar

生成された hello-microservice.zip ファイルにはマイクロサービスが含まれており、Things Cloudプラットフォームにアップロード可能となりました。

サンプルの実行

hello-microservice.zip をプラットフォームにアップロードするには、UIを使用します。管理アプリケーションの、エコシステム > マイクロサービスに移動し、マイクロサービスの追加をクリックします。マイクロサービスのZIPファイルをドロップして、サブスクライブをクリックします。マイクロサービスZIPファイルのアップロードに関する詳細は、カスタムマイクロサービスを参照してください。

マイクロサービスがテナントによって正常にアップロードされ、サブスクライブされた後、それはDockerコンテナ内で実行されます。管理アプリケーションでマイクロサービスのステータスおよびログタブを確認することで、これを確認できます。

マイクロサービスを試すには、curlのようなコマンドラインツールを使用します。tenantID は、ユーザーアイコン をクリックしたときに表示される右の引き出しのプラットフォーム情報の下にあります。

$ curl -u '<tenantID>/<username>:<password>' https://<URL>/service/hello/environment
{
    "microserviceIsolation": "MULTI_TENANT",
    "mqttPlatformUrl": "tcp://cumulocity:1881",
    "password": "...",
    "platformUrl": "https://cumulocity:8111",
    "tenant": "mytenant",
    "user": "servicebootstrap_hello-microservice"
}

注意点として、マイクロサービスへのすべてのリクエストは自動的に認証されます。認証なしでcurlコマンドを実行してみてください。

$ curl -v https://<URL>/service/hello/environment
…
< HTTP/1.1 401 Unauthorized
…
{"error":"general/internalError","message":"No auth information found","info":"https://cumulocity.com/guides/reference/rest-implementation"}

マイクロサービスユーティリティツールの使用

マイクロサービスユーティリティツールを使用して、アプリケーションをビルド、アップロード、およびサブスクライブすることもできます。このツールは、Dockerfile とアプリケーションファイルを含む docker フォルダが必要です。

docker/Dockerfile
docker/application.py
cumulocity.json

ソースコード

このHello worldマイクロサービスのソースコードは、GitHubリポジトリにあります。さらに、GitHubリポジトリには、より包括的なPythonマイクロサービスアプリケーションも用意されています。このアプリケーションはThings Cloud REST APIを使用し、マイクロサービスが稼働しているかどうかを確認したり、デバイスを作成してランダムな測定値を取得したり、特定のテナントの現在のアプリケーションサブスクリプションを取得したりするためのエンドポイントを公開しています。

Node.js マイクロサービス

Things Cloud は、Javaを使用してマイクロサービスを開発するためのSDKを提供します。ただし、一般的な要件を満たす限り、マイクロサービスを開発するための好みの技術スタックを選択することができます。

この例では、Node.jsベースのマイクロサービスを作成してデプロイする方法を学びます。アプリケーションは、マイクロサービスが正常に動作しているかどうかを確認し、一部の環境変数を取得するためのエンドポイントを公開します。

このアプリケーションは、Things Cloud @c8y/client JavaScriptライブラリを使用してアラームにサブスクライブします。新しいアラームが作成されると、Slackチャンネルに通知が届きます。

前提条件

  • Things Cloud 認証情報(テナント、ユーザー、パスワード)。
  • SlackアプリとOAuthトークンにメッセージを投稿するSlackチャンネル。
  • Dockerのローカルインストール。
  • 次のコンテンツを含む、ルートディレクトリにある .env ファイル:
PORT=80
SLACK_OAUTH_TOKEN=<YOUR-TOKEN-GOES-HERE>
SLACK_CHANNEL_ID=<YOUR-CHANNEL_ID-GOES-HERE>

マイクロサービスの開発

Node.jsアプリケーションの構成設定

まず、ファイルを含むフォルダー node-microservice を作成します。そのフォルダー内で、次のコマンドを使用してプロジェクトを初期化します。

$ npm init

これにより、プロジェクトを識別し、その依存関係を管理するための package.json ファイルが作成されます。プロンプトが表示されたら、プロジェクトの情報を入力し、エントリポイントとして app.js を指定します。ファイルが作成されると、以下を使って依存関係をインストールします。

$ npm install --save @c8y/client @slack/web-api dotenv express

最終的に、package.json ファイルは次のようになります。

{
  "name": "node-microservice",
  "version": "1.0.0",
  "description": "Things Cloud microservice application",
  "main": "app.js",
  "dependencies": {
    "@c8y/client": "^1004.0.7",
    "@slack/web-api": "^5.0.1",
    "dotenv": "^8.0.0",
    "express": "4.17.0"
  },
  "scripts": {
    "start": "node app.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "<your-name>",
  "license": "MIT"
}

ソースコードの追加

次に、アプリケーションのメインエントリポイントとなるファイル app.js を作成します。Expressフレームワークを使用してポート80でリッスンするサーバーを起動し、エンドポイントを定義し、コントローラーが Things Cloud とSlack APIを使用するように要求します。

"use strict";

require("dotenv").config();
const express = require("express");
const app = express();

// Application endpoints
const routes = require("./routes");
routes(app);

// Server listening on port 80
app.use(express.json());
app.listen(process.env.PORT);
console.log(`${process.env.APPLICATION_NAME} started on port ${process.env.PORT}`);

// Things Cloud and Slack controllers
require("./controllers");

既に気づいているかもしれませんが、routescontrollers が必要です。以下の内容を持つ routes.js ファイルを作成します。

"use strict";

module.exports = function(app) {
    // Hello world
    app.route("/").get(function(req, res) {
        res.json({ "message" : "Hello world!" });
    });

    // Health check
    app.route("/health").get(function(req, res) {
        res.json({ "status" : "UP" });
    });

    // Environment variables
    app.route("/environment").get(function(req, res) {
        res.json({
            "appName" : process.env.APPLICATION_NAME,
            "platformUrl" : process.env.C8Y_BASEURL,
            "microserviceIsolation" : process.env.C8Y_MICROSERVICE_ISOLATION,
            "tenant" : process.env.C8Y_BOOTSTRAP_TENANT,
            "bootstrapUser" : process.env.C8Y_BOOTSTRAP_USER,
            "bootstrapPassword" : process.env.C8Y_BOOTSTRAP_PASSWORD
        });
    });
};

この時点で、マイクロサービスはウェブ経由でアクセスできるようになり、「Hello world」メッセージを返したり、マイクロサービスが正常に動作しているかどうかを確認したり、一部の環境変数を取得したりできます。

コントローラーを実装するには、まずSlackアプリを作成し、Web APIを使用するためのトークンを取得する必要があります。Slack API: Applications にアクセスして新しいアプリケーションを作成します。ワークスペースを選択し、アプリに名前を付けます(例:C8Y Slackボット)。次に、OAuthアクセストークンを取得してください。

Slackアプリとトークンを準備したら、以下の内容を持つ controllers.js ファイルを作成します。

"use strict";

/********************* Slack *********************/

// Create a new instance of the WebClient class with the OAuth access token
const { WebClient } = require("@slack/web-api");
const web = new WebClient(process.env.SLACK_OAUTH_TOKEN);

// Slack channel ID to know where to send messages to
const channelId = process.env.SLACK_CHANNEL_ID;

// Format a message and post it to the channel
async function postSlackMessage (adata) {
    // Alarm severity
    let color = {
        "WARNING" : "#1c8ce3",
        "MINOR"   : "#ff801f",
        "MAJOR"   : "#e66400",
        "CRITICAL": "#e0000e"
    };

    // Send a message from this app to the specified channel
    let src = adata.source;
    await web.chat.postMessage({
        channel: channelId,
        attachments : [{
            "text": adata.text,
            "fields": [
                {
                    "title": "Source",
                    "value": `<${src.self}|${src.name ? src.name : src.id}>`,
                    "short": true
                },
                {
                    "title": "Alarm type",
                    "value": adata.type,
                    "short": true
                }
            ],
            "color": color[adata.severity]
        }]
    });
}

/********************* Things Cloud *********************/

const { Client, FetchClient, BasicAuth } = require("@c8y/client");

const baseUrl = process.env.C8Y_BASEURL;
let cachedUsers = [];

// Get the subscribed users
async function getUsers () {
    const {
        C8Y_BOOTSTRAP_TENANT: tenant,
        C8Y_BOOTSTRAP_USER: user,
        C8Y_BOOTSTRAP_PASSWORD: password
    } = process.env;

    const client = new FetchClient(new BasicAuth({ tenant, user, password }), baseUrl);
    const res = await client.fetch("/application/currentApplication/subscriptions");

    return res.json();
}

// where the magic happens...
(async () => {

    cachedUsers = (await getUsers()).users;

    if (Array.isArray(cachedUsers) && cachedUsers.length) {
        // List filter for unresolved alarms only
        const filter = {
            pageSize: 100,
            withTotalPages: true,
            resolved: false
        };

        try {
            cachedUsers.forEach(async (user) => {
                // Service user credentials
                let auth = new BasicAuth({
                    user:     user.name,
                    password: user.password,
                    tenant:   user.tenant
                });

                // Platform authentication
                let client = await new Client(auth, baseUrl);

                // Get filtered alarms and post a message to Slack
                let { data } = await client.alarm.list(filter);
                data.forEach((alarm) => {
                    postSlackMessage(alarm);
                });

                // Real time subscription for active alarms
                client.realtime.subscribe("/alarms/*", (alarm) => {
                    if (alarm.data.data.status === "ACTIVE") {
                        postSlackMessage(alarm.data.data);
                    }
                });
            });
            console.log("listening to alarms...");
        }
        catch (err) {
            console.error(err);
        }
    }
    else {
        console.log("[ERROR]: Not subscribed/authorized users found.");
    }

})();

このコードには2つの部分があります。最初の部分は、SlackのOAuthトークンとチャンネルID(メッセージが投稿されるチャットグループ)を必要とします。アラームの重大度に応じた色を使用してメッセージをフォーマットします。このメッセージはSlackチャンネルに投稿されます。

2つ目の部分は、Things Cloud プラットフォームへの基本認証を使用し、すべてのアクティブアラームを取得してSlackチャンネルにアラームメッセージを投稿します。その後、アラームにサブスクライブし、サブスクライブされたテナントで新しいアラームが作成されるたびにSlackチャンネルに通知します。

Dockerfileとアプリケーションマニフェスト

以下の内容を持つマイクロサービスマニフェスト cumulocity.json を作成します。

{
    "apiVersion": "2",
    "version": "1.0.0-SNAPSHOT",
    "provider": {
        "name": "Things Cloud"
    },
    "isolation": "MULTI_TENANT",
    "replicas": 2,
    "requiredRoles": [
        "ROLE_ALARM_READ",
        "ROLE_ALARM_ADMIN"
    ],
    "roles": [ ]
}

最後に、Dockerはマイクロサービスのビルド方法を知る必要があります。次のように Dockerfile を作成してください。

FROM node:alpine

WORKDIR /usr/app

COPY ./package.json ./
RUN npm install
COPY ./ ./

CMD ["npm", "start"]

マイクロサービスの展開

必要なファイルがすべて揃ったら、マイクロサービスアプリケーションのビルドとデプロイは非常に簡単です。以下のDockerコマンドを実行してDockerイメージをビルドし、image.tar として保存します。

$ docker build -t node-microservice .
$ docker save node-microservice > "image.tar"

次に、image.tar をマニフェスト cumulocity.json と共にZIPファイルにパックします。

$ zip node-microservice cumulocity.json image.tar

結果として得られる node-microservice.zip ファイルには、あなたのマイクロサービスが含まれており、Things Cloud プラットフォームにアップロードする準備が整いました。プラットフォームに node-microservice.zip をアップロードすることは、UIを介して行うことができます。管理アプリケーションで、エコシステム > マイクロサービスに移動し、マイクロサービスを追加をクリックします。マイクロサービスのZIPファイルをドロップして、次にサブスクライブをクリックします。

マイクロサービスZIPファイルのアップロードに関する詳細については、カスタムマイクロサービスを参照してください。

マイクロサービスの検証

マイクロサービスがテナントに正常にアップロードされてサブスクライブされると、Dockerコンテナで実行されます。リクエストは次のようなものになります。

GET <URL>/service/node-microservice/environment

HEADERS:
  "Authorization": "<AUTHORIZATION>"

適切な認証情報(サブスクライブされたテナントのユーザーとパスワード)を使用すると、次のようなレスポンスが返されます。

{
  "appName": "node-microservice",
  "platformUrl": "http://cumulocity:8111",
  "microserviceIsolation": "MULTI_TENANT",
  "tenant": "t...",
  "bootstrapUser": "...",
  "bootstrapPassword": "..."
}

認証ヘッダーは「Basic <Base64(<tenantID>/<username>:<password>)>」として形成されます。例えば、テナントID、ユーザー名、パスワードがそれぞれ t0071234testusersecret123 である場合、次のコマンドを使用してBase64文字列を取得できます。

$ echo -n t0071234/testuser:secret123 | base64
dDAwNzEyMzQvdGVzdHVzZXI6c2VjcmV0MTIz

認証ヘッダーは次のようになります。"Authorization": "Basic dDAwNzEyMzQvdGVzdHVzZXI6c2VjcmV0MTIz"

テナントにアクティブなアラームがある場合、あなたのSlackチャンネルに通知が届きます。また、Things Cloud REST APIを使用して新しいアラームを作成し、マイクロサービスが新しいアラームをリッスンしているかどうかを確認できます。あなたのSlackチャンネルにも通知が届きます。

Slackアプリがアラームを投稿

ソースコード

このノードマイクロサービスのコードは、私たちの公共のGitHubリポジトリにあります。