Can I have a smaller Prometheus
wejick.wordpress.com
wejick.wordpress.com
...for a grand total binary size of 5 MB.
So many modern systems are huge because of the complex dependency tree they pull in. My entire binary likely fits within the L3 cache of the CPU you are using.
If anything it'll use more memory from the remains of the decompression stub and not being able to be clever about only reading from the backing file needed memory pages.
The same Prometheus binary you can run on a Pi scales to millions of series and millions of samples per second.
The Prometheus TSDB itself is only one part of a the larger system. But, compared to librrd, it's vastly more functional.
Besides the scaling I mentioned
* It's ACID compliant.
* It has WAL for reliability.
* It has CPU and memory efficient compression.
* It has an efficient mmap-based data loader.
RRDtool, while efficient from a '90s perspective, is a toy by comparison. And yes, I've used rrdtool. Back in old-school days when Cacti was the new hot shit compared to MRTG.
You don't have to use a whole Prometheus server to make something like this work.
If disk space is an issue, do you or have you considered decompressing on the fly ?
See also: log4j
What am I missing?
If I knew, it would have a CVE number.
However, the less code you have, the fewer the places there are where you need to ask that question.
Well... if size of the executable is really a concern, perhaps Victoria Metrics is worth considering; my amd64 executable is about 17MiB in size.
VictoriaMetrics sweetens the deal by offering a solution to long-term storage and more flexible service architecture without leaving the simple and highly interopable Prometheus ecosystem.
Taught to me on HN recently: https://news.ycombinator.com/item?id=30041763
Seems like eventually this should become a priority for library writers to support dead code analysis, otherwise going to get worse and worse..
Those organizations also tend to spew a lot of code, most of it quite tangled together. They're definitely the largest dependencies I regularly see.
We went from using Prometheus itself to scrape metrics from services and export them to influxdb (fine, but very heavy), to using kapacitor (an utter nightmare for this particular use case), to just writing our own Prometheus metrics harvester from scratch (took about 3 days with service discovery). The current solution has been in place for more than a year and it’s perfect.
I love influxdb’s on-disk compression, but its query planner and unpredictable ram usage leave a lot to be desired (at least the 1.x versions).
There are THREE different ways to query data: InfluxQL, Flux and TICKScripts…all inferior to PromQL imo. The worse is Influx encourages you to mix them (e.g. PromQL in TICK), causing even more confusion. Documentation on advanced use cases is non existent.
Been an absolute nightmare tbh. The new company had a similar evolution too: holding non-metrics data in influx while also holding metrics. Trying to at least move metrics onto a Prom/Thanos stack here soon.
Regarding docs on advanced use cases, if you haven't already, try posting questions in the community forum: https://community.influxdata.com. Or, if you prefer Slack, there's a link at the top of that page to join our community Slack. We do our best to help with specific issues in those spaces and we also look for common themes that are causing problems for multiple users so that we can focus our efforts there, whether that's bug fixes, performance, features, or docs.
To be fair, we still haven't paid you guys a dime so I can't complain. But if you're interested in hearing my thoughts I can email you after next week's holiday.
Minor nit on "TSM (not TSI)" - TSM (.tsm files) are the data files and that format hasn't changed. TSI is the newer, although mature at this point, indexing option that spills onto disk and can therefore be larger than the original in-mem index. You're probably using in-mem indexing.
We're always interested in feedback. My name is david. You can email me at <name> at influxdata dot com.
One of the things that kills me is running fluentd because you have to fuck around with ruby gems in containers every two minutes to get it to do something reasonable.
This is pain. Prom is not.
As for next steps, I can't imagine the Prometheus crew would object to a proposal + PR to make the Service Discovery an optional add-on in the next major version. It does open a can of worms around how such an add-on would be distributed if not built into the binary. (Caveat: I have no familiarity with this particular project or its unique constraints or goals.)
The main problem is, Go doesn't have any kind of reasonable loadable library system.
The current proposal is to make it easier to do compile-time plugins, similar to how Caddy and CoreDNS do things.
We don't even need a major version to add such a thing. Just the time to write the feature. The thing is, it's just not that important an improvement. When you are running Prometheus in a production environment, it will end up using gigabytes of memory and disk space to operate. The savings of a few megabytes of binary and runtime memory are just not that important.
podman run postgres:14 du -sh /usr/lib/postgresql
24M /usr/lib/postgresql
This includes the binary.Our smallest is 32 cores and 1TB of RAM.
Could you elaborate? I use Prometheus to scrape from an HTTP endpoint in various Pods in Kubernetes, so the service discovery is pretty useful to me.
I could see the Kubernetes & the other SDs split out of the core binary if default size is really an issue. Or are you talking about something else?
Prometheus scrapes the same text format as OpenMetrics 1.0 and over 700 public exporters use this format, and there are TONS of other non-Prometheus software that consume the exact same text format. Prometheus's biggest competitor, Datadog (which is not open source mind you), consumes it too. I think even Grafana consumes it directly. It's becoming an IETF standard[0].
Would I have preferred JSON over a custom text format like this? Yeah. But to claim an open source project like Prometheus with effectively no business at all is using a text format like this to have vendor lock-in? That's quite a stretch.
[0] https://github.com/OpenObservability/OpenMetrics/blob/main/s...
I find the GP's claims weird - I've written a relative ton of collectors, exporters, and translators and the format is pretty OK, not worse than most that came before it and better than lots - but I think this relationship is backwards. Prometheus "scrapes OpenMetrics" because OpenMetrics was formal documentation of what Prometheus was already doing for years.
I would not have preferred JSON. That an exposed metric is also a query is also pretty close to a schematic definition is nice.
http.HandleFunc("/metrics", func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Add("content-type", "text/plain")
w.Write([]byte("# HELP foo_bar The numbers of foos barred.\n# TYPE foo_bar counter\nfoo_bar 42\n"))
})
The client library is largely to keep track of running counters (and gauges, histograms, etc.), with a small amount of code to actually report those metrics when scraped. It's a very simple format.The content type MUST be: application/openmetrics-text; version=1.0.0; charset=utf-8
https://github.com/OpenObservability/OpenMetrics/blob/main/s...
However Prometheus was designed before JSON was standardized itself, so I'm just glad they didn't choose XML!
IIRC (it's been almost a decade since I used varz), having multiple label values would be a map of maps in varz. It got quite ugly if you wanted to have a number of dimensions.
The other commenters have pointed out that it _is_ based on another open standard, but admittedly one less common than say, JSON. So you'll generally have to implement your own metrics producer or use a client library, that's true.
However it's also a dead simple format and you can probably implement it with a for-loop or a shell script.
JSON, especially free-form JSON, is not a good format for efficient metrics monitoring.
OpenMetrics's production rule for the format says:
labels = "{" [label *(COMMA label)] "}"
And yet, the Prometheus Java client library exports a trailing comma with no subsequent label.As for fp, I've seen parsers break on `e` v. `E`, and `NaN`/`Inf`/ vs. `nan`/`inf`. The latest IETF draft even has a comment,
; Not 100% sure this captures all float corner cases.
Control characters are allowed in descriptors and label values and no normalization form is specified.IIRC, Java is different from Python is different from Go. So, really, this is a standardization in languages problem. We tried to work around these as best we could in the OM format.
What? It's 100mb vs 25mb, like 75MB of data more... Who cares about a binary being 75MB larger in 2022?
Everything is layers. We build things on top of other things. If every layer had that attitude, then the bloat would be enormous. It's already getting there.
We should praise judicious effort into optimizing any of the resources used in the systems build, at every layer.
Now whether that's a particularly useful observation I'm still not sure.
Even though the application might only need two or three endpoints in kubernetes - which would be trivial to implement in go in just a couple of lines - they favor strong typing and include the SDK which is several megabytes. And the same for AWS, Azure, ...
I'm not passing any judgment here by the way.
also just worth oting that the memory impact of statically compiling in general is probably massive. most systems probably would have a good percent of these libraries in memory already if promtheus were using dynamic linking.
Removing everything but flie and static configs reduces it to about 50MiB.
Interesting for more embedded use cases, but not really a big deal when you're using a few GiB of memory for TSDB ingestion buffering.
" Why optimize a binary that's 109GB? That's too small to matter."
my first linux computer had 4MB of RAM but that doesnt' mean I try to fit anything into that (once I upgraded to 32MB, I could run g++, emacs, X11 and xterm at the same time!)
Because you wanted to be able to run more stuff, or because you wanted to be able to run the exact same executables, just bigger?
basically nobody is swapping because of a 128MB executable. if you are, get more ram or don't run prometheus.
Only the parts of the code that are in use are paged into the page cache. So if you only use a couple of the features, it fits in cache just fine.
https://news.ycombinator.com/newsguidelines.html
Edit: we've had to ask you repeatedly to follow the site guidelines. Could you please review them and start following them now?
Don't FANNG people obsess over bloat because they're trying to reach billions of customers? It might not seem that way since their pages are bigger but I'd be surprised if they were happy to leave 10s of millions of customers on the table.
Still the TV does fine with 2GB. Doesn't seem fair to complain.
By design you should not install prometheus on every server you monitor - it's designed to scrape metrics
Its a database, webui with support for email, webhooks, slack, pagerduty, aws api and many others. 100megs does not sound like a lot for all Pormetheus provides
Actually, performance too.
Tenths of megabytes of CPU instructions is complexity.
This kind of bloat is the number one enemy of security, as any security engineer could confirm.
Unless an application is filled with JPEGs or uncompressed arbitrary data files, its size reflects lines of code (machine code, interpreted code, etc). Bigger the app, the more lines of code.
Every line of code has a non-zero bug probability. Every new line of code increases probability. More lines of code, higher probability. Bugs include security bugs; higher probability of bugs, higher probability of security bugs.
CPU cache is finite. Only so many lines of code can be cached or optimized. Larger size takes up more room in memory, which when combined with lot of other gigantic apps, means less memory for heap space, disk cache, etc. Larger size also takes up more room on disk, which adds up when you don't delete old builds on disk and loop over a build process. Since larger size means more lines of code, that means longer compile times, which means longer wait every time you change a line and need to recompile, copy an artifact somewhere, retest.
More lines of code means more code executed. If you have 10 lines of code in a function, and you add 100 lines to it, the compiler doesn't just optimize away all 100 new lines, it's going to add more machine code and code paths. Unless you only ever add new code paths, some of that new code will extend existing code paths or add instructions, and that means more CPU cycles to complete execution. (Same concept for interpreted code)
More lines of code means more code paths. More code paths increases complexity. The more code paths, the longer and more difficult testing gets to the point you can't even develop enough tests to cover all the code paths, so it's impossible to even find all the bugs. More complexity leads to difficulty in humans understanding and working with the codebase, and difficulty in understanding leads to slower and more error-prone development.
Larger means more network bandwidth, meaning file transfers take longer, increasing speed to iterate and producing worse UX. If people download your app every 10 minutes in their CI/CD pipeline, larger size means more network bandwidth used. "Free" CDNs have limits; the larger a project gets, the more file size affects network performance, reliability, and cost. If you pay for bandwidth, a 100MB file costs 100x more than a 1MB file.
The more apps you use that are big, the more every one of these effects increase. One big app you might not notice. 100 big apps lead to noticeable slowness, bugs, less memory, less disk space.
And half an hour ago I was (once again) checking out hosting providers and lamenting the fact that most don't seem to offer support for loading custom ISOs so I could install a 30 megabyte distro and make the most out of the cheap plans that only offer something like 10 gigabytes of storage. Half of it is wasted after you install one of the these obese mainstream distros.
IMO the point of a hosting provider is supposed to be that you can have some peace of mind and not worry about your shit breaking (that's still a worry as I continue to host everything at home). Instead with providers like this, you worry about them breaking your shit.
From a security stand point, reduced application code decreases risk. It was service discovery code he removed, what if it reached out to discover services on application start up, that's a potential attack vector.
Agreed. I've see a similar pattern with certain open source libraries.
The first example I think of is the spf13/viper [1] library, used to load configuration into go applications. Viper is equipped with code for reading config from various file formats, environment variables, as well as remote config sources such as etcd, consul. If you introduce the viper library as a dependency of your application to merely read config from environment variables and YAML files in the local filesystem, then your go application suddenly gains a bunch of transitive dependencies on modules related to remote config loading for various species of remote config provider. It's not uncommon for these kind of remote config loading dependencies to have security vulnerabilities.
As well as the potential increased attack surface if a bunch of unnecessary code to load application configuration from all manner of remote config providers ends up in your application binary [2], if you work in an environment that monitors for vulnerabilities in open source dependencies, if you depend on an open source library that drags in dozens of transitive dependencies you don't really need, it adds a fair bit of additional overhead re: detecting, investigating and patching the potential vulnerabilities.
I guess there's arguably a "Hickean" simple-vs-easy tradeoff in how such libraries are designed. The "easy" design, that makes it quick for developers to get started and achieve immediate success with a config loading library, is to include code to load config from all popular supported config sources into the default configuration of the library, reducing the amount of steps a new user has to do to get the library to work for their use case. A less easy but arguably "simpler" design might be to only include a common config-provider interface in the core module and push all config-provider-specific client/adaptor code into separate modules, and force the user to think about which config sources they want to read from and then manually add and integrate the dependencies for the corresponding modules that contain the additional code they want.
edit: there has indeed been some discussion about the proliferation of dependencies, and what to do about them, in viper's issue tracker [3] [4]
[1] https://github.com/spf13/viper [2] this may or may not actually happen, depending on which function calls you actually use and what the compiler figures out. If your application doesn't call any remote-config-provider library functions then you shouldn't expect to find any in your resulting application binary, even if the dependency is there at the coarser-grain module dependency level [3] https://github.com/spf13/viper/issues/887 [4] https://github.com/spf13/viper/issues/707
I think this is one of those cases where, in the absence of profiling or some other hack (e.g. ensuring all routines within a library are cleanly segregated across page boundaries within the static binary and the I/O scheduler doesn't foil your intent), dynamic linking would prove superior, at least for such large amounts of code.
You can run it locally but the "prometheus" way for iot env would be a central prometheus server that scrapes the iot devices running a prometheus exporter, which tend to be very light weight
The only thing you really need to worry about on a Pi is that the default kernels are still 32-bit, and are set to 2GiB kernel boundary. So you'll be limited to how much TSDB storage can be mmap'd unless you switch to a 64-bit kernel.
You may want to consider agent mode on your IoT device, and stream the data to an external server/service.
https://prometheus.io/docs/prometheus/latest/feature_flags/#...
top reports this as RES.
IIRC, debugging information is in a separate part of the process, so it's not loaded until it's used. Does that make it free? Probably not quite, but the kernel can ignore it until the process (presumably via its debugger) looks at it.
One thing Microsoft got right a long time ago was separating out debug symbols into their own file by default. I think that's still awkward on Linux.
At least for .deb and .rpm packages, the default build process automatically extracts debug symbols into separate packages. Eg the process of building package `foo` also produces `foo-dbgsym` and `foo-debuginfo` packages respectively that contain debug symbols for every involved binary and library, while `foo` contains the stripped files.
So anyone who wants to debug a coredump / live process just installs the corresponding -dbgsym / -debuginfo package and now gdb has all the debug info it needs.
Distros have also started incorporating debuginfod into their repos so that gdb can download symbols automatically. So you don't even have to hunt for the right debuginfo package.
I'd love to know why 100MB is that big of a deal. If network is slow, cache locally. Seems like nothing here to worry about.