Loki: Implement common net interface/instance addr (#4950)

* Implement common net interface/instance addr.

- Implement two new common configurations: instance_interface_names,
  which is a list of net interfaces used to discover addresses and
  instance_addr, used to advertise a component address.

* Add docs to new common configurations.

* Fix lint issue: missing line.

* Add missing CHANGELOG entry.

* Change default common net interface from `127.0.0.1` to `""`.

* Add missing `InstanceAddr` docstring.
pull/5049/head
Dylan Guedes 4 years ago committed by GitHub
parent 305b41aedb
commit c0bec07e0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 13
      docs/sources/configuration/_index.md
  3. 24
      pkg/loki/common/common.go
  4. 31
      pkg/loki/config_wrapper.go
  5. 137
      pkg/loki/config_wrapper_test.go
  6. 2
      pkg/lokifrontend/frontend/v2/frontend.go

@ -1,5 +1,6 @@
## Main
* [4950](https://github.com/grafana/loki/pull/4950) **DylanGuedes**: Implement common instance addr/net interface
* [4949](https://github.com/grafana/loki/pull/4949) **ssncferreira**: Add query `queueTime` metric to statistics and metrics.go
* [4938](https://github.com/grafana/loki/pull/4938) **DylanGuedes**: Implement ring status page for the distributor
* [5023](https://github.com/grafana/loki/pull/5023) **ssncferreira**: Move `querier.split-queries-by-interval` to a per-tenant configuration

@ -2371,6 +2371,19 @@ This way, one doesn't have to replicate configuration in multiple places.
# and path_prefix is empty.
[persist_tokens: <boolean>: default = false]
# A common list of net interfaces used internally to look for addresses.
# If a more specific "instance_interface_names" is set, this is ignored.
# If "instance_interface_names" under the common ring section is configured,
# this common "instance_interface_names" is only applied to the frontend, but not for
# ring related components (ex: distributor, ruler, etc).
[instance_interface_names: <list of string>]
# A common address used by Loki components to advertise their address.
# If a more specific "instance_addr" is set, this is ignored.
# If "instance_addr" under the common ring section is configured, this common "instance_addr"
# is only applied to the frontend, but not for ring related components (ex: distributor, ruler, etc).
[instance_addr: <string>]
# A common ring configuration to be used by all Loki rings.
# If a common ring is given, its values are used to define any undefined ring values.
# For instance, you can expect the `heartbeat_period` defined in the common section

@ -3,6 +3,8 @@ package common
import (
"flag"
"github.com/grafana/dskit/flagext"
"github.com/grafana/loki/pkg/storage/chunk/aws"
"github.com/grafana/loki/pkg/storage/chunk/azure"
"github.com/grafana/loki/pkg/storage/chunk/gcp"
@ -11,13 +13,28 @@ import (
"github.com/grafana/loki/pkg/util"
)
// Config holds common config that can be shared between multiple other config sections
// Config holds common config that can be shared between multiple other config sections.
//
// Values defined under this common configuration are supersede if a more specific value is defined.
type Config struct {
PathPrefix string `yaml:"path_prefix"`
Storage Storage `yaml:"storage"`
PersistTokens bool `yaml:"persist_tokens"`
ReplicationFactor int `yaml:"replication_factor"`
Ring util.RingConfig `yaml:"ring"`
// InstanceInterfaceNames represents a common list of net interfaces used to look for host addresses.
//
// Internally, addresses will be resolved in the order that this is configured.
// By default, the list of used interfaces are, in order: "eth0", "en0", and your loopback net interface (probably "lo").
InstanceInterfaceNames []string `yaml:"instance_interface_names"`
// InstanceAddr represents a common ip used by instances to advertise their address.
//
// For instance, the different Loki rings will have this stored in its key-value store to be later retrieved by other components.
// You can check this during Loki execution under ring status pages (ex: `/ring` will output the address of the different ingester
// instances).
InstanceAddr string `yaml:"instance_addr"`
}
func (c *Config) RegisterFlags(_ *flag.FlagSet) {
@ -25,6 +42,11 @@ func (c *Config) RegisterFlags(_ *flag.FlagSet) {
throwaway.IntVar(&c.ReplicationFactor, "common.replication-factor", 3, "How many ingesters incoming data should be replicated to.")
c.Storage.RegisterFlagsWithPrefix("common.storage", throwaway)
c.Ring.RegisterFlagsWithPrefix("", "collectors/", throwaway)
// instance related flags.
c.InstanceInterfaceNames = []string{"eth0", "en0"}
throwaway.StringVar(&c.InstanceAddr, "common.instance-addr", "", "Default advertised address to be used by Loki components.")
throwaway.Var((*flagext.StringSlice)(&c.InstanceInterfaceNames), "common.instance-interface-names", "List of network interfaces to read address from.")
}
type Storage struct {

@ -85,6 +85,8 @@ func (c *ConfigWrapper) ApplyDynamicConfig() cfg.Source {
applyPathPrefixDefaults(r, &defaults)
applyInstanceConfigs(r, &defaults)
applyDynamicRingConfigs(r, &defaults)
appendLoopbackInterface(r, &defaults)
@ -110,6 +112,35 @@ func (c *ConfigWrapper) ApplyDynamicConfig() cfg.Source {
}
}
// applyInstanceConfigs apply to Loki components instance-related configurations under the common
// config section.
//
// The list of components making usage of these instance-related configurations are: compactor's ring,
// ruler's ring, distributor's ring, ingester's ring, query scheduler's ring, and the query frontend.
//
// The list of implement common configurations are:
// - "instance-addr", the address advertised to be used by other components.
// - "instance-interface-names", a list of net interfaces used when looking for addresses.
func applyInstanceConfigs(r, defaults *ConfigWrapper) {
if !reflect.DeepEqual(r.Common.InstanceAddr, defaults.Common.InstanceAddr) {
r.Ingester.LifecyclerConfig.Addr = r.Common.InstanceAddr
r.CompactorConfig.CompactorRing.InstanceAddr = r.Common.InstanceAddr
r.Distributor.DistributorRing.InstanceAddr = r.Common.InstanceAddr
r.Ruler.Ring.InstanceAddr = r.Common.InstanceAddr
r.QueryScheduler.SchedulerRing.InstanceAddr = r.Common.InstanceAddr
r.Frontend.FrontendV2.Addr = r.Common.InstanceAddr
}
if !reflect.DeepEqual(r.Common.InstanceInterfaceNames, defaults.Common.InstanceInterfaceNames) {
r.Ingester.LifecyclerConfig.InfNames = r.Common.InstanceInterfaceNames
r.CompactorConfig.CompactorRing.InstanceInterfaceNames = r.Common.InstanceInterfaceNames
r.Distributor.DistributorRing.InstanceInterfaceNames = r.Common.InstanceInterfaceNames
r.Ruler.Ring.InstanceInterfaceNames = r.Common.InstanceInterfaceNames
r.QueryScheduler.SchedulerRing.InstanceInterfaceNames = r.Common.InstanceInterfaceNames
r.Frontend.FrontendV2.InfNames = r.Common.InstanceInterfaceNames
}
}
// applyDynamicRingConfigs checks the current config and, depending on the given values, reuse ring configs accordingly.
//
// 1. Gives preference to any explicit ring config set. For instance, if the user explicitly configures Distributor's ring,

@ -1344,3 +1344,140 @@ common:
assert.Equal(t, 1, config.Ingester.LifecyclerConfig.RingConfig.ReplicationFactor)
})
}
func Test_instanceAddr(t *testing.T) {
t.Run("common instance addr isn't applied when addresses are explicitly set", func(t *testing.T) {
yamlContent := `distributor:
ring:
instance_addr: mydistributor
ingester:
lifecycler:
address: myingester
ruler:
ring:
instance_addr: myruler
query_scheduler:
scheduler_ring:
instance_addr: myscheduler
frontend:
address: myqueryfrontend
compactor:
compactor_ring:
instance_addr: mycompactor
common:
instance_addr: 99.99.99.99
ring:
instance_addr: mycommonring`
config, _, err := configWrapperFromYAML(t, yamlContent, nil)
assert.NoError(t, err)
assert.Equal(t, "mydistributor", config.Distributor.DistributorRing.InstanceAddr)
assert.Equal(t, "myingester", config.Ingester.LifecyclerConfig.Addr)
assert.Equal(t, "myruler", config.Ruler.Ring.InstanceAddr)
assert.Equal(t, "myscheduler", config.QueryScheduler.SchedulerRing.InstanceAddr)
assert.Equal(t, "myqueryfrontend", config.Frontend.FrontendV2.Addr)
assert.Equal(t, "mycompactor", config.CompactorConfig.CompactorRing.InstanceAddr)
})
t.Run("common instance addr is applied when addresses are not explicitly set", func(t *testing.T) {
yamlContent := `common:
instance_addr: 99.99.99.99`
config, _, err := configWrapperFromYAML(t, yamlContent, nil)
assert.NoError(t, err)
assert.Equal(t, "99.99.99.99", config.Distributor.DistributorRing.InstanceAddr)
assert.Equal(t, "99.99.99.99", config.Ingester.LifecyclerConfig.Addr)
assert.Equal(t, "99.99.99.99", config.Ruler.Ring.InstanceAddr)
assert.Equal(t, "99.99.99.99", config.QueryScheduler.SchedulerRing.InstanceAddr)
assert.Equal(t, "99.99.99.99", config.Frontend.FrontendV2.Addr)
assert.Equal(t, "99.99.99.99", config.CompactorConfig.CompactorRing.InstanceAddr)
})
t.Run("common instance addr doesn't supersede instance addr from common ring", func(t *testing.T) {
yamlContent := `common:
instance_addr: 99.99.99.99
ring:
instance_addr: 22.22.22.22`
config, _, err := configWrapperFromYAML(t, yamlContent, nil)
assert.NoError(t, err)
assert.Equal(t, "22.22.22.22", config.Distributor.DistributorRing.InstanceAddr)
assert.Equal(t, "22.22.22.22", config.Ingester.LifecyclerConfig.Addr)
assert.Equal(t, "22.22.22.22", config.Ruler.Ring.InstanceAddr)
assert.Equal(t, "22.22.22.22", config.QueryScheduler.SchedulerRing.InstanceAddr)
assert.Equal(t, "99.99.99.99", config.Frontend.FrontendV2.Addr) // not a ring.
assert.Equal(t, "22.22.22.22", config.CompactorConfig.CompactorRing.InstanceAddr)
})
}
func Test_instanceInterfaceNames(t *testing.T) {
t.Run("common instance net interfaces aren't applied when explicitly set at other sections", func(t *testing.T) {
yamlContent := `distributor:
ring:
instance_interface_names:
- mydistributor
ingester:
lifecycler:
interface_names:
- myingester
ruler:
ring:
instance_interface_names:
- myruler
query_scheduler:
scheduler_ring:
instance_interface_names:
- myscheduler
frontend:
instance_interface_names:
- myfrontend
compactor:
compactor_ring:
instance_interface_names:
- mycompactor
common:
instance_interface_names:
- mycommoninf
ring:
instance_interface_names:
- mycommonring`
config, _, err := configWrapperFromYAML(t, yamlContent, nil)
assert.NoError(t, err)
assert.Equal(t, []string{"mydistributor"}, config.Distributor.DistributorRing.InstanceInterfaceNames)
assert.Equal(t, []string{"myingester"}, config.Ingester.LifecyclerConfig.InfNames)
assert.Equal(t, []string{"myruler"}, config.Ruler.Ring.InstanceInterfaceNames)
assert.Equal(t, []string{"myscheduler"}, config.QueryScheduler.SchedulerRing.InstanceInterfaceNames)
assert.Equal(t, []string{"myfrontend"}, config.Frontend.FrontendV2.InfNames)
assert.Equal(t, []string{"mycompactor"}, config.CompactorConfig.CompactorRing.InstanceInterfaceNames)
})
t.Run("common instance net interfaces is applied when others net interfaces are not explicitly set", func(t *testing.T) {
yamlContent := `common:
instance_interface_names:
- commoninterface`
config, _, err := configWrapperFromYAML(t, yamlContent, nil)
assert.NoError(t, err)
assert.Equal(t, []string{"commoninterface"}, config.Distributor.DistributorRing.InstanceInterfaceNames)
assert.Equal(t, []string{"commoninterface"}, config.Ingester.LifecyclerConfig.InfNames)
assert.Equal(t, []string{"commoninterface"}, config.Ruler.Ring.InstanceInterfaceNames)
assert.Equal(t, []string{"commoninterface"}, config.QueryScheduler.SchedulerRing.InstanceInterfaceNames)
assert.Equal(t, []string{"commoninterface"}, config.Frontend.FrontendV2.InfNames)
assert.Equal(t, []string{"commoninterface"}, config.CompactorConfig.CompactorRing.InstanceInterfaceNames)
})
t.Run("common instance net interface doesn't supersede net interface from common ring", func(t *testing.T) {
yamlContent := `common:
instance_interface_names:
- ringsshouldntusethis
ring:
instance_interface_names:
- ringsshouldusethis`
config, _, err := configWrapperFromYAML(t, yamlContent, nil)
assert.NoError(t, err)
assert.Equal(t, []string{"ringsshouldusethis"}, config.Distributor.DistributorRing.InstanceInterfaceNames)
assert.Equal(t, []string{"ringsshouldusethis"}, config.Ingester.LifecyclerConfig.InfNames)
assert.Equal(t, []string{"ringsshouldusethis"}, config.Ruler.Ring.InstanceInterfaceNames)
assert.Equal(t, []string{"ringsshouldusethis"}, config.QueryScheduler.SchedulerRing.InstanceInterfaceNames)
assert.Equal(t, []string{"ringsshouldntusethis"}, config.Frontend.FrontendV2.InfNames) // not a ring.
assert.Equal(t, []string{"ringsshouldusethis"}, config.CompactorConfig.CompactorRing.InstanceInterfaceNames)
})
}

@ -45,7 +45,7 @@ type Config struct {
func (cfg *Config) RegisterFlags(f *flag.FlagSet) {
f.StringVar(&cfg.SchedulerAddress, "frontend.scheduler-address", "", "DNS hostname used for finding query-schedulers.")
f.DurationVar(&cfg.DNSLookupPeriod, "frontend.scheduler-dns-lookup-period", 10*time.Second, "How often to resolve the scheduler-address, in order to look for new query-scheduler instances. Also used to determine how often to poll the scheduler-ring for addresses if the scheduler-ring is configured.")
f.DurationVar(&cfg.DNSLookupPeriod, "frontend.scheduler-dns-lookup-period", 10*time.Second, "How often to resolve the scheduler-address, in order to look for new query-scheduler instances. Also used to determine how often to poll the scheduler-ring for addresses if the scheduler-ring is configured.")
f.IntVar(&cfg.WorkerConcurrency, "frontend.scheduler-worker-concurrency", 5, "Number of concurrent workers forwarding queries to single query-scheduler.")
cfg.InfNames = []string{"eth0", "en0"}

Loading…
Cancel
Save