Skip to content

Commit

Permalink
Streamline Dockerfiles and improve Docker solution
Browse files Browse the repository at this point in the history
  • Loading branch information
renemarc committed Dec 20, 2018
1 parent c2edeac commit b054751
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 63 deletions.
34 changes: 15 additions & 19 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Dockerfile for ARM v6
# Dockerfile
#
# This file will be used on non balenaCloud/openBalena environments, like local
# balenaEngine/Docker builds or Docker Hub.
Expand All @@ -8,25 +8,26 @@
# https://www.balena.io/docs/reference/base-images/base-images/
#

# Declare pre-build variables
ARG DEVICE_NAME=raspberry-pi

# Define base image
FROM balenalib/raspberry-pi-alpine:latest
FROM balenalib/${DEVICE_NAME}-alpine-python:2

# Declare build environment variables
ENV AIRTHINGSWAVE_VERSION 0.2
ENV VERSION 0.2.1
ENV CRON_PERIOD hourly
# Declare build variables
ARG AIRTHINGSWAVE_VERSION=0.2
ARG VERSION=0.2.2

# Label image with metadata
LABEL org.label-schema.name="balena AirthingsWave" \
org.label-schema.description="Airthings Wave radon detector bridge for single-board computers." \
org.label-schema.vcs-url="https://github.com/renemarc/balena-airthingswave" \
LABEL org.label-schema.description="Airthings Wave radon detector bridge for single-board computers." \
org.label-schema.docker.cmd="docker run --detach --restart=unless-stopped --env-file=env.list --net=host --cap-add=NET_ADMIN --name=airthingswave \$(docker build --quiet .)" \
org.label-schema.docker.cmd.debug="docker run -it --rm --env-file=env.list --net=host --cap-add=NET_ADMIN --name=airthingswave \$(docker build --quiet .) bash" \
org.label-schema.name="balena AirthingsWave" \
org.label-schema.url="https://airthings.com/wave/" \
org.label-schema.vcs-url="https://github.com/renemarc/balena-airthingswave" \
org.label-schema.version=${VERSION} \
org.label-schema.schema-version="1.0"

# Start QEMU virtualization
RUN ["cross-build-start"]

# Setup application directory
WORKDIR /usr/src/app

Expand Down Expand Up @@ -54,18 +55,13 @@ RUN apk add \
py-setuptools

# Copy project files in their proper locations
ARG CRON_PERIOD=hourly
COPY ["crontask.sh", "/etc/periodic/${CRON_PERIOD}/airthingswave-mqtt"]
COPY ["docker-entrypoint.sh", "/usr/local/bin/"]
COPY ["config.yaml", "start.sh", "./"]

ENTRYPOINT ["docker-entrypoint.sh"]

# Start the main loop
#CMD [ "/usr/sbin/crond",
# "-f",
# "-d", "8"
# ]
#CMD ["crond", "-f", "-d", "8"]
CMD ["/usr/src/app/start.sh"]

# Complete QEMU virtualization
RUN ["cross-build-end"]
24 changes: 12 additions & 12 deletions Dockerfile.template
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@
# @link https://www.balena.io/docs/learn/develop/dockerfile/#dockerfile-templates
#

# Declare pre-build variables
ARG DEVICE_NAME=%%BALENA_MACHINE_NAME%%

# Define base image
FROM balenalib/%%BALENA_MACHINE_NAME%%-alpine-python:2
FROM balenalib/${DEVICE_NAME}-alpine-python:2

# Declare build environment variables
ENV AIRTHINGSWAVE_VERSION 0.2
ENV VERSION 0.2.1
ENV CRON_PERIOD hourly
# Declare build variables
ARG AIRTHINGSWAVE_VERSION=0.2
ARG VERSION=0.2.2

# Label image with metadata
LABEL org.label-schema.name="balena AirthingsWave" \
org.label-schema.description="Airthings Wave radon detector bridge for single-board computers." \
org.label-schema.vcs-url="https://github.com/renemarc/balena-airthingswave" \
LABEL org.label-schema.description="Airthings Wave radon detector bridge for single-board computers." \
org.label-schema.name="balena AirthingsWave" \
org.label-schema.url="https://airthings.com/wave/" \
org.label-schema.vcs-url="https://github.com/renemarc/balena-airthingswave" \
org.label-schema.version=${VERSION} \
org.label-schema.schema-version="1.0"

Expand Down Expand Up @@ -50,15 +52,13 @@ RUN apk add \
py-setuptools

# Copy project files in their proper locations
ARG CRON_PERIOD=hourly
COPY ["crontask.sh", "/etc/periodic/${CRON_PERIOD}/airthingswave-mqtt"]
COPY ["docker-entrypoint.sh", "/usr/local/bin/"]
COPY ["config.yaml", "start.sh", "./"]

ENTRYPOINT ["docker-entrypoint.sh"]

# Start the main loop
#CMD [ "/usr/sbin/crond",
# "-f",
# "-d", "8"
# ]
#CMD ["crond", "-f", "-d", "8"]
CMD ["/usr/src/app/start.sh"]
128 changes: 98 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Turn a single-board computer ([Raspberry Pi](https://www.raspberrypi.org/)) into

Useful if your [radon](https://en.wikipedia.org/wiki/Radon#Health_risks) detector is located too far from your home automation hub, or if you need to use your hub's Bluetooth antenna for something else.

This project creates Docker/balena images based on Alpine Linux that weigh less than 120 MiB on a Raspberry Pi. ⚖️
This project creates [Docker](#docker-)/[balena](#balena-) images based on Alpine Linux that weigh less than 120 MiB on a Raspberry Pi. ⚖️

<div align="center">
<figure>
Expand Down Expand Up @@ -47,13 +47,14 @@ Of course you _could_ do all of this on your own, but do you _really_ want to mi
## Table of contents 📑

1. [Prerequisites](#prerequisites-)
2. [Preparation](#preparation-)
3. [Installation](#installation-)
4. [Configuration](#configuration-)
5. [Dockerfiles](#dockerfiles-)
6. [Alternatives](#alternatives-)
7. [Contributing](#contributing-)
8. [Thanks](#thanks-)
2. [balena](#balena-)
1. [Preparation](#preparation-)
2. [Installation](#installation-)
3. [Configuration](#configuration-)
3. [Docker](#docker-)
4. [Alternatives](#alternatives-)
5. [Contributing](#contributing-)
6. [Thanks](#thanks-)

<p align="right"><a href="#top" title="Back to top">🔝</a></p>

Expand All @@ -62,20 +63,26 @@ Of course you _could_ do all of this on your own, but do you _really_ want to mi
1. At least one [Airthings Wave radon detector](https://airthings.com/wave/).
2. Your favourite [Internet of Things](https://en.wikipedia.org/wiki/Internet_of_things) (IoT) device that offers both Bluetooth Low Energy (BLE) and network access, like the inexpensive [Raspberry Pi Zero W](https://www.raspberrypi.org/products/raspberry-pi-zero-w/).
3. Working access to an MQTT broker, either [a public one](https://github.com/mqtt/mqtt.github.io/wiki/public_brokers), your own hosted [Mosquitto](https://mosquitto.org/) instance or [the Home Assistant addon](https://www.home-assistant.io/addons/mosquitto/).
4. [A free-tier account](https://dashboard.balena-cloud.com/signup) on [balenaCloud](https://balena-cloud.com/) along with [a properly set SSH public key](https://www.balena.io/docs/learn/getting-started/raspberrypi3/nodejs/#adding-an-ssh-key) into your account.
4. (Recommended) [A free-tier account](https://dashboard.balena-cloud.com/signup) on [balenaCloud](https://balena-cloud.com/) along with [a properly set SSH public key](https://www.balena.io/docs/learn/getting-started/raspberrypi3/nodejs/#adding-an-ssh-key) into your account.
5. (Recommended) [The balena command-line tools](https://www.balena.io/docs/reference/cli/). Do read up on their [friendly development guidelines](https://www.balena.io/docs/learn/develop/local-mode/).

Let's play! 🤠

<p align="right"><a href="#top" title="Back to top">🔝</a></p>

## Preparation 🍔
## balena 📦

Follow these simple steps to quickly get your app running on a dedicated device using balenaCloud. If you want more control, [try the Docker solution instead](#docker-).

For reference, the balena framework will build the container using the [`./Dockerfile.template`](./Dockerfile.template) which employs placeholders so that the correct system architecture is picked for you during installation. Easy!

### Preparation 🍔

1. [Create a new application](https://dashboard.balena-cloud.com/login) on balenaCloud dashboard and select the appropriate IoT hardware.
2. Add a new device to your app. Start with _development mode_ for local testing, or go directly for _production mode_ if you know what you're doing.
3. (Optionally) Configure the downloaded image to give your device a custom hostname instead of the generic `balena`:

```sh
```shell
sudo balena local configure /path/to/downloaded/image.img
```

Expand All @@ -85,37 +92,37 @@ Your hardware is ready; it's now time to [install the project! ⬇️](@installa

<p align="right"><a href="#top" title="Back to top">🔝</a></p>

## Installation 💻
### Installation 💻

1. Git clone this project's repository:

```sh
```shell
git clone git@github.com:renemarc/balena-airthingswave.git
```

2. Add your balena application as a secondary remote to the cloned repo:

```sh
```shell
git remote add balena <username>@git.balena-cloud.com:<username>/<appname>.git
```

3. Push the code to balenaCloud and wait for it to build and provision your device:

```sh
```shell
git push balena master
```

Great! You are now ready to [configure the project. ⬇️](#configuration-)

<p align="right"><a href="#top" title="Back to top">🔝</a></p>

## Configuration ⚙
### Configuration ⚙

Either modify the [`config.yaml`](./config.yaml) file with your MQTT and Airthings Wave(s) information, or ideally [declare environment variables](https://www.balena.io/docs/learn/manage/serv-vars/) that will then be automatically replaced in the said configuration file.
Either modify the [`./config.yaml`](./config.yaml) file with your MQTT and Airthings Wave(s) information, or ideally [declare environment variables](https://www.balena.io/docs/learn/manage/serv-vars/) that will then be automatically replaced in the said configuration file.

I **strongly** suggest simply using environment variables, either at the whole fleet level, at the single device level, or at a mix of both. Configuration is easier to update this way and [if you live in a McMansion](https://www.ted.com/talks/kate_wagner_i_hate_mcmansions_and_you_should_too) you can provision multiple devices with the same codebase. Yay!

```sh
```shell
MQTT_BROKER 192.168.1.1
MQTT_PORT 1883
MQTT_USERNAME user
Expand All @@ -129,9 +136,9 @@ WAVES_NAME_3 radon/garage
WAVES_ADDR_3 cc:78:ab:00:00:0c
```

The Waves names are used as MQTT topic prefixes, so name them however you prefer. If you have more than one Wave that you want to query, do modify the [`config.yaml`](./config.yaml) file to add more entries.
The Waves names are used as MQTT topic prefixes, so name them however you prefer. If you have more than one Wave that you want to query, do modify the [`./config.yaml`](./config.yaml) file to add more entries.

Which MAC address to use? Leave that empty for now and [proceed to the first run below. ⬇️](#first-run)
Which MAC address to use? Leave that empty for now and [proceed to the first run below. ⬇️](#first-run-)

### First run 🏃

Expand All @@ -150,19 +157,19 @@ Which MAC address to use? Leave that empty for now and [proceed to the first run

SSH into your device (only if _development mode_ was selected earlier) or use the balenaCloud app dashboard terminal to [find your Wave's MAC address](https://airthings.com/raspberry-pi/) by issuing this command:

```sh
```shell
python /usr/src/app/find_wave.py
```

Press Ctrl+C when scanning seems to be done. Take note of the MAC address for the Wave that you want to use, and either modify [`config.yaml`](./config.yaml) or ideally create sets of environment variables for each Wave that you want to use.
Press Ctrl+C when scanning seems to be done. Take note of the MAC address for the Wave that you want to use, and either modify [`./config.yaml`](./config.yaml) or ideally create sets of environment variables for each Wave that you want to use.

Once configured, either `git push` your changes or restart the device.

### Cron job ⏲
### Cron job ⏲

If your above parameters are correct, you should be receiving new MQTT messages every hour. Keep an eye on the streaming device logs in the balenaCloud dashboard and use an MQTT client to debug incoming messages.

Want to receive quicker updates? [Modify the appropriate Dockerfile](#dockerfiles-) and change the `CRON_PERIOD` variable from `hourly` to `15min` or something else that you need.
Want to receive quicker updates? Modify the [`./Dockerfile.template`](./Dockerfile.template) and change the **CRON_PERIOD** argument from **hourly** to **15min** [or to something else](https://wiki.alpinelinux.org/wiki/Alpine_Linux:FAQ#My_cron_jobs_don.27t_run.3F).

<div align="center">
<figure>
Expand All @@ -177,17 +184,78 @@ Want to receive quicker updates? [Modify the appropriate Dockerfile](#dockerfile

<p align="right"><a href="#top" title="Back to top">🔝</a></p>

## Dockerfiles 🐳
## Docker 🐳

Want more control or wish to run this container on some multi-purpose shared hardware? Here are some useful steps.

Compared to the [balena solution](#balena-), here the regular [`./Dockerfile`](./Dockerfile) is used.

<figure>
<div align="center">
<img src="https://media.giphy.com/media/6AFldi5xJQYIo/giphy.gif" alt="Containers being moved in a yard">
</div>
</figure>

### Build and run 🏗️

1. Fork or clone this project's repository.
2. Edit the [`./env.list`](./env.list) file to setup your environment variables. [See configuration above ⬆️](#configuration-) for details.

3. Build the image:

For a Raspberry Pi or Zero:

```shell
docker build --tag=airthingswave .
```

For everything else, specify the **DEVICE_NAME** argument with the relevant lowercase machine name [from balena base images](https://www.balena.io/docs/reference/base-images/base-images/). For a Raspberry Pi 3 for instance:

```shell
docker build --build-arg DEVICE_NAME=raspberrypi3 \
--tag=airthingswave .
```

4. Run the project as an auto-starting container:

```shell
docker run --detach --restart=unless-stopped \
--env-file=env.list \
--net=host --cap-add=NET_ADMIN \
--name=airthingswave \
airthingswave
```

`--net=host` gives the container access to the host's network devices, including Bluetooth.\
`--cap-add=NET_ADMIN` gives the container network privileges.

5. Perform the steps [outlined in first run above ⬆️](#first-run-) using while inside the container:

```shell
docker exec -it airthingswave bash
```

Which [Dockerfile](https://docs.docker.com/engine/reference/builder/) to use? balenaCloud will pick [`Dockerfile.template`](./Dockerfile.template) for you. Easy!
6. Should you wish to [change the cron job frequency](#cron-job-), you can pass the **CRON_PERIOD** (default: **hourly**) argument while first building the image:

### [`⚙️ Dockerfile.template`](./Dockerfile.template)
```shell
docker build --build-arg CRON_PERIOD=15min \
--tag=airthingswave .
```

balenaCloud will automatically use this one, which employs placeholders so that the correct system architecture is selected at build time.
Once ready and working, you can alternatively use this example one-liner to build and run the project:

### [`⚙️ Dockerfile`](./Dockerfile)
```shell
docker run --detach --restart=unless-stopped \
--env-file=env.list \
--net=host --cap-add=NET_ADMIN \
--name=airthingswave $(docker build --quiet .)
```

Should you want to build your own Docker image, this regular Dockerfile will build one for a Raspberry Pi or Zero. If you use some other hardware, just change the `FROM balenalib/raspberry-pi-` part with [any other supported base images](https://www.balena.io/docs/reference/base-images/base-images/). This file supports [QEMU virtualization](https://en.wikipedia.org/wiki/QEMU) so that [ARM architecture](https://en.wikipedia.org/wiki/ARM_architecture) images can be built on non-ARM hardware.
<figure>
<div align="center">
<img src="https://media.makeameme.org/created/containers-containers-everywhere.jpg" alt="Buzz Lightyear saying Containers everywhere!" width="400">
</div>
</figure>

<p align="right"><a href="#top" title="Back to top">🔝</a></p>

Expand Down
4 changes: 2 additions & 2 deletions docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
set -o errexit -o noclobber -o nounset -o pipefail

# Replace placeholders found in configuration file with environment variables
sed -e "$(env | sed 's/[\%]/\\&/g;s/\([^=]*\)=\(.*\)/s%${\1}%\2%/')" \
sed --expression "$(env | sed 's/[\%]/\\&/g;s/\([^=]*\)=\(.*\)/s%${\1}%\2%/')" \
< /usr/src/app/config.yaml \
> /usr/src/app/airthingswave-mqtt.yaml
>| /usr/src/app/airthingswave-mqtt.yaml

# Pass through the CMD directive
exec "$@"
11 changes: 11 additions & 0 deletions env.list
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#
# Docker environment variables
#

MQTT_BROKER=192.168.1.1
MQTT_PORT=1883
MQTT_USERNAME=user
MQTT_PASSWORD=super-secret-password

WAVES_NAME_1=airthingswave
WAVES_ADDR_1=cc:78:ab:00:00:00

0 comments on commit b054751

Please sign in to comment.