factorish is a project I developed to make it easier to take legacy applications and make them behave like a 12factor application. See Factorish and The Twelve-Fakter App for a more detailed discussion on 12faktor apps.
The framework for it was based on the work by the opdemand folks on deis which I adapted for my docker-percona_galera project which aims to set up automagically clustering MySQL servers and then slowly morphed into this.
I have included a small example hello world python app which simply reads a config file and prints out the value for a field in there. This config file is written by confd based on environment variables (by default) or etcd keys ( with a few minor changes).
What does it do?
factorish is a is mainly a very opinionated
Vagrantfile which spins up a development environment consisting of a number of CoreOS servers (that form an
etcd cluster) and a helper
factorish.rb which reads configuration data from
factorish.yml and environment variables builds and launches the services and applications which are defined in the
You can build and run the
factorish/example image to see the basic functionality using environment variables for service discovery by running:
$ docker build -t factorish/example example $ docker run -ti -p 8080:8080 factorish/example $ curl localhost:8080 Luke, I am your father $ docker run -ti -e SERVICES_EXAMPLE_TEXT=mother -p 8080:8080 factorish/example $ curl localhost:8080 Luke, I am your mother
However launching a factorish development environment is as simple as running
vagrant up. The default cluster size is 3 VMs and as the first VM provisions it will start a private docker registry which it will use to host your images so that you only have to build or download them once. It will then build your application as defined in the
factorish.rb file and will launch it on each VM.
Factorish now supports using flannel networking by default. This means that each container will gets its own IP address and port forwarding is not needed. if you wish to skip flannel you can set
factorish.yml before running vagrant.
$ vagrant up rewriting userdata Bringing machine 'core-01' up with 'virtualbox' provider... ==> core-01: Importing base box 'coreos-beta'... ==> core-01: Matching MAC address for NAT networking... ... ==> core-01: ++ echo Creating a Private Registry ==> core-01: Creating a Private Registry ==> core-01: Building factorish/example image ==> core-01: ++ docker pull 10.0.2.2:5000/factorish/example ==> core-01: - factorish/example pulled from private registry. ==> core-01: - run ./clean_registry if you expected this to rebuild. ==> core-01: Running factorish/example ... $ vagrant ssh core-01
The registry and its images are persisted on your host in
registry/ if you want
vagrant up to automatically rebuild them you should clean that directory out by running
If you set the mode ( either via environment variable, or setting $mode in
factorish.rb ) to
test it will not try to built the Applications docker image but will instead try to download it from the docker registry.
If the evironment variable 'DEBUG' is set the scripts will run in verbose mode ( bash
set -x ) to give you more insight into what is happening.
registrator is run on each VM which watches the docker socket and pushes bound ports to etcd to help with service discovery:
$ etcdctl ls /services --recursive /services/registry /services/registry/core-01:registry:5000 /services/example /services/example/core-01:example:8080 /services/example/core-02:example:8080 /services/example/core-03:example:8080 $ etcdctl get /services/example/core-01:example:8080 172.17.8.101:8080
The included example application will now be available via a port mapping so that you can interact with it:
The startup script uses a default of
father for the environment variable of
SERVICES_EXAMPLE_TEXT if you have not specified one. To change the config option simply restart the docker container and set
$ curl 172.17.8.101:8080 Luke, I am your father
with etcd enabled you can change values live:
$ curl localhost:8080 Luke, I am your father $ etcdctl set /service/example/text bacon bacon $ curl localhost:8080 Luke, I am your bacon
A bunch of functions are also made available to you via some userdata magic based on your application definitions which can be run inside any of the VMs.
run_<app name>- run the application container
kill_<app_name>- kill (and rm) the application container
build_<app_name>- rebuild the application image
<app_name>- get a
bashprompt inside the running container
cleanup- clean up the `/services/ etcd namespace.
Dockerfileexists which adds
python:2base image and copies the local filesystem into the container as
/appand builds the python app ready to be run.
confd.tomlis a config file for
confdto look at
./templatesfor the templates and their metadata for configuring the application.
templates/supervisor.confis a template that
confdwrites out for
supervisorto be able to run the application as well as
runitis a lightweight process supervisor. Each app that it is configured to run will send its logs/output to
stdoutof supervisor which means that you get all the benefits of a process supervisor but still keep the 12 factor style logging to stdout through to the docker logging subsystems.
bin/bootthe startup script for the example application. It sets up a bunch of environment variables which can be passed in via the
docker runcomamnd to affect how the application runs, It then runs
confdonce to do an initial configuration before running
supervisorto start your application and other componments.
bin/publish_etcdis a small script that ensures that the application is running by checking via netstat that it is listening on the expected port. It publishes this information to
etcdwith a short TTL. If the application fails to respond it will quickly time out and the container will destroy itself allowing for reliable service discovery of your application to other systems.
The example app found in
./example not only gives you an easy way to check out the tooling itself, but it also provides a framework in which you can port legacy applications easily to the CoreOS/Docker ecosystem using
etcd to build out configs etc.
Paul Czarkowski (email@example.com)
Copyright 2014 Paul Czarkowski
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.