We have been running containers in production for more than a year now and want to share some of the lessons learnt, by open sourcing our container suite.
So why another container suite? Haven't you already written one (https://github.com/previousnext/docker-containers)? We have been running containers since Docker 0.7, at that time we were only running them as dev/test environments which attempted to mimic what we ran on production VMs. At the time, the easiest way forward was to write containers which felt familiar to us.
With our adoption of Kubernetes over the last year we were able to rethink the way that we ran containers, with the main goal of thinking about our containers as processes and not VMs.
Today we are open sourcing the containers that run on our internal platform, Skipper.
We are open sourcing these containers because we want to share some of the lessons learnt during this process.
I wanted to talk about this first because I believe it is the most important.
At every opitunity we get we leverage the "official" Docker Hub base images.
This means alot less work for us when a new version of PHP comes out, and we only add what we want on top.
We also get the benefits of the Docker Hub security scanning these base images.
Health checking is very important for running production applications, and even more important when running containers.
These checks allow container frameworks to call a HTTP endpoint, get a response code and make decisions on whether your application should be restarted or removed from the load balancer.
Our containers ship with 2 Drupal health checks:
These health checks can only be accessed from behind the load balancer (no public requests) and ask the application the following:
- Can I connect to the database?
- Can I bootstrap Drupal?
- Can I write to the filesystem?
If a developer wants to provide their own set of health checks, they include them when we deploy a project (bake a new image). These checks added it to the folder "/var/www/healthz" folder and accessible via the same "/healthz/custom.php" url path.
Typically when a container gets run it inherits the static configuration which gets baked into it at build time. If you provide that container with more memory it goes to
waste because you have not reconfigured your processes to consume all that new memory, sure, you can lock down your containers and document that they run at X amount of memory and CPU, but by doing that you are also restricting developers and their requirements.
To solve this we wrote a tool called: Tuner
Tuner is a simple app which does the following:
- Takes in 3 environment variables TUNER_MAX, TUNER_PROC and TUNER_MULTIPLIER (for over allocation of resources)
- Returns a configuration to Stdout which you can write to wherever you like
$ export TUNER_MAX=1024 $ export TUNER_PROC=64 $ export TUNER_MULTIPLIER=3 $ tuner --conf=apache > /etc/apache2/mods-enabled/tuner.conf
This allows container frameworks to pass in information about the size of the container, and use tuner to write the required configuration.
To use this with our containers, its as simple as setting those 3 environment variables at runtime:
$ docker run -d -e TUNER_MAX=1024 -e TUNER_PROC=64 previousnext/7.x-dev
For more information, see https://github.com/previousnext/tuner
All our "dev" containers extend the corresponding production container to add extra tools, languages and scripts.
eg. php7.x-dev inherits from php7.x
This means we run local very close to what is run on production, and keep our production containers as slim as possible.
We have come along way since writing our first set of containers, at the time they looked alot more like VMs (got the job done, but not very elegant).
This container set is PreviousNexts way to put our best foot forward to show the community how we run containers, show off a couple of our ideas and get feedback.