github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/ruler/base/ruler_ring.go (about)

     1  package base
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/go-kit/log"
    10  	"github.com/grafana/dskit/flagext"
    11  	"github.com/grafana/dskit/kv"
    12  	"github.com/grafana/dskit/netutil"
    13  	"github.com/grafana/dskit/ring"
    14  
    15  	util_log "github.com/grafana/loki/pkg/util/log"
    16  )
    17  
    18  const (
    19  	// If a ruler is unable to heartbeat the ring, its better to quickly remove it and resume
    20  	// the evaluation of all rules since the worst case scenario is that some rulers will
    21  	// receive duplicate/out-of-order sample errors.
    22  	ringAutoForgetUnhealthyPeriods = 2
    23  )
    24  
    25  // RingOp is the operation used for distributing rule groups between rulers.
    26  var RingOp = ring.NewOp([]ring.InstanceState{ring.ACTIVE}, func(s ring.InstanceState) bool {
    27  	// Only ACTIVE rulers get any rule groups. If instance is not ACTIVE, we need to find another ruler.
    28  	return s != ring.ACTIVE
    29  })
    30  
    31  // RingConfig masks the ring lifecycler config which contains
    32  // many options not really required by the rulers ring. This config
    33  // is used to strip down the config to the minimum, and avoid confusion
    34  // to the user.
    35  type RingConfig struct {
    36  	KVStore          kv.Config     `yaml:"kvstore"`
    37  	HeartbeatPeriod  time.Duration `yaml:"heartbeat_period"`
    38  	HeartbeatTimeout time.Duration `yaml:"heartbeat_timeout"`
    39  
    40  	// Instance details
    41  	InstanceID             string   `yaml:"instance_id" doc:"hidden"`
    42  	InstanceInterfaceNames []string `yaml:"instance_interface_names" doc:"default=[<private network interfaces>]"`
    43  	InstancePort           int      `yaml:"instance_port" doc:"hidden"`
    44  	InstanceAddr           string   `yaml:"instance_addr" doc:"hidden"`
    45  	NumTokens              int      `yaml:"num_tokens"`
    46  
    47  	// Injected internally
    48  	ListenPort int `yaml:"-"`
    49  
    50  	// Used for testing
    51  	SkipUnregister bool `yaml:"-"`
    52  }
    53  
    54  // RegisterFlags adds the flags required to config this to the given FlagSet
    55  func (cfg *RingConfig) RegisterFlags(f *flag.FlagSet) {
    56  	hostname, err := os.Hostname()
    57  	if err != nil {
    58  		panic(fmt.Errorf("failed to get hostname, %w", err))
    59  	}
    60  
    61  	// Ring flags
    62  	cfg.KVStore.RegisterFlagsWithPrefix("ruler.ring.", "rulers/", f)
    63  	f.DurationVar(&cfg.HeartbeatPeriod, "ruler.ring.heartbeat-period", 5*time.Second, "Period at which to heartbeat to the ring. 0 = disabled.")
    64  	f.DurationVar(&cfg.HeartbeatTimeout, "ruler.ring.heartbeat-timeout", time.Minute, "The heartbeat timeout after which rulers are considered unhealthy within the ring. 0 = never (timeout disabled).")
    65  
    66  	// Instance flags
    67  	cfg.InstanceInterfaceNames = netutil.PrivateNetworkInterfacesWithFallback([]string{"eth0", "en0"}, util_log.Logger)
    68  	f.Var((*flagext.StringSlice)(&cfg.InstanceInterfaceNames), "ruler.ring.instance-interface-names", "Name of network interface to read address from.")
    69  	f.StringVar(&cfg.InstanceAddr, "ruler.ring.instance-addr", "", "IP address to advertise in the ring.")
    70  	f.IntVar(&cfg.InstancePort, "ruler.ring.instance-port", 0, "Port to advertise in the ring (defaults to server.grpc-listen-port).")
    71  	f.StringVar(&cfg.InstanceID, "ruler.ring.instance-id", hostname, "Instance ID to register in the ring.")
    72  	f.IntVar(&cfg.NumTokens, "ruler.ring.num-tokens", 128, "Number of tokens for each ruler.")
    73  }
    74  
    75  // ToLifecyclerConfig returns a LifecyclerConfig based on the ruler
    76  // ring config.
    77  func (cfg *RingConfig) ToLifecyclerConfig(logger log.Logger) (ring.BasicLifecyclerConfig, error) {
    78  	instanceAddr, err := ring.GetInstanceAddr(cfg.InstanceAddr, cfg.InstanceInterfaceNames, logger)
    79  	if err != nil {
    80  		return ring.BasicLifecyclerConfig{}, err
    81  	}
    82  
    83  	instancePort := ring.GetInstancePort(cfg.InstancePort, cfg.ListenPort)
    84  
    85  	return ring.BasicLifecyclerConfig{
    86  		ID:                  cfg.InstanceID,
    87  		Addr:                fmt.Sprintf("%s:%d", instanceAddr, instancePort),
    88  		HeartbeatPeriod:     cfg.HeartbeatPeriod,
    89  		TokensObservePeriod: 0,
    90  		NumTokens:           cfg.NumTokens,
    91  	}, nil
    92  }
    93  
    94  func (cfg *RingConfig) ToRingConfig() ring.Config {
    95  	rc := ring.Config{}
    96  	flagext.DefaultValues(&rc)
    97  
    98  	rc.KVStore = cfg.KVStore
    99  	rc.HeartbeatTimeout = cfg.HeartbeatTimeout
   100  	rc.SubringCacheDisabled = true
   101  
   102  	// Each rule group is loaded to *exactly* one ruler.
   103  	rc.ReplicationFactor = 1
   104  
   105  	return rc
   106  }