diff --git a/cli/cli.go b/cli/cli.go index 7ca6504e8..3322f8ef2 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -58,6 +58,7 @@ var ( mapAllPorts bool noMev bool noValidator bool + noProm bool ) const ( @@ -250,6 +251,7 @@ func runCliCmd(cmd *cobra.Command, args []string) []error { VlExtraFlags: *vlExtraFlags, MapAllPorts: mapAllPorts, Mev: !noMev && !noValidator, + Prom: !noProm, } elPort, clPort, err := generate.GenerateScripts(gd) if err != nil { @@ -334,6 +336,8 @@ func init() { cliCmd.Flags().BoolVar(&noMev, "no-mev-boost", false, "Not use mev-boost if supported") + cliCmd.Flags().BoolVar(&noProm, "no-prometheus", false, "Not use prometheus monitoring") + cliCmd.Flags().BoolVar(&noValidator, "no-validator", false, "Exclude the validator from the full node setup. Designed for execution and consensus nodes setup without a validator node. Exclude also the validator from other flags. If set, mev-boost will not be used.") cliCmd.Flags().StringVar(&jwtPath, "jwt-secret-path", "", "Path to the JWT secret file") diff --git a/configs/messages.go b/configs/messages.go index 0ff70fdc8..75d928ebe 100644 --- a/configs/messages.go +++ b/configs/messages.go @@ -22,6 +22,7 @@ const ( DependenciesOK = "All dependencies are installed on host machine" GeneratingDockerComposeScript = "Generating docker-compose script for current selection of clients" GeneratingEnvFile = "Generating environment file for current selection of clients" + GeneratingPrometheusFile = "Generating prometheus.yml file for the enabled prometheus service" Exiting = "Exiting..." InstructionsFor = "Instructions for %s" OSNotSupported = "installation not supported for %s" @@ -103,6 +104,8 @@ Follow https://launchpad.ethereum.org/ and happy staking!` DefaultAdditionalApiPortCL = "4001" DefaultMetricsPortVL = "5056" DefaultMevPort = "18550" + DefaultPrometheusPort = "9090" + DefaultGrafanaPort = "3000" MapAllPortsWarning = "You are mapping all ports for the clients!!! Make sure this is intended. This could make the clients vulnerable to attacks. Be sure to setup a firewall." CheckpointUrlUsedWarning = "A Checkpoint Sync Url will be used for the consensus node. Using %s ." NoBootnodesFound = "No bootnodes found for %s/%s/%s" diff --git a/internal/pkg/generate/generate_scripts.go b/internal/pkg/generate/generate_scripts.go index 226403f1c..6bfe3aacf 100644 --- a/internal/pkg/generate/generate_scripts.go +++ b/internal/pkg/generate/generate_scripts.go @@ -68,6 +68,8 @@ func GenerateScripts(gd GenerationData) (elPort, clPort string, err error) { "CLAdditionalApi": configs.DefaultAdditionalApiPortCL, "VLMetrics": configs.DefaultMetricsPortVL, "MevPort": configs.DefaultMevPort, + "PromPort": configs.DefaultPrometheusPort, + "GrafanaPort": configs.DefaultGrafanaPort, } ports, err := utils.AssingPorts("localhost", defaultsPorts) if err != nil { @@ -90,6 +92,12 @@ func GenerateScripts(gd GenerationData) (elPort, clPort string, err error) { return "", "", err } + log.Info(configs.GeneratingPrometheusFile) + err = generatePromFile(gd) + if err != nil { + return "", "", err + } + return ports["ELApi"], ports["CLApi"], nil } @@ -218,6 +226,8 @@ func generateDockerComposeScripts(gd GenerationData) (err error) { XeeVersion: xeeVersion, Mev: mev && gd.Mev, MevPort: gd.Ports["MevPort"], + PromPort: gd.Ports["PromPort"], + GrafanaPort: gd.Ports["GrafanaPort"], CheckpointSyncUrl: gd.CheckpointSyncUrl, FeeRecipient: gd.FeeRecipient, ElDiscoveryPort: gd.Ports["ELDiscovery"], @@ -234,6 +244,7 @@ func generateDockerComposeScripts(gd GenerationData) (err error) { ElExtraFlags: gd.ElExtraFlags, ClExtraFlags: gd.ClExtraFlags, VlExtraFlags: gd.VlExtraFlags, + Prom: gd.Prom, Bootnodes: bootnodes, MapAllPorts: gd.MapAllPorts, SplittedNetwork: splittedNetwork, @@ -352,6 +363,61 @@ func generateEnvFile(gd GenerationData) (err error) { return nil } +/* +generatePromFile : +This function is responsible for generating the prometheus.yml file needed for +the prometheus service to run + +params :- +a. executionClient string +Execution client whose script was generated +b. ClMetricsPort string +Consensus client metrics port +c. VlMetricsPort string +Validator client metrics port +d. ElMetricsPort string +Execution client metrics port + +returns :- +a. error +Error if any +*/ +func generatePromFile(gd GenerationData) (err error) { + rawBaseTmp, err := templates.Prometheus.ReadFile(filepath.Join("prometheus", "prometheus.tmpl")) + if err != nil { + return + } + + baseTmp, err := template.New("prometheus").Parse(string(rawBaseTmp)) + if err != nil { + return + } + + // TODO: Use OS wise delimiter for these data structs + data := PrometheusData{ + ExecutionClient: gd.ExecutionClient.Name, + ClMetricsPort: gd.Ports["CLMetrics"], + VlMetricsPort: gd.Ports["VLMetrics"], + ElMetricsPort: gd.Ports["ELMetrics"], + } + + // Print prometheus configuration file + log.Infof(configs.PrintingFile, "prometheus.yml") + err = baseTmp.Execute(os.Stdout, data) + if err != nil { + return fmt.Errorf(configs.PrintingFileError, ".yml", err) + } + fmt.Println() + + err = writeTemplateToFile(baseTmp, filepath.Join(gd.GenerationPath, "prometheus.yml"), data, false) + if err != nil { + return fmt.Errorf(configs.GeneratingScriptsError, gd.ExecutionClient.Name, gd.ConsensusClient.Name, gd.ValidatorClient.Name, err) + } + log.Infof(configs.CreatedFile, filepath.Join(gd.GenerationPath, "prometheus.yml")) + + return nil +} + /* writeTemplateToFile : Write template to `file`. diff --git a/internal/pkg/generate/types.go b/internal/pkg/generate/types.go index ed64b5eaa..86b6917b0 100644 --- a/internal/pkg/generate/types.go +++ b/internal/pkg/generate/types.go @@ -52,6 +52,7 @@ type GenerationData struct { MapAllPorts bool Mev bool Ports map[string]string + Prom bool } // DockerComposeData : Struct Data object to be applied to docker-compose script @@ -67,7 +68,10 @@ type DockerComposeData struct { VlRemoteDpl bool XeeVersion bool Mev bool + Prom bool MevPort string + PromPort string + GrafanaPort string CheckpointSyncUrl string FeeRecipient string ElDiscoveryPort string @@ -89,3 +93,11 @@ type DockerComposeData struct { SplittedNetwork bool ClCheckpointSyncUrl bool } + +// PrometheusData : Struct Data object to be applied to prometheus.yaml (configuration file of prometheus service) +type PrometheusData struct { + ExecutionClient string + ElMetricsPort string + ClMetricsPort string + VlMetricsPort string +} diff --git a/templates/prometheus/prometheus.tmpl b/templates/prometheus/prometheus.tmpl new file mode 100644 index 000000000..4a8672497 --- /dev/null +++ b/templates/prometheus/prometheus.tmpl @@ -0,0 +1,15 @@ +global: + scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. + evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. + +scrape_configs: + - job_name: 'validator' + static_configs: + - targets: ['localhost:{{.VlMetricsPort}}'] + - job_name: 'execution' + static_configs: + - targets: ['localhost:{{.ElMetricsPort}}'] + {{if eq .ExecutionClient "geth"}}metrics_path: /debug/metrics/prometheus{{end}} + - job_name: 'consensus' + static_configs: + - targets: ['localhost:{{.ClMetricsPort}}'] \ No newline at end of file diff --git a/templates/services/docker-compose_base.tmpl b/templates/services/docker-compose_base.tmpl index 5321d9547..7bb998927 100644 --- a/templates/services/docker-compose_base.tmpl +++ b/templates/services/docker-compose_base.tmpl @@ -28,6 +28,20 @@ services: sh -c "{{if or .CcRemoteCfg .VlRemoteCfg }}wget -O /tmp/app/config.yml ${CONFIG_URL};{{end}}{{if or .CcRemoteGen .VlRemoteGen }}wget -O /tmp/app/genesis.ssz ${GENESIS_URL};{{end}}{{if or .CcRemoteDpl .VlRemoteDpl }}echo -n ${DEPLOY_BLOCK} > /tmp/app/deploy_block.txt;{{end}}"{{end}} {{template "consensus" .}} {{template "validator" .}} +{{if .Prom}} + prometheus: + image: ubuntu/prometheus + ports: + - "{{.PromPort}}:9090" + container_name: prometheus-container + volumes: + - './prometheus.yml:/etc/prometheus/prometheus.yml' + environment: + - TZ=UTC + grafana: + image: grafana/grafana + ports: + - "{{.GrafanaPort}}:3000"{{ end }} networks: sedge: diff --git a/templates/services/merge/execution/geth.tmpl b/templates/services/merge/execution/geth.tmpl index 1929bc7e4..220854b4d 100644 --- a/templates/services/merge/execution/geth.tmpl +++ b/templates/services/merge/execution/geth.tmpl @@ -24,7 +24,8 @@ command:{{with .TTD}} - --override.terminaltotaldifficulty=${TTD}{{end}} - --bootnodes=${EC_BOOTNODES} - - --syncmode=${EC_SYNC_MODE} + - --syncmode=${EC_SYNC_MODE}{{if .Prom}} + - --metrics.addr=0.0.0.0{{end}} - --http - --http.addr=0.0.0.0 - --http.vhosts=* diff --git a/templates/services/merge/execution/nethermind.tmpl b/templates/services/merge/execution/nethermind.tmpl index e1382df39..c6a0b17f6 100644 --- a/templates/services/merge/execution/nethermind.tmpl +++ b/templates/services/merge/execution/nethermind.tmpl @@ -12,13 +12,16 @@ - ${EC_JWT_SECRET_PATH}:/tmp/jwt/jwtsecret ports: - "{{.ElDiscoveryPort}}:{{.ElDiscoveryPort}}/tcp" - - "{{.ElDiscoveryPort}}:{{.ElDiscoveryPort}}/udp"{{if .MapAllPorts}} + - "{{.ElDiscoveryPort}}:{{.ElDiscoveryPort}}/udp" + - "{{.ElMetricsPort}}:{{.ElMetricsPort}}/tcp"{{if .MapAllPorts}} - "{{.ElApiPort}}:{{.ElApiPort}}" - "{{.ElAuthPort}}:{{.ElAuthPort}}"{{end}} expose: - {{.ElApiPort}} - {{.ElAuthPort}} - command: + command:{{if .Prom}} + - --Metrics.ExposePort={{.ElMetricsPort}} + - --Metrics.Enabled=true{{end}} - --config={{if .SplittedNetwork}}${EL_NETWORK}{{else}}${NETWORK}{{end}} - --datadir=/nethermind/data - --log=${NETHERMIND_LOG_LEVEL}{{with .TTD}} diff --git a/templates/templates.go b/templates/templates.go index b08085659..d39e21d1f 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -34,3 +34,6 @@ var DepositCLI embed.FS //go:embed scripts var Scripts embed.FS + +//go:embed prometheus +var Prometheus embed.FS