Howto (ish) EmonCMS on an existing Ubuntu box via virtual machine/vagrant

Hi folks,

just added batteries, and that added enough complexity that my old hacked together monitoring system based on perl scripts and RRDtool databases just wasn’t going to cope any more. Found OpenEnergyMonitor and did a bit of reading. Totally get that you can get all sorts of issues installing something complex on an existing Linux box - my NUC already has MySql on it doing stuff that I don’t want to disturb… However, I also didn’t want to run yet another server, so spent some time looking for ways around it.

In the end, I dusted off some old knowledge of using Vagrant to manage virtual machines and came up with a script that will

  • Install Vagrant and all pre-reqs on an Ubunto box (1804, 2004 tested)
  • Configure and initialize an Ubuntu 2004 virtual machine in Vagrant using LibVirt/Qemu
  • Download, configure and run the ‘init.sh’ script to install emoncms in the VM with no interaction
  • Arrange for the feed data directories and the backup directories to be on the host, not in the VM (unfortunately I couldn’t work out how to get the MySql directories shared from the host and keep Mysql working)
  • Setup a regular backup (weekly) by putting a cron job into the VM
  • Arrange for the VM to restart on reboots of the host.
  • Makes the emoncms web server available on port 8080 of the host

Basically this makes minimal changes required to your host system and puts all the emoncms stuff into its own virtual machine where it will not affect (or be affected by) stuff on your host system.

Here’s the script

#!/bin/bash
sudo apt-get install -y vagrant bridge-utils  cpu-checker  libguestfs-tools  libvirt-clients  libvirt-daemon-system qemu-kvm
sudo adduser $USER libvirt
sudo adduser $USER kvm

mkdir emoncms
cd emoncms

sudo virsh pool-define /dev/stdin <<EOF
<pool type='dir'>
  <name>default</name>
  <target>
    <path>/var/lib/libvirt/images</path>
  </target>
</pool>
EOF

sudo virsh pool-start default
sudo virsh pool-autostart default

cat > Vagrantfile << EOF
Vagrant.configure("2") do |config|
  config.vm.box = "generic/ubuntu2004"
  config.vm.provision :shell, path: "install.sh"
  config.vm.network "forwarded_port", guest: 80, host: 8080
  config.vm.synced_folder 'data', '/var/opt/emoncms', type: '9p', accessmode: 'mapped'
end
EOF

cat > install.sh << EOF
#!/usr/bin/bash

sed -i s_http://us.archive.ubuntu.com/ubuntu_http://mirror.ox.ac.uk/sites/archive.ubuntu.com/ubuntu/_g /etc/apt/sources.list
#sed -i 's_http://us.archive.ubuntu.com/ubuntu_mirror://mirrors.ubuntu.com/mirrors.txt_g' /etc/apt/sources.list

wget https://raw.githubusercontent.com/openenergymonitor/EmonScripts/stable/install/init.sh
chmod +x init.sh && echo y | ./init.sh

cd /opt/openenergymonitor/EmonScripts/install/

sed -i  s/user=pi/user=vagrant/g config.ini
sed -i s/emonSD_pi_env=1/emonSD_pi_env=0/g config.ini
sed -i s/install_emonhub=true/install_emonhub=false/g config.ini
sed -i s/install_emoncms_emonpi_modules=true/install_emoncms_emonpi_modules=false/g config.ini
sed -i s/install_firmware=true/install_firmware=false/g config.ini
sed -i s/install_emonpilcd=true/install_emonpilcd=false/g config.ini
sed -i s/install_emonsd=true/install_emonsd=false/g config.ini
sed -i s/install_wifiap=true/install_wifiap=false/g config.ini
sed -i s/emoncms_modules[config]=stable//g config.ini
sed -i s/emoncms_modules[wifi]=stable//g config.ini
sed -i s/emoncms_modules[setup]=stable//g config.ini

echo n | ./main.sh

echo '5 3 * * 0 root /opt/emoncms/modules/backup/emoncms-export.sh' | sudo tee /etc/cron.d/emoncms-backup

EOF

mkdir data
chmod a+w data
sudo chgrp kvm data
sudo vagrant up

cat > emoncms-vagrant.service << EOF
[Unit]
Description=Emoncms Vagrant Virtual Machine
After=network.target
StartLimitIntervalSec=0
[Service]
Type=oneshot
RemainAfterExit=yes
User=root
WorkingDirectory=`pwd`
ExecStart=/usr/bin/vagrant up
ExecStop=/usr/bin/vagrant halt

[Install]
WantedBy=multi-user.target
EOF

sudo cp emoncms-vagrant.service /etc/systemd/system
sudo systemctl daemon-reload
sudo systemctl enable emoncms-vagrant
sudo systemctl start emoncms-vagrant

sudo virsh list

echo "EmonCMS should now be running on http://localhost:8080"
echo "Timeseries data will be in `pwd`/data"
echo "Backups will be made weekly to `pwd`/data/backup"
echo "The server should restart automatically after reboots"

Some things to note…

The script will make a subdirectory ‘emoncms’ of the folder in which it is run. You need to keep that subdirectory and the stuff in it. In particular it will contain the ‘VagrantFile’ that defines the virtual machine and the data directory that is shared to the virtual machine.

If you need to log into the virtual machine, go to the emoncms directory and run sudo vagrant ssh

Apparently it’s possible to get vagrant to work without sudo. I failed to figure it out.

If you want to test this in a throwaway Linux virtual machine and happen to be using VMWare, you must enable ‘Virtualize Intel Vt-x/EPT’ and ‘Virtualize IOMMU’ in the Processor settings otherwise the inner virtual machine will fail to work.

At this point, you have an emoncms system which is only accessible via port 8080 from your existing Linux box. You probably want to configure a reverse proxy using either NGINX or Apache to allow access from other places. I needed to do both since (for historical reasons) I use Apache for http and NGINX for https termination.

Here’s an Apache config snippet that will make your server appear under a /energy path

        <Location /energy>
                ProxyPreserveHost On
                ProxyAddHeaders Off
                ProxyPass http://localhost:8080/
                ProxyPassReverse http://localhost:8080/
                RequestHeader set "X-Forwarded-Host" "<existing linux server name>/energy/"
        </Location>

Here’s an NGINX snippet that will do pretty much the same thing

        location /energy/ {
                sub_filter 'http://<full hostname of server>' 'https://<full hostname of server>';
                sub_filter_once off;
                sub_filter_types application/xhtml+xml;

                proxy_set_header Host $host;
                proxy_set_header        X-Real-IP $remote_addr;
                proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header        X-Forwarded-Proto $scheme;
                proxy_set_header        X-Forwarded-Host <full hostname of server>/energy;
                proxy_pass http://localhost:8080;
                proxy_buffering off;
        }

        location = /energy {
                return 302 /energy/;
        }

Not sure if the subfilter stuff at the top is strictly necessary - I copied the core of this config from another site which did need that.

Anyway, maybe someone will find this useful.

cheers,

Robin