Why Stateful Applications in K8s need a headless service

Through out the years, Kubernetes had the reputation of being most suited for stateless applications, and that is wrong. The open-source project focused heavily in its recent versions on running stateful apps with ease on the orchestration platform. By stateful applications, we mean (micro-)services that save data into a persistent storage, for use by clients, servers or other services. For instance, a Mysql host in K8s or a more cloud native apps like Cassandra and MongoDB with master and slaves and hard-logic stuff.

This stateful logic of doing the work comes with some complexities, like expecting the host name to be stable (constant), or maybe we need a master and a bunch of slaves with its own replication strategy. This is where the kubernetes community figured the limitations of replicaSet and deployments, which led to the developement of a new resource called “Statefulset”. It´s similar to the replicaset but most suited for stateful apps with some guarantees that make managing those apps easy. Most importantly replicas has indices and scales (up/down ) according to that. So that Cassandra can elect a leader in a democratic way and maintain that as long as the app stands, which is awesome!

Furthermore, StatefulSet comes with the capability of running replicas with stable DNS name/entries that target each replica. And here comes the role of “Headless service” to deliver that. As stated in the offical documentation of Kubernetes, the K8s-API will create new endpoints that map directly to the pods. This logic is mandatory in StatefulSet as it requires a headless service(ClusterIP=none), for now. PS:That may change in the upcoming versions.

The domain managed by this Service takes the form: $(service name).$(namespace).svc.cluster.local, where “cluster.local” is the cluster domain. As each Pod is created, it gets a matching DNS subdomain, taking the form: $(podname).$(governing service domain), where the governing service is defined by the serviceName field on the StatefulSet.

This is different to the regular services that create a single DNS entry that abstracts the proxying to all the underlying pods.

This means with a headless service we will end up with DNS entries like this: mongo (svc), mongo-0.mongo(pod1), mongo-1.mongo(pod2), mongo-2.mongo(pod3). If you don´t have to target a specific instance of MongoDB, you can always interact with the service, but if your app needs an explicit and unique communication with specific pods you can target it easily. Therefore you can use these DNS names directly with your back-end app and the connection String URI would be like this maybe: "mongodb://mongo-0.mongo,mongo-2.mongo:27017/dbname_?"

So even if something unexpected happens like a pod failure and the provisioning of a new pod with a new IP @ , the stable DNS entry will ensure the mapping between your Java app and the underlying Disk (Storage that holds your data).

Finally, statefulSet is all about the cloud native movement that shapes how IT systems are architectured for the future. If you´re into the old school pattern, you can still use traditional deployment/replicaSet for the stateful logic, but please note that you´re missing something Big. :)