observer: add TCP prober (#7118)

This is potentially useful for diagnosing issues with connection
timeouts, which could have separate causes from HTTP errors. For
instance, a connection timeout is more likely to be caused by network
congestion or high CPU usage on the load balancer.
This commit is contained in:
Jacob Hoffman-Andrews 2023-10-27 09:11:18 -07:00 committed by GitHub
parent bba8fd31a6
commit c84201c09a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 90 additions and 3 deletions

View File

@ -14,7 +14,7 @@ import (
// MonConf is exported to receive YAML configuration in `ObsConf`.
type MonConf struct {
Period config.Duration `yaml:"period"`
Kind string `yaml:"kind" validate:"required,oneof=DNS HTTP CRL TLS"`
Kind string `yaml:"kind" validate:"required,oneof=DNS HTTP CRL TLS TCP"`
Settings probers.Settings `yaml:"settings" validate:"min=1,dive"`
}

View File

@ -8,6 +8,7 @@ import (
_ "github.com/letsencrypt/boulder/observer/probers/crl"
_ "github.com/letsencrypt/boulder/observer/probers/dns"
_ "github.com/letsencrypt/boulder/observer/probers/http"
_ "github.com/letsencrypt/boulder/observer/probers/tcp"
_ "github.com/letsencrypt/boulder/observer/probers/tls"
)

View File

@ -0,0 +1,36 @@
package tcp
import (
"net"
"time"
)
type TCPProbe struct {
hostport string
}
// Name returns a string that uniquely identifies the monitor.
func (p TCPProbe) Name() string {
return p.hostport
}
// Kind returns a name that uniquely identifies the `Kind` of `Prober`.
func (p TCPProbe) Kind() string {
return "TCP"
}
// Probe performs the configured TCP dial.
func (p TCPProbe) Probe(timeout time.Duration) (bool, time.Duration) {
start := time.Now()
dialer := &net.Dialer{
Timeout: timeout,
FallbackDelay: -1,
}
c, err := dialer.Dial("tcp", p.hostport)
if err != nil {
return false, time.Since(start)
}
c.Close()
return true, time.Since(start)
}

View File

@ -0,0 +1,45 @@
package tcp
import (
"github.com/letsencrypt/boulder/observer/probers"
"github.com/letsencrypt/boulder/strictyaml"
"github.com/prometheus/client_golang/prometheus"
)
// TCPConf is exported to receive YAML configuration.
type TCPConf struct {
Hostport string `yaml:"hostport"`
}
// Kind returns a name that uniquely identifies the `Kind` of `Configurer`.
func (c TCPConf) Kind() string {
return "TCP"
}
// UnmarshalSettings takes YAML as bytes and unmarshals it to the to an
// TCPConf object.
func (c TCPConf) UnmarshalSettings(settings []byte) (probers.Configurer, error) {
var conf TCPConf
err := strictyaml.Unmarshal(settings, &conf)
if err != nil {
return nil, err
}
return conf, nil
}
// MakeProber constructs a `TCPPProbe` object from the contents of the
// bound `TCPPConf` object.
func (c TCPConf) MakeProber(_ map[string]prometheus.Collector) (probers.Prober, error) {
return TCPProbe{c.Hostport}, nil
}
// Instrument is a no-op to implement the `Configurer` interface.
func (c TCPConf) Instrument() map[string]prometheus.Collector {
return nil
}
// init is called at runtime and registers `TCPConf`, a `Prober`
// `Configurer` type, as "TCP".
func init() {
probers.Register(TCPConf{})
}

View File

@ -83,9 +83,14 @@ monitors:
recurse: true
query_name: google.com
query_type: A
-
-
period: 2s
kind: HTTP
settings:
settings:
url: http://letsencrypt.org/foo
rcodes: [200, 404]
-
period: 10s
kind: TCP
settings:
hostport: acme-v02.api.letsencrypt.org:443