A base Docker images for all images using single service per container principle. Contains S6 init system via S6 overlay.
Different layers are build on top of each other and they include scripts for different needs. The goal is to provide easy base to build web application development containers.
/etc/cont-init.dincludes scripts, which are run in order at init time before main command. Different layers add scripts here. More in S6 overly documentation./etc/services.d/contains service directories for S6 services (copied to/run/s6/services/by S6 overlay)/datashared data volume for application containers/localis a link to/data/$CONTAINER_TYPE(created by/etc/cont-init.d/00_local_data)/srcany files here are copied to/srv/(by/etc/cont-init.d/10_copy_srcs)
-
base - just s6 init and utilities
Utils
apt_install,apt_purgeand apt configs are included to help build different layers and application container. Theapt_install [packages...]udpates the index, installspackagesand removes debs and the index. Theapt_purge [packages...]does it's best to ensure nothing is left after removing packages.The script
/usr/local/lib/prefix-logscan be used to prefix logs with a service name before printed to stdout. To use it, link service./log/runto it.Shell functions in
/usr/local/lib/cont-init-functions.shhelp creating simplecont-init.dscripts. Read the file and check the example below to know more.Utils
run_services <service> [service...]andstart_services <service> [service...]can be used by the main script to run service once or start it with restart policy respectively. Both are just wrappers for s6-svc.There are some other scripts, explore and read them when needed.
-
dbipc (on top of base) - postgresql and rabbitmq
create-db.sh <user> <database_name> [<command> [args...]]starts PostgreSQL, creates an user and a db, runs the command to initialise it and finally dumps the contents to/srv/db-$dabase_mame.sql.gz. The purpose is to run this in Dockerfile to prebuild complex database, so initial container start is faster.init-db.sh <user> <database_name> [<command> [args...]]checks if user can connect to databasedatabase_name. If it can't, it will create the database and the user, and will populate the database with/srv/db-$database_name.sql.gzif it exists, or with the command. If the connection can be made, then the command is run to update it. Environment variableDATABASE_IS_EMPTYis set totruefor first case.Both commands will set following environment variables and run the command as
user:DATABASE_USER=$user,DATABASE_NAME=$database_name,DATABASE_IS_EMPTY=[true|],USER=$user -
python3 (on top of dbipc) - base for Python projects
pip_installfor Dockerfiles. It is just alias forpip3 install --no-cache-dir --disable-pip-version-check./etc/cont-init.d/90_virtualenvcreates virtual environments under/localfor every/srv/<app>/requirements.txt. For example, if you mount a development code to/srv/myapp, then/local/venv_myappwill be created with requirements from the project. -
django (on top of python3) - utilities for Django based projects
django-migrate.sh [bootstrap]is tool to be used withcreate-db.shandinit-db.sh. If there is requirements.txt, it will execute./manage.py makemigrationsand if any are created, it will print them to stdout. All migrations should be created by the developer, but this is here to identify missing migrations. Next, it will run./manage.py migrateto ensure database is updated. Lastly, if$DATABASE_IS_EMPTYistrueand there exists a bootstrap script, it will be execute usingpython3. The bootstrap script is the first argument fordjango-migrate.shor/srv/db-$DATABASE_NAME-bootstrap.py.run-django.sh [-u user] [-d database_name] [-a app_path] [-s setup_script] [-i init_script]whereuserdefaults to$CONTAINER_TYPE,database_nametouser,app_pathto/srv/$user,setup_scriptto/srv/$app_name-setup.pyandinit_scriptto/srv/$app_name-init.sh. Theapp_nameis the directory name of$app_path.-
If
/local/venv_$app_name/bin/activateexists, it will be sourced. Check/etc/cont-init.d/90_virtualenvfrom python3 layer. -
Execute
init-db.sh $user $database_name django-migrate.sh, to ensure database state. -
If
requirements.txtexists, run./manage.py compilemessages -
Run
./manage.py collectstatic -
If
$setup_scriptexists, run it usingpython3as$user -
If
$init_scriptexists, run it as root. -
Finally, as
$user:- If first argument is
manage, execute./manage.py <rest of the arguments> - If any arguments, execute all the arguments
- Else, execute
./manage.py help
- If first argument is
The script will exit on first failure (
set -x). -
For an exmaple, a Django application container could be created with a Dockerfile:
FROM apluslms/service-base:django-latest
ENV CONTAINER_TYPE="myapp" \
DJANGO_LOCAL_SETTINGS="/srv/myapp-django-settings.py" \
DJANGO_SECRET_KEY_FILE="/local/django/secret_key.py"
RUN adduser --system --no-create-home --disabled-password --gecos "A webapp server,,," --home /srv/myapp --ingroup nogroup myapp
RUN mkdir /srv/myapp && chown myapp.nogroup /srv/myapp
COPY myapp-cont-init.sh /etc/cont-init.d/myapp
COPY myapp-django-settings.py \
myapp-init.sh \
db-myapp-bootstrap.py \
/srv/
COPY myapp /srv/myapp/
WORKDIR /srv/myapp/
RUN python3 -m compileall -q .
RUN pip_install -r requirements.txt && rm requirements.txt
RUN python3 manage.py compilemessages 2>&1
RUN create-db.sh myapp myapp django-migrate.sh
EXPOSE 8000
CMD [ "manage", "runserver", "0.0.0.0:8000" ]and myapp-django-settings.py:
STATIC_ROOT = '/local/myapp/static/'
MEDIA_ROOT = '/local/myapp/media/'
DATABASES = { 'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'myapp' }}
CACHES = { 'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/run/myapp/django-cache' }}and myapp-cont-init.sh:
#!/bin/sh
. /usr/local/lib/cont-init-functions.sh
ENSURE_DIR_MODE=2755
ENSURE_DIR_USER=myapp
ENSURE_DIR_GROUP=nogroup
ensure_dir /run/myapp
ensure_dir /local/myapp
ensure_dir /local/myapp/static
ensure_dir /local/myapp/mediaThe example Django app uses local settings and secret key functions from a Django essentials package:
update_settings_with_file(__name__,
environ.get('DJANGO_LOCAL_SETTINGS', 'local_settings'),
quiet='DJANGO_LOCAL_SETTINGS' in environ)
update_secret_from_file(__name__, environ.get('DJANGO_SECRET_KEY_FILE', 'secret_key'))