DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Key Considerations When Implementing Virtual Kubernetes Clusters
  • Solving Four Kubernetes Networking Challenges
  • Kubernetes Service Types
  • Exploring Intercooler.js: Simplify AJAX With HTML Attributes

Trending

  • How to Submit a Post to DZone
  • Enforcing Architecture With ArchUnit in Java
  • Chat With Your Knowledge Base: A Hands-On Java and LangChain4j Guide
  • MCP Servers: The Technical Debt That Is Coming
  1. DZone
  2. Software Design and Architecture
  3. Containers
  4. Monitoring Kubernetes Service Topology Changes in Real Time

Monitoring Kubernetes Service Topology Changes in Real Time

Stateful, distributed, containerized applications require real-time monitoring of cluster changes. Learn how EndpointSlices API provides a scalable solution.

By 
Udbhav Prasad user avatar
Udbhav Prasad
·
Nov. 01, 24 · Tutorial
Likes (5)
Comment
Save
Tweet
Share
19.4K Views

Join the DZone community and get the full member experience.

Join For Free

Horizontally scalable data stores like Elasticsearch, Cassandra, and CockroachDB distribute their data across multiple nodes using techniques like consistent hashing. As nodes are added or removed, the data is reshuffled to ensure that the load is spread evenly across the new set of nodes.

When deployed on bare-metal clusters or cloud VMs, database administrators are responsible for adding and removing nodes in a clustered system, planning the changes at times of low load to minimize disruption to production workloads.

In the world of applications running on container-orchestration platforms like Kubernetes, containers can be terminated or restarted due to frequent deployments, scheduling issues, node failures, network issues, and other unexpected changes. In this article, we’ll dive into how applications maintaining a distributed state can keep track of nodes entering and leaving the cluster.

Workload Management

Applications on Kubernetes run as containers inside pods. Since managing individual pods for distributed applications is cumbersome, Kubernetes provides higher-level abstractions that manage the pod for you.

  • Deployments are suitable for running stateless applications like web servers, where all pods are equal and indistinguishable. No pod has a particular identity and can be freely replaced.
  • StatefulSets are better suited for stateful applications, where pods are expected to have a distinct identity and are not freely interchangeable.

StatefulSets are the appropriate workload management choice for our particular example of a data store application. Additionally, creating a Kubernetes Service will expose the distributed application to clients as a single, named network application.

Tracking Pod Lifecycles Within a Service

You have a StatefulSet up and running with a Service that clients can talk to, which finally brings us to the main problem of tracking pod creation: deletion and modification in real-time.

Kubernetes assigns a stable hostname to each pod in a StatefulSet. A major drawback to using the hostname to address pods directly is that DNS results are cached, including results of failed requests made against unavailable pods.

Enter the EndpointSlices API, which provides a scalable tracking of network endpoints within a Kubernetes cluster. With the appropriate selectors, this can be used to track the lifecycle of pods within a specific Service, as long as the Service has a selector specified.

Specifically, the application can invoke the List and Watch API endpoints.

First, the application creates a Kubernetes API client.

Go
 
import (
  ...
  discovery "k8s.io/api/discovery/v1"
  metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  "k8s.io/apimachinery/pkg/watch"
  "k8s.io/client-go/kubernetes"
  "k8s.io/client-go/rest"
  ...
)

func main() {
  ctx, cancel := base.InitContext()
  defer cancel()

  // Create a kubernetes API clientset
  k8sRestConfig, err := rest.InClusterConfig()
  if err != nil {
    log.Fatalf("error getting in-cluster config: %v", err)
  }
  k8sClient, err := kubernetes.NewForConfig(k8sRestConfig)
  if err != nil {
    log.Fatalf("error creating k8s client: %v", err)
  }


Next, the application invokes the List endpoint to retrieve the currently available backends. Note that the app label is the value of the Service selector label. This ensures that the List and Watch APIs target the pods in our specific Service.

Go
 
  appLabel := "your-service-label"
  podNamespace := "your-service-namespace"

  // List the current service endpoints.
  endpointSlice, err := k8sClient.DiscoveryV1().EndpointSlices(podNamespace).List(ctx, metav1.ListOptions{
    LabelSelector:  appLabel,
    Watch:          true,
  })
  if err != nil {
    log.Fatalf("error listing endpoint slices: %v", err)
  }


Now that the application has the current set of addresses, it can monitor for changes from this state by invoking the Watch API. This is done in a background loop to allow the application to proceed with the rest of its work. The List API provides a checkpoint called ResourceVersion that we can instruct the Watch to start observing events from.

The Watch API pushes the results into the ResultChan channel.

Go
 
  // ResourceVersion is like a checkpoint that we're instructing the Watch to start from.
  resourceVersion := endpointSlice.ResourceVersion

  go func() {
    // Start a watch on the service endpoints
    watch, err := k8sClient.DiscoveryV1().EndpointSlices(podNamespace).Watch(ctx, metav1.ListOptions{
      LabelSelector: appLabel,
      Watch:         true,
      // Start watching from the appropriate resource version
      ResourceVersion: resourceVersion,
    })
    if err != nil {
      log.Fatalf("error watching endpoint slices: %v", err)
    }

    // Loop until the context is done
    for {
      select {
      case <-ctx.Done():
        break
      case event := <-watch.ResultChan():
        handleWatchEvent(watch);
      }
    }
  }()

  // Start the server / do other work
}


The application can then handle the Watch events appropriately.

Go
 
func handleWatchEvent(event watch.Event) {
  // Cast the event into an EndpointSlice event
  endpointSlice, ok := event.Object.(*discovery.EndpointSlice)
  if !ok {
    log.Fatalf("unexpected event object")
  }

  // From https://pkg.go.dev/k8s.io/apimachinery/pkg/watch#Event
  // Object is:
  //  * If Type is Added or Modified: the new state of the object.
  //  * If Type is Deleted: the state of the object immediately before deletion.
  //  * If Type is Bookmark: the object (instance of a type being watched) where
  //    only ResourceVersion field is set. On successful restart of watch from a
  //    bookmark resourceVersion, client is guaranteed to not get repeat event
  //    nor miss any events.
  //  * If Type is Error: *api.Status is recommended; other types may make sense
  //    depending on context.
  switch event.Type {
  case watch.Added:
    // Handle Added event
  case watch.Modified:
    // Handle Modified event
  case watch.Deleted:
    // Handle Deleted event
  case watch.Error:
    // Handle Error event
  }
}


It's important to note from the docs that the Added and Modified events send the full new state of the object. Therefore if the application is maintaining the endpoint addresses internally, they don't need to be incrementally updated.

The final step is to ensure that your application has the permissions to call the EndpointSlices API resources

YAML
 
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: server-endpoints-role
rules:
- apiGroups: ["discovery.k8s.io"]
  resources: ["endpointslices"]
  verbs: ["list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: server-endpoints-rolebinding
subjects:
- kind: ServiceAccount
  name: server
roleRef:
  kind: Role
  name: server-endpoints-role
  apiGroup: rbac.authorization.k8s.io


Conclusion

The EndpointSlices Kubernetes API provides a scalable, extensible, and convenient way to monitor pod lifecycle events in a Kubernetes Service in real-time, enabling seamless load balancing in stateful distributed applications.

API Kubernetes Event Load balancing (computing) pods

Opinions expressed by DZone contributors are their own.

Related

  • Key Considerations When Implementing Virtual Kubernetes Clusters
  • Solving Four Kubernetes Networking Challenges
  • Kubernetes Service Types
  • Exploring Intercooler.js: Simplify AJAX With HTML Attributes

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!