Buddhism has its Four Noble Truths. Plato had his Ideals.
We've spent close to a decade running applications on top of the Cloud Foundry platform-as-a-service (PaaS), and here's a few universal truths about application development and deployment that we hope you'll bring to your next Kubernetes project.
One of the great joys of working with Cloud Foundry is getting up and running quickly, by way of buildpacks. A buildpack is an impressive bit of engineering that can inspect your raw source code and build out the appropriate, containerized application execution unit, automatically. You stand on the shoulders of buildpack authors, to paraphrase the philosopher Bertrand of Chartres.
The importance of having a solid foundation cannot be overstated. In Cloud Foundry, build packs allow developers to focus almost entirely on their application code – the problem it solves, the solutions it employs. We find that this leads to happier, more productive teams that are faster to market and quicker to respond to changing customer needs.
Kubernetes went a different way: container images. We spend a lot of time crafting and maintaining Dockerfiles and the images that they tie together, and build upon (Bertrand of Chartres truly was a visionary in his own right!). While some may argue that buildpacks are inherently superior to Dockerfiles, we think this takes the short view. The best thing about Dockerfiles is that you get to choose your base image.
As an enterprise or other large organization, you absolutely must build solid base images equipped with tools and best practices. This will free up your application teams to spend more of their time improving their applications.
Standardize, Then Evangelize.
Cloud Foundry is all about standard ways of doing things. The only way to get HTTP traffic to an application is to map a route. The only way to integrate data services is through the marketplace. Applications go in spaces, which go inside of organizational namespaces. The list goes on.
There is a whole world of things that Cloud Foundry developers don't have to consider that people working with Kubernetes deal with all the time. What ingress should we use? Do we use Helm or something else? Does each application team gets it's own namespace? It's own cluster?
For your Kubernetes strategy to work, you have to standardize these bits of information. This effort absolutely must be done with input of the application developers who will ultimately be building applications to run on top of your proposed solution.
As important as having a standard is evangelizing that standard. For this, we often find a lighthouse team, an eager application team that is cross-functional and well-versed in the platform and the applications and frameworks your organization uses. We work with lighthouse teams, empowering them to not only solve their problems on top of the platform, but to document their processes, file issues for deficiencies and troublesome areas, blog about their processes, and generally preach the virtues of the standardized way.
When your application teams see other application teams succeeding on your platform, rate of adoption will be the least of your problems.
The great thing about standards is there are so many different ones to choose from!
– me, at several points in my career
Standardization has a dark side: rigidity. Overly-prescriptive standards oppress and stifle innovation and uptake. Brittle standards don't take into account problem spaces outside of those of immediate interest, and make no concessions for extracurricular activities. If your standard platform doesn't bend, it will break – developers will seek solutions outside of the smothering confines of the standard.
We have seen this time and time again with Cloud Foundry. To retain market share, CF has had to grow and evolve as use cases outside of the "push a Ruby/Go/Java web application" sphere are encountered in the wild.
There was a time you had to use buildpacks for every application on Cloud Foundry – then Diego learned how to accept vanilla OCI images.
At one point, the only way to integrate a data service with an application was through the marketplace and a service broker. Now we have User-Provided Services.
All applications are web applications, right? Nope, and now Cloud Foundry has TCP Routing services for non-HTTP workloads.
Whatever you standardize on, make sure you have enough flexibility to bend to the inexorable winds of change, or be prepared to make a new standard in a few years.
Mind The (Knowledge) Gap.
All platforms attempt to encapsulate and abstract away certain aspects of the system, so that you don't have to know how they work. The flat memory model is a cute abstraction over the incredible monstrosity that is the modern memory architecture. Programming languages are built for beautiful simple machines so that developers don't need to know how the metal in the CPU actually thinks.
PaaSes like Cloud Foundry, and runtime orchestrators like Kubernetes are no different. One of the virtues often extolled by fans of buildpacks is that they relieve the developer of having to know how their code gets deployed. OCI image adherents claim likewise, but for lower layers of the image stack.
Both camps are right, and both camps are wrong. There will always be a knowledge gap, and at some point in your career you and your team will need to cross it. The biggest decision you need to make is when you attempt that.
Buildpacks defer crossing the gap until later. At some point in the future, an application, without any changes, will stop working with the available buildpacks. This is a huge problem for Cloud Foundry teams, because it incentivizes a stagnation of infrastructure – "Please don't upgrade the Java buildpack," say the developers. "Our applications won't build on the latest version, and we don't know why."
Building OCI images force you to cross the gap sooner. You can't help but solve deployment issues if you hope to get the application image built and shipped. Furthermore, the Dockerfile becomes a record of the steps required to run the application, which will assist future maintenance programmers and operations staff.
Decouple Your Data.
Cloud Foundry solved "the state problem" by pushing state out of the applications and into data services, which you interact with via Cloud Foundry itself (
cf bind-service, and friends). This did two things: it greatly simplified the lives of the people who build Cloud Foundry, and it taught application developers to think of their data services external, and swappable.
For Cloud Foundry in particular, this has allowed very large installations to move from one instance of the CF runtime to another – whether that's a version upgrade, a distribution change (proprietary CF → Open Source CF), or an execution runtime swap (remember the move from DEA to Diego?)
With Kubernetes, it's way too easy to run your data services right next to your application workloads. In some cases this is desirable – in development, for instance, you can spin up an ephemeral PostgreSQL instance without any input from your data services team. In production, however, you're usually better off decoupling your data deployments from your application deployments.