Greengrass V2をDockerコンテナ内で実行させる（on Raspberry Pi）

Greengrass V2が、Dockerコンテナ内での実行に対応したようですので試してみました。

https://docs.aws.amazon.com/ja_jp/greengrass/v2/developerguide/run-greengrass-docker.html

Dockerイメージですが、V1はarmやarm64向けにも提供されていましたが、V2に関してはamd64向けのみの提供のようです。（そしてlatestタグがV2に向いているのでV1をlatestで使っている人はびっくりしていることでしょう）

https://hub.docker.com/r/amazon/aws-iot-greengrass

Raspberry Pi（Raspberry Pi OS 32bit）上で、試してみました。

# マネジメントコンソールでの設定

いつもながら、やることがたくさんあります。

## モノの登録

マネジメントコンソールでIoT Coreの画面を開きます。
「単一のAWS IoT モノの登録」を選択。

![ap-northeast-1.console.aws.amazon.com_iot_home_region=ap-northeast-1(Laptop with MDPI screen) (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180441/4a311c34-a0ea-f5cb-9930-7eaf8c1fff73.png)

名前をつけます。

![ap-northeast-1.console.aws.amazon.com_iot_home_region=ap-northeast-1(Laptop with MDPI screen) (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180441/ce56f80a-194b-e27a-9d9f-7984ad5ac0e3.png)

「1-Click 証明書作成」を選択。

![ap-northeast-1.console.aws.amazon.com_iot_home_region=ap-northeast-1(Laptop with MDPI screen) (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180441/3e24344f-43fb-df24-66d5-b6c241351117.png)

証明書が作成されますので、全てダウンロードしておきます。ルートCAも取得しましょう。
「有効化」ボタンのクリックも忘れずに。

![ap-northeast-1.console.aws.amazon.com_iot_home_region=ap-northeast-1(Laptop with MDPI screen) (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180441/470f7d51-9e22-518a-70ae-70b74777fafd.png)

ポリシーは後で追加しますので、まずはモノを登録してしまいましょう。

![ap-northeast-1.console.aws.amazon.com_iot_home_region=ap-northeast-1(Laptop with MDPI screen) (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180441/b839444a-d8ac-0f83-1d1d-74f62cbd7797.png)

## IAMポリシー、ロールの作成

Greengrass V2は動作にIAMポリシーが必要なので作成します。
（画面キャプチャがなくてすいません）

```json:IAMポリシー（GreengrassV2TokenExchangeRoleAccess）
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:DescribeCertificate",
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents",
                "logs:DescribeLogStreams",
                "iot:Connect",
                "iot:Publish",
                "iot:Subscribe",
                "iot:Receive",
                "s3:GetBucketLocation"
            ],
            "Resource": "*"
        }
    ]
}
```

IAMロールは、先程作成したGreengrassV2TokenExchangeRoleAccessポリシーをアタッチ。信頼関係はこんな感じです。

```json:IAMロールの信頼関係（GreengrassV2TokenExchangeRole）
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "credentials.iot.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

## IoTロール、ポリシーの設定

IoT Coreのマネジメントコンソールに戻ります。
「安全性」の中にある「ロールエイリアス」を選択。
作成ボタンを選択。

![ap-northeast-1.console.aws.amazon.com_iot_home_region=ap-northeast-1(Laptop with MDPI screen) (11).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180441/b13c9b81-2cb0-6e1b-5d81-652dfe3c04b4.png)

エイリアス先のロールとして、先程作成したIAMロールを選択。

![ap-northeast-1.console.aws.amazon.com_iot_home_region=ap-northeast-1(Laptop with MDPI screen) (12).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180441/b6dad0f8-dbb1-fb40-63d9-8b184378d2aa.png)

次にIoTポリシーを作成。

「安全性」の中にある「ポリシー」を選択。

1つめはgreengrass-docker-on-pi-pilicy

```json:greengrass-docker-on-pi-pilicy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Publish",
        "iot:Subscribe",
        "iot:Receive",
        "iot:Connect",
        "greengrass:*"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
```

アドバンストモードに切り替えて、JSONを貼り付けると楽ちんです。

![ap-northeast-1.console.aws.amazon.com_iot_home_region=ap-northeast-1(Laptop with MDPI screen) (9).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180441/4f926b36-f644-2bb5-d436-835218d0887d.png)

もう一つはGreengrassCoreTokenExchangeRoleAliasPolicy
先程作ったロールエイリアスを指定します。

```json:GreengrassCoreTokenExchangeRoleAliasPolicy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:AssumeRoleWithCertificate",
      "Resource": "arn:aws:iot:ap-northeast-1:xxxxxxxxxxxx:rolealias/GreengrassCoreTokenExchangeRoleAlias"
    }
  ]
}
```

## 証明書にポリシーをアタッチ

管理-モノのメニューから対象のモノを選択
→セキュリティタブを選択
→証明書を選択
→アクション-ポリシーのアタッチを選択
→greengrass-docker-on-pi-pilicyポリシーとGreengrassCoreTokenExchangeRoleAliasPolicyポリシーを選択

## エンドポイント情報の取得

二種類のエンドポイントが必要です。
マネジメントコンソール上からは取得できそうにないので、CloudShellで取得します。
あとで使うのでメモしておきましょう。

```bash
aws iot describe-endpoint --endpoint-type iot:Data-ATS
```

```bash
aws iot describe-endpoint --endpoint-type iot:CredentialProvider
```


# Dockerイメージのビルド

この章の作業はRaspberry Pi上で行います。

DockerfileはGitHubに公開されているのでこれを参考にしましょう。
https://github.com/aws-greengrass/aws-greengrass-docker

```bash
git clone -b v2.1.0 https://github.com/aws-greengrass/aws-greengrass-docker.git
```

取得したファイルはこんな感じです。

```bash
$ tree 
.
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── docker-compose.yml
├── greengrass-2.1.0.zip.sha256
└── greengrass-entrypoint.sh
```

Dockerfileのベースイメージがamazonlinux:2なのですが、arm向けには提供されておらず、残念ながらこのままでは動きません。
今回はベースイメージをpython:3.8-busterにしてみました。
また、必要なパッケージをyumではなくapt-getでインストールするように変更しました。

```Dockerfile:Dockerfile
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0

+FROM python:3.8-buster

# Author
LABEL maintainer="AWS IoT Greengrass"

# Replace the args to lock to a specific version
ARG GREENGRASS_RELEASE_VERSION=2.1.0
ARG GREENGRASS_ZIP_FILE=greengrass-${GREENGRASS_RELEASE_VERSION}.zip
ARG GREENGRASS_RELEASE_URI=https://d2s8p88vqu9w66.cloudfront.net/releases/${GREENGRASS_ZIP_FILE}
ARG GREENGRASS_ZIP_SHA256=${GREENGRASS_ZIP_FILE}.sha256

# Set up Greengrass v2 execution parameters
# TINI_KILL_PROCESS_GROUP allows forwarding SIGTERM to all PIDs in the PID group so Greengrass can exit gracefully
ENV TINI_KILL_PROCESS_GROUP=1 \ 
    GGC_ROOT_PATH=/greengrass/v2 \
    PROVISION=false \
    AWS_REGION=us-east-1 \
    THING_NAME=default_thing_name \
    THING_GROUP_NAME=default_thing_group_name \
    TES_ROLE_NAME=default_tes_role_name \
    TES_ROLE_ALIAS_NAME=default_tes_role_alias_name \
    COMPONENT_DEFAULT_USER=default_component_user \
    DEPLOY_DEV_TOOLS=false \
    INIT_CONFIG=default_init_config
RUN env

# Entrypoint script to install and run Greengrass
COPY "greengrass-entrypoint.sh" /
COPY "${GREENGRASS_ZIP_SHA256}" /

# Install Greengrass v2 dependencies

RUN apt-get update -y && apt-get upgrade -y && apt-get install -y tar unzip wget sudo procps openjdk-11-jre-headless && \
    wget $GREENGRASS_RELEASE_URI && sha256sum -c ${GREENGRASS_ZIP_SHA256} && \
    rm -rf /var/lib/apt/lists/* && \
    chmod +x /greengrass-entrypoint.sh && \
    mkdir -p /opt/greengrassv2 $GGC_ROOT_PATH && unzip $GREENGRASS_ZIP_FILE -d /opt/greengrassv2 && rm $GREENGRASS_ZIP_FILE && rm $GREENGRASS_ZIP_SHA256

ENTRYPOINT ["/greengrass-entrypoint.sh"]
```

ビルドします。

```bash
docker build -t aws-iot-greengrass:2.1.0 .
```

# Docker Composeの準備

必要なファイルを準備します。
ディレクトリ構成はこんな感じになります。
証明書類は、このファイル名に変更の上、格納。

```bash
$ tree 
.
├── .env
├── docker-compose.yml
├── greengrass-v2-certs
│   ├── AmazonRootCA1.pem
│   ├── device.pem.crt
│   ├── private.pem.key
│   └── public.pem.key
└── greengrass-v2-config
    └── config.yaml
```

```text:.env
GGC_ROOT_PATH=/greengrass/v2
AWS_REGION=ap-northeast-1
PROVISION=false
COMPONENT_DEFAULT_USER=ggc_user:ggc_group
DEPLOY_DEV_TOOLS=true
INIT_CONFIG=/tmp/config/config.yaml
```

```yaml:docker-compose.yml
version: '3.7'
 
services:
  greengrass:
    init: true
    container_name: aws-iot-greengrass
    image: aws-iot-greengrass:2.1.0
    env_file:
      - .env
    volumes:
      - ./greengrass-v2-config:/tmp/config/:ro
      - ./greengrass-v2-certs:/tmp/certs:ro
```

config.yamlのエンドポイントは取得したものに変更。

```yaml:greengrass-v2-config/config.yaml
---
system:
  certificateFilePath: "/tmp/certs/device.pem.crt"
  privateKeyPath: "/tmp/certs/private.pem.key"
  rootCaPath: "/tmp/certs/AmazonRootCA1.pem"
  rootpath: "/greengrass/v2"
  thingName: "greengrass-docker-on-pi"
services:
  aws.greengrass.Nucleus:
    componentType: "NUCLEUS"
    version: "2.1.0"
    configuration:
      awsRegion: "ap-northeast-1"
      iotRoleAlias: "GreengrassCoreTokenExchangeRoleAlias"
      iotDataEndpoint: "[iot:Data-ATSのエンドポイント]"
      iotCredEndpoint: "[iot:CredentialProviderのエンドポイント]"
```

# 起動

準備が整いました。長かった。

```bash
$ docker-compose up
Creating network "greengrass-on-docker_default" with the default driver
Creating aws-iot-greengrass ... done
Attaching to aws-iot-greengrass
aws-iot-greengrass | Installing Greengrass for the first time...
aws-iot-greengrass | Root user is already configured to execute commands as other users.
aws-iot-greengrass | Using specified init config file at /tmp/config/config.yaml
aws-iot-greengrass | Running Greengrass with the following options: -Droot=/greengrass/v2 -Dlog.store=FILE -Dlog.level= -jar /opt/greengrassv2/lib/Greengrass.jar --provision false --deploy-dev-tools true --aws-region ap-northeast-1 --start false --component-default-user ggc_user:ggc_group --init-config /tmp/config/config.yaml
aws-iot-greengrass | /greengrass-entrypoint.sh: 51: [: false: unexpected operator
aws-iot-greengrass | Creating user ggc_user 
aws-iot-greengrass | ggc_user created 
aws-iot-greengrass | Creating group ggc_group 
aws-iot-greengrass | ggc_group created 
aws-iot-greengrass | Added ggc_user to ggc_group 
aws-iot-greengrass | Nucleus start set to false, exiting...
aws-iot-greengrass | Making loader script executable...
aws-iot-greengrass | Starting Greengrass...
aws-iot-greengrass | + set +m
aws-iot-greengrass | + dirname /greengrass/v2/alts/current/distro/bin/loader
aws-iot-greengrass | + PWD=/greengrass/v2/alts/current/distro/bin
aws-iot-greengrass | + sigterm_received=0
aws-iot-greengrass | + cd /greengrass/v2/alts/current/distro/bin/../../../..
aws-iot-greengrass | + pwd
aws-iot-greengrass | + GG_ROOT=/greengrass/v2
aws-iot-greengrass | + echo Greengrass root: /greengrass/v2
aws-iot-greengrass | + LAUNCH_DIR=/greengrass/v2/alts/current
aws-iot-greengrass | + CONFIG_FILE=
aws-iot-greengrass | + is_directory_link /greengrass/v2/alts/new
aws-iot-greengrass | + [ -L /greengrass/v2/alts/new ]
aws-iot-greengrass | + is_directory_link /greengrass/v2/alts/broken
aws-iot-greengrass | + [ -L /greengrass/v2/alts/broken ]
aws-iot-greengrass | Greengrass root: /greengrass/v2
aws-iot-greengrass | + is_directory_link /greengrass/v2/alts/old
aws-iot-greengrass | + [ -L /greengrass/v2/alts/old ]
aws-iot-greengrass | + j=1
aws-iot-greengrass | + [ 1 -le 3 ]
aws-iot-greengrass | + [ 0 -eq 0 ]
aws-iot-greengrass | + launch_kernel
aws-iot-greengrass | + is_directory_link /greengrass/v2/alts/current
aws-iot-greengrass | + [ -L /greengrass/v2/alts/current ]
aws-iot-greengrass | + [ -d /greengrass/v2/alts/current ]
aws-iot-greengrass | + [ -f /greengrass/v2/alts/current/launch.params ]
aws-iot-greengrass | + cat /greengrass/v2/alts/current/launch.params
aws-iot-greengrass | + JVM_OPTIONS=-Dlog.level= -Dlog.store=FILE
aws-iot-greengrass | + JVM_OPTIONS=-Dlog.level= -Dlog.store=FILE -Droot=/greengrass/v2
aws-iot-greengrass | + OPTIONS=--setup-system-service false
aws-iot-greengrass | + [ ! -z ]
aws-iot-greengrass | + echo JVM options: -Dlog.level= -Dlog.store=FILE -Droot=/greengrass/v2
aws-iot-greengrass | + echoJVM options: -Dlog.level= -Dlog.store=FILE -Droot=/greengrass/v2
aws-iot-greengrass |  Nucleus options: --setup-system-service falseNucleus options: --setup-system-service false
aws-iot-greengrass | 
aws-iot-greengrass | + child_pid=
aws-iot-greengrass | + trap echo Received SIGTERM; sigterm_received=1; kill -TERM ${child_pid} TERM
aws-iot-greengrass | + child_pid=92
aws-iot-greengrass | + java -Dlog.store=FILE -Dlog.level= -Dlog.store=FILE -Droot=/greengrass/v2 -jar /greengrass/v2/alts/current/distro/lib/Greengrass.jar --setup-system-service false
aws-iot-greengrass | + wait 92
aws-iot-greengrass | Launching Nucleus...
aws-iot-greengrass | Launched Nucleus successfully.
```

マネジメントコンソールからも確認できました。

![ap-northeast-1.console.aws.amazon.com_iot_home_region=ap-northeast-1(Laptop with MDPI screen) (19).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180441/d5e97344-07d3-b389-1796-425b83cb33db.png)