2
2

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.

Dockerコンテナ上でGreengrass(V1)を実行する

Posted at

#はじめに
AWSはGreengrass Coreソフトウェアと依存関係がインストールされたDocker file、Dockerイメージを公開しています。
今回は、以下の公式ドキュメントを参考に、EC2上のDockerコンテナでGreengrassを実行します。
参考:Docker コンテナでの AWS IoT Greengrass の実行
#1 環境構築
公式ドキュメントの「Prerequisites」を参考に、Greengrassを実行する環境を用意します。
今回は最終的に以下の構成となります。
image.png
##1-1 ネットワーク設定
VPC周辺を以下の通り作成します。
なお、作業は東京リージョンで行い、サブネットはap-northeast-1aに作成しています。
※詳細な手順は以下を参照のこと
 参考:AWS上にエッジ環境を構築してGreengrass(V1)の動きを確認する - 1-1 ネットワーク周りの作成

作成するもの 詳細
VPC CIDR:10.0.0.0/16
サブネット CIDR:10.0.0.0/24
インターネットゲートウェイ VPCにアタッチする
ルートテーブル 10.0.0.0/16 -> local , 0.0.0.0/0 -> IGW

##1-2 インスタンスの作成
次に、今回Greengrassを動かすEC2インスタンスを作成します。
※詳細な手順は以下を参照のこと
 参考:AWS上にエッジ環境を構築してGreengrass(V1)の動きを確認する - 1-2 EC2インスタンスの作成

1.以下の通りセキュリティグループを作成する
※アウトバウンドルールはデフォルトのままとしている

タイプ ポート範囲 ソース 備考
SSH 22 マイIP 自身の環境からEC2にSSH接続できるようにするため
カスタムTCP 8883 任意の場所 IoT Coreとの通信用

image.png
2.インスタンスを以下の通り作成する

設定項目 選択した内容
AMI Amazon Linux 2 AMI(64ビット(x86))
インスタンスタイプ t2.micro
ネットワーク ※作成したVPC
サブネット ※作成したサブネット
自動割り当てパブリックIP 有効
セキュリティグループ ※作成したセキュリティグループ

3.yum upgradeyum update を実施する

$ sudo yum upgrade -y
$ sudo yum update -y

4.タイムゾーンを変更する
参考:【Linux】タイムゾーン(Timezone)の変更

#タイムゾーンファイルの変更
$ sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

#タイムゾーンが変更されていることを確認
$ date
Wed Apr 14 18:29:31 JST 2021

#再起動後にUTCに戻ることを防ぐため /etc/sysconfig/clock を編集
$ sudo vim /etc/sysconfig/clock
/etc/sysconfig/clock
ZONE="Asia/Tokyo"
UTC=false

##1-3 DockerとAWS CLIのインストール
公式ドキュメントのPrerequisitesにある通り、DockerとAWS CLIをインストールします。
「AWS CLI version 2」タブの要件に従います。

1.Dockerをインストールする
参考:Amazon Linux2にDockerをインストールする

#Dockerインストール
$ sudo yum install -y docker

#ec2-userをdockerグループに追加(コマンド実行後ログインし直す)
$ sudo usermod -a -G docker ec2-user

#バージョン確認
$ docker version
Client:
 Version:           20.10.4
#中略
Server:
 Engine:
 Version:           20.10.4
#後略

#自動起動設定
$ sudo systemctl enable docker

2.AWS CLIをバージョン2にアップデートする
参考:Linux で AWS CLI バージョン 2 をインストールする

#現在のバージョン確認
$ aws --version
aws-cli/1.18.147 Python/2.7.18 Linux/4.14.225-169.362.amzn2.x86_64 botocore/1.18.6

#AWS CLI V1の削除
$ which aws
/usr/bin/aws

#AWS CLI V2のインストール
$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"

$ unzip awscliv2.zip

$ sudo ./aws/install
You can now run: /usr/local/bin/aws --version

#インストールされたことの確認
$ aws --version
aws-cli/2.1.38 Python/3.8.8 Linux/4.14.225-169.362.amzn2.x86_64 exe/x86_64.amzn.2 prompt/off

以上で以下の構成まで作成できました。
image.png
#2 Amazon ECRからGreengrassコンテナイメージを取得する
公式ドキュメントに記載の通り、Greengrassのコンテナイメージを取得します。
参考:ステップ 1. Amazon ECR から AWS IoT Greengrass コンテナイメージを取得する

1.awsコマンドを実行するため、ユーザのクレデンシャル情報環境変数に設定する
※あるいはaws configureコマンドで設定を行う
 なお、利用するIAMユーザには ecr:GetAuthorizationToken のポリシーがアタッチされている必要がある

#環境変数の設定
$ export AWS_ACCESS_KEY_ID='AWSのアクセスキー'
$ export AWS_SECRET_ACCESS_KEY='AWSのシークレットアクセスキー'

#設定されていることの確認
$ echo $AWS_ACCESS_KEY_ID
$ echo $AWS_SECRET_ACCESS_KEY

2.Greengrassのコンテナイメージをプルする

#Amazon ECRレジストリにログイン
$ aws ecr get-login-password --region  us-west-2 | docker login --username AWS --password-stdin https://216483018798.dkr.ecr.us-west-2.amazonaws.com
WARNING! Your password will be stored unencrypted in /home/ec2-user/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

#Greengrassコンテナイメージを取得
$ docker pull 216483018798.dkr.ecr.us-west-2.amazonaws.com/aws-iot-greengrass:latest

#取得結果
$ docker images
REPOSITORY                                                        TAG       IMAGE ID       CREATED       SIZE
216483018798.dkr.ecr.us-west-2.amazonaws.com/aws-iot-greengrass   latest    d32ac0324f03   8 weeks ago   1.25GB

3.シンボリックリンクとハードリンクの保護を有効にする

#rootユーザに切り替え(sudoで実行してもPermission Deniedになったため)
$ sudo su -

#/etc/sysctl.confに設定を追加
$ echo '# AWS Greengrass' >> /etc/sysctl.conf 
$ echo 'fs.protected_hardlinks = 1' >> /etc/sysctl.conf 
$ echo 'fs.protected_symlinks = 1' >> /etc/sysctl.conf

4.IPv4 ネットワーク転送を有効にする

#ファイル末尾に「net.ipv4.ip_forward = 1」を追記
vim /etc/sysctl.conf

#設定の反映
$ sysctl -p
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
net.ipv4.ip_forward = 1

#3 GreengrassグループとCoreの作成
IoT CoreのマネジメントコンソールでGreengrassグループを作成します。
作成後、証明書とコアの設定ファイルを取得します。
参考:AWS IoT Greengrass の AWS IoT の設定

1.IoT Coreのマネジメントコンソールで Greengrass > クラシック(V1) > 「グループの作成」の順にクリック
image.png
2.「デフォルト作成を使用」をクリック
image.png
3.任意のグループ名を入力して「次へ」をクリック
image.png
4.任意のCoreの名前を入力して「次へ」をクリック
image.png
5.「グループとCoreの作成」をクリック
image.png
6.「これらのリソースはtar.gzとしてダウンロードしてください」をクリックしてセキュリティリソースを取得したら、「完了」をクリック
※ルートCAの取得はのちの手順で行う
※Greengrass CoreのソフトウェアはDockerイメージとして取得済なのでここでは不要
image.png
#4 Greengrassをローカルで実行
セキュリティリソースをEC2インスタンスにアップロードし、Greengrass Coreを起動します。

1.先ほどダウンロードしたtar.gzファイルをEC2インスタンスにアップロードする
※今回はFileZillaを利用した
2.tar.gzファイルを解凍し、greengrass用のディレクトリに格納する

#格納先のディレクトリを作成
$pwd
/home/ec2-user

$ mkdir greengrass

#解凍する
$ tar xzvf xxxxxxxxxx.tar.gz -C greengrass/
certs/xxxxxxxxxx.cert.pem
certs/xxxxxxxxxx.private.key
certs/xxxxxxxxxx.public.key
config/config.json

$ ls greengrass/
certs  config

3.AmazonのルートCA証明書を取得する

#モノの証明書、秘密鍵が格納されているディレクトリに移動
$ cd greengrass/certs/

#ルートCA証明書を取得
$ sudo wget -O root.ca.pem https://www.amazontrust.com/repository/AmazonRootCA1.pem

$ ls root.ca.pem
root.ca.pem

4.IoT Greengrassコンテナをを実行する

#certs、configディレクトリをバインド、8883のフォワーディングなどを行い実行
$ docker run --rm --init -it --name aws-iot-greengrass \
 --entrypoint /greengrass-entrypoint.sh \
 -v '証明書を格納したディレクトリのパス':/greengrass/certs \
 -v 'configファイルを格納したディレクトリのパス':/greengrass/config \
 -p 8883:8883 \
 216483018798.dkr.ecr.us-west-2.amazonaws.com/aws-iot-greengrass:latest

#成功すると以下の通り出力される
grep: /greengrass/ggc/deployment/group/group.json: No such file or directory
Setting up greengrass daemon
Validating hardlink/softlink protection
Waiting for up to 1m10s for Daemon to start

Greengrass successfully started with PID: 12

#5 検証用の各種設定
今回は、最終的にGreengrassデバイスからのMQTTメッセージで実行するLambdaを用意し、動作することを確認します。
これを検証するための準備を行います。
##5-1 グループのLambda設定
公式ドキュメントの以下の引用の通り、GreengrassをDocker上で実行する場合は、Lambdaを「コンテナなし」で実行する必要があります。
対象グループのLambdaのデフォルト設定を先に変えておきます。

Docker コンテナで AWS IoT Greengrass で実行する場合、すべての Lambda 関数はコンテナ化を使用しないで実行する必要があります。このステップでは、グループのデフォルトのコンテナ化を [No container (コンテナなし)] に設定します。グループを初めてデプロイする前に行う必要があります。
引用元:ステップ 4. Greengrassグループに対して「コンテナなし」コンテナ化を構成する

1.IoT Coreのマネジメントコンソールで Greengrass > クラシック(V1) > グループ の順にクリック
2.対象のグループ名 > 設定 の順にクリック
image.png
3.「Lambda ランタイム環境」でデフォルトの Lambda 関数コンテナ化で「コンテナなし」を選択し、「デフォルトのLambda実行設定を更新する」をクリック
image.png
4.「続行」をクリック
image.png
##5-2 Lambdaの作成
MQTTメッセージを受け取ったあとに以下の挙動をするLambdaを作成します。

  • ローカルのログファイルに書き込む
  • IoT CoreにMQTTメッセージを送る

1.ローカルで以下のプログラムを作成する

docker_test.py
import greengrasssdk
import logging
import sys
import json
import datetime

logger = logging.getLogger(__name__)
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)

client = greengrasssdk.client("iot-data")

logfile = "/tmp/docker_test.log"

def docker_test(event, context):
    date = datetime.datetime.now()
    timestamp = date.strftime("%Y-%m-%d %H:%M:%S")
    
    client.publish(
        topic="test/dockertest",
        payload=json.dumps(
            {
                "function": "docker_test", 
                "message": "This is Test",
                "version":  "1.0",
                "timestamp": timestamp
            }
        )
    )
    
    f = open(logfile, mode="a")
    f.write(timestamp + " success!\n")
    f.close()

2.以下で「AWS IoT Greengrass SDK for Python」をダウンロードし、「greengrasssdk」フォルダと先ほど作成したプログラムをまとめてzipファイルとして圧縮する
GitHub:aws-iot-devise-sdk-for-python
3.Lambdaのマネジメントコンソール画面で 関数 > 「関数の作成」の順にクリック
4.以下の通り設定して「関数の作成」をクリック

  • 一から作成
  • 関数名:※任意の関数名を設定
  • ランタイム:Python3.7

5.アップロード元 > .zipファイル > アップロード > 先ほど作成したzipファイルを選択し、「保存」をクリックする
6.ランタイム設定の「編集」をクリックし、ハンドラを「[ファイル名].[関数名]」にして「保存」をクリック
※上記プログラムのファイル名を「docker_test.py」としていた場合は「docker_test.docker_test」となる
7.アクション > 新しいバージョンを発行 > 「発行」の順にクリック
8.エイリアスタブ > 「エイリアスを作成」の順にクリックして、任意のエイリアス名を入力し、先ほど発行したバージョンを選択して「保存」をクリック
##5-3 Greengrassデバイスの用意
Docker上で動くGreengrass Coreと通信するGreengrassデバイスを用意します。
今回はこちらもEC2を利用して仮想デバイスという形で用意します。

1.IoT Coreのマネジメントコンソールで Greengrass > クラシック(V1) > グループ の順にクリック
2.対象のグループ名 > デバイス > 「デバイスの追加」の順にクリック
3.「新しいデバイスの作成」をクリック
4.任意のモノの名前を入力して「次へ」をクリック
5.1-Clickデプロイの「デフォルトを使用」をクリック
6.「これらのリソースはtar.gzとしてダウンロードしてください」をクリックしてファイルをダウンロードし、「完了」をクリック
7.Greengrassデバイス用に「1-2 インスタンスの作成」と同じ設定でEC2インスタンスを用意する
※VPC、サブネット、セキュリティグループは上記で指定したものと同じものを利用

8.インスタンスが起動したらsshで接続し sudo yum upgradesudo yum updateを行う
9.Greengrassデバイス用のインスタンスに先ほどダウンロードしたtar.gzファイルをアップロードして解凍する
※私はFileZillaを利用した

#ファイル格納用のディレクトリを作成
$ pwd
/home/ec2-user
$ mkdir .certs

#tar.gzファイルの解凍
$ tar xzvf xxxxxxxxxx-setup.tar.gz -C .certs
xxxxxxxxxx.cert.pem
xxxxxxxxxx.private.key
xxxxxxxxxx.public.key

10.以下のPythonプログラムを作成、実行して、グループ証明書を取得
※/home/ec2-user/.certs はいかにgroupCA.crtとしてグループ証明書を上書きで作成

get_groupCA.py
import requests
import json
import argparse

#get args
psr = argparse.ArgumentParser(
        description = "get groupCA"
        )

psr.add_argument("-t", "--things", required=True, help="thing name")
psr.add_argument("-c", "--cert", required=True, help="cert file")
psr.add_argument("-k", "--key", required=True, help="private key")

args = psr.parse_args()

things = args.things
cert = args.cert
key = args.key

#set variables for requests
url = "https://greengrass-ats.iot.ap-northeast-1.amazonaws.com:8443/greengrass/discover/thing/" + things

#set path for CA file
CAfile = "/home/ec2-user/.certs/groupCA.crt"

#get present group CA
response = requests.get(url, cert=(cert, key))

#make groupCA file
jsonData = response.json()
groupCA = jsonData["GGGroups"][0]['CAs'][0]

f = open(CAfile, "w")
f.write(groupCA)
f.close()
$ python --things 'モノの名前' --cert 'モノの証明書' --key '秘密鍵'

11.MQTT通信用にmosquittoをインストールする

$ sudo amazon-linux-extras install epel -y

$ sudo yum install mosquitto -y

##5-4 Greengrassグループの設定
Lambdaの実行、各通信のためにGreengrassグループの設定を行います。
###Lambdaの設定
作成したLambdaをグループに紐づけます。

1.IoT Coreのマネジメントコンソールで Greengrass > クラシック(V1) > グループ の順にクリック
2.対象のグループ名 > Lambda > 「Lambdaの追加」の順にクリック
3.「既存のLambdaを使用」 > 作成したLambdaを選択して「次へ」 > 作成したエイリアスを選択して「完了」をクリック
4.追加されたLambdaの「…」 > 設定の編集 の順にクリック
5.設定の以下項目を設定して、「更新」をクリック

  • コンテナ化:グループのデフォルトを使用(現在:コンテナなし)
  • Lambdaのライフサイクル:オンデマンド関数

###サブスクリプション
実行したLambdaからIoT Coreへのメッセージ、IoT CoreおよびGreengrassデバイスからLambdaへのメッセージをPub/Subできるようにサブスクリプションを追加します。

1.対象のグループでサブスクリプション > 「サブスクリプションの追加」の順にクリック
2.以下のサブスクリプションを作成する

ソース ターゲット トピック
Lambda IoT Core test/dockertest ※Lambda内で指定したtopic
IoT Core Lambda ※任意のtopic
Greengrassデバイス Lambda ※任意のtopic

###コアの接続情報検出設定
コンテナ上のGreengrass Coreの接続情報を手動で設定します。
※自動検出にした場合、コンテナ内のIPのみに上書きされてしまい、GreengrassデバイスからEC2インスタンスのIP宛にメッセージを送っても受け取ることができなかった

1.対象のグループで 設定 をクリック
2.Core接続情報を「接続情報の手動管理」に変更する
3.対象のグループで コア > コア名 の順にクリック
4.接続 > 編集 の順にクリック
5.接続情報を追加して「更新」をクリック

  • エンドポイント:※Greengrassコンテナを動かしているEC2インスタンスのローカルIP
  • ポート:8883

##5-5 ログの格納先作成
Lambdaがログを書き出す先はコード上はGreengrassコンテナの/tmpとしています。
ここで書き出されたログファイルがコンテナ終了後も残るように、コンテナ上の/tmpとマッピングするローカルディレクトリを用意します。

1.Greengrassコンテナを動かすEC2インスタンスにsshで接続してディレクトリを作成する

$ cd /home/ec2-user/greengrass

$ mkdir tmp

$ ls
certs config log

#コンテナ上でLambdaが書き込めるように全権を付与
$ chmod 777 log

2.Greengrassコンテナを起動中のターミナルで Ctrl + C を実行してコンテナを停止する
※これで上手くいかない場合は docker ps -a でコンテナIDを控えて、docker stop [控えたコンテナID] で停止する
3.先ほど作成したディレクトリとコンテナ上の /tmp をマッピングして再度コンテナを起動する

docker run --rm --init --name aws-iot-greengrass \
 --entrypoint /greengrass-entrypoint.sh \
 -v /home/ec2-user/greengrass/certs:/greengrass/certs \
 -v /home/ec2-user/greengrass/config:/greengrass/config \
 -v /home/ec2-user/greengrass/tmp:/tmp \
 -p 8883:8883 \
 216483018798.dkr.ecr.us-west-2.amazonaws.com/aws-iot-greengrass:latest

以上の手順が完了したらGreengrassグループをデプロイする。
※AWS上で設定したグループの情報はコンテナを停止→起動するたびにデプロイが必要

デプロイによって、予定していた以下の構成となります。
image.png
#6 検証
MQTTメッセージをトリガとしてコンテナ上のGreengrassがLambdaを実行できることを確認します。
##6-1 IoT Core -> Lambdaで検証
IoT CoreからのMQTTメッセージをトリガとしてLambdaを実行できることを確認します。

1.IoT Coreのマネジメントコンソールで テスト をクリック
2.トピックのフィルターに「test/dockertest」と入力し、「サブスクライブ」をクリック
※Lambda内で指定しているtopic
3.トピックに公開するタブに移動し、トピック名にサブスクリプションで指定したtopicを入力し、「発行」をクリック
4.以下を確認

  • IoT Coreでメッセージをサブスクライブできる
  • Greengrassコンテナを動かしているEC2のローカルにログファイルが書き出されている

##6-2 Greengrassデバイス -> Lambda で検証
次にGreengrassデバイスからのMQTTメッセージでLambdaが実行されることを確認します。
この検証は、コンテナ上のGreengrassとGreengrassデバイスが通信できることの確認も含んでいます。

1.先ほどの検証で開始したIoT Coreのサブスクライブはそのままとする
2.Greengrassデバイス用のEC2に接続してMQTTをパブリッシュする

mosquitto_pub --cafile 'グループ証明書' --cert 'モノの証明書' --key '秘密鍵' \
 -i 'モノの名前' --tls-version tlsv1.2 \
 -h 'Greengrassコンテナを動かしているEC2インスタンスのローカルIP' -p 8883 \
 -q 0 -t 'サブスクリプションで指定したtopic' \
 -m "{\"message\":\"Helloworld\"}" -d

3.同じく以下を確認

  • IoT Coreでメッセージをサブスクライブできる
  • Greengrassコンテナを動かしているEC2のローカルにログファイルが書き出されている

#7 おわりに
以上、Dockerコンテナ上で動くGreengrass(V1)の挙動を簡単に確認しました。

GreengrassデバイスからCoreへの通信がハマりどころでした。
自動検出を有効にしていたためにコンテナのローカルIPがGreengrass Coreの接続情報とされており、通信が成功しませんでした。
※手順でも記載した通りですが、Greengrass Coreの接続情報としてEC2インスタンス自体のローカルIPを静的に指定することで解消
#8 参考文献(文中で登場していないもの)

2
2
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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?