Skip to content

Refactoring

Jeff Lindsay edited this page Apr 5, 2015 · 6 revisions

What is this?

Dokku started as a 100 line Bash script that managed the workflow around deploying Heroku apps to Docker containers on any standard Ubuntu host. And thanks to the fairly early addition of a plugin system, the project's capabilities have been greatly expanded by the community.

A year and a half later, though it went through a period of disrepair, a few stepped up to get the project back on track. Bugs were fixed, features were added, some refactoring happened. However, nothing to the degree I've had in mind. For the last year I've had a re-architected version of Dokku in mind that until the Deis sponsorship I haven't been able to spend time on.

Here I'm going to explain what I had in mind so that myself, maintainers, and contributors can combine forces to make this happen. But this is intended to be a living document based on discussions around it in #dokku.

What's already great about Dokku?

Whatever we do, we don't want to abandon what makes Dokku great. Here is my breakdown of what makes Dokku great.

Bash

Love it or hate it, Bash is a big reason for Dokku's popularity across web developer communities. Even though most aren't great writing good Bash, it's accessible and familiar enough to read and hack on no matter what your primary language is. The shell model also suggested the plugin hook model that's been so central to Dokku.

Components

Most of the real work of Dokku is done by: Docker, buildstep, nginx, and sshd. Some smaller components such as pluginhook, sshcommand, and previously gitreceive helped glue everything together. Dokku itself is just a distribution of these components and a core workflow script and system of hooks. This made Dokku extremely learnable; first you'd start with the heart of Dokku, a 100 line (now 200 line) script, then move on to clearly defined components and core plugins. The components also lead to reuse and remixing, and helped spawn or support a number of other projects.

SSH

The main interface to Dokku is git and the command line, both provided by SSH. For developers, it meant commands could be added as shell scripts. For users it meant no client was necessary other than a standard SSH client. The "normal" way of doing this is to design a REST API, build a web application, and write and distribute a client to use the API. We skipped all this, but still had a simple and secure interface with comparable user experience by leveraging SSH.

Plugins

Being so simple and hackable meant it was easy to make changes and customizations to Dokku. But it wasn't until plugins were introduced that these could be shared by the community. The plugin system also helped further modularize core Dokku functionality. But the real value came from users sharing their integrations and new features, some of them making their way back into Dokku.

Single Host

The focus on being a single-host PaaS seemed like it would limit Dokku to being just a toy. And it is a fun toy! But the simplicity it has over real distributed, multi-host PaaS projects has made some users even prefer it for production use. The single host model definitely has a market; from personal use, to development environments, to application staging, to real production use. Dokku has more varied use than its more complex "production-grade" multi-host counterparts.

Nginx

If you're deploying a web application the old fashioned way, you're probably going to be using Nginx. So why introduce something different here? Although there are lots of fancy new solutions, Nginx is robust and familiar. It's the modern industry standard for a reason.

Quick Start

From the beginning, Dokku had been optimized for getting started quickly. Near instant gratification. As an example, in the early days, the Docker Hub was even slower than it is today. It was faster to download buildstep from S3, which helped keep Time to First Deploy under 5 minutes, as the original demo video advertised.

As another example, configuration isn't necessary to get started, it just works right out of the box, trying to be intelligent about hostnames and virtualhosts. Creating apps before deploying is unnecessary because that just adds an extra step and barrier to seeing your app running on your own private Heroku.

What can be better about Dokku?

So almost all of the above areas can be improved, but at a high-level, here are some of the fundamental, foundational changes I have in mind for Dokku.

100% Containerized

Refactored Dokku should run entirely inside Docker itself. This is one of the biggest changes, but has significant benefits.

Fixed platform, but works the same everywhere

Dokku is currently built against whatever the recent Ubuntu is. As such, we can say we effectively "work with Ubuntu 14.x", but there are still many flavors of that depending on the cloud provider or what else has been put on the system. Dokku mostly assumes it will own the host, yet still tries to play relatively well with others. This opens up many unknown interactions and potential for user created problems.

Running Dokku inside a container means we have total control over the exact system Dokku runs on. It means we can make more assumptions and not worry about what platform the user's host system actually is.

Not only does it allow us to fix Dokku's platform, but opens up support to run Dokku anywhere Docker can run. This means immediate support for non-Ubuntu platforms of various versions.

Simpler installation and upgrade process

Across all platforms, Dokku installation is: 1) install Docker (in some cases, you can even skip this), 2) run the Dokku container. It will pull the latest or a specific version tag from a registry and set itself up. There's still room for platform specific packages that do these two steps, but this makes them simple and completely decoupled from Dokku's native set up process.

Upgrading is just a matter of docker pull and restarting the container. User data and configuration is kept separate and can be migrated via automatic Dokku migration scripts.

Custom builds of Docker are just as manageable since the workflow is also a native Docker command: docker build.

Better, faster, more portable testing

This new architecture means testing Dokku can also happen anywhere that can run Docker. These days, many open source CI providers support Docker. Although Docker-in-Docker is a workable solution these days, it will be unnecessary and make the test harness much simpler.

It also opens up the possibility to develop and test Docker without Vagrant. Although a VM is still technically involved, Dokku can "run on OS X" with boot2docker or whatever comes next as a lightweight "just Docker" solution for OS X. Again, this simplifies testing and the development workflow for many.

Overall Modernization

Docker

Lots has changed since Dokku was released. Docker has new features and better ways of doing things that Dokku hasn't yet taken advantage of in some cases. Best practices and better tooling have been developed in the ecosystem. Although Dokku will always focus on single-host simplicity, if it can retain or increase simplicity by using a component ready for use in a distributed environment, all the better.

Bash

Another major modernization that has to happen is Dokku's use of Bash. Even though Dokku should strive for minimalism and value density, there will be a fair amount of Bash. Since Dokku was created, many of us have discovered "better ways" to write Bash and have established coding guidelines that will keep Dokku's codebase, and hopefully the plugin community's codebases, clean and consistent while increasing simplicity and reuse.

Although many have been either excited or annoyed to write new functionality for Dokku in Bash, ultimately it should be encouraged to do as little as possible by wrapping proven tools and components, or even building new components that aren't Bash and can be used elsewhere. But for the Bash that does need to be written to glue it all together and maintain the accessibility of today's Dokku, we should be writing much better Bash.

Configuration / User Data

TODO

Add-ons

TODO

New Components

Since Dokku was released, not only has tooling in the Docker ecosystem improved, but tooling around the exact workflows and processes Dokku provides has improved. We should be taking advantage of these to simplify our core codebase and to increase our capabilities. Some of these have been developed, some are in the process of being finished. Here are some examples:

Buildstep to become Buildkit

Buildstep has become very popular outside of Dokku, but it's also dated in terms of design. Both Deis and Flynn have abandoned Buildstep in favor of Flynn's slugbuilder and slugrunner. However, as images, slugbuilder and slugrunner still don't exactly fit everybody's needs, which is why Deis keeps a fork in their tree. Meanwhile, Buildstep continues to mature, and all three have to stay updated with Heroku and various buildpacks. In response, I built Herokuish as a core component to consolidate testing and support, and to allow various workflows required by different platforms, and pull from the fixes and development of all approaches.

As a first step for adoption, a PR was opened against Buildstep to use Herokuish, allowing even pre-refactored Dokku to take advantage of and start to validate Herokuish. Deis also plans to adopt Herokuish or a component based on Herokuish as it was developed under their sponsorship.

In working with clients we've learned that many people have lots of variations on how they want to do their build process. On top of this, best practices in the Docker world has been to avoid the Heroku buildpack stack altogether and focus on optimized Docker images. Dokku supports native Dockerfile builds, but Buildkit would allow this code to be dropped from Dokku. Buildkit would also support "runtime images", a new more efficient and Docker native alternative to buildpacks.

Buildstep wrapping Herokuish turns Buildstep into just a light shim for those previously using Buildstep. Buildstep also maintains a fair amount of attention (and use in non-Dokku production systems!). This combined with demand for a more encompassing solution, we think it would be a good project to turn into a more powerful build orchestration container, slightly renamed, called Buildkit.

Buildkit, which would "replace" Buildstep in Dokku, would be 100% compatible with existing buildpack building, but would also introduce the ability to do Docker builds, or anything else, using a plugin system. It would also be useful in a "standalone" daemon mode, letting you do just builds without Dokku using another new component allowing git pushes over SSH and HTTP. This lets Buildkit become another useful component in the Docker ecosystem; something we've already wished existed for a number of clients.

Plugn: More structure and tooling around plugins

Pluginhook is a very simple concept, and as a tool has enabled the Dokku plugin ecosystem. However, we've seen problems with it. There's issues around compatibility and discoverability, using hacks to use it slightly differently, and Dokku has become responsible for code to manage these plugins.

Since a number of other projects have either needed or would benefit from such a system with these issues addressed, a new project is in development called Plugn. Plugn builds on the model Pluginhook developed and adds more structure and tooling.

Plugn works as a command line tool to manage and use pluginhook-style plugins. It uses and encourages a simple plugin metadata file that can be used by the tool as well as indexes. Besides installing and removing plugins, it also manages enabling and disabling plugins, and exposes a configuration system for plugins. It intentionally doesn't do dependency resolution, but does allow you to express compatibility with other plugins and the host application. It also introduces and supports new ways to use plugins, including sourcing shell scripts, implement detect-and-select patterns (like the buildpack protocol), and using specifically configured plugins for certain hooks (pattern of drivers, backends, etc).

There's also experimental support for compiled daemon plugins and "external" plugins (that run in a separate container or even on a separate host).

Nginx Appliance

TODO

Execd, new gitreceive, HTTP git

TODO

Community

Support

TODO

Documentation

TODO

Plugin Repository / Index

TODO