API Builder

Running API Builder on Raspberry Pi Cluster using Docker Swarm – Part 3

In Part 2 of this series, I showed how I was able to run API Builder on RasPi.  In Part 3, I’m going to show how I containerized API Builder to run on Docker.

Docker

I’m not going to cover the details on Docker, but here’s a link if you need to review.

Dockerfile

The Dockerfile is used to build a Docker image. Then, I create an image that is used to run containers.

In my case, I’m going to create a Dockerfile that builds an image of my API Builder project from Part 2. If you recall, we have a API Builder project named ‘mycoolproject’, and I set up my RasPi to run my LED API. I’m going to use this project to build my Docker image.

When I created the API Builder project, a default Dockerfile was created in the root directory. This default Dockerfile needed to update to set the base image to an ARM version of Node.js.  

Now, my Dockerfile looks like this:

# See the README.md for usage and configuration info

FROM arm32v7/node:8.11.1
COPY . /app
WORKDIR /app
RUN npm install --production
CMD ["node", "."]

What about the LED libraries? If you recall from Part 2, I had to run my python script with root permissions to get access to the GPIO header. Docker will allow me to grant root permissions to a container using the ‘privileged’ flag for the ‘run’ command. However, once we deploy to Docker Swarm things get little more difficult. Note, Docker Swarm does not allow us to set the ‘privileged’ flag or attach the GPIO device either. To make this work I will have to use the virtual GPIO interface that doesn’t require root permissions. Unfortunately, the LED library I used in Part 2 only uses the native GPIO device. The good news: there’s a GoLang version that works with the sysfs virtual GPIO interface.

With all that information above, my updated Dockerfile is shown below:

# See the README.md for usage and configuration info

FROM arm32v7/node:8.11.1

RUN apt-get update && \
    apt-get install -qy build-essential git curl ca-certificates && \
    curl -sSLO https://storage.googleapis.com/golang/go1.7.5.linux-armv6l.tar.gz && \
    mkdir -p /usr/local/go && \
    tar -xvf go1.7.5.linux-armv6l.tar.gz -C /usr/local/go/ --strip-components=1

ENV PATH=$PATH:/usr/local/go/bin/
ENV GOPATH=/go/

RUN mkdir -p /go/src/axway.com/led

COPY set-led.go /go/src/axway.com/led

WORKDIR /go/src/axway.com/led

RUN go get -d -v

# RUN go build
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o setled .

COPY . /app
WORKDIR /app

RUN cp /go/src/axway.com/led/setled /app

RUN npm install --production

CMD ["node", "."]

If you look closely at the Dockerfile, you will see that I’m using a new Golang script to enable the LEDs. I created a file in the root of my project named ‘set-led.go’

$ touch set-led.go

Then, I add the following to the ‘set-led.go’ file.

package main

import (
     . "github.com/alexellis/blinkt_go/sysfs"
     "fmt"
     "os"
     "strconv"
)

func main() {
    fmt.Println(os.Args)
    var err error
    var r, g, b, pixel int =  100,0,0,0

if r, err = strconv.Atoi(os.Args[1]); err != nil {
panic(err)
}

if g, err = strconv.Atoi(os.Args[2]); err != nil {
panic(err)
}

if b, err = strconv.Atoi(os.Args[3]); err != nil {
panic(err)
}

if pixel , err = strconv.Atoi(os.Args[4]); err != nil {
panic(err)
}

    brightness := 0.5
    blinkt := NewBlinkt(brightness)

    blinkt.SetClearOnExit(false)

    blinkt.Setup()

    Delay(100)

 blinkt.Clear()
            blinkt.SetPixel(pixel, r, g, b)
    blinkt.Show()
}

This go script is compiled when the Docker image is created.

I updated the API to use this golang script by updating the apis/pixelrgbled.js file with code below:

var APIBuilder = require('@axway/api-builder-runtime');
var cmd = require('node-cmd');

var RGBPixelAPI = APIBuilder.API.extend({
    group: 'LEDapi',
    path: '/api/led',
    method: 'GET',
    description: 'this is an api that shows how to light an LEDs on RaspPi ',
        // Just any model so i can load the api.
    // model: 'testuser',
    // before: 'pre_example',
    // after: 'post_example',
    parameters: {
        r: { description: 'red', type:'query', optional:false },
        g: { description: 'green', type:'query', optional:false },
        b: { description: 'blue', type:'query', optional:false },
        p: { description: 'pixel', type:'query', optional:false }
    },
    action: function (req, resp, next) {
        console.log(req.params);
        var red = req.params.r;
        var green = req.params.g;
        var blue = req.params.b;
        var pixel = req.params.p;

        cmd.get('./setled '+red+' '+green+' '+blue+' '+pixel, function(err, data, stderr){
            console.log('setled : ',data)
         resp.response.status(200);
            resp.send(data);
            next();
        });
    }
});
module.exports = RGBPixelAPI;

Building the Docker Image

Build a Docker image using the Dockerfile we updated above:

{0}nbsp;docker build . -t  pc0:5000/test-apib:latest

Once the build is completed, I can now run a container using the pc0:5000/test-apib:latest image:

$ docker run -it --rm -v /sys:/sys -p 9080:8080 pc0:5000/test-apib bash

I’m mounting the ‘sys’ file system into my container, so my golang script can write to /sys/class/gio

Now, you can test just like I did in Part 2 of this series.

That’s all for now! Check out the other blogs in this series: