Building and Deploying Greengrass Components in a Dockerized Environment

Building and Deploying Greengrass Components in a Dockerized Environment

Develop AWS IoT Greengrass components locally using the Greengrass Core Docker image.

Takahiro Iwasa
8 min read

The Greengrass Core Docker image provides a local environment for developing AWS IoT Greengrass components without physical edge hardware. See the official documentation for details.

This guide builds a Greengrass component that publishes an MQTT message to AWS IoT Core every second and deploys it locally to a Docker container with the Greengrass CLI.

By the end of this example, your project directory will look like this:

Terminal window
components/
├── mqtt_publisher/
├── .gitignore
├── gdk-config.json
├── main.py
├── recipe.yaml
├── requirements.txt
docker/
├── greengrass-v2-credentials/
├── credentials
├── .env
├── docker-compose.yml

Developing a Custom Greengrass Component

Installing Greengrass Development Kit (GDK)

Install the Greengrass Development Kit (GDK):

Important

Running pip install gdk installs a library unrelated to the Greengrass Development Kit.

Terminal window
pip install -U git+https://github.com/aws-greengrass/[email protected]

Starting Development

Initialize your Greengrass component by running gdk component init:

Terminal window
mkdir ./components
gdk component init \
--language python \
--template HelloWorld \
--name components/mqtt_publisher

This command generates a basic component structure with the following files and directories:

Terminal window
components/
├── mqtt_publisher/
├── src/
├── greeter.py
├── tests/
├── test_greeter.py
├── .gitignore
├── gdk-config.json
├── main.py
├── README.md
├── recipe.yaml
ℹ️ Note

The src and tests directories will not be used in this example.

Configuring Component Metadata

Update the gdk-config.json file with the component metadata. If the component is not being published to an S3 bucket using gdk component publish, the publish.bucket field (line 10) does not need to be set.

Here’s an example of the updated gdk-config.json:

gdk-config.json
{
"component": {
"com.example.MqttPublisher": {
"author": "wasabee.dev",
"version": "0.0.1",
"build": {
"build_system": "zip"
},
"publish": {
"bucket": "<PLACEHOLDER_BUCKET>",
"region": "ap-northeast-1"
}
}
},
"gdk_version": "1.0.0"
}

For additional details, refer to the official documentation on the GDK CLI configuration file.

🔥 Caution

Do not use NEXT_PATCH as the value for version. It will cause errors when deploying the component using greengrass-cli deployment create.

Writing the Python Script

Create a main.py script for your component. This script will publish MQTT messages to the /mqtt-publisher topic every second.

main.py
import json
import random
from datetime import datetime
from time import sleep
import boto3
client = boto3.client('iot-data')
def main():
payload = {
"value": random.randint(1, 10000),
"datetime": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
}
while True:
client.publish(
topic='/mqtt-publisher',
payload=json.dumps(payload).encode(),
qos=1,
contentType='application/json',
)
print(f'Message was sent successfully: {payload}')
sleep(1)
if __name__ == "__main__":
main()

Create requirements.txt to list the component’s dependencies. The install lifecycle defined in recipe.yaml installs them when the component is deployed.

requirements.txt
boto3==1.26.65

Component Recipe

Create recipe.yaml to define the component’s metadata, dependencies, artifacts, and lifecycle commands. See the official documentation for the recipe specification.

Here’s an example:

recipe.yaml
---
RecipeFormatVersion: "2020-01-25"
ComponentName: "{COMPONENT_NAME}"
ComponentVersion: "{COMPONENT_VERSION}"
ComponentDescription: "This is an mqtt publisher written in Python."
ComponentPublisher: "{COMPONENT_AUTHOR}"
ComponentDependencies:
aws.greengrass.TokenExchangeService:
VersionRequirement: '^2.0.0'
Manifests:
- Platform:
os: all
Artifacts:
- URI: "s3://BUCKET_NAME/COMPONENT_NAME/COMPONENT_VERSION/mqtt_publisher.zip"
Unarchive: ZIP
Lifecycle:
Install: "pip3 install --user -r {artifacts:decompressedPath}/mqtt_publisher/requirements.txt"
Run: "python3 -u {artifacts:decompressedPath}/mqtt_publisher/main.py"

Component Dependencies

Because the script uses boto3 to communicate with AWS IoT Core, add the aws.greengrass.TokenExchangeService component to ComponentDependencies. The token exchange service exposes a local endpoint that supplies AWS credentials to the custom component.

For more details, refer to the official documentation.

AWS IoT Greengrass provides a public component, the token exchange service component, that you can define as a dependency in your custom component to interact with AWS services. The token exchange service provides your component with an environment variable, AWS_CONTAINER_CREDENTIALS_FULL_URI, that defines the URI to a local server that provides AWS credentials.

Lifecycle Hooks

The Lifecycle section specifies commands to execute during component installation and at runtime:

  • Install: Installs Python libraries listed in requirements.txt.
  • Run: Executes the main.py script when the component starts.

Recipe Placeholders

Placeholders in the recipe (e.g., {COMPONENT_NAME}) are replaced with values from gdk-config.json during the build process. These placeholders include:

  • {COMPONENT_NAME}
  • {COMPONENT_VERSION}
  • {COMPONENT_AUTHOR}
  • Artifacts URI (BUCKET_NAME, COMPONENT_NAME, and COMPONENT_VERSION)

Building the Component

Use gdk component build to build the component with the Greengrass Development Kit:

Terminal window
cd components/mqtt_publisher
gdk component build

The build places the recipes and artifacts in greengrass-build. A local Docker deployment does not require gdk component publish.

🔥 Caution

Using NEXT_PATCH as the version value in gdk-config.json will cause deployment failures when running bin/greengrass-cli deployment create.

Greengrass Core in Docker

This section explains how to set up Greengrass Core in a Docker container, configure credentials, and deploy components.

For the remaining steps, work in the <PROJECT_ROOT>/docker directory.

Security Credentials

Greengrass Core requires AWS credentials for automatic resource provisioning. Use temporary credentials from sts get-session-token instead of long-term credentials.

The following AWS resources will be provisioned:

  • AWS IoT
    • Greengrass Core Device
    • IoT Thing
    • IoT Thing Group
    • Certificate
    • Policies (two)
    • Token Exchange Role Alias
  • AWS IAM
    • Token Exchange Role
    • Token Exchange Role Policy

Generate temporary credentials:

Terminal window
aws sts get-session-token

Save the credentials to a file:

Terminal window
mkdir ./greengrass-v2-credentials
nano ./greengrass-v2-credentials/credentials

Example content for credentials:

docker/greengrass-v2-credentials/credentials
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
aws_session_token = AQoEXAMPLEH4aoAH0gNCAPy...truncated...zrkuWJOgQs8IZZaIv2BXIa2R4Olgk

Environment File

Create a .env file to configure environment variables for the Greengrass Core installer. Refer to the official documentation for more details.

Example .env file:

docker/.env
GGC_ROOT_PATH=/greengrass/v2
AWS_REGION=ap-northeast-1
PROVISION=true
THING_NAME=MyGreengrassCore
THING_GROUP_NAME=MyGreengrassCoreGroup
TES_ROLE_NAME=GreengrassV2TokenExchangeRole
TES_ROLE_ALIAS_NAME=GreengrassCoreTokenExchangeRoleAlias
COMPONENT_DEFAULT_USER=ggc_user:ggc_group

Running Greengrass Core

Create a docker-compose.yml file to run Greengrass Core in Docker. Refer to the documentation for further information.

Example docker-compose.yml:

docker/docker-compose.yml
version: '3.7'
services:
greengrass:
init: true
container_name: aws-iot-greengrass
image: amazon/aws-iot-greengrass:latest
volumes:
- ./greengrass-v2-credentials:/root/.aws/:ro
- ../components:/root/components
env_file: .env
ports:
- '8883:8883'

Run the container:

Terminal window
docker-compose up -d
docker-compose logs -f greengrass

The logs should show that the Nucleus launched successfully:

aws-iot-greengrass | Launching Nucleus...
aws-iot-greengrass | Launched Nucleus successfully.

Deploying AWS-provided Components

Greengrass CLI

Install the Greengrass CLI component (aws.greengrass.Cli) for local deployments. After installation, it can be found in /greengrass/v2/bin.

Terminal window
docker-compose exec greengrass bash
cd /greengrass/v2
ls bin
Important

Do not use Greengrass CLI in production environments.

We recommend that you use this component in only development environments, not production environments. This component provides access to information and operations that you typically won’t need in a production environment. Follow the principle of least privilege by deploying this component to only core devices where you need it.

Token Exchange Service

Deploy aws.greengrass.TokenExchangeService so the custom component can call AWS services through credentials supplied by its local endpoint.

https://docs.aws.amazon.com/greengrass/v2/developerguide/interact-with-aws-services.html

Greengrass core devices use X.509 certificates to connect to AWS IoT Core using TLS mutual authentication protocols. These certificates let devices interact with AWS IoT without AWS credentials, which typically comprise an access key ID and a secret access key.

Deploying from AWS IoT Greengrass Console

Use the AWS IoT Greengrass console to deploy the AWS-provided components, including Greengrass Nucleus.

After the deployment succeeds, /greengrass/v2/logs/greengrass.log contains entries like the following:

[INFO] (Thread-4) com.aws.greengrass.deployment.IotJobsHelper: Job status update was accepted. {Status=SUCCEEDED, ThingName=MyGreengrassCore, JobId=}
[INFO] (pool-2-thread-11) com.aws.greengrass.status.FleetStatusService: fss-status-update-published. Status update published to FSS. {trigger=THING_GROUP_DEPLOYMENT, serviceName=FleetStatusService,
[INFO] (pool-2-thread-11) com.aws.greengrass.deployment.DeploymentDirectoryManager: Persist link to last deployment. {link=/greengrass/v2/deployments/previous-success}
[INFO] (Thread-4) com.aws.greengrass.deployment.IotJobsHelper: Received empty jobs in notification . {ThingName=MyGreengrassCore}

Updating Token Exchange Role

Update the GreengrassV2TokenExchangeRole IAM policy to grant permissions for MQTT publishing:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:Connect",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "iot:Publish",
"Resource": "arn:aws:iot:*:<AWS_ACCOUNT_ID>:topic//mqtt-publisher*"
}
]
}

Attach the policy:

Terminal window
aws iam put-role-policy \
--role-name GreengrassV2TokenExchangeRole \
--policy-name IoTPolicy \
--policy-document file://policy.json

Deploying the Custom Component Locally

Deploy your custom component using the Greengrass CLI greengrass-cli deployment create inside the Docker container.

Terminal window
cd /greengrass/v2
bin/greengrass-cli deployment create \
--recipeDir /root/components/mqtt_publisher/greengrass-build/recipes \
--artifactDir /root/components/mqtt_publisher/greengrass-build/artifacts \
--merge "com.example.MqttPublisher=0.0.1"

Check the deployment status using greengrass-cli deployment status:

Terminal window
bin/greengrass-cli deployment status -i <DEPLOYMENT_ID>

The command returns a successful deployment status:

INFO: Connection established with event stream RPC server
<DEPLOYMENT_ID>: SUCCEEDED

Monitor logs to ensure the component is running:

Terminal window
cd /greengrass/v2/logs
tail -f com.example.MqttPublisher.log

Expected log output:

[INFO] (Copier) com.example.MqttPublisher: stdout. Message was sent successfully: {'value': 31, 'datetime': '2023-02-27 12:31:35'}. {scriptName=services.com.example.MqttPublisher.lifecycle.Run, serviceName=com.example.MqttPublisher, currentState=RUNNING}

Testing with AWS IoT Test Client

Use the MQTT test client in the AWS IoT console to verify messages published to the /mqtt-publisher topic.

  1. Open the MQTT test client.
  2. Enter /# or /mqtt-publisher in Topic filter.
  3. Click Subscribe.

You should see the published messages from your custom component.

Conclusion

The GDK and the Greengrass Core Docker image provide a local workflow for building an MQTT-publishing component and verifying its messages in AWS IoT Core without physical edge hardware.

The GDK build, local greengrass-cli deployment create command, and component logs provide a short development feedback loop. The same recipe and artifact structure can later be used with a physical core device.

Do not deploy the Greengrass CLI component to production devices. It exposes local deployment and debugging operations intended only for development environments.

About the author

Takahiro Iwasa

Takahiro Iwasa

Software Developer

This blog shares technical notes from hands-on projects—architecture, implementation, and AWS service integrations.