Skip to main content

Introducing Husarnet Action for GitHub Actions

ยท 8 min read
Dominik Nowak
CTO @ Husarnet

GitHub Actions is a mature and extremely popular CI/CD tool. It allows you to create workflows for software deployment, testing, building ... whatever. In GitHub Actions you build your workflows by using blocks automating common tasks called Actions.

One of those building blocks is now Husarnet Action! ๐ŸŽ‰

Learn how you can leverage Husarnet Action in your workflow to deploy code to devices with no public IP like laptop, Raspberry Pi, some random VM sitting on your server or even to ESP32 microcontrollers. All by adding a few lines to your existing GitHub Actions yaml file.

Deploy Code To Edge Devices At Scale With Husarnet Action For GitHub Action

info

Find Husarnet Action in the GitHub marketplace:

https://github.com/marketplace/actions/husarnet

Use Cases#

With Husarnet Action you can treat any device like it has a public IP. Thanks to that it enables CI/CD pipelines not only for servers but also directly for IoT / edge devices.

We have prepared two GitHub repository templates allowing you to test Husarnet Action in practical use cases:

Example projectDescription
esp32-internet-ota ESP32 OTA updateA template project with CI/CD for Over The Air (OTA) firmware update to ESP32 via the Internet, directly from GitHub workflow. Read more...
ga-rsync-demo Copy files to remote host with no public IPShowing how to copy files / build artifacts from GitHub workflow directly to Raspberry Pi (or other edge / IoT devices) with rsync. Read more...

You will find detailed instructions showing how to run those projects in their repo README.md files, but in the next section I will focus only on GitHub workflow yml files to show you how Husarnet Action is used.

ESP32 OTA over the Internet (deploy to ESP32)#

Let's use Husarnet Action for deploying firmware to ESP32 powered devices over the Internet.

We have prepared esp32-internet-ota template project where both source code for ESP32 and GitHub workflow is ready to use (just define your own repository secrets).

Here's how GitHub workflow used to deploy to ESP32 looks like:

ota-update.yml
name: ESP32 OTA update
on:  push:    branches:      - 'main'
jobs:  build:    runs-on: ubuntu-20.04
    steps:
    - name: Checkout      uses: actions/checkout@v2
    - name: Connecting the GitHub workflow to Husarnet VPN network      uses: husarnet/husarnet-action@v2      with:        join-code: ${{ secrets.HUSARNET_JOINCODE }}
    - name: ESP32 software reset      run: curl -X POST 'http://${{ secrets.HUSARNET_HOSTNAME }}:8080/reset'
    - name: Installing platformio      run: pip3 install -U platformio
    - name: Building a firmware for ESP32      run: |        export SSID=${{ secrets.WIFI_SSID }}        export PASS=${{ secrets.WIFI_PASS }}        export JOINCODE=${{ secrets.HUSARNET_JOINCODE }}        export HOSTNAME=${{ secrets.HUSARNET_HOSTNAME }}        pio run
    - name: Uploading a firmware to ESP32      run: >        curl --http0.9 -# -v        -H 'Accept: */*'        -H 'Accept-Encoding: gzip, deflate'        -H 'Connection: keep-alive'        -F "MD5="$(md5sum "${{ github.workspace }}/.pio/build/esp32dev/firmware.bin" | cut -d ' ' -f 1)""        -F 'firmware=@${{ github.workspace }}/.pio/build/esp32dev/firmware.bin'        'http://${{ secrets.HUSARNET_HOSTNAME }}:8080/update'
    - name: Stop Husarnet      run: sudo systemctl stop husarnet

What is going on in the workflow:

  1. Connecting our workflow to the same Husarnet network as the ESP32 board we want to flash (using HUSARNET_JOINCODE). ESP32 VPN connection needs to be present while starting that workflow, so the first firmware update is needed to be done manually. Full instruction is in README.

  2. Installing PlatformIO and building a firmware with 4 environment variables provided as repository secrets:

    GitHub repo secretdescription
    WIFI_SSID and WIFI_PASSssid (name) and password for the Wi-Fi network that will be hardcoded in firmware for ESP32 (we replace the whole firmware, so we need to also provide network credentials)
    HUSARNET_JOINCODEa secret key you will get from your Husarnet Dashboard account at https://app.husarnet.com allowing you to connect new devices to your Husarnet network. It looks like fc94:...:932a/xhfqwPxxxetyCExsSPRPn9
    HUSARNET_HOSTNAMEa handy name for your ESP32 allowing other hosts to reach it, without using its Husarnet IPv6 address (eg. ping6 my-awesome-esp32 instead of ping6 fc94:f632:c8d9:d2b6:ad18:ed16:ed7e:9f3f
  3. Building a firmware (firmware.bin file) using PlatformIO

  4. Uploading the firmware over the Internet to ESP32 with curl.

  5. Turning off Husarnet network (even when a workflow finishes its job, before it quits it need some time during which it is still Husarnet connected)

After all these steps new firmware is uploaded to ESP32 and it boots immediately after that on the remote device.

Deploy The Fleet

By combining workflow with matrix of different job configurations you can flash remotely not only a single ESP32 by, the whole fleet at once.

Just make a few modifications in the ota-update.yml file:

ota-update.yml (with matrix)
name: ESP32 OTA update
on:  push:    branches:      - 'main'
jobs:  build:    runs-on: ubuntu-20.04    strategy:      matrix:        esp32-id:         - 'motion-sensor-1'        - 'motion-sensor-2'        - 'motion-sensor-3'        - 'motion-sensor-4'        - ...        - 'motion-sensor-n'    steps:      ...    - name: Building a firmware for ESP32      run: |        export SSID=${{ secrets.WIFI_SSID }}        export PASS=${{ secrets.WIFI_PASS }}        export JOINCODE=${{ secrets.HUSARNET_JOINCODE }}        export HOSTNAME=${{ matrix.esp32-id }}        pio run
    - name: Uploading a firmware to ESP32      run: >        curl --http0.9 -# -v        -H 'Accept: */*'        -H 'Accept-Encoding: gzip, deflate'        -H 'Connection: keep-alive'        -F "MD5="$(md5sum "${{ github.workspace }}/.pio/build/esp32dev/firmware.bin" | cut -d ' ' -f 1)""        -F 'firmware=@${{ github.workspace }}/.pio/build/esp32dev/firmware.bin'        'http://${{ matrix.esp32-id }}:8080/update'      ...

According to official docs: A job matrix can generate a maximum of 256 jobs per workflow run. That means you can update the firmware concurrently for up to 256 ESP32 powered devices at once. Of course you can create multiple workflows to multiply that number. ESP32 OTA over the internet is pretty simple with GitHub Actions & Husarnet Action.

Thanks to that your code and remote firmware update setup are covered by a version control in a single GitHub repository. That's a true Infrastructure as Code way of handling IoT fleets.

Deploying to edge devices (deploy to Raspberry Pi)#

If you want to deploy build artifacts, or just copy random files from your GitHub workflow to the edge devices like Raspberry Pi or other SBC / headless system, you can use Husarnet Action to enable that as well.

In the example we will use rsync to copy files from our workflow to Raspberry Pi.

copy-to-remote.yml
name: Deploy to Raspberry Pi
on:  push:  workflow_dispatch:
jobs:  copy-to-remote-host:    runs-on: ubuntu-20.04    steps:    - name: Checkout      uses: actions/checkout@v2
    - name: Connecting to Husarnet VPN network      uses: husarnet/husarnet-action@v2      with:        join-code: ${{ secrets.HUSARNET_JOINCODE }}
    - name: Wait until connection with peer is established (with 2m timeout)      run: ping6 -w 120 -c 1 ${{ secrets.HUSARNET_TARGET_HOSTNAME }}
    - name: Install SSH private key      uses: shimataro/ssh-key-action@v2      with:        key: ${{ secrets.SSH_PRIVATE_KEY }}        known_hosts: ${{ secrets.KNOWN_HOSTS }}
    - name: Rsync over SSH      run: >        rsync -vr         ./files/         root@${{ secrets.HUSARNET_TARGET_HOSTNAME }}:/root/files
    - name: Stop Husarnet      run: sudo systemctl stop husarnet

What is going on in the workflow?

  1. Connecting our workflow to the same Husarnet network as Raspberry Pi (using HUSARNET_JOINCODE).
  2. Making sure that peer-to-peer connection with the target host (Raspberry Pi) is established.
  3. Installing private SSH key allowing GitHub workflow SSH access to remote Raspberry Pi (SSH_PRIVATE_KEY repository secret need to be defined). The public SSH key of the key pair needs to be added to /etc/known_hosts in Raspberry Pi.
  4. Using rsync to send files from ./files folder of GitHub workflow to /root/files directory on remote Raspberry Pi (using Husarnet hostname).

Pretty simple. Of course for a full example and step by step instructions visit the project repo.

Summary#

In this blog post I have introduced Husarnet Action for GitHub Actions that allows you to deploy build artifacts, trigger the event or send files to devices that don't have a public nor static IP address like your laptop, Raspberry Pi, NAS drive, or ESP32 microcontroller.

I have presented two demo projects showcasing how Husarnet Action works in ... well, action :) :

By combining presented GitHub Actions workflows (powered by Husarnet Action) with matrix of different job configurations, you can deploy your code to the whole fleet at once.

Now you can treat any device like it was a normal server (from your GitHub workflow point of view).

Automate deployment of code to edge devices with Husarnet Action For GitHub Actions ๐Ÿš€

COMMENTS

If you have any questions or want to discuss something related to this blog post, let's do it at Husarnet Community Forum.

Read also ...#