Exposing a set of stateful pods with stable DNS entries
TypeScriptTo expose a set of stateful pods with stable DNS entries in Kubernetes, you can use a
StatefulSet
along with aService
.StatefulSets
are suitable for stateful applications that require unique, persistent identifiers and stable networking.Here's why you would use these resources:
StatefulSet
: Manages the deployment and scaling of a set of Pods, and provides guarantees about the ordering and uniqueness of these Pods. Unlike aDeployment
, aStatefulSet
maintains a sticky identity for each of their Pods. These pods are created from the same spec, but are not interchangeable: each has a persistent identifier that it maintains across any rescheduling.Service
: Provides a stable endpoint for accessing the set of pods managed by theStatefulSet
. Typically, you'd use aClusterIP
,NodePort
, orLoadBalancer
service depending on how you want to expose these pods.
Let's create a Pulumi program in TypeScript to define a
StatefulSet
with a correspondingService
that provides stable DNS entries for stateful service discovery:-
Define the StatefulSet: This will create the desired number of pods with a given specification. Each pod in the
StatefulSet
will have a stable hostname based on the name of theStatefulSet
and its ordinal index. -
Define the Service: This service targets the pods managed by the
StatefulSet
. It's important to set theserviceName
field in theStatefulSet
's spec to the name of thisService
. -
Deploy the DNS entries: Kubernetes automatically creates DNS entries for each pod and service in the cluster if you're using
CoreDNS
orkube-dns
. TheService
will have a DNS entry, and each pod in theStatefulSet
will have a DNS entry in the formpodname.servicename.namespace.svc.cluster.local
.
Here's a simplified example of how to use Pulumi with the Kubernetes provider to accomplish this:
import * as k8s from '@pulumi/kubernetes'; // Create a Kubernetes Service targeted at StatefulSet pods const svc = new k8s.core.v1.Service("svc", { metadata: { name: "statefulservice" }, spec: { clusterIP: "None", // Setting this to 'None' creates a "headless service" (no load balancing) selector: { app: "statefulapp" }, ports: [{ port: 80, name: "http" }], }, }); // Create a StatefulSet of nginx pods const statefulSet = new k8s.apps.v1.StatefulSet("statefulset", { metadata: { name: "statefulset" }, spec: { serviceName: "statefulservice", // Must match the name of the Service replicas: 3, selector: { matchLabels: { app: "statefulapp" }, }, template: { metadata: { labels: { app: "statefulapp" }, }, spec: { containers: [{ name: "nginx", image: "nginx" }] }, }, }, }); // Export the Service name and StatefulSet name export const serviceName = svc.metadata.name; export const statefulSetName = statefulSet.metadata.name;
In this code:
- A headless Service is created with
clusterIP: "None"
which is suitable for discovering individual Pods within the StatefulSet. - The StatefulSet spec includes a
serviceName
which should match the Service designed to target the Pods in the StatefulSet. - Both resources use selectors based on labels to match up the Service with the underlying Pods.
- The exported values can be used to check the created resources in your Kubernetes cluster.
You can apply this Pulumi program by saving it in a file with a
.ts
extension, for example,statefulSet.ts
. Then, runpulumi up
from the command line in the same directory as your file, and Pulumi CLI will carry out the deployment.Remember, provided that your local environment is configured with
kubectl
and it's pointing to your Kubernetes cluster, Pulumi will use your local kubeconfig to make the appropriate calls to your cluster's API server.