AbdurRehman Khan
AbdurRehman Khan

Reputation: 925

Docker - Rebooting raspberry pi host from within docker container

I have a python application that's meant to be run on a raspberry pi. I've created a docker-compose file to set it up, and my entry point happens to be a shell script that checks various things on the host such as:

  1. Making sure SPI is enabled, if it is not then enabling it by accessing /boot/config.txt and writing to it.
  2. Installing and enabling the watchdog service.
  3. Running my docker container automatically on reboot by writing it into /etc/rc.local (although I am considering replacing this with the restart: always or unless-stopped flag from the docker-compose file)

The problem is, if I enable SPI, the raspberry pi needs to reboot to set it up (not quite sure why), but when my shell script reaches the sudo reboot command from within the docker container, I get the following error:

 Failed to connect to bus: No such file or directory
 Failed to talk to init daemon.

I understand that it's probably trying to find dbus and the init daemon within the docker container but they don't exist. How do I give my container access to these resources? Do I need to mount another volume? This is my docker-compose.yml file:

version: "3"

services:
    mongoDB:
        restart: unless-stopped
        volumes:
            - "/data/db:/data/db"
        ports:
            - "27017:27017"
            - "28017:28017"
        image: "andresvidal/rpi3-mongodb3:latest"
    mosquitto:
        restart: unless-stopped
        ports:
            - "1883:1883"
        image: "mjenz/rpi-mosquitto"
    FG:
        privileged: true
        network_mode: "host"
        depends_on:
            - "mosquitto"
            - "mongoDB"
        volumes:
            - "/home/pi:/home/pi"
            - "/boot:/boot"
        #image: "arkfreestyle/fg:v1.8"
        image: "test:latest"
        entrypoint: /app/docker-entrypoint.sh
        restart: unless-stopped

FG is my python application with the entry point docker-entrypoint.sh which looks like:

#!/bin/sh
if [ ! -f /home/pi/.initialized ]; then                                                                                                                                                                                    
    echo "Initializing..."                                                                                                                                                                                

    # Turn spi on
    if grep -Fxq "dtparam=spi=on
dtparam=watchdog=on" /boot/config.txt
    then
        echo "\nSPI is already enabled"
        echo "Creating .initialized"
        # Create .initialized hidden file
        touch /home/pi/.initialized
        echo "Starting application..."
        sudo python3 __main__.py -debug
    else
        ### Enable SPI ###
    fi
    fi

    ### Create .initialized file ### 
    echo "Rebooting in ten seconds..."
    sleep 10
    sudo reboot # This line results in the error

else
    echo "Initialized already!"
    sudo python3 __main__.py -debug                                                                                                                                                                                     
fi       

The privileged option already gives my container access to the GPIO, I imagined it would give me access to reboot as well, however it seems that is not the case. Please let me know what I need to do to be able to reboot.

Upvotes: 4

Views: 4897

Answers (1)

larsks
larsks

Reputation: 311526

My first guess is that you simply need to expose /run/dbus and /run/systemd to your container, as in:

docker run -v /run/dbus:/run/dbus -v /run/systemd:/run/systemd ...

But while that is necessary, it isn't sufficient; with just those two bind mounts, attempting to interact with the host systemd from inside the container results in:

[root@631fff40f09c /]# reboot
Failed to connect to bus: No data available
Failed to talk to init daemon.

It turns out that the in order for this to work, the container must be running in the host's global PID namespace, which means we need:

docker run -v /run/dbus:/run/dbus -v /run/systemd:/run/systemd --pid=host ...

With this in place, running reboot inside the container successfully reboots the host.

In a docker-compose.yml, that would look something like:

FG:
    privileged: true
    network_mode: "host"
    pid: "host"
    depends_on:
        - "mosquitto"
        - "mongoDB"
    volumes:
        - "/home/pi:/home/pi"
        - "/boot:/boot"
        - "/run/dbus:/run/dbus"
        - "/run/systemd:/run/systemd"
    image: "test:latest"
    entrypoint: /app/docker-entrypoint.sh
    restart: unless-stopped

Upvotes: 6

Related Questions