3
1

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.

Greengrass(V1)を実行するDockerコンテナを構築してみる

Last updated at Posted at 2021-05-18

#はじめに
以下の記事では、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 updateapt-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が起動しているかチェックするループがずっと走ってプロセスが維持される

greengrass-entryoint.sh
#!/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

Dockerfile
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) > 「グループの作成」の順にクリック
image.png
2.「デフォルト作成を使用」をクリック
image.png
3.任意のグループ名を入力して「次へ」をクリック
image.png
4.任意のCoreの名前を入力して「次へ」をクリック
image.png
5.「グループとCoreの作成」をクリック
image.png
6.「これらのリソースはtar.gzとしてダウンロードしてください」をクリックしてセキュリティリソースを取得したら、「完了」をクリック
※ルートCAの取得はのちの手順で行う
※Greengrass Coreのソフトウェアはコンテナ内で取得するのでここでは不要
image.png
##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 ファイルを作成する

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.ロールの追加 をクリック
image.png
4.「Greengrass_ServiceRole」を選択して「保存」をクリック
5.(同じく設定の画面で)デフォルトのLamnda関数コンテナ化を「コンテナなし」に変更して「デフォルトのLambda実行設定を更新する」をクリック
image.png
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 ※[ファイル名].[関数名]
備考 バージョンを発行し、エイリアスと紐づけている

コードは以下の通りです。

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()

##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)を実行する

このイメージをベースにエッジ推論用の環境も用意してみようと思います。
#参考文献(文中には登場していないもの)

3
1
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?