The “Snowball” Effect In Kubernetes

So, a weird thing occurred in Kubernetes on the GKE cluster we have at the office. I figured I would do a write up here, before I forget everything and maybe allow the Kubernetes devs to read over this as an issue (https://github.com/kubernetes/kubernetes/issues/93783)

We noticed some weirdness occurring on our cluster when Jobs and CronJobs started behaving strangely.

Jobs were spawning but seemed to not spawn any pods to go with it, even over an hour later, they were sitting there without a pod to go with it.

Investigating other jobs, I found a crazy large number of pods in one of our namespaces, over 900 to be exact. These pods were all completed pods from a CronJob.

The CronJob was scheduled to run every minute, and the definition of the CronJob had valid values for the history — sensible values for .spec.successfulJobsHistoryLimit and .spec.failedJobsHistoryLimit were set. And even if they weren’t, the defaults would (or should) be used.

So why did we have over 900 cron pods, and why weren’t they being cleaned up upon completion?

Just in case the number of pods were causing problems, I cleared out the completed pods:

kubectl delete pods -n {namespace} $(kubectl get pods -n {namespace} | grep Completed | awk '{print $1}' | xargs)

But even after that, new jobs weren’t spawning pods. And in fact, more CronJob pods were appearing in this namespace. So I disabled the CronJob

kubectl patch cronjobs -n {namespace} {cronjob-name} -p '{"spec" : {"suspend" : true }}'

But that also didn’t help, pods were still being generated. Which is weird — why is a CronJob still spawning pods even when it’s suspended?

So then I remembered that CronJobs actually generate Job objects. So I checked the Job objects and found over 3000 Job objects. Okay, something is seriously wrong here, there shouldn’t be 3000 Job objects for something that only runs once a minute.

So I went and deleted all the CronJob related Job objects:

kubectl delete job -n {namespace} $(kubectl get jobs -n {namespace} | grep {cronjob-name} | awk '{print $1}' | xargs)

This reduced the pods down, but did not help us determine why the Job objects were not spawning pods.

I decided to get Google onto the case and raised a support ticket.

Their first investigation brought up something interesting. They sent me this snippet from the Master logs (redacted)

2020-08-05 10:05:06.555 CEST - Job is created
2020-08-05 11:21:16.546 CEST - Pod is created
2020-08-05 11:21:16.569 CEST - Pod (XXXXXXX) is bound to node
2020-08-05 11:24:11.069 CEST - Pod is deleted

2020-08-05 12:45:47.940 CEST - Job is created
2020-08-05 12:57:22.386 CEST - Pod is created
2020-08-05 12:57:22.401 CEST - Pod (XXXXXXX) is bound to node

Spot the problem?

The time between “Job is created” and “Pod is created” around 80 minutes in the first case, and 12 minutes in the second one. That’s right, it took 80 minutes for the Pod to be spawned.

And this is where it dawned on me about what was possibly going on.

  • The CronJob spawned a Job object. It tried to spawn a pod, and that took a significant amount of time, far more than the 1 minute between runs
  • The next cycle, the CronJob looks to see if it has a running pod due to the .spec.concurrencyPolicy value.
  • The CronJob does not find a running pod so generates another Job object, which also gets stuck waiting for pod generation
  • And so on, and so on.

Each time, a new Job gets added, gets stuck waiting for pod generation for an abnormally long time, which causes another Job to be added to the namespace which also gets stuck…

Eventually, the pod will generate but by then there’s now a backlog of Jobs, meaning even if I suspended the CronJob, it won’t have any effect until the Jobs in the backlog are cleared or deleted (I had deleted them).

Google investigated further, and found the culprit:

Failed calling webhook, failing open www.up9.com: failed calling webhook "www.up9.com": Post https://up9-sidecar-injector-prod.up9.svc:443/mutate?timeout=30s: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)

We were testing up9 and this was using a webhook, so it looks like a misbehaving webhook was causing this problem. We removed the webhook and everything started working again.

So where does this leave us? Well, a few thoughts:

  • A misbehaving/misconfigured webhook can cause a Snowball effect in the cluster causing multiple runs of a single CronJob without cleanup — successfulJobsHistoryLimit and failedJobsHistoryLimit values are seemingly ignored.
  • This could break systems where the CronJob is supposed to be run mutually exclusively, since the delay in pod generation could allow two cron pods to spawn together, even though the CronJob has a concurrencyPolicy set as Forbid.
  • If someone managed (whether intentionally or maliciously) to install a webhook that causes this pod spawning delay, and then adds a CronJob that runs once a minute — and then maliciously crafts the job to never finish, this snowball effect will cause the cluster to run out of resource and/or scale up nodes forever or until it hits the max allowed by your configuration.

Pixelbook

Spent a big chunk of today preparing for, and attempting to upgrade my Pixelbook to Gallium OS.

I imaged it, then made a file backup of my home directory, before installing the OS, overwriting my Ubuntu, then restoring the home directory backup into the newly installed OS and then chowning the directory to me.

As a habit, I then imaged the laptop at this state.

I prepared a semi-automated script to install apps that I had installed on my Ubuntu, which included things like virt-manager, virtualbox, google-chrome and the like.

However, I soon found out that VirtualBox 6.1 seems to crash the mouse driver on reboot and the mouse pointer no longer moves and Gallium doesn’t even seem to see a pointer device when you check the mouse and touchpad option. I had to revert back to the image just after the file copy.

There is always the option of installing VirtualBox 6.0 from the Ubuntu repositories rather than the Oracle repositories, which uses a different installation setup. Maybe that will result in a different outcome.

Eventually, I restored back to my original Ubuntu installation so I can retry again tomorrow.

EDIT: Retried again the next day, and found out the sound wasn’t working, even on the live disk. Better find out what’s the deal with that…

EDIT2: Found out that my Pixelbook model doesn’t have working sound drivers on GalliumOS. I guess I will have to wait until that is fixed before using that. I guess I’m staying on Ubuntu. In the meantime, I’m going to see if I can compile a later version of the kernel to see if I can somehow get VirtualBox working better.

Slow Download Speeds on Steam For Linux

I’ve been getting horrendously slow speeds on Linux Steam (~500k/s) and 5-6Mb/s on Windows, and only now found out why. There’s a ticket on GitHub for this:

https://github.com/ValveSoftware/steam-for-linux/issues/3401

In short, the client is very aggressive on its DNS requests, which normally causes it to be throttled by servers, leading to really slow downloads. However, using dnsmasq allows the requests to be cached locally and offload the requests.

Even though the instructions are for Arch, they worked for me:

  1. Install dnsmasq
  2. Modify /etc/dnsmasq.config and add the line listen-address=127.0.0.1
  3. Restart the dnsmasq service (systemctl restart dnsmasq.service) or reboot your machine

Enjoy the speed

The Rise of Open Source Software

There’s a nice CNBC documentary talking about OSS and how it’s pretty much taken over the world. Proof if it was needed that open source is better than closed source in pretty much every scenario.

I say “pretty much” since there are definitely certain scenarios where open source is not the best option, such as proprietary encryption algorithms or something that is company-confidential.

The Fall and Rise of MasterCard

MasterCard seems to be making a lot of good choices recently with its branding showing up in a lot of high-profile cards.

Monzo, Revolut, Starling, Curve, Tide, and more are all MasterCard branded.

I used to have a MasterCard Credit Card by (IIRC) MBNA (remember them?) which I took out initially because I was going to Germany and back then (early noughties), I was advised that MasterCard was more prevalent in the European continent than Visa was, so make sure I had a MasterCard handy in case I ended up in a place that did not take Visa.

I didn’t even use that card.

Fast forward several years later, and suddenly I got a letter saying MBNA were transferring me onto Barclaycard Visa — which was the same as the other credit cards I had. So I ended up with multiple Barclaycard Visa cards, and no MasterCards.

The fact MBNA transferred me off MasterCard made me wonder why. Was there an issue with MasterCard? Were they like AmEx and causing problems with merchants? I shall never know.

I’ve been with Visa pretty much my entire adult life, and aside from that period above where I had a MasterCard, never had a MasterCard.

Eventually I decided to look at Monzo and Revolut, getting accounts successfully setup in both. Both issued MasterCard Debit cards. Starling’s app wouldn’t run on my device.

Monzo is rapidly becoming my main day-to-day account, whereas my Barclaycard is for things like larger purchases, bills, fuel, etc.

It’s strange since my last MasterCard experience, they’ve gone and pretty much disappeared, to coming back and almost every new card out there is MasterCard Debit….

%d bloggers like this: