#はじめに
AWSはGreengrass Coreソフトウェアと依存関係がインストールされたDocker file、Dockerイメージを公開しています。
今回は、以下の公式ドキュメントを参考に、EC2上のDockerコンテナでGreengrassを実行します。
参考:Docker コンテナでの AWS IoT Greengrass の実行
#1 環境構築
公式ドキュメントの「Prerequisites」を参考に、Greengrassを実行する環境を用意します。
今回は最終的に以下の構成となります。
##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との通信用 |
設定項目 | 選択した内容 |
---|---|
AMI | Amazon Linux 2 AMI(64ビット(x86)) |
インスタンスタイプ | t2.micro |
ネットワーク | ※作成したVPC |
サブネット | ※作成したサブネット |
自動割り当てパブリックIP | 有効 |
セキュリティグループ | ※作成したセキュリティグループ |
3.yum upgrade
と yum 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
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
以上で以下の構成まで作成できました。
#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) > 「グループの作成」の順にクリック
2.「デフォルト作成を使用」をクリック
3.任意のグループ名を入力して「次へ」をクリック
4.任意のCoreの名前を入力して「次へ」をクリック
5.「グループとCoreの作成」をクリック
6.「これらのリソースはtar.gzとしてダウンロードしてください」をクリックしてセキュリティリソースを取得したら、「完了」をクリック
※ルートCAの取得はのちの手順で行う
※Greengrass CoreのソフトウェアはDockerイメージとして取得済なのでここでは不要
#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.対象のグループ名 > 設定 の順にクリック
3.「Lambda ランタイム環境」でデフォルトの Lambda 関数コンテナ化で「コンテナなし」を選択し、「デフォルトのLambda実行設定を更新する」をクリック
4.「続行」をクリック
##5-2 Lambdaの作成
MQTTメッセージを受け取ったあとに以下の挙動をするLambdaを作成します。
- ローカルのログファイルに書き込む
- IoT CoreにMQTTメッセージを送る
1.ローカルで以下のプログラムを作成する
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 upgrade
と sudo 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としてグループ証明書を上書きで作成
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上で設定したグループの情報はコンテナを停止→起動するたびにデプロイが必要
デプロイによって、予定していた以下の構成となります。
#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 参考文献(文中で登場していないもの)
- Dockerコンテナ上でGreengrassを動かしている記事
- GreengrassをDocker for Windows上で使用する準備とファイル読み出しテストの手順
- 「#2 Amazon ECRからGreengrassコンテナイメージを取得する」で実行している内容について
- Linux豆知識197 /etc/sysctl.conf
- Dockerはサービス起動時に勝手にIPフォワーディングを有効にする
- CentOS / RHEL 7でハードリンクおよびシンボリックリンクを保護する方法 - Centos - 2021
- Docker周辺で参考にした記事
- 「Got permission denied while trying to connect to the Docker daemon socket」への対応
- dockerでvolumeをマウントしたときのファイルのowner問題