coredns/plugin/proxy/upstream.go
Miek Gieben 2a32cd4159 plugin/proxy: decrease health timeouts (#1107)
Turn down the timeouts and numbers a bit:
FailTimeout 10s -> 5s
Future 60s -> 12s
TryDuration 60s -> 16s
The timeout for decrementing the fails in a host: 10s -> 2s

And the biggest change: don't set fails when the error is Timeout(),
meaning we loop for a bit and may try the same server again, but we
don't mark our upstream as bad, see comments in proxy.go. Testing this
with "ANY isc.org" and "MX miek.nl" we see:

~~~
::1 - [24/Sep/2017:08:06:17 +0100] "ANY IN isc.org. udp 37 false 4096" SERVFAIL qr,rd 37 10.001621221s
24/Sep/2017:08:06:17 +0100 [ERROR 0 isc.org. ANY] unreachable backend: read udp 192.168.1.148:37420->8.8.8.8:53: i/o timeout

::1 - [24/Sep/2017:08:06:17 +0100] "MX IN miek.nl. udp 37 false 4096" NOERROR qr,rd,ra,ad 170 35.957284ms

127.0.0.1 - [24/Sep/2017:08:06:18 +0100] "ANY IN isc.org. udp 37 false 4096" SERVFAIL qr,rd 37 10.002051726s
24/Sep/2017:08:06:18 +0100 [ERROR 0 isc.org. ANY] unreachable backend: read udp 192.168.1.148:54901->8.8.8.8:53: i/o timeout

::1 - [24/Sep/2017:08:06:19 +0100] "MX IN miek.nl. udp 37 false 4096" NOERROR qr,rd,ra,ad 170 56.848416ms
127.0.0.1 - [24/Sep/2017:08:06:21 +0100] "MX IN miek.nl. udp 37 false 4096" NOERROR qr,rd,ra,ad 170 48.118349ms
::1 - [24/Sep/2017:08:06:21 +0100] "MX IN miek.nl. udp 37 false 4096" NOERROR qr,rd,ra,ad 170 1.055172915s
~~~

So the ANY isc.org queries show up twice, because we retry internally -
this is I think WAI.

The `miek.nl MX` queries are just processed normally as no backend is
marked as unreachable.

May fix #1035 #486
2017-09-24 20:05:36 +01:00

206 lines
4.5 KiB
Go

package proxy
import (
"fmt"
"net"
"strconv"
"time"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/pkg/dnsutil"
"github.com/coredns/coredns/plugin/pkg/healthcheck"
"github.com/coredns/coredns/plugin/pkg/tls"
"github.com/mholt/caddy/caddyfile"
"github.com/miekg/dns"
)
type staticUpstream struct {
from string
healthcheck.HealthCheck
IgnoredSubDomains []string
ex Exchanger
}
// NewStaticUpstreams parses the configuration input and sets up
// static upstreams for the proxy plugin.
func NewStaticUpstreams(c *caddyfile.Dispenser) ([]Upstream, error) {
var upstreams []Upstream
for c.Next() {
upstream := &staticUpstream{
from: ".",
HealthCheck: healthcheck.HealthCheck{
FailTimeout: 5 * time.Second,
MaxFails: 3,
Future: 12 * time.Second,
},
ex: newDNSEx(),
}
if !c.Args(&upstream.from) {
return upstreams, c.ArgErr()
}
upstream.from = plugin.Host(upstream.from).Normalize()
to := c.RemainingArgs()
if len(to) == 0 {
return upstreams, c.ArgErr()
}
// process the host list, substituting in any nameservers in files
toHosts, err := dnsutil.ParseHostPortOrFile(to...)
if err != nil {
return upstreams, err
}
for c.NextBlock() {
if err := parseBlock(c, upstream); err != nil {
return upstreams, err
}
}
upstream.Hosts = make([]*healthcheck.UpstreamHost, len(toHosts))
for i, host := range toHosts {
uh := &healthcheck.UpstreamHost{
Name: host,
Conns: 0,
Fails: 0,
FailTimeout: upstream.FailTimeout,
CheckDown: checkDownFunc(upstream),
}
upstream.Hosts[i] = uh
}
upstream.Start()
upstreams = append(upstreams, upstream)
}
return upstreams, nil
}
func (u *staticUpstream) From() string {
return u.from
}
func parseBlock(c *caddyfile.Dispenser, u *staticUpstream) error {
switch c.Val() {
case "policy":
if !c.NextArg() {
return c.ArgErr()
}
policyCreateFunc, ok := healthcheck.SupportedPolicies[c.Val()]
if !ok {
return c.ArgErr()
}
u.Policy = policyCreateFunc()
case "fail_timeout":
if !c.NextArg() {
return c.ArgErr()
}
dur, err := time.ParseDuration(c.Val())
if err != nil {
return err
}
u.FailTimeout = dur
case "max_fails":
if !c.NextArg() {
return c.ArgErr()
}
n, err := strconv.Atoi(c.Val())
if err != nil {
return err
}
u.MaxFails = int32(n)
case "health_check":
if !c.NextArg() {
return c.ArgErr()
}
var err error
u.HealthCheck.Path, u.HealthCheck.Port, err = net.SplitHostPort(c.Val())
if err != nil {
return err
}
u.HealthCheck.Interval = 4 * time.Second
if c.NextArg() {
dur, err := time.ParseDuration(c.Val())
if err != nil {
return err
}
u.HealthCheck.Interval = dur
u.Future = 2 * dur
// set a minimum of 3 seconds
if u.Future < (3 * time.Second) {
u.Future = 3 * time.Second
}
}
case "except":
ignoredDomains := c.RemainingArgs()
if len(ignoredDomains) == 0 {
return c.ArgErr()
}
for i := 0; i < len(ignoredDomains); i++ {
ignoredDomains[i] = plugin.Host(ignoredDomains[i]).Normalize()
}
u.IgnoredSubDomains = ignoredDomains
case "spray":
u.Spray = &healthcheck.Spray{}
case "protocol":
encArgs := c.RemainingArgs()
if len(encArgs) == 0 {
return c.ArgErr()
}
switch encArgs[0] {
case "dns":
if len(encArgs) > 1 {
if encArgs[1] == "force_tcp" {
opts := Options{ForceTCP: true}
u.ex = newDNSExWithOption(opts)
} else {
return fmt.Errorf("only force_tcp allowed as parameter to dns")
}
} else {
u.ex = newDNSEx()
}
case "https_google":
boot := []string{"8.8.8.8:53", "8.8.4.4:53"}
if len(encArgs) > 2 && encArgs[1] == "bootstrap" {
boot = encArgs[2:]
}
u.ex = newGoogle("", boot) // "" for default in google.go
case "grpc":
if len(encArgs) == 2 && encArgs[1] == "insecure" {
u.ex = newGrpcClient(nil, u)
return nil
}
tls, err := tls.NewTLSConfigFromArgs(encArgs[1:]...)
if err != nil {
return err
}
u.ex = newGrpcClient(tls, u)
default:
return fmt.Errorf("%s: %s", errInvalidProtocol, encArgs[0])
}
default:
return c.Errf("unknown property '%s'", c.Val())
}
return nil
}
func (u *staticUpstream) IsAllowedDomain(name string) bool {
if dns.Name(name) == dns.Name(u.From()) {
return true
}
for _, ignoredSubDomain := range u.IgnoredSubDomains {
if plugin.Name(ignoredSubDomain).Matches(name) {
return false
}
}
return true
}
func (u *staticUpstream) Exchanger() Exchanger { return u.ex }