stoyle
K8s OOMKilled pods
Hey. Been searching the interwebs and the forums, but don’t seem to find an answer to this problem.
Our service runs several pods in a kubernetes cluster. They are a bit memory hungry, each limited to 1,7 GB of k8s memory. It seems I may have to increase this again, but of course this prevents horizontal scaling. Every now and then k8s kills our pods with OOMKilled.
My gut feeling is that our erlang/elixir/phoenix application does not need this much memory, it just does not know about the memory limits. OOMKilled typically happens when a pod tries to hog more memory than is assigned.
Looking at phoenix live dashboard, it at least it shows the app thinks it has the entire node’s memory accessible.
What should we do? Is there a way to tell beam not to use more memory thank 1,7 GB, or is there a setting where beam reads the k8s memory limits? Or is there some other solution?
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #performance
- #security










Most Liked
hubertlepicki
I had similar issues and would be interested to find a solution, but I didn’t find such flag.
In fact, figuring out which processes were even responsible for allocating memory was difficult on BEAM.
I ended up sampling the state of the system every 30s or so and be warned if we detect a process that exceeds something like 15 MB of memory.
The code I’m using is something like:
I run
Infra.Bloat.Findevery 30s and log if something was found. Then, I look at the logs and if something showed up I fix the memory leak.The usual suspects, according to my experience are:
With GraphQL resolvers or just a web requests that are crashing your pod, you need to know that BEAM is able to allocate a lot of memory very very fast, so you won’t catch all of such spikes. Some will kill your pod, others won’t but will go unnoticed, so I ended up just running the sampling code constantly on production and making sure nothing new exceeds the arbitrary memory limit I’ve set up. This seems to work really well at scale and I am able to detect memory leak issues before they are the problem.
dominicletz
As BEAM is process-oriented you can set a per-process memory limit. By default, no process has a memory limit and they are all allowed to consume as much as they want.
You can change that default though. E.g. to change the limit to a reasonable 10MB if you’re launching your instance from a shell script add the export
ELIXIR_ERL_OPTIONS:Or if you’re using a release put that into your
rel/vm.args.eexIn addition to that, it’s possible to change the max process memory on a per-process level. So you can go with a default limit of 10MB per process as above but then increase that for certain “important” processes, or reduce it for less important processes.
Also, you could stay without a global default limit but set a per request limit from an embedded plug. For E.g. adding this to the beginning of your endpoint definition would set all phoenix request memory limits to 1MB:
If you’re using liveview this does not affect the liveview processes as they don’t run through these endpoint plugs. In that case, you could do it from the mount callback or similar.
Hope this helps.
Cheers!
sb8244
A book suggestion on this topic is Erlang in Anger. It’s free and is one of the best resources that I read to understand what things can go wrong and how to diagnose them.
That said, one thing I might try in a high-memory situation is to force GC globally to see if the memory drops. I do this with
Process.list() |> Enum.each(& :erlang.garbage_collect/1). If you do see a large memory drop, then you may have a “memory leak”. But it may not be a memory leak like you’re used to where there’s unfreed memory, but rather processes that don’t have the opportunity to GC due to the lifecycle of the BEAM. One thing you can do to give the opportunity to GC more frequently is to set the VM flag of-env ERL_FULLSWEEP_AFTER 20in your VM.args file.I set the above flag in every Elixir app I build now. I have not seen any negative side effects from it (can increase CPU usage, but I did not see a noticeable change) and the benefits can be significant in some long-lived processes.
I wrote an old post about how I diagnosed this in one of my services. The post itself is irrelevant because Phoenix Channels hibernate by default now, but the content itself is still relevant.
edit: 1 more question. What does your memory usage look like? There are several types of memory in the BEAM (process, binary, atom, etc) and that can determine the specific issue. I am not familiar with LiveDashboard as I have used
observer_cliin my projects, but I imagine that’s one of the main breakdowns it provides.Last Post!
stoyle
This turned into quite a few insights, at least for me. Thanks again for all your help and knowledge.
I don’t think there is an actual solution to this problem. So don’t think I will mark any answers as a solution. But at least I will summarize what we have done.
A final change we’ve done. We are running our pods in prod now without a k8s limits memory setting, i.e. the pods may hog as much memory as they like. Baseline seems to be between 500-700 MB, which is ok. Every now and then a pod will allocate more than over 1,7 GB, but since the host has a bit of free memory, this should be ok. If a node starts to struggle k8s will kill the pods anyways.
Only been running this for a few hours, but no OOMKilled pods so far at least.