Technology Topics by Brains

ブレインズテクノロジーの研究開発機関「未来工場」で働くエンジニアが、先端オープン技術、機械学習×データ分析(異常検知、予兆検知)に関する取組みをご紹介します。

GreengrassをDocker for Windows上で使用する準備とファイル読み出しテストの手順

こんにちは、ブレインズテクノロジーの岩城です。

2018年11月末に開催されたAWS re:Invent2018で、AWS Greengrass IoTをDockerコンテナ上で起動できるようになったことが発表されました。

aws.amazon.com

この機能を生かしたデータ活用手法について、先日公開した「Dockerコンテナ上で起動したGreengrassとWindowsとの連携で広がる新たなデータ活用の選択肢」という記事でご紹介しました。

本記事では少し技術的な内容に踏み込み、GreengrassをDocker for Windows上で使用するための準備の手順と、チュートリアルを拡張して実施したGreengrassからWindows上のファイル読み込みトライアルをご紹介します。

技術ではなく応用例に関心がある場合は、下記の記事をご参照ください。 blog.brains-tech.co.jp

環境について

使用する環境としては、前回の記事でも紹介した三菱電機製の産業用PC MELIPC1002-Wを使用します。MELIPCは生産設備などから集めたデータを集約・加工する機能と、集めてきたデータを活用するためのインターフェースを備えた産業用途のPCです。MELIPCについての詳細は前回の記事やこちらの製品ページなどご参照ください。 http://www.mitsubishielectric.co.jp/fa/products/edge/melipc/items/mi1000/index.html

今回の取り組みでは、MELIPC上のWindows10にDocker for Windowsをインストールし、このDocker環境でGreengrassを動作させます。

使用したWindowsのスペックについては下記の画像をご覧ください。

f:id:brains_iwaki:20190124184333p:plain

読み込みの対象とするcsvファイルはこちらで準備したダミーのファイル (sample_data1.csv, sample_data2.csv)を使用しています。

実施概要

今回実施した内容は、Docker for Windows上でGreengrassを実行し、ローカル環境にあるcsvファイルを読み込ませてクラウドに送信させるというものです。

基本的な流れはAWSが提供している下記のリンクのチュートリアル通りですが、先々の応用を考え、一部変更を加えています。

docs.aws.amazon.com

大きな変更点は、下記の3点です。

  1. Hello Worldの代わりに、ローカル環境にあるcsvファイルを読みとるLambda関数を用意する。
  2. ローカル環境にcsvファイルを準備しておく。
  3. Dockerコンテナを起動する際に、マウントするホストの情報をコマンドに追加しておく。

今回は出荷状態のMELIPCを使用したので、Dockerのインストールやコマンドのインストールなども一から実施しています。
ユーザーを作成する部分などはさすがに割愛しますが、Greengrassを動かす前の事前準備から一通り行った作業を記載していきます。

Dockerコンテナ上でGreengrassを使用するための下準備

まずは、Dockerコンテナ上でGreengrassを使用できるように環境を整えていきます。

事前準備の概要

今回の一連のトライアル実施のため、まずはチュートリアルの指示通り、下記のソフトウェアをインストールしました(括弧内はバージョン)。

  • Docker (18.09)
  • Python (3.7) (※3.6以上ならOK)
  • pip (18.1)
  • AWS CLI (1.16)

その他、さらに前提として、AWSのアカウントを所有している必要があるので、持っていない場合は事前に作成しておきましょう。

Dockerのインストール

まずはDockerをインストールします。

チュートリアルの中のDockerへのリンクを開き、Docker Community Edition (CE)をダウンロードします。

docs.docker.com

今回はWindowsなので、スクロールしてDocker Desktop for Windows (Microsoft Windows 10)のリンクに飛びます。

(参考)
https://docs.docker.com/docker-for-windows/install/

f:id:brains_iwaki:20190124183815p:plain
Docker for WIndowsのインストールページ

Install Docker Desktop for Windows desktop appの中からDocker Desktop Installer.exeをダウンロードし、実行した後は、ウィザードに従ってインストールを進めていきます。

途中でDocker ToolboxかDocker for Windowsを選択する箇所があります。今回の用途ではDocker for Windowsを選択します。
但し、Docker for Windowsを使用する場合はVirtualBoxが起動できなくなるので注意してください(後述)。

インストール後は一度再起動し、Docker for Windowsのdesktop appを開きます。
この際、有効化が必要等のメッセージが出た場合は、メッセージを読み、問題なさそうであればOKを押して進めていきます。

以上でDockerのインストールは完了…と思ったのですが改めて起動したところ、“Not enough memory to start Docker Desktop”というメッセージが出ました。
これはDocker DesktopのsettingsからMemoryの値を下げることで解決できました。

(参考)
https://qiita.com/chakimar/items/868298096ebf9186d690

最終的な設定はこちらの画像のようになります。

f:id:brains_iwaki:20190124183933p:plain
最終的なDockerのsettingsの内容

再起動などをした時などにDockerがどのような状態になっているかは、右下のステータスバーから確認できます。

f:id:brains_iwaki:20190124184041p:plain

ここの白い鯨のアイコンが上記の画像の状態で停止していれば起動している状態です。

なお、一つ前に記載した設定画面は、このアイコンを右クリックしてsettingsを選んだ画面から開きます。

完全に起動ができたら、最後に、コマンドプロンプトで

docker version

などのコマンドを実行すると正しくdockerがインストールできているかの確認ができます。

Docker Desktop for Windowsのインストールにおける注意点

インストールページに記載されているように、Docker Desktop for Windowsインストールの際は下記の点に注意が必要です。

(1)Docker ToolboxとDocker Machineユーザー向けの注意点

Docker Desktop for Windowsでは、Microsoft Hyper-Vを使用します。 そのため、Hyper-Vが使用可能になっている必要があるのですが、Hyper-Vが使用可能な状態の場合は、VirtualBoxが使用できなくなります。 Hyper-VとVirtualBoxを並行して利用することができないので、注意しましょう。
なお、Hyper-Vの設定変更はインストーラーの方で行なってくれます。

(2)システム要件

こちらもインストールページに記載がある通りです。本記事の執筆時点では、下記のような条件があります。

  • Windows 10 64bit: Pro, Enterprise or Education
  • BIOSのVirtualizationがenableになっていること ( Hyper-Vとは別の話。基本的にenableらしい)
  • CPUがSLAT-capableであること
  • 最低4GBのRAM

今回使用したMELPICに搭載されているWindowsOSは、

  • Windows 10 Enterprise
  • 実装RAM: 4GB
  • システムの種類: 64ビット オペレーティングシステム、x64ベースプロセッサ

となっていたので、この辺りの条件はクリアできていたようです。

参考までに、今回使用したMELIPC M1002-W上のWindows上のスペックを再掲します。 f:id:brains_iwaki:20190124184333p:plain

Pythonのインストール

次に、Pythonの公式サイトからWindows用のPython3.6以上のインストールを行います。

https://www.python.org/downloads/windows/

種類がいくつもありますが、今回はx86-64 executable installerを選択しました。
簡単に補足すると、x86-64は64ビット版 (x86は32ビット版)、 executable installerはインストールに必要な要素を全てまとめてexeファイル内で保持しているようなインストールファイルです。

(参考)
https://stackoverflow.com/questions/38651672/difference-between-web-based-and-executable-installers-for-python-3-on-windows
https://python-forum.io/Thread-Python-3-x86-vs-x64-Inquiry

pipのインストール

pipは上記インストールに標準で入っていました。

updateが必要なので、下記のコマンドプロンプトで下記のコマンドを実行しました。

python -m pip install —-upgrade pip

ここは少々記憶が怪しいのですが、確か普通にpip install —upgrade pipを行うと上手くいかなかったように思います。 エラーメッセージでこのコマンドを提示してくれたかもしれないので、一度試してみて、駄目そうなら使ってみてください。

(参考)
https://qiita.com/icoxfog417/items/278ea9e217ac6fb7f10b

AWS CLIのインストール

AWS CLIのインストールは下記のコマンドです。

pip install awscli --upgrade —user

(参考)
https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html

また、AWS CLIがコマンドプロンプトから実行できるようにパスの追記などを行う必要があります。 この辺りは下記のリンクを参照してください。

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-windows.html#awscli-install-windows-path

Greengrass in Docker Containerの実行とファイル読み込みテスト

ここまででようやく下準備が終わりました。 次に、GreengrassをDocker for Windows上で起動し、テストを行います。
改めて、チュートリアルへのリンクを掲載しておきます。

docs.aws.amazon.com

作業としては、

  1. Greengrassを動作させるコンテナのイメージの取得
  2. Greengrassコアとグループの準備 (+Lambda関数の準備)
  3. AWS IoT Greengrassをローカルで起動 (コンテナを立ち上げる)
  4. Greengrassコア側の設定
  5. デプロイ
  6. AWS IoTを使用してのテスト

という流れになります。

1. コンテナイメージのプル

AWSでAWS IoT Greengrassの依存関係などの設定が完了済みのイメージが提供されているので、今回はそれを利用します。

チュートリアル通り、下記のコマンドを順に実行していきます。

(1)ログインコマンドの取得 まずは下記のコマンドを実行します。このコマンドを使うことで、出力としてDockerクライアントの認証に必要なログインコマンドを取得することができます。

aws ecr get-login --registry-ids 216483018798 --no-include-email --region us-west-2

configurationの設定ができていない場合はここでCredential Errorが発生することがあるので、その際は下記のリンクなどを参考に解決してください。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-envvars.html

(2) (1)で出力されたコマンドをそのままコピーして実行します。

(3)下記コマンドでコンテナイメージを取得します。

docker pull 216483018798.dkr.ecr.us-west-2.amazonaws.com/aws-greengrass-docker/amazonlinux:latest

あるいは、FAQの方でイメージを直接ダウンロードできるリンクが掲載されていたので、こちらをダウンロードして、docker loadコマンドでimageを取り込む形も取れるでしょうか。

https://aws.amazon.com/jp/greengrass/faqs/

興味のある方は上記リンク内で、 “Docker コンテナで AWS IoT Greengrass を実行できますか?”という文言で検索してみてください。

2. Greengrassのグループとコアを作成 + Lambda関数の準備

まずは通常のGreengrassのグループおよびコアを作成する作業を行います。

これについては、基本的なGreengrassのチュートリアル通りに進める流れになります。

AWS IoT の AWS IoT Greengrass の設定

この作業が初めての方は、一旦ここは後回しにして、通常のGreengrassのチュートリアルを行うところから始めるのが良いかと思います。

作業の途中でダウンロードする証明書情報と設定ファイルは後ほど使用するので保存しておきます。
また、チュートリアルにも記載がありますが、AWS IoT Greengrassコアソフトウェアは今回使用しているイメージに準備されているので、ダウンロードする必要はありません。

次に、Lambda関数の準備もここで実施しておきます。

今回使用したLambda関数のコードはこちらです。

import greengrasssdk
import platform
import os


client = greengrasssdk.client('iot-data')
my_platform = platform.platform()

TARGET_TOPIC = os.getenv("TARGET_TOPIC", "hello/world")
RETURN_MESSAGE = os.getenv("RETURN_MESSAGE", "Hello world! Sent from Greengrass Core.")
TARGET_DIR_PATH = os.getenv("TARGET_DIR_PATH", "/tmp")

def greengrass_hello_world_run():
    if not my_platform:
        client.publish(topic=TARGET_TOPIC, payload=RETURN_MESSAGE)
    else:
        client.publish(topic=TARGET_TOPIC, payload= RETURN_MESSAGE + ' Running on platform: {}'.format(my_platform))
    if os.path.isdir(TARGET_DIR_PATH):
        file_list = [os.path.join(TARGET_DIR_PATH, item) for item in os.listdir(TARGET_DIR_PATH) if (item.endswith(".csv")) and (item.startswith(".") == False)]
        file_list.sort()
        if len(file_list) > 0:
            for use_file in file_list:
                with open(use_file, "r") as f:
                    first_row = f.readline()
                first_row = str(first_row)
                main_filename = use_file.split("/")[-1]
                client.publish(topic=TARGET_TOPIC, payload="File name is {}".format(main_filename))                
                client.publish(topic=TARGET_TOPIC, payload="First row of this file: {}".format(first_row))
        else:
            client.publish(topic=TARGET_TOPIC, payload="There is no csv file")
    else:
        client.publish(topic=TARGET_TOPIC, payload="The TARGET_DIR_PATH dose not exist.")

def function_handler(event, context):
    greengrass_hello_world_run()
    return

この関数では、環境変数としてファイルの読み込み先のパスやトピックの発行先、ベースになるメッセージの内容を指定できるようにしています。

関数の中でgreengrasssdkなどを使っているので、ライブラリも忘れずにアップロードしてください。 チュートリアルで提供されているHelloWorld.pyを改修すると早いかもしれません。

handlerの設定などについては、下記の画像を参考にしてください。

f:id:brains_iwaki:20190124191402p:plain

これを通常のLambda関数のアップロードの手順に従い、AWS上にアップロードしておきます。
また、latestの状態ではGreengrassにデプロイできないので、バージョン発行と、必要であればエイリアス発行も実施しておきます。

Lambda関数の準備周りの手順はこちらをご参照ください。
Lambda 関数の作成とパッケージ化

3. AWS IoT Greengrassをローカルで実行

まずは先ほどダウンロードしてきた証明書情報などを所定のフォルダに準備しておきます。 最後のdocker runだけは少しコマンドを追加しますが、基本的にはチュートリアル通りに実施すれば問題ありません。

改めてリンクを再掲します。 Docker コンテナでの AWS IoT Greengrass の実行

なお、証明書と設定フォルダが入ったgzファイルを解凍する作業では、Windowsだけで行う場合はWinZipや7-Zipなど、解凍のためのツールが必要になります。 面倒な場合は別のところで解凍しておいたほうが楽かもしれません。

証明書と設定フォルダ、ルートCA証明書の準備ができたら、はじめにダウンロードしてきたイメージをdocker runで起動します。

ここでは、今回の目的であるcsvファイルの読み込みのため、ローカルのcsvファイルへのマウント情報を追加しています。 (-v c:/Users/%USERNAME%/Downloads/gg_data_root:/tmp/gg_data_root のところ)

お手本のコードにマウントに必要なコマンドが記載されているので、書き方はそこを参照すると良いかと思います。

docker run --rm --init -it --name aws-greengrass-docker --entrypoint /greengrass-entrypoint.sh -v c:/Users/%USERNAME%/Downloads/certs:/greengrass/certs -v c:/Users/%USERNAME%/Downloads/config:/greengrass/config -v c:/Users/%USERNAME%/Downloads/gg_data_root:/tmp/gg_data_root -p 8883:8883 216483018798.dkr.ecr.us-west-2.amazonaws.com/aws-greengrass-docker/amazonlinux:latest

無事Greengrassが起動できると、コマンドプロンプトは下記のような状態になります。

f:id:brains_iwaki:20190124185247p:plain

この状態であれば、Greengrassが正しく起動しています。

4. Greengrassグループの設定

次はいよいよクラウド上でGreengrassの設定を行い、エッジ側に情報を配信 (デプロイ)します。

ここでは、コンソール上のAWS IoT Greengrassのページから、2番目の項目で作成したGreengrassのグループに、各種設定をつけていきます。

今回設定を行うのはLambda関数とサブスクリプションの設定の2つだけです。 ローカルリソースアクセスやコネクタ、ML推論などの機能は、コンテナなしの設定では使用できません。

更なる応用を見越した場合、ロールの割り当てやCloudWatchにログをあげる設定などを適宜行うことになりますが、 このトライアルではそれらの設定は不要です。

Lambda関数

まずは関数の設定を3箇所実施します。

下記の画像のように、

  • コンテナ化: コンテナなし
  • タイムアウト: 30秒
  • Lambdaのライフサイクル: 存続時間が長く無制限に稼働する関数にする

という設定にします。

f:id:brains_iwaki:20190124185702p:plain

次に、Lamba関数で使用する環境変数も設定しておきます。

f:id:brains_iwaki:20190124185347p:plain

ここで使用するトピックはサブスクリプションとテストで使用します。

また、データへのパスは、先ほどdocker runさせた時にマウントさせたパスにします。
今回は、/tmp/gg_data_root_data_dir/sample_dataの直下にcsvが入るようにマウントさせていることに注意してください。

サブスクリプション

HelloWorld関数のチュートリアルを元に、相互にやり取りできるように設定します。 今回は下記のような設定にしました。

f:id:brains_iwaki:20190124185802p:plain

解説

もともとのGreengrassではLambda関数の起動のたびにコンテナを立てていたのですが、この設定により、Lambda関数をOSプロセスとして実施できるようになり、 Docker上で動作できるようになった、という話のようです。

一方、「コンテナなし」の設定にすることで、ローカルリソースアクセスやコネクタ、Greengrass ML Inferenceなどの機能が使えなくなるといデメリットがあります。 ローカルリソースアクセスについては、この記事で紹介しているように直接アクセスの設定をすることで回避できますが、 他の機能を使っている場合は何か対策を行う必要が出てきます。

ちなみに、Greengrass ML InferenceはAmazon SageMakerやS3にあるモデルソースを使ってMLワークフローをシンプルに行うためのサービスで、 Lambda上で動かすなどの一般の機械学習とは特に関係ありません。そちらはコンテナなしでも普通に使うことができます。(ライブラリの準備等は必要です)

5. デプロイ実施

無事グループの設定が完了したら、Greengrassをデプロイします。 Greengrassが正しく起動した状態で、設定が適切になされていれば、グループの情報がエッジ側に配信されます。

f:id:brains_iwaki:20190124185927p:plain

配信後、左上の表示が正常に完了したことを示したら、テストを実施します。

6. テスト

最後にテストを行います。ここではGreengrass自体のチュートリアルのHelloWorld関数のテストと同様の設定を行います。 AWS IoTのコンソールのテストのページで下記のようにトピックのサブスクライブを行い、起動側のトピックを発行します。

f:id:brains_iwaki:20190124190050p:plain

すると、次の画像のように、デプロイしたLambda関数が動作し、csvファイルのファイル名と、1行目を読み込んだ結果をAWS IoT側に送信してきます。

f:id:brains_iwaki:20190124190104p:plain

今回はダミーのデータを入れたsample1.csv, sample2.csvという二つのファイルを準備していたので、 それらのファイル名と1行目の内容を送信してきてくれています。

これで、ファイルの読み込みが正しくできたことが確認できました。

今後の応用

以上、Dockerコンテナ上で起動したGreengrassを用いてのWindows上のファイル読み込みの簡単なトライアルを行いました。

ファイルの読み込みができたので、今後は本格的なデータ活用に進めていくことになるかと思います。

今回はAWSが提供したイメージをそのまま使用しましたが、既存ライブラリの活用とLambda関数の容量制限等を踏まえると、 ソフトウェアとして継続的に使用していくためにイメージの拡張が必要になってきそうです。

まとめ

以上で今回の記事は終了です。

長くなってしまいましたが、これでようやく本格的なGreengrassの活用に向けてのスタートラインに辿り着きました。 このステップは前提として、次にどのようにデータを活用していくか、あるいはアプリケーションをどのように構成していくかといった 本格的な取り組みに進んでいくことになると思います。

この技術を生かしてより一層データ活用が進んで行くことを期待しています。


ブレインズテクノロジーでは「共に成長できる仲間」を募集中です。
採用ページはこちら

参考資料

Dockerコンテナ上で起動したGreengrassとWindowsとの連携で広がる新たなデータ活用の選択肢

こんにちは、ブレインズテクノロジーの岩城です。

2018年11月末に開催されたAWS re:Invent2018で、AWS Greengrass IoTをDockerコンテナ上で起動できるようになったことが発表されました。これにより、GreengrassをWindows上で活用する際のハードルが下がり、データ活用を検討する際の選択肢がこれまで以上に広がっています。

今回の記事ではエッジコンピューティングに関する最近のトピックとして、GreengrassをWindowsと連携することで可能になった新しいデータ活用のイメージを紹介します。

より技術的なことに焦点を当てた記事も後日公開予定なので、実際にGreengrassをWIndows上で動かすことに興味がある方はこちらもご参照ください。

[2019年2月4日追記]
GreengrassをDocker for Windowsで使用するまでの準備と、今回テストとして実施した内容をまとめた記事を公開しました。

blog.brains-tech.co.jp

背景

従来は、GreengrassのソフトウェアはLinux系のパッケージのみで提供されており、かつDockerコンテナ上で動かすことはできませんでした。

しかし、AWS re:Invent2018で発表があったように、現在はGreengrassをDockerコンテナ上で起動することができるようになっています。

aws.amazon.com

Linuxだけでなく、WindowsやmacOS上からでもコンテナ経由でGreengrassをより容易に使えるようになった結果、 データを収集するところからの他のデバイスとの連携という点を含め、これまではなかった効率的なデータ活用の選択肢が生まれています。

実際にどのように可能性が広がったかより明確にするため、この技術の活用イメージとして生産現場からGreengrassまでのデータの流れの一例を紹介します。

AWS IoT Greengrassとは

補足として、AWS IoT Greengrassについても簡単に紹介しておきます。

AWS IoT Greengrass (以下Greengrass)はクラウドのAWSの機能の一部をローカルデバイスでも実行できるようにすることができるソフトウェアです。 例えば、弊社の場合はクラウドで提供している異常検知機能をローカルのコンピュータ (エッジコンピュータ)で実行するために使用しています。

ざっくりとした説明ですが、Greengrassを使用することで、クラウド環境で動作している一部のアプリケーションをローカル(エッジ側)のPCでも動作させることができ、 連携対象の設定などもまとめて行うことができると認識いただければ良いかと思います。

クラウドで使用している機能やデバイス間の設定をインターネット経由でエッジ側に配信するので、 ソフトウェアのアップデートやシステムの設定を少ない労力で実施できるほか、スケールさせやすいなどのメリットがあります。

AWS IoT Greengrassについては、こちらの資料も分かりやすかったのであわせてご参照ください。

dev.classmethod.jp

そもそものエッジコンピューティングのメリットとしては、データを取得したその場で実施したい処理を行うことができるため、少ない遅延で結果を得られることや クラウドに送るデータをエッジ側で加工することで、データの送信量を削減できることなどがありました。

他にもいくつかメリットがあるので、エッジコンピューティングそのものに興味がある方は弊社CTO中澤も登壇したAWS re:Invent:2018: Machine Learning at the OkO Edge (IOT214)(英語)、弊社林の過去の登壇資料AWS summit 2018の資料などをご参照ください。

re:Invent2018の映像はAWS Senior ManagerのDavid Nunnerleyによるご講演から始まり、18:12頃からアイシン・エィ・ダブリュ株式会社の佐藤雅則様のご講演、27:55頃から弊社中澤の講演という構成になっています。

www.youtube.com

www.slideshare.net

blog.brains-tech.co.jp

https://d1.awsstatic.com/events/jp/2018/summit/tokyo/aws/12.pdf

活用イメージ例

生産現場からのデータの集約・加工

生産現場などのデータを活用するためには、まずはデータを集約し、使える形にする必要があります。

このような役割を果たす仕組みや設備は数多くあるかと思いますが、今回は三菱電機の産業用PC MELIPC1002-Wを使用した場合の例を紹介します。

MELIPC MI1002-Wは、下記の画像に示したような、およそ15cm x 20cmのほどの産業用PCです。

f:id:brains_iwaki:20190124182856p:plain
MELIPC1002-Wの外観

MELIPCはWindowsを搭載しており、主にデータを集約・加工する役割と、次のデータ活用のインターフェイスとしての役割を持ちます。

また、データの集約・加工という観点では、MELIPCは「Edgecross」という、データの収集・加工のためのソフトウェアを標準でインストールしています。

このソフトウェアを使用することで、各種機器からデータを集約し、 設定に応じて加工を施した後にcsvファイルやデータベース(PostgresSQL)に保存することなどを行うことができます。

技術的な検討を実施するにあたり、今回MELIPCの実機をお借りすることができたので、そちらで行った設定や検証については別記事でご紹介します。

MELIPCおよびEdgecrossの詳細については下記の製品ページや紹介ページをご参照ください。
http://www.mitsubishielectric.co.jp/fa/products/edge/melipc/items/mi1000/index.html
https://www.edgecross.org/ja/edgecross/

集約・加工されたデータの活用

MELIPCによって収集・加工されたデータはWindows上などに保存されます。このデータをDocker for Windows経由でWindows上で動作しているGreengrassが読み出し、より高度なデータ活用を行います。 ここでのデータ活用としては、例えば弊社の場合のGreengrassを用いたエッジ環境上での異常検知が分かりやすいと思います。

ここまで紹介した例でのデータの流れを一旦整理すると下記のようになります。

  1. まずは生産現場などでデータを取得。
  2. 取得したデータをMELIPC上のEdgecrossにより集約・加工し、MELIPC内のWindows上などに保存。
  3. 保存したデータをDocker on Windows上のコンテナ内で動作しているGreengrassが読み出し、異常検知などの用途に活用。

あくまでも一例ですが、このような流れで得られたデータを使うことができます。

Greengrassからクラウドへのデータ連携

Greengrassで目的の用途で使用された後の結果やデータなどは、次の目的に応じ、クラウドにアップロードすることができます。 クラウドへのアップロードには通常のネットワークを使用する他、SORACOMさんが提供しているIoT向けの安価なワイヤレス通信を利用する選択肢があります。

soracom.jp

データをアップロードする場合は、例えば新規に得られたデータを使って異常検知モデルを更新したり、他の分析に使用することができます。

もちろん、データのアップロードは行わず、異常検知結果や実施した記録だけをクラウドに記録すしたり、あるいは全く何もしないという選択肢もあります。この辺りはどのようにデータを使いたいか、という方針に従って決めていくことになります。

活用イメージ例のまとめ

以上を踏まえ、MELIPCによるデータ収集、SORACOMのサービスを利用したクラウドとの連携、弊社の異常検知サービスImpulseをGreengrass上で使用した場合のデータ活用例の全体像をまとめたものが下記の図です。

f:id:brains_iwaki:20190129123450p:plain
GreengrassとWindowsの連携を利用したデータ活用イメージ例

データ収集技術や通信技術などの従来から実現していた仕組みに加え、今回可能になったGreengrassとWindowsの連携により、これまで以上にデータ活用の選択肢が広がっていることを感じていただけますと幸いです。

まとめ

GreengrassがDocker上で起動できるようになったことをきっかけに広がった選択肢や活用イメージ例について、MELIPCやSORACOMなどの既存サービスとの連携も含めて紹介しました。

今回の技術的進歩がもたらした大きなメリットは、使いたいデバイスやソフトウェアがある場合や、既に導入している機器をベースに新しい取り組みを行いたい場合などに可能な選択肢が増えたことにあるのではないでしょうか。

用途やアプローチは数多くあるかと思いますが、この技術で広がった選択肢により、世の中のデータ活用が更に進むことを期待しています。


ブレインズテクノロジーでは「共に成長できる仲間」を募集中です。
採用ページはこちら

参考資料

速報 re:invent 2018

こんにちは、ブレインズテクノロジーの佐々木です。現在AWSのイベントであるre:invent 2018に参加しています。

こちらの時間で11月28日(水)の午前中にCEOのAndy Jassyによる基調講演が行われました。 基調講演は例年新しいサービス・機能が発表される場であるため、毎年非常に注目されています。

今年もたくさんの新サービスが発表されましたので、簡単に紹介したいと思います。 なお、この記事は速報のため、内容に誤りがある場合がございます。ご了承ください。

f:id:ryotasasaki:20181129042326j:plain
会場の様子

続きを読む

JavaScriptでデータフレーム操作 - dataframe-js -

はじめに

こんにちは、ブレインズテクノロジーの佐々木です。4月に新卒で入社しました。学生時代は熱帯林を這いつくばって植物と戯れていました。

CSVからデータを読み取って、ちょっとした分析をしたい!というシーンはよくありますよね。そうした手軽なデータ操作のツールとしては、PythonのpandasやRのdplyrなどが機能も充実していて使いやすいため、人気があるようです。また、大量のデータを製品環境で扱うような場面では、hadoopやsparkなどの分散処理基盤を活用することも多いようです。ただ、これらの選択肢が便利すぎるがゆえに、フロントエンドでデータ操作に迫られたときにストレスを感じる人も多いのではないでしょうか。

ということで、今回は、ブラウザ上でも便利にデータ操作ができるJavaScriptライブラリ、dataframe-jsに入門します。

インストールと準備

といいつつ実際にブラウザで動かすのは面倒なので、今回はnode上で動かします。基本的にはブラウザでも同様に動く(と信じて)います。またもっと手軽に試したい方はJupyter notebookにIJavaScriptカーネルを入れると良いかもしれません(ここまでやると、pandas使えよ!ってツッコミが入りそうなのでやめました)。

npm install dataframe-jsでインストールできます。 前提として、以下のようなディレクトリ構成で作業しています。

.
├── iris.js
├── main.js
├── package.json

csvからデータを読み込む場合、データそのものではなくてPromiseが返ってきます。今回は以下のようなIrisクラスにメソッドを書き加えて、main.jsから呼び出す形で作業を進めて行きます。

  • iris.js
const DataFrame = require('dataframe-js').DataFrame;

module.exports = class Iris {
  constructor(params) {
    this.url = params.url;
    this.dfPromise = DataFrame.fromCSV(this.url);
  }
  
  showDF() {
    this.dfPromise.then(df => {
      df.show();
    });
  }
}
  • main.js
const Iris = require('./dataframe');

const iris = new Iris({
    url: 'https://raw.githubusercontent.com/uiuc-cse/data-fa14/gh-pages/data/iris.csv'
});

iris.showDF();

node main.jsで実行します。

ちょっとアヤメの話

それでは本題に入ります。この記事のゴールは、iris(アヤメ)データセットを用いて種ごとに花弁(petal)と萼片(sepal)のサイズの関係を調べ(て、I. versicolor, I. virginica, I. setosa各種の形態的特性とその適応的意義について考察す)ることとします。

USDA Forest Serviceによると、この三種はいずれも湖畔沿などの湿地に生息しsetosa(アラスカ), versicolor(五大湖付近), virginica(ニューヨークからフロリダまで)の順に寒冷地に分布しています。一般に寒い方が大気飽差が小さく(相対湿度が大きく)蒸散で水を失いにくいことが知られています。また、組織が幅広だと乾燥重量あたりの蒸散量が増えるので、virginica, versicolor, setosa の順に花弁および萼片が細長であるという仮説をたてました。

この仮説を検証するために、各3種ごとに花弁と萼片の縦横の比の平均と標準偏差を計算し、比較します。

f:id:ryotasasaki:20181009144400p:plain http://suruchifialoke.com/2016-10-13-machine-learning-tutorial-iris-classification/

データ分析

まずはデータの基本的な整形をしてみます。行のフィルターにはfilter, 列の選択にはselectが使えます。使い方はpandasというよりも、Rのdplyrやsparkのデータフレームに似ています。

  • iris.js
filterAndSelectDF() {
    this.dfPromise.then(df => {
      df
        .filter(row => row.get("species") === "versicolor")
        .select("sepal_length", "sepal_width", "species")
        .show(3);
    });
}
  • 結果
sepal_length sepal_width species
7 3.2 versicolor
6.4 3.2 versicolor
6.9 3.1 versicolor

次に、花弁縦横比(縦/幅)を計算してみます。

  • iris.js
mutateWLratio() {
    this.dfPromise.then(df => {
      df
        .map(row => row.set('sepal_wlratio', row.get('sepal_length') / row.get('sepal_width')))
        .map(row => row.set('petal_wlratio', row.get('petal_length') / row.get('petal_width')))
        .select("sepal_wlratio", "petal_wlratio", "species")
        .show(3);
    });
}
  • 結果
sepal_wlratio petal_wlratio species
1.4571 6.9999 setosa
1.6333 6.9999 setosa
1.46875 6.5 setosa

最後に、種ごとにグループ演算をして各種の形質の平均・標準偏差を求めます。groupByで指定した列に対しGroupedDataFrameオブジェクトが返されます。GroupedDataFrameオブジェクトはaggregateメソッドをもち、グループ(種)ごとに関数を適用できます。

今回は花弁・萼片それぞれに平均と分散を種ごとに計算し、一つのデータフレームにまとめています。

  • iris.js
calcSppStats() {
    this.dfPromise.then(df => {
      const groupedDF = df
      .chain(
        row => row.set('sepal_wlratio', row.get('sepal_length') / row.get('sepal_width')),
        row => row.set('petal_wlratio', row.get('petal_length') / row.get('petal_width'))
      )
      .select('sepal_wlratio', 'petal_wlratio', 'species')
      .groupBy('species');
      groupedDF
        .aggregate(group => group.stat.mean('sepal_wlratio'))
        .rename('aggregation', 'sepal_wlratio_sp_mean')
        .join(
          groupedDF
            .aggregate(group => group.stat.sd('sepal_wlratio'))
            .rename('aggregation', 'sepal_wlratio_sp_sd')
        , 'species', 'inner')
        .join(
          groupedDF
            .aggregate(group => group.stat.mean('petal_wlratio'))
            .rename('aggregation', 'petal_wlratio_sp_mean')
        , 'species', 'inner')
        .join(
          groupedDF
            .aggregate(group => group.stat.sd('petal_wlratio'))
            .rename('aggregation', 'petal_wlratio_sp_sd')
        , 'species', 'inner')
        .show(3);
    });
}
  • 結果
species sepal_wlratio_sp_mean sepal_wlratio_sp_sd petal_wlratio_sp_mean petal_wlratio_sp_sd
setosa 1.4745 0.1186 7.0779 3.1237
versic 2.1604 0.2286 3.2428 0.3124
virginica 2.2304 0.2469 2.7806 0.4073

また、group.stat.statsというメソッドを使うと各種統計量がまとめて計算できます。

分析まとめ(アヤメ)

以上の結果をテーブルにまとめました。

species 花弁縦横比(縦/幅)・平均 ± 標準偏差 萼片縦横比(縦/幅)・平均 ± 標準偏差
setosa 1.48 ± 0.12 7.10 ± 3.12
versicolor 2.16 ± 0.23 3.24 ± 0.31
virginica 2.23 ± 0.25 2.78 ± 0.41

花弁は仮説の通り、virginica, versicolor, setosa の順に細長でした。しかし、萼片については反対にsetosa, versicolor, virginicaの順に細長でした。考察として、花弁や萼片の形状(縦横比)だけでなく面積そのものや生理学的特性も考慮する必要があると考えられます。

まとめ(dataframe-js)

流石にpandasには劣りますが、基本的なデータ操作はストレスなくできた気がします。クライアント側でデータ操作がスムーズにできることで、plotly.jsなどと組み合わせて(df.toCollection()とすると簡単にオブジェクトに変換できます)1. リッチな可視化が比較的簡単に実装できるようになる、2. pandasなどを使ってデータ処理をするためだけにサーバーサイドに処理を投げる必要がなくなりデータフローがすっきりさせられる、などのメリットがあるのではないでしょうか。

追記

はてなブログでJavaScriptが実行できることを知ったので、一応試しておきました。表示ボタンを押すとhttps://raw.githubusercontent.com/uiuc-cse/data-fa14/gh-pages/data/iris.csv のデータがロードされ、一連のこれでブラウザでも同様に動く(と信じて)ことも確認できました。

結果はここに出ます。
<script src="https://cdn.rawgit.com/Gmousse/dataframe-js/master/dist/dataframe-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<input id='show_button' type=button value='表示'>
<script type="text/javascript">
$(function() {
    $('#show_button').click(function() {
        const DataFrame = dfjs.DataFrame;
        DataFrame.fromCSV('https://raw.githubusercontent.com/uiuc-cse/data-fa14/gh-pages/data/iris.csv')
            .then(df => {
                const groupedDF = df
                    .chain(
                    row => row.set('sepal_wlratio', row.get('sepal_length') / row.get('sepal_width')),
                    row => row.set('petal_wlratio', row.get('petal_length') / row.get('petal_width'))
                    )
                    .select('sepal_wlratio', 'petal_wlratio', 'species')
                    .groupBy('species');
                const resDF = groupedDF
                    .aggregate(group => group.stat.mean('sepal_wlratio'))
                    .rename('aggregation', 'sepal_wlratio_sp_mean')
                    .join(
                        groupedDF
                        .aggregate(group => group.stat.sd('sepal_wlratio'))
                        .rename('aggregation', 'sepal_wlratio_sp_sd')
                    , 'species', 'inner')
                    .join(
                        groupedDF
                        .aggregate(group => group.stat.mean('petal_wlratio'))
                        .rename('aggregation', 'petal_wlratio_sp_mean')
                    , 'species', 'inner')
                    .join(
                        groupedDF
                        .aggregate(group => group.stat.sd('petal_wlratio'))
                        .rename('aggregation', 'petal_wlratio_sp_sd')
                    , 'species', 'inner');
                const resJSON = JSON.stringify(resDF.toCollection(), null, 2);
                $('#result').text(resJSON);
            })
    });
});
</script>


ブレインズテクノロジーでは「共に成長できる仲間」を募集中です。
採用ページはこちら

参考

まだ日本語対応していないAmazon LexのBotをAmazon Translateで翻訳させてみた

こんにちは、ブレインズテクノロジーの桑原です。

ブレインズテクノロジーは、ImpulseやNeuronといった、企業の生産性を劇的に向上させるサービス開発に従事していますが、私達自身の生産性も向上させるため、利用するシステムの探求と刷新を繰り返しています。

最近では、アマゾンウェブサービスが提供する機械学習サービスのAmazon Lexを利用して、営業記録やスケジュールの登録を簡素化できないかと模索をしていましたが、Amazon Lexとの会話は全て英語でした。

この度、Amazon Translateが日本語に対応したことを記念して、Amazon Lexとの英会話をリアルタイムに日本語に翻訳するBotを作ってみたいと思います。

f:id:brains-tech:20180830010820p:plain:w200
アマゾンウェブサービスが提供する機械学習サービス

Slack Botがやること

Slackでスケジュールを登録したいと呟くと、Amazon Lexが何の予定なのかや日時を聞いてくれるBotです。本記事ではAmazon Lexと会話するところまでを解説しますが、これをGoogle Calendar等のカレンダーと連携させて実際に予定を入れることもできたりします。

なお、ZapierがなくてもSlackとAWS Lambdaをつなぐことは出来ますが、今回は極力プログラムを書かないことを目指すためZapierを採用しています。

f:id:brains-tech:20180826220337p:plain

Amazon Translateとは?

深層学習モデルを使用して、従来の統計ベースやルールベースの翻訳アルゴリズムよりも正確で自然な翻訳を提供するニューラル機械翻訳サービスです。

2018年4月にサービスが提供され、当初は下記6言語が英語間翻訳に対応していましたが、

  • アラビア語
  • 中国語 (簡体字)
  • フランス語
  • ドイツ語
  • ポルトガル語
  • スペイン語

AWS Summit 2018 New Yorkにて、2018年7月から日本語を含めた下記6言語にも対応することが発表されました。

  • 日本語
  • ロシア語
  • イタリア語
  • 中国語 (繁体字)
  • トルコ語
  • チェコ語

Amazon Translateの詳細はこちら

Amazon Lexとは?

音声やテキストを使用して、任意のアプリケーションに対話型インターフェイスを構築するサービスです。

Amazon Alexaと同様、音声のテキスト変換には自動音声認識 (ASR)、テキストの意図認識には自然言語理解 (NLU) という高度な深層学習機能が使われているため、チャットBotのようなアプリケーションでリアルな会話を実現することができます。しかし、残念ながら現時点の対応言語は英語のみとなっており、日本語には対応していません (2018年8月時点) 。

Amazon Lexの詳細はこちら

Slack Botを作る

それでは、Slack Botを作っていきます。AWS Lambdaのところで簡単なプログラムを書きますが、後はポチポチするだけで構築完了です。大きくハマることがなければ1〜2時間程度で終わるはずです。

構築ステップ

Amazon LexとSlackの融合

公式サイトのマニュアルに詳しい手順が記載されているので、これを参考にしながら設定をしていきます。本記事では、少しややこしい連携設定を補足します。

  • ステップ 1: Amazon Lexボットを作成する
  • ステップ 2: Slackにサインアップして Slackチームを作成する
  • ステップ 3: Slackアプリケーションを作成する
  • ステップ 4: SlackアプリケーションとAmazon Lexボットを統合する
  • ステップ 5: Slack統合を完了する
  • ステップ 6: 中間テストをする

Amazon TranslateとSlackの融合

Amazon LexがSlackに返答した英語をZapierがひろってAWS Lambdaに投げる仕組みです。こちらもGUIを使ってお手軽に設定をしていきます。

  • ステップ 7: Slackに着信Webフックを追加する
  • ステップ 8: AWS Lambda関数に付与するロールを作成する
  • ステップ 9: AWS Lambda関数を作成する
  • ステップ 10: ZapierでSlackとAWS Lambdaを統合する
  • ステップ 11: 統合テストをする

ステップ 1: Amazon Lexボットを作成する

AWSマネージドコンソールからAmazon Lexを選択します。いくつかサンプルが準備されていますので、今回はその中から[ScheduleAppointment]を選択します。後は、好きなようにサンプルをいじり、[Build]と[Publish]まで実行します。

f:id:brains-tech:20180830124454p:plain

なお、[Build]が完了すると画面右側の[Test Chatbot]でテストチャットを行うことができます。こちらが入力する言葉を変えても、それに応じた返答をし、アポイントを予約するところまで導いてくれることが確認できます。

f:id:brains-tech:20180826234133p:plain:w300

ステップ 2: SlackにサインアップしてSlackチームを作成する

既存のSlackチームを使いたい場合は、このステップをとばしてOKです。

ステップ 3: Slackアプリケーションを作成する

Slack APIコンソールを開き、[Start Buildings]を押下してアプリを作成します。 [Bot Users]と[Interactive Components]の設定をした後、 [Basic Information]のClient IDClient SecretVerification Tokenをメモしておきます。

f:id:brains-tech:20180827112047p:plain

ステップ 4: SlackアプリケーションとAmazon Lexボットを統合する

AWSマネージドコンソールからAmazon Lexを選択します。先ほど作成したAmazon Lexボットを選択し、[Channels]タブからSlackとの連携設定をします。Client IDClient SecretVerification Tokenにはステップ 3のSlack APIコンソールでメモしておいた値を入力します。

f:id:brains-tech:20180827210833p:plain

[Activate]が完了するとPostback URLOAuth URLが表示されるので、こちらをメモしておきます。

f:id:brains-tech:20180827212342p:plain

ステップ 5: Slack統合を完了する

Slack APIコンソールを開き、[OAuth & Permissions]で権限設定をしたあと、Redirect URLsにステップ 4のAWSマネージドコンソールでメモしておいたOAuth URLを入力します。

f:id:brains-tech:20180827213800p:plain

[Interactive Components]と[Event Subscriptions]のRequest URLに、ステップ 4のAWSマネージドコンソールでメモしておいたPostback URLを入力します。

f:id:brains-tech:20180827215339p:plain
f:id:brains-tech:20180827215414p:plain

これで、連携設定は完了です!

ステップ 6: 中間テストをする

Slackにログインし、テストをしたいチャンネルにSlack APIで作ったBotユーザを招待します。 ここでは、realbot子さんが#test-channelにlexbot子さんを招待しています。

f:id:brains-tech:20180827223216p:plain

少し分かりづらいのですが、realbot子さんがこちらが入力したメッセージ。lexbot子さんがAmazon Lexが返してくれているメッセージです。

f:id:brains-tech:20180830114539p:plain

このように、lexbot子さんは英語しか話せないので、次のステップ以降でAmazon TranslateとSlackを融合させて、日本語を話してもらうようにします。

ステップ 7: Slackに着信Webフックを追加する

Slack App ディレクトリを開き、着信 Webフックを追加します。

# lexbot子さんの着信 Webフックを利用してもOKですが、今回は表示名を変えたかったため、別の着信 Webフックを追加しています。

f:id:brains-tech:20180829225619p:plain

名前やアイコンを指定したあと、Webhook URLをメモしておきます。

f:id:brains-tech:20180830125305p:plain

ステップ 8: AWS Lambda関数に付与するロールを作成する

AWSマネージドコンソールからIAMを選択します。[ロール]->[ロールの作成]を押下し、AWSサービスのLambdaを選択します。

f:id:brains-tech:20180829214638p:plain

TranslateReadOnlyポリシーを選択します。

f:id:brains-tech:20180829215132p:plain

任意のロール名を入力しロールを作成します。

f:id:brains-tech:20180829215301p:plain

ステップ 9: AWS Lambda関数を作成する

AWSマネージドコンソールからAWS Lambdaを選択します。[関数の作成]を選択して、関数を一から作成します。 Zapierを利用する場合、関数の名前は必ずzapier_evalとしてください。ステップ 8で作成したロールを選択して関数を作成します。

f:id:brains-tech:20180830130047p:plain

関数コードを入れて保存します。

import json
import boto3
import unicodedata
from urllib.parse import parse_qs
import urllib.request

def chk_lang(string):
    
    for char in string:
        name = unicodedata.name(char)
        if 'CJK UNIFIED' in name or 'HIRAGANA' in name or 'KATAKANA' in name:
            return True
    return False
    
def lambda_handler(event, context):
    
    plain_text = event['plain']
    lang = chk_lang(plain_text)

    if lang: return {'statusCode': 101}

    translate = boto3.client(service_name = 'translate', region_name = 'us-east-1', use_ssl = True)
        
    response = translate.translate_text(Text = plain_text, 
                SourceLanguageCode = 'en', TargetLanguageCode = 'ja')

    translated_text = response['TranslatedText']
    
    hook = 'https://hooks.slack.com/services/xxxxx/xxxxx' #ステップ 7で確認したWebhook URLを入力
    headers = {'Content-Type' : 'application/json'}
    method = 'POST'
    
    json_data = {'text' : '`(ja) ' + translated_text + '`'}
    json_data = json.dumps(json_data).encode('utf-8')

    request = urllib.request.Request(hook,
                data = json_data,
                method = method, headers=headers)

    urllib.request.urlopen(request)
    
    return translated_text

ステップ 10: ZapierでSlackとAWS Lambdaを統合する

Zapier Exploreを開き、[Make a Zap!]を選択します。

# Zapierは様々なサービスを無料で連携させることができますが、AWS Lambdaを連携させる場合は有償となります。

Triggerは、SlackにNew Message Posted to Channelがあった場合とします。

f:id:brains-tech:20180829234023p:plain

Channelを指定し、Trigger for Bot Messages?をnoからyesに変更します。

f:id:brains-tech:20180829235032p:plain

今回は、Invoke Functionを選択します。返り値を受け取ってあれこれしたい場合はRun Codeがいいでしょう。

f:id:brains-tech:20180829235959p:plain

Functionにはステップ 9で作成したzapier_evalを選択、Argumentsでeventオブジェクトに渡すデータとしてSlackのTextを指定します。

f:id:brains-tech:20180830001744p:plain

ZapのステータスをONにしたら、全ての設定が終了です。ふぅ

ステップ 11: 統合テストをする

Slackにログインし、realbot子さんがlexbot子さんに話しかけます。すると、tranbot子さんが英語を日本語に翻訳してくれました!

f:id:brains-tech:20180830131538p:plain

おわりに

トリガーとなる英語を適切に入力しないとAmazon Lexは反応してくれませんが、Hi (やあ) やHello (こんにちは) といった簡単な言葉をトリガーにしておけば、

  1. 英語で入力
  2. 英語で返答 (by Amazon Lex)
  3. 日本語に翻訳 (by Amazon Translate)

から

  1. 日本語で入力
  2. 英語に翻訳 (by Amazon Translate)
  3. 英語で返答 (by Amazon Lex)
  4. 日本語に翻訳 (by Amazon Translate)

とすることもできそうです。

それが実現できると、InputとOutputの言語はAmazon Translateが対応する12言語を自由に組み合わせることができるようになります。 Amazon Lexが英語にしか対応していなくても、多言語コミュニケーションが可能になる訳ですね。

余談

無茶なキーワードを入れたら返ってきた返答です。普段は敬語のtranbot子さんですが、たまに不機嫌になるのでご注意を (笑)

f:id:brains-tech:20180830004242p:plain


ブレインズテクノロジーでは「共に成長できる仲間」を募集中です。
採用ページはこちら

WindowsのDockerで慣れないこと(実践編)

Neuron開発チームの木村です。

Neuronは、ほとんどの場合Windowsにインストールされるため、テスト環境をWindows上に構築しています。 このテスト環境構築を効率化するため、docker上でのWindows利用を模索しています。

前回に引き続き、 Windows上のdockerでイメージをビルドする際・コンテナを動かす際に慣れないことを紹介します。

前回検討した導入方法から、dockerは「Hyper-Vコンテナ + LCOW」の形態で利用しています。 (そのため、Docker for Windowsを利用する場合とは異なる状況であることに注意ください。)

Windowsのバージョンは、
Windows 10 Pro バージョン 1803 (Version 10.0.17134.165)、

dockerのバージョンは、

Client:
 Version:           master-dockerproject-2018-07-18
 API version:       1.38
 Go version:        go1.10.3
 Git commit:        48fbb12b
 Built:             Wed Jul 18 23:51:23 2018
 OS/Arch:           windows/amd64
 Experimental:      false

Server:
 Engine:
  Version:          master-dockerproject-2018-07-18
  API version:      1.38 (minimum version 1.24)
  Go version:       go1.10.3
  Git commit:       7f91801
  Built:            Thu Jul 19 00:00:43 2018
  OS/Arch:          windows/amd64
  Experimental:     true

LCOWのバージョンは、4.14.29-0aea33bcです。

以下、OSがWindowsであるコンテナを「WindowsOSコンテナ」、OSがLinuxであるコンテナを「LinuxOSコンテナ」と呼びます。

目次

イメージビルド編

multi-stage buildsで、名前に半角スペースを含むファイルをCOPYできない

multi-stage buildsでイメージビルドする際、名前に半角スペースを含むファイル(または、それを含むディレクトリ)を、WindowsのイメージにCOPYしようとすると、ビルドが以下のエラーで止まります。

COPY failed: file does not exist

よくある半角スペースの問題と考え、"で括るために
COPY --from=builder ["src/sample file", "."]のJSON形式で指定しても、今度は別のエラーで止まります。

COPY failed: Forbidden path outside the build context

昔からあるファイル・ディレクトリ名の半角スペース問題に、久しぶりに遭遇しました。(厳密には別の問題のようですが…)


RUN/CMDで使われるシェルを意識する

RUN/CMDにシェル形式でコマンドを指定する際、コマンド実行に使われるシェルがcmd(コマンドプロンプト)なのかpowershellなのかを意識することが重要です。powershell特有のコマンドを使う際は自然と意識すると思いますが、このふたつは環境変数の取得方法がだいぶ異なるため、javaのような自分でパスを通すコマンドを使う時に注意が必要です。

cmd powershell
環境変数VARの取得方法 %VAR% $env:VAR
存在しない環境変数NONEから取得される値 %NONE%という文字列 空文字列

シェル形式で書かれたRUN/CMDのコマンド実行に使われるシェルは、デフォルトがcmd(cmd /S /C)ですが、SHELLで変更することができます。
使用するイメージのDockerfileを読むか、docker inspectでイメージのCmdプロパティからシェルを特定するとよいでしょう。たとえば、openjdk:8u171-jdk-nanoserverpowershellをシェルに指定しています。


コンテナ実行編

[LinuxOSコンテナ] メモリ割り当てが変更できない

LinuxOSコンテナに対して、docker run時に--memoryオプションから割当メモリを指定しても無視され、 以下のように、約1GBのメモリが割り当てられます。

PS C:\> docker run --rm --memory 2g alpine:3.8 cat /proc/meminfo
MemTotal:         985568 kB
MemFree:          870932 kB
MemAvailable:     805808 kB
...

WindowsOSコンテナに対しては、以下のように、指定通り約2GBのメモリが割当たります。

PS C:\> docker run --rm --memory 2g microsoft/windowsservercore:1803 systeminfo
...
Total Physical Memory:     2,559 MB
Available Physical Memory: 2,194 MB
...

mobyのissueを読む感じでは、 LCOWを使用しているために起きる問題のようです…


[LinuxOSコンテナ] user指定が効かない

LinuxOSコンテナに対して、docker run時に--userオプションを指定しても無視され、 以下のように、rootユーザで実行されます。

PS C:\> docker run --rm --user guest alpine:3.8 whoami
root

LCOW使用時の既知のバグのようです。 イメージビルド時のUSER指定も効かないため、LinuxOSコンテナは常にrootで動くことになります。

そして、この影響なのか、コンテナ内のディレクトリ・ファイルの所有者とグループが大体root:rootになってしまいます。


[LinuxOSコンテナ] mountされたvolumeに対してchmod/chownが効かない

「イメージビルド時のVOLUME」や「docker run時の--volumeオプション」でvolumeに指定されたディレクトリ以下に対して、chmod/chownが効きません。
以下の例のように、chmodを行っても、終了コードが0であるにも関わらずパーミッションが変更されません(例ではjenkins/jenkins:2.134イメージを使用)。

root@fcdd7ae24179:/var/jenkins_home# ls -la
total 0
-r-xr-xr-x 1 root root  102 Jul 30 05:02 copy_reference_file.log
drwxrwxrwx 1 root root 4096 Jul 30 05:02 init.groovy.d
root@fcdd7ae24179:/var/jenkins_home# chmod 755 copy_reference_file.log
root@fcdd7ae24179:/var/jenkins_home# echo $?
0
root@fcdd7ae24179:/var/jenkins_home# ls -la
total 0
-r-xr-xr-x 1 root root  102 Jul 30 05:02 copy_reference_file.log
drwxrwxrwx 1 root root 4096 Jul 30 05:02 init.groovy.d

既知の問題ではあるようですが、回避策は特にない状況です。
このことが起因する問題には色々当たりましたが、大きかったのはjenkinsでリポジトリをダウンロードする際にエラーが起きることでした。 volume指定を使わないことでエラーを回避しましたが、docker cpでjenkins設定の永続化を行うことになってしまいました…


稼働中のコンテナに対してdocker cpできない

稼働中のコンテナに対してdocker cpを実行すると、以下のエラーが発生してコピーができません。

PS C:\Users\user\Documents> docker cp 7961ee45d606:/var .
Error response from daemon: filesystem operations against a running Hyper-V container are not supported

なお、停止中のコンテナに対しては正常にコピーできます。


おわりに

Windows上のdockerにて、イメージビルドやコンテナ実行の際に慣れないことを紹介しました。 LinuxOSコンテナについて、「volume周りの問題」と「メモリが最大1GB」がかなり厄介な問題です。

上記の他にも以下のようなことがありました。

  • コンテナ自体を作成できないイメージがある
    • 例えば、openjdk:8u171-jdk-nanoserver
    • しかし、openjdk:8u171-jdk-windowsservercore はコンテナ実行可能
  • LinuxOSコンテナ用のDockerfileを、WindowsOS用に書き換えるには細かいところも気にしないとならない
    • javaのclasspath区切りは、Windowsでは:ではなく;、など
  • dockerdを再起動すると、元々稼働していたコンテナがstartできなくなる

やはり、experimentalなLCOWを使ってLinuxOSコンテナを扱うのは、まだ早いのでしょうか。 WindowsOSコンテナのみに絞ればよいかもしれませんが、そうするとLCOWを使う理由もないですし…

docker上でのWindows利用について、LCOWは引き続き試してゆきますが、 現状では、Docker for Windowsが可能な範囲で模索してゆくほうがよさそうです。


ブレインズテクノロジーではエンジニアを募集中です
採用ページはこちら

AngularでPlotlyのグラフを描画してみた

ブレインズテクノロジーの加藤です。

例年以上に暑い日々が続いていますが、如何お過ごしでしょうか。
ブレインズテクノロジーでは、そんな暑い夏にも負けないくらいの空前のボルダリング熱波が巻き起こっています。
週に1回、有志でジムに通っていますが、いい運動になりますし、課題をクリアしたときの達成感はひとしおです。

さて今回は、AngularでPlotlyを動かしてグラフ描画する方法についてまとめます。

はじめに

Angularを触ったことのない人には、下記のチュートリアルがおすすめです。
ヒーローがどうのこうのという謎アプリを作らされますが、何となくのイメージが掴めると思います。

Angular 日本語ドキュメンテーション

新しいアプリケーションを作成

下記のコマンドで、新しいアプリケーションを作ります。
名前は何でもいいですが、今回はとりあえず「angular-plotly」とします。

$ ng new angular-plotly

Plotlyファイルのimport

先ほど作った「angular-plotly」下に移動し、npm経由でplotlyの型定義ファイルを取得します。

$ cd angular-plotly
$ npm install plotly.js --save
$ npm install @types/plotly.js --save

Plotlyによるグラフ描画の実装

シンプルに下記サンプルの棒グラフ「Basic Bar Chart」を描画します。
グラフ出力時のデータなどは、サイトの情報をそのまま用いることにします。
Javascript Graphing Library D3.js-based Bar Charts | Examples | Plotly

ここからは、実際のtypescriptのコードとhtmlファイルを記載します。

  • src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import * as Plotly from 'plotly.js';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent implements OnInit {
  ngOnInit() {
    const data = [
      {
        x: ['giraffes', 'orangutans', 'monkeys'],
        y: [20, 14, 23],
        type: 'bar'
      } as Plotly.ScatterData
    ];
    Plotly.newPlot('myDiv', data);
  }
}
  • src/app/app.component.html
<div id='myDiv'></div>

解説

簡単にコードを解説します。

import * as Plotly from 'plotly.js';

で、先程npm経由で取得したplotlyを呼べるようにしています。

また、

import { Component, OnInit } from '@angular/core';

で、起動時の動作を定義するインターフェース「OnInit」をimportしています。
「OnInit」をimplementsした「ngOnInit」関数にてPlotly描画の定義をすることにより、Angular起動と同時にグラフ描画処理が実行されます。

export class AppComponent implements OnInit {
  ngOnInit() {
    const data = [
      {
        x: ['giraffes', 'orangutans', 'monkeys'],
        y: [20, 14, 23],
        type: 'bar'
      } as Plotly.ScatterData
    ];
    Plotly.newPlot('myDiv', data);
  }
}

「ngOnInit」関数で定義している内容は、前述の「Basic Bar Chart」のJSコードを一部改変したものです。
Plotlyで描画する際に、dataの型を「Plotly.ScatterData」に変換しています。

Angular起動

下記のコマンドを実行することにより、Angularが起動します。

$ ng serve --open

・・・が、画面に何も描写されないと思います。

デベロッパーツールで確認すると、下記のようなエラーが出ています。

Uncaught ReferenceError: global is not defined

回避策として、src/polyfills.tsの文末に以下を記載します。

 // "global is not defined"の対応
(window as any).global = window;

この状態で再起動すると、無事にグラフが描画されました。

f:id:kato_takayuki:20180723171618p:plain

おわりに

今回は最もシンプルなグラフを生成しましたが、同じ要領でいろいろなグラフが生成出来ると思います。

おわり。


ブレインズテクノロジーでは「共に成長できる仲間」を募集中です。
採用ページはこちら