mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
fn: add docker-containerd to prometheus processes (#863)
*) switch to /proc/<pid>/cmdline due to /proc/<pid>/status process name truncation. *) Optional override with FN_PROCESS_COLLECTOR_LIST
This commit is contained in:
@@ -17,6 +17,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/fnproject/fn/api/agent"
|
"github.com/fnproject/fn/api/agent"
|
||||||
"github.com/fnproject/fn/api/agent/hybrid"
|
"github.com/fnproject/fn/api/agent/hybrid"
|
||||||
@@ -63,6 +64,8 @@ const (
|
|||||||
EnvCertKey = "FN_NODE_CERT_KEY"
|
EnvCertKey = "FN_NODE_CERT_KEY"
|
||||||
EnvCertAuth = "FN_NODE_CERT_AUTHORITY"
|
EnvCertAuth = "FN_NODE_CERT_AUTHORITY"
|
||||||
|
|
||||||
|
EnvProcessCollectorList = "FN_PROCESS_COLLECTOR_LIST"
|
||||||
|
|
||||||
// Defaults
|
// Defaults
|
||||||
DefaultLogLevel = "info"
|
DefaultLogLevel = "info"
|
||||||
DefaultLogDest = "stderr"
|
DefaultLogDest = "stderr"
|
||||||
@@ -530,10 +533,17 @@ func WithPrometheus() ServerOption {
|
|||||||
return func(ctx context.Context, s *Server) error {
|
return func(ctx context.Context, s *Server) error {
|
||||||
reg := promclient.NewRegistry()
|
reg := promclient.NewRegistry()
|
||||||
reg.MustRegister(promclient.NewProcessCollector(os.Getpid(), "fn"),
|
reg.MustRegister(promclient.NewProcessCollector(os.Getpid(), "fn"),
|
||||||
promclient.NewProcessCollectorPIDFn(dockerPid(), "dockerd"),
|
|
||||||
promclient.NewGoCollector(),
|
promclient.NewGoCollector(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for _, exeName := range getMonitoredCmdNames() {
|
||||||
|
san := promSanitizeMetricName(exeName)
|
||||||
|
err := reg.Register(promclient.NewProcessCollectorPIDFn(getPidCmd(exeName), san))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
exporter, err := prometheus.NewExporter(prometheus.Options{
|
exporter, err := prometheus.NewExporter(prometheus.Options{
|
||||||
Namespace: "fn",
|
Namespace: "fn",
|
||||||
Registry: reg,
|
Registry: reg,
|
||||||
@@ -590,8 +600,35 @@ func WithZipkin(zipkinURL string) ServerOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prometheus only allows [a-zA-Z0-9:_] in metrics names.
|
||||||
|
func promSanitizeMetricName(name string) string {
|
||||||
|
res := make([]rune, 0, len(name))
|
||||||
|
for _, rVal := range name {
|
||||||
|
if unicode.IsDigit(rVal) || unicode.IsLetter(rVal) || rVal == ':' {
|
||||||
|
res = append(res, rVal)
|
||||||
|
} else {
|
||||||
|
res = append(res, '_')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine sidecar-monitored cmd names. But by default
|
||||||
|
// we track dockerd + containerd
|
||||||
|
func getMonitoredCmdNames() []string {
|
||||||
|
|
||||||
|
// override? empty variable to disable trackers
|
||||||
|
val, ok := os.LookupEnv(EnvProcessCollectorList)
|
||||||
|
if ok {
|
||||||
|
return strings.Fields(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// by default, we monitor dockerd and containerd
|
||||||
|
return []string{"dockerd", "docker-containerd"}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO plumbing considerations, we've put the S pipe next to the chandalier...
|
// TODO plumbing considerations, we've put the S pipe next to the chandalier...
|
||||||
func dockerPid() func() (int, error) {
|
func getPidCmd(cmdName string) func() (int, error) {
|
||||||
// prometheus' process collector only works on linux anyway. let them do the
|
// prometheus' process collector only works on linux anyway. let them do the
|
||||||
// process detection, if we return an error here we just get 0 metrics and it
|
// process detection, if we return an error here we just get 0 metrics and it
|
||||||
// does not log / blow up (that's fine!) it's also likely we hit permissions
|
// does not log / blow up (that's fine!) it's also likely we hit permissions
|
||||||
@@ -602,50 +639,62 @@ func dockerPid() func() (int, error) {
|
|||||||
|
|
||||||
return func() (int, error) {
|
return func() (int, error) {
|
||||||
if pid != 0 {
|
if pid != 0 {
|
||||||
// make sure it's docker pid.
|
// make sure it's our pid.
|
||||||
if isDockerPid("/proc/" + strconv.Itoa(pid) + "/status") {
|
if isPidMatchCmd(cmdName, pid) {
|
||||||
return pid, nil
|
return pid, nil
|
||||||
}
|
}
|
||||||
pid = 0 // reset to go search
|
pid = 0 // reset to go search
|
||||||
}
|
}
|
||||||
|
|
||||||
err := filepath.Walk("/proc", func(path string, info os.FileInfo, err error) error {
|
if pids, err := getPidList(); err == nil {
|
||||||
if err != nil || pid != 0 {
|
for _, test := range pids {
|
||||||
// we get permission errors digging around in here, ignore them and press on
|
if isPidMatchCmd(cmdName, test) {
|
||||||
return nil
|
pid = test
|
||||||
|
return pid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// /proc/<pid>/status
|
|
||||||
if strings.Count(path, "/") == 3 && strings.Contains(path, "/status") {
|
|
||||||
if isDockerPid(path) {
|
|
||||||
// extract pid from path
|
|
||||||
pid, _ = strconv.Atoi(path[6:strings.LastIndex(path, "/")])
|
|
||||||
return io.EOF // end the search
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// keep searching
|
return pid, io.EOF
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err == io.EOF { // used as sentinel
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return pid, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDockerPid(path string) bool {
|
func isPidMatchCmd(cmdName string, pid int) bool {
|
||||||
// first line of status file is: "Name: <name>"
|
fs, err := os.Open("/proc/" + strconv.Itoa(pid) + "/cmdline")
|
||||||
f, err := os.Open(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer fs.Close()
|
||||||
|
|
||||||
// scan first line only
|
rd := bufio.NewReader(fs)
|
||||||
scanner := bufio.NewScanner(f)
|
tok, err := rd.ReadSlice(0)
|
||||||
scanner.Scan()
|
if err != nil || len(tok) < len(cmdName) {
|
||||||
return strings.HasSuffix(scanner.Text(), "dockerd")
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Base(string(tok[:len(tok)-1])) == cmdName
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPidList() ([]int, error) {
|
||||||
|
var pids []int
|
||||||
|
dir, err := os.Open("/proc")
|
||||||
|
if err != nil {
|
||||||
|
return pids, nil
|
||||||
|
}
|
||||||
|
defer dir.Close()
|
||||||
|
|
||||||
|
files, err := dir.Readdirnames(0)
|
||||||
|
if err != nil {
|
||||||
|
return pids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pids = make([]int, 0, len(files))
|
||||||
|
for _, tok := range files {
|
||||||
|
if conv, err := strconv.ParseUint(tok, 10, 64); err == nil {
|
||||||
|
pids = append(pids, int(conv))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setMachineID() {
|
func setMachineID() {
|
||||||
|
|||||||
Reference in New Issue
Block a user