Tuesday, February 3, 2015

Installing SABnzbd in Docker on OMV

For anyone reading this post, I'm going to assume you know what Docker is, you have it installed and that you are familiar with some of the basic Docker commands.

If you simply want to get SABnzbd up and running in Docker, you can find several images in the Docker repository.  If you want to create your own Dockerfile, with a build customised to your own preferences, then read on.  If you can't be bothered reading through the information, just scroll to the bottom of this post, where I'll summarise with a complete Dockerfile you can cut and paste.  It's been tested to work on Open Media Vault, and should work on any Debian system... in fact, it should probably work on any Linux system with Docker running.

As a starting point, go to the OMV interface and create a new group called "media" and a new user called "sabnzbd", and make the user sabnzbd a member of the group media.  You don't have use these exact names, whatever suits your purposes is fine.

Then use ssh to connect to your OMV box -- the rest of these instructions will be via command prompt.

We want to find the userid and the groupid of the user and group we've just created.  So run the following commands;

getent group media
getent passwd sabnzbd

And take note of the groupid (it's the number) and the userid (the first of the two numbers).  Now create a working directory (for simplicity sake, call it sabnzbd), change into the directory and create a new file called Dockerfile using your preferred file editor (ie, nano, vi, emacs).  Start entering the following;

FROM debian:wheezy
MAINTAINER Your Name <your@email.com>
Ok, so this tells Docker that your container will be built using a Debian Jessie template.  Now add the following;
ENV GROUP xxxx
ENV GROUPID xxxx
ENV USER xxxx
ENV USERID xxxx
ENV SERVERPORT xxxx
ENV CONFIGDIR xxxx
ENV DATADIR xxxx

This sets some environment variables that we'll use later in the script. Wherever you see the xxxx, you need to customise this with your information. We already know the GROUP, GROUPID, USER and USERID, so put that information in accordingly. The SERVERPORT is the port that sabnzbd will listen to for incoming connections. By default, this is 8080, but if you want to change it, you'll need to modify this variable. The CONFIGDIR is where you'll store any config information for sabnzbd. This is purely a personal choice, but I like to use /etc/downloaders/sabnzbd. Lastly, the DATADIR is the directory where you plan to store your downloads. On Open Media Vault, this is probably going to be a shared folder somewhere in /media.  Next add the following;

## Prepare dependencies and then cleanup
RUN echo "deb http://http.debian.net/debian wheezy non-free" >> /etc/apt/sources.list.d/debian-nonfree.list

RUN apt-get update && apt-get upgrade -qy
RUN apt-get install python-cheetah par2 python-yenc unzip unrar git python-openssl sudo -qy
RUN apt-get clean &&\
rm -rf /var/lib/apt/lists/* &&\
rm -rf /tmp/*

Ok, this section handles the installation of the basic pre-requisites.  First we add the debian non-free repository into our list of sources.  This is because we want to install the non-free version of unrar.  We then run apt-get update and apt-get upgrade.  The flag "-qy" is necessary to stop apt from prompting you for any confirmation (which breaks the Docker build).  After our sources are updated, we install the packages we need, and run a basic cleanup.

RUN git clone https://github.com/sabnzbd/sabnzb/opt/sabnzbd

Once the basic pre-requisites have been installed, we take a copy of the sabnzbd code and put it in the directory /opt/sabnzbd.  You can put it wherever you like, but since it is running in Docker, it doesn't really matter.

## Add a sabnzbd user and media group
RUN groupadd -g ${GROUPID} ${GROUP} && useradd -u ${USERID} -s /usr/sbin/nologin -g ${GROUP} ${USER}
RUN chown -R ${USER}:${GROUP} /opt/sabnzbd RUN mkdir -p ${CONFIGDIR} && chown -R ${USER}:${GROUP} ${CONFIGDIR} RUN chmod u+rw ${CONFIGDIR}

Ok, this is where things get a bit funky.  Most Docker scripts assume that the application running inside the container will run a root.  Normally, running a web application as root has security issues.  But generally, since SABnzbd is being isolated inside a container, this should be less of an issue.

But, the problem is, any downloads created by SABnzbd will be written to the file system as root.  Depending on the permission structures you set up on your file system, this may or may not be an issue.  On my system, I've decided to set up a dedicated sabnzbd user that will exist on both the host system AND within the docker container.  Therefore, anything written by the user sabnzbd inside the container, will also be accessible by the user sabnzbd outside of the container.  In order to make this "work" though, the user ids need to be the same.  This is why we needed to get the groupid and userid for the user and group we created earlier.  Next add the following;

## Create Volumes
VOLUME ${CONFIGDIR}
VOLUME ${DATADIR}
Ok... one of the problems with containers is that they tend to be transient.  That is, they aren't necessarily designed for storing data permanently.  The intention is to spawn a container, based on a standard image, to be run as long as it is required.  When the container is no longer required, you can safely delete it, knowing that you can spawn a new container off the image whenever you want.

Of course, this creates a problem for things like configurations, which you probably want to save to be used again and again.  By specifying volumes, we are telling Docker to reserve these directories for permanently stored information.  Later, when we run the container, we'll map the volumes back to corresponding directories on the host system.  By performing this mapping, the data sits on the host system, but can be accessed inside the container.  If the container gets shutdown, you can still access the data inside volumes on the host.  If you delete the container, the data inside volumes remains untouched.

In the SABnzbd images, we'll specify two volumes.  The first, is a place to store configuration information -- which on my system is /etc/downloaders/sabnzbd.  The second volume is a place to store downloaded data, which on my system is /export/Downloads.  Next step;
## Expose the port sabnzbd will run on
EXPOSE ${SERVERPORT}
Ok, this exposes a particular port that SABnzbd will be running on.  When traffic is directed to your host system, firstly the Docker daemon needs to know to intercept that data, and forward it to a particular container.  And secondly, the container that receives the traffic, needs to be configured to list on that particular port.  The EXPOSE command basically configures containers built from our image to listen to a specific port.  In the case of SABnzbd, this is typically 8080, although you can configure it to something different on your system.  Next add the following to your Dockerfile;

RUN echo "#!/bin/bash" >> /opt/sabnzbd/Start.sh
RUN echo "sudo -u ${USER} /usr/bin/python /opt/sabnzbd/SABnzbd.py --config-file=${CONFIGDIR} --server :${SERVERPORT}" >> /opt/sabnzbd/Start.sh
RUN chmod +x /opt/sabnzbd/Start.sh
Ok, so this basically creates a start script inside your container.  The start script is run using bash, and contains a single command to run SABnzbd, using the parameters we've specified at the start of our Dockerfile.  Lastly, we use chmod to make the script executable.  Finally, add the last line to your Dockerfile;
ENTRYPOINT ["/opt/sabnzbd/Start.sh"]
This last step tells your SABnzbd image that the first thing a new container should do, is run the start script we just built in the previous step.  In other words, a new container spawned from our SABnzbd image, will immediately start running our start script, which has been designed to fire up SABnzbd.  Pretty simple really.

To Summarise


If you followed the instructions above, your Dockerfile should look something like this below (obviously, with the xxxx's replaced with your own personally configurations)

FROM debian:wheezy
MAINTAINER Your Name <your@email.com>

ENV GROUP xxxx
ENV GROUPID xxxx
ENV USER xxxx
ENV USERID xxxx
ENV SERVERPORT xxxx
ENV CONFIGDIR xxxx
ENV DATADIR xxxx

## Prepare dependencies and then cleanup
RUN echo "deb http://http.debian.net/debian wheezy non-free" >> /etc/apt/sources.list.d/debian-nonfree.list
RUN apt-get update && apt-get upgrade -qy
RUN apt-get install python-cheetah par2 python-yenc unzip unrar git python-openssl p7zip-full sudo -qy
RUN apt-get clean &&\
        rm -rf /var/lib/apt/lists/* &&\
        rm -rf /tmp/*

## Clone sabnzbd
RUN git clone https://github.com/sabnzbd/sabnzbd /opt/sabnzbd

## Add a sabnzbd user and media group
RUN groupadd -g ${GROUPID} ${GROUP} && useradd -u ${USERID} -s /usr/sbin/nologin -g ${GROUP} ${USER}
RUN chown -R ${USER}:${GROUP} /opt/sabnzbd
RUN mkdir -p ${CONFIGDIR} && chown -R ${USER}:${GROUP} ${CONFIGDIR}
RUN chmod u+rw ${CONFIGDIR}

## Create Volumes
VOLUME ${CONFIGDIR}
VOLUME ${DATADIR}

## Expose the port sabnzbd will run on
EXPOSE ${SERVERPORT}

RUN echo "#!/bin/bash" >> /opt/sabnzbd/Start.sh
RUN echo "sudo -u ${USER} /usr/bin/python /opt/sabnzbd/SABnzbd.py --config-file=${CONFIGDIR} --server :${SERVERPORT}" >> /opt/sabnzbd/Start.sh
RUN chmod +x /opt/sabnzbd/Start.sh

ENTRYPOINT ["/opt/sabnzbd/Start.sh"]
Now that your Dockerfile is complete, let's run this sucker!!  Check out Part 2, where I give you the basics for building your image, and getting it running.




No comments:

Post a Comment