Table of Contents
This base docker image is designed to:
- Correctly run microservice with multiple processes:
- Clean up zombie processes.
- Graceful shutdown on
docker stop
. - Easy to run extra services (syslog, cron, ssh, etc.).
- On essential service crash you can either restart it or stop container.
- Handle persistent storage permissions and migrations:
- Microservice can run as non-root account "app".
- Account "app" is the owner of attached data volume directory.
- In case of using bind mount this account will have same UID/GID as mounted directory - so you don't get files with unusual ownership in this directory as result of running container.
- In case of using new volume it ownership will be changed to random UID/GID (to better isolate container's data and processes). When container will start next time using same volume it'll use same UID/GID.
- Be small and secure, thanks to Alpine Linux.
FROM powerman/alpine-runit-volume:v0.4.0
# [OPTIONAL] Change default directory with volume (/data):
ENV VOLUME_DIR=/home/app/var/data
# [OPTIONAL] Move syslog log dir to default volume dir:
ENV SYSLOG_DIR=$VOLUME_DIR/syslog
# [OPTIONAL] Use anonymous volume if real volume was not provided:
VOLUME $VOLUME_DIR
# Add your files:
COPY . .
# [OPTIONAL] Use cron service and setup /home/app/crontab for user "app":
RUN ln -s /etc/sv/dcron /etc/service/cron; \
install -m 0600 /home/app/crontab /etc/crontabs/app; \
echo app >> /etc/crontabs/cron.update
# Either install your runit services (./init/ directory is just an example):
RUN ln -nsf "$PWD"/init/* /etc/service/
# or run your microservice as PID1 (without runit):
CMD ["my-pid-1-app"]
These environment variables can be provided when starting container:
APP_UID
,APP_GID
: numeric UID/GID to be used for "app" account and to set ownership for root directory of attached volume.- If
APP_UID=0
then both will be ignored. - I recommend to use values between 1000 and 60000 to avoid conflicts with existing accounts.
- If
VOLUME_DIR
: directory with data volume (/data
by default)SYSLOG_DIR
: directory with syslog logs (/var/log
by default)
To run your command using "app" account: chpst -u app …
(use in your
service's ./run
and ./finish
scripts).
To gracefully shutdown container on essential service exit/crash add to
that service's ./finish
script (run as root): sv down . /etc/sv/runsvdir
By default, your service's STDOUT/STDERR will be sent to docker logs
(using "stdout" log stream for both). To redirect your service's STDOUT to
syslog you can pipe output of your service to logger
tool using this
./log/run
script for your service:
#!/bin/sh
sv start syslog >/dev/null 2>&1 || exit 1
exec chpst -u app logger
Syslog service is enabled by default (unless you'll run your app as PID 1)
and save logs into SYSLOG_DIR
. You can configure maximum log size and
amount of old rotated log files (10 x 1MB files by default) and other
features (duplicating selected log records to docker log, sending to
network syslog by UDP, etc.) using
/var/log/config.
If you enable /etc/sv/dcron
service you can setup cron tasks using this
crontab format.
When container starts setup-volume
will be executed as ENTRYPOINT
to:
- create user account "app"
- use UID/GID provided in environment variables
APP_UID
/APP_GID
ifAPP_UID
is greater than 0 - use UID/GID of current owner of root directory of data volume, if owner's UID is greater than 0
- generate random UID/GID between 10000 and 42767 (inclusive)
- if
/var/run/docker.sock
exists add user "app" to its group
- use UID/GID provided in environment variables
- ensure root directory of data volume belongs to user "app"
- use path for data volume provided in environment variable
VOLUME_DIR
or/data
(ifVOLUME_DIR
is empty)
- use path for data volume provided in environment variable
- run
CMD
If you'll replace ENTRYPOINT
with your own script make sure it'll finish
with exec setup-volume "$@"
if it doesn't use account "app" or call
setup-volume true
before using account "app".
Main rationale to create this project instead of using existing alternatives was to automate permission/ownership management of data volume and provide much simpler implementation to run multiple processes.