Skip to main content
Kratos provides a flexible service discovery system that supports multiple registries including Consul, etcd, Nacos, and Kubernetes.

Overview

Service discovery in Kratos includes:
  • Service registration on startup
  • Automatic deregistration on shutdown
  • Health checks and heartbeats
  • Client-side load balancing
  • Multiple discovery backends

Registry Interface

Kratos defines standard interfaces for service registration and discovery:
package registry

// Registrar is service registrar.
type Registrar interface {
	// Register the registration.
	Register(ctx context.Context, service *ServiceInstance) error
	// Deregister the registration.
	Deregister(ctx context.Context, service *ServiceInstance) error
}

// Discovery is service discovery.
type Discovery interface {
	// GetService return the service instances in memory according to the service name.
	GetService(ctx context.Context, serviceName string) ([]*ServiceInstance, error)
	// Watch creates a watcher according to the service name.
	Watch(ctx context.Context, serviceName string) (Watcher, error)
}

// ServiceInstance is an instance of a service in a discovery system.
type ServiceInstance struct {
	ID        string            `json:"id"`
	Name      string            `json:"name"`
	Version   string            `json:"version"`
	Metadata  map[string]string `json:"metadata"`
	Endpoints []string          `json:"endpoints"`
}

Using Consul

1
Install Consul Client
2
Install the Kratos Consul integration:
3
go get github.com/go-kratos/kratos/contrib/registry/consul/v2
4
Configure Consul Registry
5
Set up Consul as your service registry:
6
package server

import (
	"github.com/go-kratos/kratos/contrib/registry/consul/v2"
	"github.com/go-kratos/kratos/v2/registry"
	consulAPI "github.com/hashicorp/consul/api"
)

func NewRegistrar(conf *conf.Registry) registry.Registrar {
	// Create Consul client
	c := consulAPI.DefaultConfig()
	c.Address = conf.Consul.Address
	c.Scheme = conf.Consul.Scheme
	
	cli, err := consulAPI.NewClient(c)
	if err != nil {
		panic(err)
	}
	
	// Create Consul registry
	r := consul.New(cli)
	return r
}
7
Register Service on Startup
8
Register your service when the application starts:
9
package main

import (
	"github.com/go-kratos/kratos/v2"
	"github.com/go-kratos/kratos/v2/registry"
	"github.com/go-kratos/kratos/v2/transport/http"
	"github.com/go-kratos/kratos/v2/transport/grpc"
)

func newApp(logger log.Logger, hs *http.Server, gs *grpc.Server, r registry.Registrar) *kratos.App {
	return kratos.New(
		kratos.Name("user.service"),
		kratos.Version("v1.0.0"),
		kratos.Metadata(map[string]string{
			"region": "us-west",
			"zone":   "zone-a",
		}),
		kratos.Logger(logger),
		kratos.Server(hs, gs),
		kratos.Registrar(r),
	)
}
10
Service Discovery Client
11
Discover and connect to other services:
12
package data

import (
	"context"
	"time"

	"github.com/go-kratos/kratos/contrib/registry/consul/v2"
	"github.com/go-kratos/kratos/v2/transport/grpc"
	consulAPI "github.com/hashicorp/consul/api"
)

func NewOrderServiceClient(conf *conf.Data) (orderv1.OrderClient, error) {
	// Create Consul client
	c := consulAPI.DefaultConfig()
	c.Address = conf.Consul.Address
	cli, err := consulAPI.NewClient(c)
	if err != nil {
		return nil, err
	}
	
	// Create service discovery
	r := consul.New(cli)
	
	// Connect with service discovery
	conn, err := grpc.DialInsecure(
		context.Background(),
		grpc.WithEndpoint("discovery:///order.service"),
		grpc.WithDiscovery(r),
		grpc.WithTimeout(5*time.Second),
	)
	if err != nil {
		return nil, err
	}
	
	return orderv1.NewOrderClient(conn), nil
}

Using Etcd

Install Etcd Client

go get github.com/go-kratos/kratos/contrib/registry/etcd/v2

Configure Etcd Registry

import (
	"github.com/go-kratos/kratos/contrib/registry/etcd/v2"
	clientv3 "go.etcd.io/etcd/client/v3"
)

func NewRegistrar(conf *conf.Registry) registry.Registrar {
	// Create etcd client
	client, err := clientv3.New(clientv3.Config{
		Endpoints: []string{conf.Etcd.Address},
	})
	if err != nil {
		panic(err)
	}
	
	// Create etcd registry
	r := etcd.New(client)
	return r
}

Using Nacos

Install Nacos Client

go get github.com/go-kratos/kratos/contrib/registry/nacos/v2

Configure Nacos Registry

import (
	"github.com/go-kratos/kratos/contrib/registry/nacos/v2"
	"github.com/nacos-group/nacos-sdk-go/clients"
	"github.com/nacos-group/nacos-sdk-go/common/constant"
	"github.com/nacos-group/nacos-sdk-go/vo"
)

func NewRegistrar(conf *conf.Registry) registry.Registrar {
	sc := []constant.ServerConfig{
		*constant.NewServerConfig(conf.Nacos.Address, conf.Nacos.Port),
	}
	
	cc := constant.ClientConfig{
		NamespaceId:         conf.Nacos.NamespaceId,
		TimeoutMs:           5000,
		NotLoadCacheAtStart: true,
	}
	
	client, err := clients.NewNamingClient(
		vo.NacosClientParam{
			ClientConfig:  &cc,
			ServerConfigs: sc,
		},
	)
	if err != nil {
		panic(err)
	}
	
	r := nacos.New(client)
	return r
}

Kubernetes Service Discovery

Using Kubernetes DNS

Leverage Kubernetes built-in service discovery:
// Direct service connection via Kubernetes DNS
conn, err := grpc.DialInsecure(
	context.Background(),
	grpc.WithEndpoint("order-service.default.svc.cluster.local:9000"),
)

Using Kubernetes API

go get github.com/go-kratos/kratos/contrib/registry/kubernetes/v2
import (
	"github.com/go-kratos/kratos/contrib/registry/kubernetes/v2"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

func NewRegistrar() registry.Registrar {
	// Create in-cluster config
	config, err := rest.InClusterConfig()
	if err != nil {
		panic(err)
	}
	
	// Create clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err)
	}
	
	// Create registry
	r := kubernetes.NewRegistry(clientset)
	return r
}

Load Balancing

Built-in Load Balancing Strategies

Kratos supports multiple load balancing algorithms:
import (
	"github.com/go-kratos/kratos/v2/selector"
	"github.com/go-kratos/kratos/v2/selector/wrr"
	"github.com/go-kratos/kratos/v2/selector/p2c"
	"github.com/go-kratos/kratos/v2/selector/random"
)

// Weighted Round Robin
conn, err := grpc.DialInsecure(
	context.Background(),
	grpc.WithEndpoint("discovery:///order.service"),
	grpc.WithDiscovery(r),
	grpc.WithSelector(wrr.NewBuilder()),
)

// Power of Two Choices (P2C)
conn, err := grpc.DialInsecure(
	context.Background(),
	grpc.WithEndpoint("discovery:///order.service"),
	grpc.WithDiscovery(r),
	grpc.WithSelector(p2c.NewBuilder()),
)

// Random
conn, err := grpc.DialInsecure(
	context.Background(),
	grpc.WithEndpoint("discovery:///order.service"),
	grpc.WithDiscovery(r),
	grpc.WithSelector(random.NewBuilder()),
)

Custom Load Balancing

Implement custom load balancing logic:
type CustomSelector struct{}

func (s *CustomSelector) Select(ctx context.Context, nodes []selector.Node) (selector.Node, selector.DoneFunc, error) {
	// Custom selection logic
	node := nodes[0] // Select first node
	
	done := func(ctx context.Context, di selector.DoneInfo) {
		// Called after request completes
		if di.Err != nil {
			// Handle error
		}
	}
	
	return node, done, nil
}

Service Metadata

Adding Metadata

Include metadata with service registration:
app := kratos.New(
	kratos.Name("user.service"),
	kratos.Version("v1.0.0"),
	kratos.Metadata(map[string]string{
		"region":      "us-west-2",
		"zone":        "zone-a",
		"environment": "production",
		"team":        "platform",
	}),
	kratos.Registrar(r),
)

Filtering by Metadata

Filter service instances by metadata:
import "github.com/go-kratos/kratos/v2/selector/filter"

// Filter by region
conn, err := grpc.DialInsecure(
	context.Background(),
	grpc.WithEndpoint("discovery:///user.service"),
	grpc.WithDiscovery(r),
	grpc.WithNodeFilter(filter.Version("v1.0.0")),
)

Health Checks

HTTP Health Endpoint

Implement health check endpoints:
internal/server/http.go
func NewHTTPServer(c *conf.Server) *http.Server {
	srv := http.NewServer(
		http.Address(c.HTTP.Addr),
	)
	
	// Health check endpoint
	srv.HandleFunc("/health", func(ctx http.Context) error {
		return ctx.String(200, "OK")
	})
	
	return srv
}

Consul Health Checks

Configure Consul health checks:
import "github.com/go-kratos/kratos/contrib/registry/consul/v2"

r := consul.New(cli,
	consul.WithHealthCheck(true),
	consul.WithHealthCheckInterval(10), // Check every 10 seconds
)

Best Practices

Service Names

Use consistent, descriptive service names across your infrastructure

Health Checks

Always implement proper health check endpoints

Graceful Shutdown

Deregister services gracefully on shutdown

Metadata

Use metadata for routing, versioning, and feature flags

Complete Example

internal/server/registrar.go
package server

import (
	"github.com/go-kratos/kratos/contrib/registry/consul/v2"
	"github.com/go-kratos/kratos/v2/registry"
	consulAPI "github.com/hashicorp/consul/api"

	"yourproject/internal/conf"
)

func NewRegistrar(c *conf.Registry) registry.Registrar {
	cfg := consulAPI.DefaultConfig()
	cfg.Address = c.Consul.Address
	cfg.Scheme = c.Consul.Scheme
	
	cli, err := consulAPI.NewClient(cfg)
	if err != nil {
		panic(err)
	}
	
	return consul.New(cli,
		consul.WithHealthCheck(true),
		consul.WithHealthCheckInterval(10),
	)
}

func NewDiscovery(c *conf.Registry) registry.Discovery {
	cfg := consulAPI.DefaultConfig()
	cfg.Address = c.Consul.Address
	cfg.Scheme = c.Consul.Scheme
	
	cli, err := consulAPI.NewClient(cfg)
	if err != nil {
		panic(err)
	}
	
	return consul.New(cli)
}

Next Steps

Configuration

Configure your registry settings

Deployment

Deploy with service discovery enabled

Build docs developers (and LLMs) love