Enable profiling of Crossplane and the RBAC manager

I'm on the fence as to whether to add this as a helper to
crossplane-runtime. At the moment it seems a little too basic/small.

Signed-off-by: Nic Cope <nicc@rk0n.org>
This commit is contained in:
Nic Cope 2023-04-27 10:18:42 -07:00
parent 22766a2c7e
commit f90a06ae06
2 changed files with 67 additions and 1 deletions

View File

@ -18,6 +18,8 @@ limitations under the License.
package core
import (
"net/http"
"net/http/pprof"
"path/filepath"
"time"
@ -46,6 +48,8 @@ import (
"github.com/crossplane/crossplane/internal/xpkg"
)
const pprofPath = "/debug/pprof/"
// Command runs the core crossplane controllers
type Command struct {
Start startCommand `cmd:"" help:"Start Crossplane controllers."`
@ -67,6 +71,8 @@ func (c *Command) Run() error {
}
type startCommand struct {
Profile string `placeholder:"host:port" help:"Serve runtime profiling data via HTTP at /debug/pprof."`
Namespace string `short:"n" help:"Namespace used to unpack and run packages." default:"crossplane-system" env:"POD_NAMESPACE"`
ServiceAccount string `help:"Name of the Crossplane Service Account." default:"crossplane" env:"POD_SERVICE_ACCOUNT"`
CacheDir string `short:"c" help:"Directory used for caching package images." default:"/cache" env:"CACHE_DIR"`
@ -96,7 +102,34 @@ type startCommand struct {
}
// Run core Crossplane controllers.
func (c *startCommand) Run(s *runtime.Scheme, log logging.Logger) error { //nolint:gocyclo // Only slightly over (11).
func (c *startCommand) Run(s *runtime.Scheme, log logging.Logger) error { //nolint:gocyclo // Only slightly over.
if c.Profile != "" {
// NOTE(negz): These log messages attempt to match those emitted by
// controller-runtime's metrics HTTP server when it starts.
log.Debug("Profiling server is starting to listen", "addr", c.Profile)
go func() {
// Registering these explicitly ensures they're only served by the
// HTTP server we start explicitly for profiling.
mux := http.NewServeMux()
mux.HandleFunc(pprofPath, pprof.Index)
mux.HandleFunc(filepath.Join(pprofPath, "cmdline"), pprof.Cmdline)
mux.HandleFunc(filepath.Join(pprofPath, "profile"), pprof.Profile)
mux.HandleFunc(filepath.Join(pprofPath, "symbol"), pprof.Symbol)
mux.HandleFunc(filepath.Join(pprofPath, "trace"), pprof.Trace)
s := &http.Server{
Addr: c.Profile,
ReadTimeout: 2 * time.Minute,
WriteTimeout: 2 * time.Minute,
Handler: mux,
}
log.Debug("Starting server", "type", "pprof", "path", pprofPath, "addr", s.Addr)
err := s.ListenAndServe()
log.Debug("Profiling server has stopped listening", "error", err)
}()
}
cfg, err := ctrl.GetConfig()
if err != nil {
return errors.Wrap(err, "Cannot get config")

View File

@ -18,6 +18,9 @@ limitations under the License.
package rbac
import (
"net/http"
"net/http/pprof"
"path/filepath"
"strings"
"time"
@ -42,6 +45,8 @@ const (
ManagementPolicyBasic = string(rbaccontroller.ManagementPolicyBasic)
)
const pprofPath = "/debug/pprof/"
// KongVars represent the kong variables associated with the CLI parser
// required for the RBAC enum interpolation.
var KongVars = kong.Vars{
@ -69,6 +74,8 @@ func (c *Command) Run() error {
}
type startCommand struct {
Profile string `placeholder:"host:port" help:"Serve runtime profiling data via HTTP at /debug/pprof."`
ProviderClusterRole string `name:"provider-clusterrole" help:"A ClusterRole enumerating the permissions provider packages may request."`
LeaderElection bool `name:"leader-election" short:"l" help:"Use leader election for the conroller manager." env:"LEADER_ELECTION"`
ManagementPolicy string `name:"manage" short:"m" help:"RBAC management policy." default:"${rbac_manage_default_var}" enum:"${rbac_manage_enum_var}"`
@ -82,6 +89,32 @@ type startCommand struct {
// Run the RBAC manager.
func (c *startCommand) Run(s *runtime.Scheme, log logging.Logger) error {
log.Debug("Starting", "policy", c.ManagementPolicy)
if c.Profile != "" {
// NOTE(negz): These log messages attempt to match those emitted by
// controller-runtime's metrics HTTP server when it starts.
log.Debug("Profiling server is starting to listen", "addr", c.Profile)
go func() {
// Registering these explicitly ensures they're only served by the
// HTTP server we start specifically for profiling.
mux := http.NewServeMux()
mux.HandleFunc(pprofPath, pprof.Index)
mux.HandleFunc(filepath.Join(pprofPath, "cmdline"), pprof.Cmdline)
mux.HandleFunc(filepath.Join(pprofPath, "profile"), pprof.Profile)
mux.HandleFunc(filepath.Join(pprofPath, "symbol"), pprof.Symbol)
mux.HandleFunc(filepath.Join(pprofPath, "trace"), pprof.Trace)
s := &http.Server{
Addr: c.Profile,
ReadTimeout: 2 * time.Minute,
WriteTimeout: 2 * time.Minute,
Handler: mux,
}
log.Debug("Starting server", "type", "pprof", "path", pprofPath, "addr", s.Addr)
err := s.ListenAndServe()
log.Debug("Profiling server has stopped listening", "error", err)
}()
}
cfg, err := ctrl.GetConfig()
if err != nil {