A standard startup template for Docker server images. Available as a base CentOS 7 image under the name 'lenz/whaleware' - https://registry.hub.docker.com/r/lenz/whaleware
- Server instances should have a fine-grained life cycle. Starting up a web instance does not mean that the instance is immediately ready to serve requests. It might have to warm itself up, update a local database, load resources, etc. You do not want to add an instance to a load balancing pool before it's ready to run.
- Server instances should be able to report their own health periodically.
- Server instances should contain a watchdog process that will restart them as needed.
- As the number of configuration variables tends to be quite large, instead of passing a large set of parameters, we prefer using a JSON structure. This JSON structure is then filled in with embedded default parameters, so that only non-default ones are to be passed externally.
- Though we have a preference for JSON over HTTP, notifications should be environment-agnostic.
You could store the current lifecycle information and app health on a database, or on
an external webapp, on
etcd
or in an Elasticsearch database. You should be able to choose. - Running a server within Docker means receiving Docker signals - so you can start an orderly shutdown in case the instance needs to terminate.
- Data should be stored in a separate directory, ready to mount a data-only container (if you want to) or to run without persistent data at all.
- You should be able to customize easily all of the features above, and decide what to use and what to ignore. You want notifications? edit one file. You don't want notifications? just leave it blank.
- Using a common filesystem layout. Just replace files as needed under
/ww/usr
- All-JSON configuration. The configuration is passed as a JSON file that is merged with an existing defaults file. So you do not have to track a large number of separate environment variables.
- Centrally reports the state of an instance.
- Periodically reports health statistics.
- Monitors the application and restarts it if needed
- Makes no assumption on the kind of services used for reporting - we use HTTP, but you may hot-swap the reporting scripts to implement your own checks and reporting format.
- Tries to terminate all services gracefully on stops.
When an instance is started (via /ww/run
) it gets the external configuration from the CFG
parameter, applies any defaults found in /www/etc/default_cfg.json
and saves it under
/www/etc/cfg.json
. This configuration can be queried at any time though the /ww/cfg
script.
On first start, it runs the boot script to edit the default image according to the configuration received externally. This happens each time an image is booted.
Then it checks to see if the data-only container is initialized. Of course this only makes sense
if you have a data-only container mounted under /data
. If none is mounted, the webapp will
always perform a first boot.
If /data
is not initialized, it launches the firstboot script so you can create databases, copy
configuration scripts, etc.
After this, the upgrade script is run. This is used to detect if /data
contains material from
a previous version that needs upgrading.
After this, the service(s) are started and warmup is called. This is needed to run any warmup queries that might be needed to provide acceptable production performance.
Only at this point the app is considered UP and is ready to serve instances.
Every 30 seconds, the monitor script is run. It is supposed to query the app and return a JSON object with health information (number of queries processed, free memory, etc.). This information is posted to a database through the pushstats script.
If the monitor scripts returns with an error code, then all services are stopped and restarted (through svcdown and svcup).
While the app is running, if you issue a docker stop
the run process will shutdown services and
terminate cleanly.
Most likely, you will NOT need all of these stages in your app. So you can simply create the scripts you need and leave other scripts blank.
whaleware offers a common pattern to manage permanent storage. All storage should be stored under the "/data" directory. If you want this to be permanent, you can either use a data-only container that exports it or mount it as a local folder.
The simplest way to run a permanent data-only container is to create a named container and bind it to your whaleware image:
docker run --name=MYWBT loway/data true
docker run --volumes-from MYWBT -p 8080:8080 -d loway/wombatdialer
By using a named container, the likelihood of binding to the wrong container is reduced.
You can also bind your whaleware image to a local folder, just like in:
mkdir -p /opt/data/WBT1DATA
docker run -v /opt/data/WBT1DATA:/data -p 8080:8080 -d loway/wombatdialer
This makes it easy to access and inspect the contents of your data container at the price of a somehow "messier" configuration on the host.
whaleware stores all configuration as a JSON file. The default configuration is stored
in /ww/etc/defaults.json
. When the app is run, a current configuration is computed by merging
it with the contents of the CFG environment variable, that is to contain a JSON structure.
You then access the results of the merge though the /ww/cfg
script.
This way you can override the default configuration on the command line, as in:
docker run -e CFG='{"memory":400}' -p 8080:8080 -d loway/wombatdialer
And you can still have fairly complex configuration.
/ww
- where all files for whaleware live/ww/usr
- where scripts are to be put/ww/files
- where your own configuration files can be put/ww/etc
- where the default configuration and the current configuration are stored/data
- where all data for an app is to be positioned/backup
- a mount point for backup scripts to send data to the host
Default scripts:
/ww/run
- the script that orchestrates all other scripts/ww/cfg
- reads a configuration value from the current configuration
Under /ww/usr
are the following scripts. Replace the one(s) you need.
boot
- the script that sets the sytem up on an image boot. Sets the system up but for the/data
directory. Runs as first script on each and every boot.firstboot
- on the first boot, the/data
directory is initialized. It happens just once if you have permanent storage.upgrade
- upgrades the system (eg applies database schema changes). It is run on every boot.warmup
- starts services and warms them up. It is run on every boot.monitor
- return a JSON blob about the state of the service. If it returns with an error code different than zero, the webapp needs to be restarted.
Helper scripts:
svcdown
- shut down the serversvcup
- starts the serverlifecycle [STATE]
- notifies a central server about the life-cycle of the current app. The default implementation just logs it on stdout.pushstats [JSON]
- pushes the JSON blob returned bymonitor
to a monitoring service. The default implementation does nothing.
An app progresses trough a life-cycle. The life-cycle can be pushed externally through the lifecycle
script so
that an external system can be aware of the current state of the app.
- DOCKERUP - An optional state notified by a startup script before the app is started
- BOOT - System is running the
boot
script. - FIRSTBOOT - System is running the
firstboot
script. - UPGRADING - System is running the
upgrading
script. - WARMUP - System is running the
warmup
script. - UP - System is ready to serve requests.
- RESTARTING - System is being restarted, because either the
monitor
script encountered a problem or a SIGHUP was received by the Docker host. After the restart, the system is flagged as UP. - STOPPED - System is being stopped because it received a SIGTERM from Docker (likely a "docker stop" was performed)
- ERROR - System is in an unrecoverable error state and is terminating (not currently used).
- DOCKERDOWN - An optional state notified by a stop script after the Docker app is shutdown
- wombatdialer. - a next generation dialer application for the Asterisk PBX. It is interesting because it is a Java web application with embedded MySQL and has a complex life cycle - so you can see the basic components in action.
- asterisk-load-test. - a set of plain Asterisk images that you can hot-swap. AMI and ARI ports are up and ready for developement and testing. Now available for Asterisk 1.8, Asterisk 11, Asterisk 12 and Asterisk 13.