By using AWS re:Post, you agree to the AWS re:Post Terms of Use

Deploying a web app to an AWS IoT Greengrass Core device - Part 1

7 minute read
Content level: Intermediate
5

I got several questions from customer and from users here on re:Post about deploying React web apps in Greengrass and with this article series I wanted to dimistify the process and provide some best practices.

The idea for this series of article is born as I got several questions from customer and on re:Post about deploying React web apps on an AWS IoT Greengrass Core device. I wanted to give specific instructions to address this specific use case, but also teach some generic best practices for creating applications for Greengrass Core.

First of all, let’s make things clear: you can deploy everything in Greengrass. Greengrass does not make any assumptions on your code, apart from the fact that it can be executed on the destination host. If you can run it from the shell - you can run it in Greengrass. In fact, it does not even to be code: it could be an ML model, a configuration file, etc.

Having made this preamble, you have already understood that you can serve a React web app from Greengrass, as well as web apps created in any other framework, like Vue.js or Angular. In this article I will go over some of the options to serve React web apps and how to best deploy them in Greengrass. I do not pretend this to be an exhaustive list. There are probably other options and I would be happy to hear from you about them. And if there is demand I am happy to create specific articles for other frameworks.

How to server web apps

Let’s rule out using npm start: while you could serve the React app with npm start this option only makes sense if you actively developing the app on the target device and for some reason you need to do live modifications. In all other cases it is better to develop the app locally and deploy the build folder (ie from npm run build) to the device once you are happy with it, similar to what you would do if you were deploying to cloud.

In this series of articles I am going to cover few ways to serve the app using different web servers solutions:

In this first article I am showing how to use live-server.

Prerequisites

I assume you have an AWS Account and know your way around the console, and in particular the AWS IoT and Amazon S3 services. I also assume you know how to install Greengrass Core software and possibly how to create components.

Packaging the React app

In order to deploy the app we need to first build the deployable assets. The assets are then published as a Greengrass component and the component deployed to the Greengrass Core device. The Greengrass Core software fetches the assets for the web app (also called artifacts in the recipe) and executes the instructions to serve them.

To keep things simple and focus on the important things let’s use a really basic app: the demo app that is created by create-react-app.

npx create-react-app my-greengrass-app
cd my-greengrass-app
npm run build

The React app assets can be found in the newly created ./build folder.

Create the Greengrass component

I pack the ./build folder as a Zip archive and upload it to Amazon S3. Then, I create the Greengrass component. Since this is a pretty simple component I use the console, but feel free to use the gdk CLI (instructions will be provided at the end in case you are not familiar with the tool).

cd build
zip -r ../app.zip .

I open the S3 console https://console.aws.amazon.com/s3 , and upload the app.zip archive to the S3 bucket. I also check that the bucket is in the same region as the Greengrass device.

After having uploaded the file, I select it and select Copy S3 URI and paste in a notepad. I will need this information when I create the component.

Now that we have uploaded the artifact, let’s create the component.

I open the console to create a new component, I select Enter recipe as YAML and paste the following content:

RecipeFormatVersion: 2020-01-25
ComponentName: com.example.ReactApp
ComponentVersion: 0.0.1
ComponentDescription: ""
ComponentPublisher: Amazon
ComponentDependencies: {}
Manifests:
- Platform:
    os: /linux/
  Lifecycle:
    Install: |-
      npm init -y
      npm install live-server
    Run:|-
      node node_modules/live-server/live-server.js {artifacts:decompressedPath}/app
  Artifacts:
  - Uri: <REPLACE_WITH_S3_URI>
    Unarchive: ZIP
    Permission:
      Read: OWNER
      Execute: NONE

I replace the S3 URI with the correct value and I select Create component.

Now we are ready to deploy the component to the device and see it in action.

I assume you have Greengrass up and running on your device already. If this is not the case, this it the right time to install it. Just select Create core device in the IoT console and follow the instructions or follow the tutorial.

Deploy the web app to the device

In the AWS IoT console, I go to the list of my components, I select com.example.ReactApp and select Deploy . Next, I pick the target, either as the specific device or the Thing Group that the device belongs to. I select Skip to review and then Deploy. In few seconds I am able to access the React web page on the device IP address on port 3000.

Few improvements

The React app is served on a standard port defined by live-server. What if we want to use a different port and make this configurable?

To do this I can use configuration parameter for the component. Configuration parameters allow to specify default values at build time and give the ability to override those values at deployment time.

I define a PORT parameter and give value 3000. Then, I use that parameter in the script to launch live-server. This is how the final recipe looks like:

RecipeFormatVersion: 2020-01-25
ComponentName: com.example.ReactApp
ComponentVersion: 0.0.2
ComponentDescription: ""
ComponentPublisher: Amazon
ComponentDependencies: {}
ComponentConfiguration:
  DefaultConfiguration:
    PORT: 3000
Manifests:
- Platform:
    os: /linux/
  Lifecycle:
    Install: |-
      npm init -y
      npm install live-server
    Run:|-
      node node_modules/live-server/live-server.js {artifacts:decompressedPath}/build --port={configuration:/PORT}
  Artifacts:
  - Uri: <REPLACE_WITH_S3_URI>
    Unarchive: ZIP
    Permission:
      Read: OWNER
      Execute: NONE

To make the modifications, go to the com.example.ReactApp component in the console, I select Create new version and paste the above recipe. I also replace the S3 URI with the right value.

Once done, I select Deploy to deploy it to the device. During the deployment wizard, at the component configuration stage, I can select the com.example.ReactApp component to change the port to a different one.

In the next article in this series, I am showing how to use Caddy to serve the content. Caddy is a binary compiled for the target platform and we will see how to specify a recipe that can be deployed to multiple platforms, for example a Raspberry Pi 4 (arm64) and an Intel NUC (amd64).

Stay tuned!

GDK

As promised, here are the instructions to create the component via the Greengrass Development Kit CLI. I create a gdk-config.json in the ./build folder with the following content, taking care of replacing BUCKET and REGION with your specific values.

{
  "component": {
    "com.example.ReactApp": {
      "author": "Amazon",
      "version": "NEXT_PATCH",
      "build": {
        "build_system" : "zip"
      },
      "publish": {
        "bucket": "<BUCKET>",
        "region": "<REGION>"
      }
    }
  },
  "gdk_version": "1.1.0"
}

Then, I run gdk component publish to publish the component to my account. Since this is a CLI tool I need to have valid credentials available in the shell.