1. Exposing a set of stateful pods with stable DNS entries

    TypeScript

    To expose a set of stateful pods with stable DNS entries in Kubernetes, you can use a StatefulSet along with a Service. 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 a Deployment, a StatefulSet 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 the StatefulSet. Typically, you'd use a ClusterIP, NodePort, or LoadBalancer service depending on how you want to expose these pods.

    Let's create a Pulumi program in TypeScript to define a StatefulSet with a corresponding Service that provides stable DNS entries for stateful service discovery:

    1. 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 the StatefulSet and its ordinal index.

    2. Define the Service: This service targets the pods managed by the StatefulSet. It's important to set the serviceName field in the StatefulSet's spec to the name of this Service.

    3. Deploy the DNS entries: Kubernetes automatically creates DNS entries for each pod and service in the cluster if you're using CoreDNS or kube-dns. The Service will have a DNS entry, and each pod in the StatefulSet will have a DNS entry in the form podname.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, run pulumi 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.