The Operating System is the Target

I recently had to solve a small problem that involved collecting some information from around 1000 machines, do some computation on the information and then do some work on each machine to fix something. It wasn't anything fancy, just more machines than I could do by hand. I could have used a language like Python or Ruby to drive the whole thing, but I decided I should just use the tools that come with the OS (in this case, FreeBSD). I used ssh to connect to each machine, but doing that in serial would take too long, so I used xargs to do it in parallel. I needed to store some information about each host, so I used the file system as a key-value database where the name of the file was the machine and the contents were the information. With sort and uniq I could do a range of interesting set operations. And it all worked just great. Using the file system as my database, rather than an in-memory data structure let me do a lot of debugging easily as each phase was on disk and could be inspected. Going in parallel was just a matter of how many ssh processes I was willing to run at once. I could have solved it in Python, figuring out a library for doing SSH, hoping it's async or using a bunch of threads and using in-memory data structures to store the data. But parallelizing the whole thing was a matter of adding -P10 to the xargs command, almost too easy. I had some edge cases in what the data looked like, so having the data from each phase on disk made debugging and doing the next phase easy. By embracing the OS, I actually got a smoother experience than I likely would have otherwise.

That's a simple example, though, and what I was doing was a one-off. A long-term solution would probably not be implemented that way. Or would it? What is the limit in this solution? A reasonable file system can handle at least an order of magnitude more files in a directory and there are some well known tricks to making directories small by sharding the data across more directories. Using ssh with xargs probably starts to feel some serious pain coming up on 10k machines. sort and uniq can work pretty well across gigabytes of data (which would be a lot of hosts). It seems like the limiting factor here is ssh, one process per connection might just be a bit too much. Ok, so we can replace the ssh and xargs with some kind of real program that handles that but keep the rest. Testing is not bad in this setup either, unit tests could just be creating a directory structure and running the phase of the system on that structure and see what happens. And we can mock things just by playing with the PATH and making a mock, for example, ssh.

Maybe just the idea of using these shell scripts and connecting them via pipes or the file system is off putting, though. It certainly doesn't feel professional. Modern programmers don't use their OS. They abstract it away so their code can run anywhere. Java has probably done the most to push this, having it as an explicitly stated goal. The Java developer does not write code for the OS they are on, they write it for the JVM. The JVM even goes as far as to provide its own DNS cache, something you'd leave to the surrounding OS infrastructure, which has some odd behaviour and worse default settings.

Using the OS as the programming API isn't new, by a long shot. The first experience I had with this was through qmail which lets the OS enforce security for it. Rather than being implemented as a single monolithic application which has access to everything and attempts to enforce permissions itself, qmail breaks itself into many different processes, each of which runs as a different user and communicate over IPC. The OS then becomes the security model.

Joyent's Manta takes a similar approach of utilizing the OS. Manta is a map-reduce framework that uses ZFS and Solaris Zones. Unlike Hadoop, Manta lets one distribute map-reduce jobs over thousands of machines where the job is any program, rather than having to be implemented in a specific framework. The demo app is implementing word count using wc and awk. By using the primitives the OS provides, the existing ecosystem of programs are already usable. Running multi-tenant involves setting up the file system permissions and creating the zones correctly, two things which may not be perfect but are well understood and possible to reason about.

The model that qmail uses is successful from a security standpoint, but qmail itself isn't all that popular these days. Postfix and OpenSMTPD are easier to administrate. It's possible that qmail was a bit too early and would be more popular if configuration managers like Ansible or Puppet existed, reducing the burden on setting it up. But one limitation of qmail is that most OS's are not designed to scale to thousands or millions of users, which qmail uses for its own user management. There exists a parallel universe, though, where a kernel developer decided to make users a more fluid and dynamic concept such that applications could outsource user management to the OS. In this universe, logging into your GMail account spawns your own service process running under your own OS user, maybe even in a jail.

Abstracting the OS away isn't inherently bad. Writing a program to run across multiple OS's will need to unify the semantics of each system somehow. But making more use of the OS where appropriate can simplify a program while making it more robust and debuggable. OS's already provide a wide range of tools to inspect what they are doing and by using the OS you get that introspection for free. Maybe next time you see yourself writing a service or tool, take a moment and see where the OS might be able to help you solve it.

UPDATE: I was reminded of Ted Dziuba's post on Taco Bell Programming which says a very similar thing to this post as well. Taco Bell Programming is about having a toolbox of as few tools as possible that can be connected in different ways to achieve a complex range of functionality. This post is about embracing the operating system as that toolbox. Ted says it best here: "functionality is an asset, but code is a liability."

Author: orbitz
Updated: 2016-06-25 Sat 16:58