#はじめに
以下の記事では、AWSが提供しているDockerイメージを利用してGreengrass(V1)のコンテナを構築しました。
参考:Dockerコンテナ上でGreengrass(V1)を実行する
今回はDockerfileを自身で用意し、Greengrass(V1)のコンテナを構築しました。
そのうえで、動作検証を行います。
2021/5/18 22:00 追記
- Greengrassグループの証明書取得・格納をしないとコンテナ内のGreengrassが起動しないところ、証明書なしでGreengrassを起動する手順になっていたので、順番を大幅修正
- AmazonのルートCAをwgetで取得する際のオプションを -o から -O に修正(-oだとPEMファイルではなくTXTとして取得してしまう)
- 抜けていた手順として「1-3 Dockerホストの設定」を追加しました
#1 作業環境
今回はEC2上にDockerをインストールし、そこでGreengrassを動かします。
##1-1 EC2インスタンスの準備
詳細な手順は割愛しますが、今回は以下の通りEC2インスタンスおよび周辺の環境を用意しています。
参考:AWS上にエッジ環境を構築してGreengrass(V1)の動きを確認する - 1 AWS上にエッジ環境を構築
###ネットワーク設定
今回は東京リージョンのap-northeast-1aに作成しています。
作成するもの | 詳細 |
---|---|
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 |
###インスタンスの作成 | |
セキュリティグループは以下の通り作成しました。 |
タイプ | ポート範囲 | ソース | 備考 |
---|---|---|---|
SSH | 22 | マイIP | 自身の環境からEC2にSSH接続できるようにするため |
カスタムTCP | 8883 | 任意の場所 | IoT Coreとの通信用 |
インスタンスは以下の通り作成しました。 |
設定項目 | 選択した内容 |
---|---|
AMI | Ubuntu Server 18.04 LTS (HVM), SSD Volume Type(64ビット(x86)) |
インスタンスタイプ | t2.micro |
ボリュームサイズ | 20GB |
ネットワーク | ※作成したVPC |
サブネット | ※作成したサブネット |
自動割り当てパブリックIP | 有効 |
セキュリティグループ | ※作成したセキュリティグループ |
##1-2 Dockerのインストール | |
インスタンスを起動したらssh接続してDockerをインストールします。 | |
手順2以降は以下を参考にインストールします。 | |
参考:Install Docker Engine on Ubuntu |
1.sudo apt-get update
を apt-get upgrade
を実行する
$ sudo apt-get update -y
$ sudo apt-get upgrade -y
2.古いバージョンのDockerを削除する
※インストールされていないはずだが、念のため実行
$ sudo apt-get remove docker docker-engine docker.io containerd runc
3.レポジトリの設定を行う
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release -y
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
#参考手順のページでは「x86_64/amd64」タブよりコピペ
$ echo \
"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
4.Dockerをインストールする
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io -y
$ apt-cache madison docker-ce
docker-ce | 5:20.10.6~3-0~ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
docker-ce | 5:20.10.5~3-0~ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
(後略)
#上記コマンドの結果より任意のバージョン(2列目)で<VERSION_STRING>を置換して以下コマンドを実行
$ sudo apt-get install docker-ce=<VERSION_STRING> docker-ce-cli=<VERSION_STRING> containerd.io
5.動作確認をする
$ docker --version
Docker version 20.10.6, build 370c289
6.ログインユーザであるubuntuでもDockerを実行できるようにする
#デフォルトではrootユーザでないと以下のエラーが表示される
$ docker run hello-world
docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/create: dial unix /var/run/docker.sock: connect: permission denied.
See 'docker run --help'.
#以下のコマンドを実行した後、再接続する
$ sudo gpasswd -a ubuntu docker
Adding user ubuntu to group docker
$ exit
#再接続後、コマンドが実行できることを確認する
$ docker run hello-wolrd
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete
Digest: sha256:5122f6204b6a3596e048758cabba3c46b1c937a46b5be6225b835d091b90e46c
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
(後略)
##1-3 Dockerホストの設定
IPv4のフォワーディング有効化とシンボリックリンクとハードリンクの保護を有効化します。
参考:Docker コンテナでの AWS IoT Greengrass の実行
1.シンボリックリンクとハードリンクの保護を有効にする
#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
2.IPv4 ネットワーク転送を有効にする
#ファイル末尾に「net.ipv4.ip_forward = 1」を追記
vim /etc/sysctl.conf
3.設定を反映する
$ sysctl -p
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
net.ipv4.ip_forward = 1
#2 GreengrassをDocker上で起動
以下からダウンロードできる「aws-greengrass-docker-1.11.0.tar.gz」を解凍し、フォルダ内のDockerfileを参考にして、Dockerfileを作成し、イメージのビルド、コンテナの構築を行います。
参考:AWS IoT Greengrass とは - AWS IoT Greengrass Docker ソフトウェア
##2-1 DockerfileからDockerイメージを作成
Dockerfileを作成し、イメージのビルドを行います。
1.Docker用のディレクトリを作成し、必要なファイルを作成する
$ pwd
/home/ubuntu
$ mkdir docker
$ cd docker
$ touch Dockerfile
#ENTRYPOINTとして指定するシェルスクリプト
$ touch greengrass-entrypoint.sh
$ chmod 744 greengrass-entrypoint.sh
2.ENTRYPOINTとしてシェルスクリプトを以下の通り編集する
※今回は「aws-greengrass-docker-1.11.0.tar.gz」のものを流用
if文のみ [[ 条件式 ]]
表記でエラーになったため [ 条件式 ]
に変更
※Lambda実行がGreengrassコンテナ内の場合は、Greengrass起動前に終了
※Greengrass起動後は、Greengrassのdaemonが起動しているかチェックするループがずっと走ってプロセスが維持される
#!/bin/sh
set -e
# Initial check before starting greengrassd
# Check if default IsolationMode is set to GreengrassContainer or any user lambdas are configured in GreengrassContainer mode
if grep -q "GreengrassContainer" /greengrass/ggc/deployment/group/group.json; then
echo "Default IsolationMode as GreengrassContainer or User Lambdas with GreengrassContainer mode aren't supported to run inside the GGC Docker Container. For troubleshooting, start a fresh deployment by following this guide: https://docs.aws.amazon.com/greengrass/latest/developerguide/run-gg-in-docker-container.html#docker-no-container. Finally, restart the GGC docker container after bind-mounting an empty deployment folder."
exit 1;
fi
/greengrass/ggc/core/greengrassd start
daemon_pid=`cat /var/run/greengrassd.pid`
# block docker exit until daemon process dies.
while [ -d /proc/$daemon_pid ]
do
# Sleep for 1s before checking that greengrass daemon is still alive
daemon_cmdline=`cat /proc/$daemon_pid/cmdline`
if [ $daemon_cmdline != ^/greengrass/ggc/packages/1.11.0/bin/daemon.* ]; then
sleep 1;
else
break;
fi;
done
3.Dockerfileを作成する
※今回はamd64/ubuntuをベースにする
※Greengrassの実行にJavaが必要だが、今回はAmazonが提供するOpenJDKであるAmazon Corretoを利用する
参考:Amazon Corretto
FROM amd64/ubuntu
ARG GREENGRASS_RELEASE_URL=https://d1onfpft10uf5o.cloudfront.net/greengrass-core/downloads/1.11.1/greengrass-linux-x86-64-1.11.1.tar.gz
ARG CORRETTO_KEY_URL=https://apt.corretto.aws/corretto.key
RUN apt update -y && \
apt-get install wget gnupg software-properties-common -y && \
#Greengrass Coreのソフトウェアをwgetで取得し解凍
wget $GREENGRASS_RELEASE_URL && \
GREENGRASS_RELEASE=$(basename $GREENGRASS_RELEASE_URL) && \
tar zxvf $GREENGRASS_RELEASE -C / && \
#不要なtar.gzファイルの削除
rm $GREENGRASS_RELEASE && \
#amazon corretto8のインストール
wget -O- $CORRETTO_KEY_URL | apt-key add - && \
add-apt-repository 'deb https://apt.corretto.aws stable main' && \
add-apt-repository ppa:deadsnakes/ppa && \
apt-get update && \
apt-get install -y java-1.8.0-amazon-corretto-jdk python3.7 && \
#Greengrass用のシステムユーザとシステムグループを作成
useradd -r ggc_user && \
groupadd -r ggc_group && \
#タイムゾーンをAsia/Tokyoに設定
ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
#aptのキャッシュをクリーン
apt clean && \
rm -rf /var/lib/apt/lists/*
#作成したgreengrass-entrypoint.shをコンテナ内にコピー
COPY "greengrass-entrypoint.sh" /
EXPOSE 8883
CMD /greengrass-entrypoint.sh
4.イメージをビルドする
$ docker build -t ubuntu-greengrass .
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu-greengrass latest ************ 3 days ago 534MB
##2-2 Greengrassグループの作成
クラウド側でグループを作成し、Greengrassの起動に必要となる証明書等を取得します。
1.IoT Coreのマネジメントコンソールで Greengrass > クラシック(V1) > 「グループの作成」の順にクリック
2.「デフォルト作成を使用」をクリック
3.任意のグループ名を入力して「次へ」をクリック
4.任意のCoreの名前を入力して「次へ」をクリック
5.「グループとCoreの作成」をクリック
6.「これらのリソースはtar.gzとしてダウンロードしてください」をクリックしてセキュリティリソースを取得したら、「完了」をクリック
※ルートCAの取得はのちの手順で行う
※Greengrass Coreのソフトウェアはコンテナ内で取得するのでここでは不要
##2-3 コンテナの起動
dockerコマンドでコンテナを起動すると長くなるので、docker-composeで実行できるようにします。
1.docker-compose をインストールする
$ sudo apt install docker-compose -y
$ docker-compose --version
docker-compose version 1.17.1, build unknown
2.コンテナ起動時にマウントするconfigや証明書を格納するディレクトリを作成する
$ cd /home/ubuntu
$ mkdir config certs log
3.先ほど取得したtar.gzファイルをアップロードする
※私はFileZillaで実施した
4.tar.gzファイルを解凍する
$ pwd
/home/ubuntu
$ ls
xxxxxxxxxx-setup.tar.gz certs config docker log
#解凍すると、certsディレクトリ、configディレクトリ配下に各ファイルが格納される
$ tar zxvf xxxxxxxxxx-setup.tar.gz
$ ls certs
xxxxxxxxxx.cert.pem xxxxxxxxxx.private.key xxxxxxxxxx.public.key
$ ls config
config.json
#tar.gzファイルは不要なので削除
$ rm xxxxxxxxxx-setup.tar.gz
4.AmazonのルートCAを取得する
$ cd certs
#config.jsonがデフォルトでroot.ca.pemと記載しているため、その通りの名前で取得する
$ wget "https://www.amazontrust.com/repository/AmazonRootCA1.pem" -O root.ca.pem
5.docker-compose.yml ファイルを作成する
version: "3"
services:
greengrass-core:
image: ubuntu-greengrass
volumes:
- /home/ubuntu/config:/greengrass/config
- /home/ubuntu/certs:/greengrass/certs
- /home/ubuntu/log:/tmp
ports:
- 8883:8883
restart: always
6.コンテナが起動することを確認する
#docker-compose.yml ファイルを格納したディレクトリに移動
$ cd docker
$ docker-compose up -d
#コンテナが起動していることを確認
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
xxxxxxxxxxxx ubuntu-greengrass "/bin/sh -c /greengr…" 3 seconds ago Up 2 seconds 0.0.0.0:8883->8883/tcp, :::8883->8883/tcp docker_greengrass-core_1
#コンテナ内に入る
$ docker-compose exec greengrass-core /bin/bash
#greengrassが起動していることを確認する
$ ps aux | grep -E "greengrass.*daemon"
root 12 0.1 1.9 566608 19356 ? Sl 21:21 0:00 /greengrass/ggc/packages/1.11.1/bin/daemon -core-dir /greengrass/ggc/packages/1.11.1 -greengrassdPid 9
root 296 0.0 0.0 3304 728 pts/0 S+ 21:23 0:00 grep --color=auto -E greengrass.*daemon
#コンテナから抜ける
$ exit
##2-4 Greengrassのグループ作成・デプロイ
Greengrassグループがデプロイできることを確認します。
1.IoT Coreのマネジメントコンソールで Greengrass > クラシック(V1) > グループ > 作成したグループ名 の順にクリック
2.設定をクリック
3.ロールの追加 をクリック
4.「Greengrass_ServiceRole」を選択して「保存」をクリック
5.(同じく設定の画面で)デフォルトのLamnda関数コンテナ化を「コンテナなし」に変更して「デフォルトのLambda実行設定を更新する」をクリック
6.グループ右上のアクション > デプロイ の順にクリック
##2-5 備忘録(エラーについて)
今回、デプロイ時に以下のエラーが発生しました。
エラーメッセージに対してそれぞれ取った対応を備忘録として残しておきます。
デフォルトのLambda関数コンテナ化が「Greengrassコンテナ」になっていた
Lambdaを紐づける前だったので、設定に手を加えず上記の状態でデプロイをしたところ、以下のエラーが発生しました。
Deployment xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx of type NewDeployment for group xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx failed error: mounting tmpfs at /greengrass/ggc/packages/1.11.1/dns failed: failed to mount with args source="tmpfs" dest="/greengrass/ggc/packages/1.11.1/dns" fstype="tmpfs" flags="0" data="": operation not permitted
上記のメッセージから理解して設定を変更したというわけではないのですが、結果的にデフォルトのLamnda関数コンテナ化を変更することで解消しました。
※いずれにしろ、デフォルトのLamnda関数コンテナ化を「Greengrassコンテナ」にしているとgreengrass-entrypoint.shはエラーになるはず
コンテナにAmazon Correto(Java)をインストールしていなかった
当初、Amazon Correttoをインストールせずにコンテナを起動していました。
その状態(かつ、上記エラーを解消した状態)でデプロイをしたところ、以下のエラーが発生しました。
Deployment xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx of type NewDeployment for group xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx failed error: worker with xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx failed to initialize with reason Unable to find java or java8 executables
こちらはエラーメッセージの通りです。
コンテナにAmazon Correttoをインストールすることで解消しました。
#3 動作検証
コンテナ内でGreengrassが動くことを確認できたので、IoT Coreとコンテナ間でMQTTメッセージのやりとりができることを確認します。
今回はコンテナ上のGreengrassにMQTTメッセージを受け取ったら起動するLambdaをデプロイし、その際にローカルへのログの書き出しとIoT Coreへのメッセージ通知をさせます。
##3-1 Lambdaの作成
今回は以下のLambdaを作成します。
※詳細な手順は以下を参照のこと
参考:Dockerコンテナ上でGreengrass(V1)を実行する - 5-2 Lambdaの作成
設定項目 | 内容 |
---|---|
ランタイム | Python3.7 |
ハンドラ | docker_test.docker_test ※[ファイル名].[関数名] |
備考 | バージョンを発行し、エイリアスと紐づけている |
コードは以下の通りです。
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()
##3-2 グループとLambdaの紐づけ
作成したLambdaをGreengrassグループに紐づけます。
なお、今回は「コンテナなし」でLambdaを実行するため、ローカルリソースアクセスの設定は不要です。
1.IoT Coreのマネジメントコンソールで Greengrass > クラシック(V1) > グループ の順にクリック
2.対象のグループ名 > Lambda > 「Lambdaの追加」の順にクリック
3.「既存のLambdaを使用」 > 作成したLambdaを選択して「次へ」 > 作成したエイリアスを選択して「完了」をクリック
4.追加されたLambdaの「…」 > 設定の編集 の順にクリック
5.設定の以下項目を設定して、「更新」をクリック
- コンテナ化:グループのデフォルトを使用(現在:コンテナなし)
- Lambdaのライフサイクル:オンデマンド関数
##3-3 サブスクリプションの設定
LambdaとIoT Core間でメッセージのpub/subができるようにサブスクリプションを追加します。
1.IoT Coreのマネジメントコンソールで Greengrass > クラシック(V1) > グループ の順にクリック
2.対象のグループ名 > サブスクリプション > 「サブスクリプションの追加」の順にクリック
3.以下のサブスクリプションを作成する
ソース | ターゲット | topic |
---|---|---|
Lambda | IoT Cloud | test/dockertest ※Lambda内で指定したtopic |
IoT Cloud | Lambda | ※任意のtopic |
以上の設定が完了したら、改めでグループをデプロイします。
##3-4 検証と結果
IoT Coreからサブスクリプションで指定したtopic宛にメッセージを送ります。
1.IoT Coreのマネジメントコンソールで テスト > MQTTテストクライアント の順にクリック
2.「トピックをサブスクライブする」タブでトピックのフィルターに「test/dockertest」(Lambda内で指定したtopic)と入力し、「サブスクライブ」をクリック
3.「トピックに公開する」タブでトピック名にサブスクリプションで指定したIoT Cloud -> Lambdaのtopicを入力し、「発行」をクリック
4.以下を確認する
- MQTTテストクライアントの「トピックをサブスクライブする」にメッセージが届く
- Dockerホスト
/home/ubuntu/log
配下にdocker_test.logが作成されている
#おわりに
(Dockerの勉強も兼ねて)Dockerfileの作成からGreengrassを実行するコンテナ環境の作成、動作検証まで行いました。
なお、今回はAmazonが提供しているということで、OpenJDK8の代わりにAmazon Corretto 8を利用しましたが、StreamManager(公式ドキュメントの手順でOpenJDK8をインストールしている)もこの環境で動くことを確認できました。
参考:Greengrass(V1)でストリームマネージャーを利用してみる
また、記事内ではIoT Core -> Greengrass -> IoT CoreのMQTT通信についてのみ記載していますが、前回の記事同様にして仮想デバイスからのメッセージ契機でコンテナ上のGreengrassのLambdaが起動することを確認できました。
参考:Dockerコンテナ上でGreengrass(V1)を実行する
このイメージをベースにエッジ推論用の環境も用意してみようと思います。
#参考文献(文中には登場していないもの)