Disclaimer: This blog contains opinions about Google technology. While I work at Google, this is my personal blog. Opinions stated here are my own, not those of my company.
In the last several posts we focused on various Kubernetes Config Connector use cases. We configured WordPress site running on Kubernetes, powered by Google Cloud MySQL database. Additionally, we explored multi-cluster ingress. In this post, I would like to step back to discuss some of the benefits of using Kubernetes Config Connector. And if you would like more details, go straight to Google Cloud Config Connector documentation.
What is Config Connector?
Config Connector is a Kubernetes extension that enables managing Google Cloud infrastructure using Kubernetes-style declarative APIs. Config Connector registers Custom resource definitions (CRDs) and controllers. It translates desired declarative state into a series of imperative API calls. It uses Kubernetes etcd to store the desired and actual state of your configuration.
Declarative and Idempotent
Traditional infrastructure management approaches are imperative. In other words, statements change the state. The main problem with it, is that every statement depends on its specific place in the flow. You cannot shuffle your statements and except the script to work. For example, let’s consider a set of statements:
1. service_account => create service account 2. pubsub_topic => create pubsub topic 3. pubsub_subscription => create pubsub subscription to pubsub_topic 4. pubsub_subscription_policy => allow service_account to read pubsub_subscription
You can reorder statements 1 and 2, but statements 3 and 4 must come after them. Additionally, you cannot simply reshuffle the statements. Best case scenario, you will get a warning if something already exists. More likely, you will get an error, attempting to reference an object that is not yet created. This example is also not idempotent, in other words, it cannot be applied multiple times without changing the result.
In contrast, with, declarative programming you are describing, what you would like to have and let the system figure it out for you.
* service account named service_account * pubsub topic named pubsub_topic * pubsub subscription to pubsub_topic named pubsub_subcription * service account with permissions to read pubsub_subscription named service_account
While on the surface it looks very similar, note that we are describing the parts, rather than telling the system what to create. In order for this to to work, the system needs to keep track of the items in progress. It should be able to build the dependency graph to finish creating the items, once it completes creating the dependencies. It is also idempotent, because I can duplicate these statements without changing the result:
* service account with permissions to read pubsub_subscription named service_account * service account with permissions to read pubsub_subscription named service_account * service account named service_account * pubsub topic named pubsub_topic * pubsub subscription to pubsub_topic named pubsub_subcription
Note, how I completely reordered and duplicated some statements. And yet, with a functional declarative model, the result will not change.
What are the downsides to declarative model with its independent order and idempotence? We are giving up determinism. We know that the system will do, some time in the future, eventually. However, there are no guarantees that it will be done, when is script finished executing. As we discussed, the system needs to learn about the parts of the system, track their progress as they are being created. This is what we are calling eventual consistency. This is also why combining declarative and imperative parts are really tricky. However this out of scope for this post.
As we discussed, declarative system has an inventory on what is being built. It tracks all the parts of your configuration and is able to revisit and complete unfinished parts, once dependencies are satisfied. And knows the desired state, what you want to have. It also knows the actual state, that is, what you have now. And it knows how to get to the desired state from any state, because it has built the dependency graph.
Given this knowledge, it can fix the broken parts of your configuration. This becomes especially valuable in infrastructure configuration, since it can be broken due to extraneous factors. For example, pods can run out out of memory and shut down. Someone may attempt to change configuration from outside, using imperative methods. Our declarative system detects and corrects this, eventually. It brings all the parts back to the desired state.
This was a very quick summary of some of the benefits and downsides of using Kubernetes Config Connector to create infrastructure in a way that is declarative, idempotent, eventually consistent and self healing.