Here's a simple way to run your apps within a docker container using runit.
First you'll need to create a Dockerfile that'll bake in the necessary alpine packages that'll we'll need.
Dockerfile
FROM alpine:3.7
ENV TERM=xterm-256color
COPY ./boot.sh /sbin/boot.sh
RUN echo "http://dl-cdn.alpinelinux.org/alpine/v3.7/community" >> /etc/apk/repositories && \
apk --update upgrade && \
apk add \
bash \
runit && \
rm -rf /var/cache/apk/* && \
chmod +x /sbin/boot.sh && \
mkdir /etc/run_once
CMD [ "/sbin/boot.sh" ]
Then you'll need to create the boot.sh file. This is run when the container is booted.
The boot file sets up the correct environment variables, runs the runit service and properly shutdowns the runit services when the container is stopped.
boot.sh
#!/bin/sh
shutdown() {
echo "shutting down container"
# first shutdown any service started by runit
for _srv in $(ls -1 /etc/service); do
sv force-stop $_srv
done
# shutdown runsvdir command
kill -HUP $RUNSVDIR
wait $RUNSVDIR
# give processes time to stop
sleep 0.5
# kill any other processes still running in the container
for _pid in $(ps -eo pid | grep -v PID | tr -d ' ' | grep -v '^1$' | head -n -6); do
timeout -t 5 /bin/sh -c "kill $_pid && wait $_pid || kill -9 $_pid"
done
exit
}
# store enviroment variables
export > /etc/envvars
PATH=/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin
# run all scripts in the run_once folder
/bin/run-parts /etc/run_once
exec env - PATH=$PATH runsvdir -P /etc/service &
RUNSVDIR=$!
echo "Started runsvdir, PID is $RUNSVDIR"
echo "wait for processes to start...."
sleep 5
for _srv in $(ls -1 /etc/service); do
sv status $_srv
done
# catch shutdown signals
trap shutdown SIGTERM SIGHUP SIGQUIT SIGINT
wait $RUNSVDIR
shutdown
The boot file will look for services in the /etc/service directory. Add your runit services in this folder and they'll be automatically run by runit.
For example, let's create a service that'll just print "hello world" to the console until terminated.
First, we'll create our service file /etc/service/helloworld/run. This file will contain the necessary code to start whatever process we want:
/etc/service/helloworld/run
#!/bin/bash
echo "Started service..."
for i in {1..1000}
do
echo "Hello world"
sleep 1
done
exit 1
If you want to run an external process, like uwsgi, you can use a service file like this:
/etc/service/web/run
#!/bin/sh -e
# pipe stderr to stdout and run app via uwsgi
exec 2>&1
exec uwsgi \
--http :80 \
--module web:app \
--enable-threads \
--processes 2 \
--threads 2
Best practise to add your services inside the docker image is to inherit from the above Dockerfile and use COPY statements to copy your service files into the docker image
You can find a working base image with runit + alpine setup at https://github.com/sanjeevan/baseimage