Conf42 Python 2022 - Online

Python Side-Cars

Video size:

Abstract

The application has been written. The container build is done on each merge. The last thing you want is to mess around with that. But there are things missing. There’s no integration with metrics. There are no readiness checks, so nodes get traffic before they are ready. Luckily, every modern container deployment system allows for “side-cars”: container images that are deployed alongside the main application containers. Writing a quick web application in Python to serve these missing bits, to be deployed as a side-car, is a popular solution.

How do you do this? How do you integrate into your container management system? And when is it better to modify the original application?

Summary

  • Moshe Zadka: Today I want to talk to you about Python sidecars. A pod is a basic execution unit. Readiness is kind of a weird thing that is basically the criterion. Is the pod good? Why do you need to know if the pod is good?
  • The next thing that I want to make sure that we're all on the same page in is deployed and services. A deployment is really just something that will automatically configure a replica set and a service. Side cars are a pattern that will be really useful when you're designing a kubernetes installation.
  • The main thing that the sidecars will do is to look good in some sense on the dashboard. That means that you want a very fast feedback loop. Python has a lot of facilities that makes iteration a lot faster. If you can chase the container code faster than the container moves, you're in an advantage.
  • The challenge I gave to myself is to feed a sidecar on a slide. That does mean that the code is optimized for the slides, not best practices. In practice it's mostly to show what bits and pieces would make a sidecars work.
  • The more sidecars you have, the better your packaging architecture is. You can build the container to be more sidecar friendly. You don't want to mush around with a container. The simpler the container is, the easier it is to manage.

Transcript

This transcript was autogenerated. To make changes, submit a PR.
My name is Moshe Zadka. My website is cobraism.com, where you can find every way of getting in touch with me known to humankind. And today I want to talk to you about Python sidecars. I do want to start with an acknowledgement of country. I live in Belmont in the San Francisco Bay Area peninsula, the ancestral homeland of the two Sholoni people. I want to start with a quick Kubernetes refresher, make sure that we're on the same page. Kubernetes is pretty big, so this is definitely not a comprehensive Kubernetes review. So let's start with nodes and containers. A pod is a basic execution unit. In Kubernetes that's what we execute. It's usually not what we configure and that's going to be really important both of these things later. A pod is made of n containers. A pod can have more than one container. They're all going to be in the same pod. They do share a network space, which means for example one twenty seven zero zero one is the same ip for all of them. They might share the process namespace. You can configure it on and off and we'll talk a little bit, but this later they can share volume mounts, so you can mount the same volume into both of them and they will share that part of the file system, but they never share the overall file system. So this is like the little bit of things that are going to be important later as we are talking about how to operate your sidecars. The other thing that's important in a pod is that readiness. Readiness is kind of a weird thing that is basically the criterion. Is the pod good? Why do you need to know if the pod is good? We'll talk about this later. This will become very important later. The usual way to determine if the pod is good is do TCP or HTTP check, which means you are sending in a TCP checks. If it connects and does a TCP hand check, it's successful. The more sophisticated checks that Kubernetes can configure is the HTTP check, which will fail on any status that is not two xx. You can also configure command checks. Basically there's a whole raft of issues around them. Usually I would not recommend them. And we'll talk about some of the ways that writing sidecars can alleviate the need to write command checks in Kubernetes. The next thing that I want to make sure that we're all on the same page in is deployed and services. There's many other things that you would need a sidecars for that are not deployments and services. But since these are the most common, let's focus on them for this talk. So a deployment is kind of a weird Kubernetes object because it's really just something that will automatically configure a replica set and a service. So if you understand those two, you also understand what deployment is. A replica set is a collection of identityless pods. And what I mean by identityless, there's no sense in this insurance things. Pod is the fifth pod in the set. If it's six pods, there's six pods. But if two die, it will bring up two other nodes, but it doesn't which one is like the true continuation of the next pod. There is no sense that this would make a sensible question. A service is something that can route to good pods. There's many ways to route and I will not go into them because for the most part they're not going to affect our talk. But it's very important because if you remember earlier, I said that radiance check is deciding what's a good pod. The reason you need to know if a pod is good or not is these service can know whether to route to it or not. And again, all of these things are a very important context because they will be important later. Side cars, not an atomic thing in Kubernetes there is no point where you will write in your Kubernetes YaMl configuration sidecar. You can if you call something a sidecar, but that's not a concept, that's not a type in Kubernetes, but it is a pattern. And specifically in Kubernetes, often when we say a pattern, what we mean is it's a shape that a YAml file takes. And when the YaMl file takes that shape, we'll call the resulting thing a side cars. What does a sidecar pattern look like? It looks like a container, a pod that has an extra container. And usually it just needs a second container because usually you have a main container that does the things that you are doing with the pod, right? If you think in terms of like a web application or a web API, this is a thing that actually runs a web server, right? If you think in terms of a queue consumer, this is a thing that consumes a queue and does something with it, nodes processing and takes it away. The idea of the sidecar is that it takes care of the rest. And obviously this is a pattern. So all these definitions of sound fuzzy and to be very honest, it's kind of not really a meaningful debate to say whether a specific thing is a sidecar or not. What's important is to know that this pattern is a pattern that will be really useful when you're designing a kubernetes installation. So you say, oh, well, what could be a useful sidecar here, right? There's no reason to get hung up on the definition of whether it's a true sidecar or not. So here's like an example of a sidecar. The main container writes a web application, and then these application wants to cache to files. Now, usually it's kind of awkward to also have the garbage collector sitting in the same process, right? Because web servers run in a specific framework that's got to manage all of these things starting to shove like an extra thread or spawning a process and then having to manage what happens if process dies. That's why we have kubernetes for, right? Like the whole point of kubernetes manages that kind of stuff, right? If you wanted to manage that, you wouldn't be using kubernetes in the first place. So what you can do is you can have a sidekick container and that looks at files that are like, say too old and removes them. So it basically functions like the garbage collection thread or process. But now Kubernetes is managing it. It's managing a different container. And one of the things that is a different container, it is built separately, right? You build those containers into separate like CI pipelines or whatever it is that you use to build containers. And so you don't have to use the same base image, you don't have to use the same platform, you don't have to use the same language. So why I thought has useful. So first of all, you can separate the resource limits, meaning you can make sure that your web application can take only so much memory and your sidecars does. A garbage collection can take so much memory and you know how much memory you allocate to each. They can't kind of go and steal from the other one's memory. And that's very useful when you want to do very careful capacity planning. At some point you will need to do. They're also legible to Kubernetes dashboards, right? If you put both of these things in the same container and they die and they get restarted, you'll have to also make sure that you keep track of that somehow to make it legible. If you put them in separate containers, Kubernetes knows if a container died, Kubernetes knows to separate the container's logs and so on. So that's very useful that you can, obviously there's like the native Kubernetes dashboard and quite potentially like whatever cloud provider has more stuff around kubernetes. So that's why I don't say the Kubernetes dashboard, it's general Kubernetes dashboards, right? It might be Prometheus grabbing data from Kubernetes. Whatever way you have observability for kubernetes, the sidecars will be legible to it. But most importantly, it simplifies these container, right? Like if we want to go with the model of a container, it's supposed to be very simple. This is a way to offload the complexity to Kubernetes. These you want it and to have each of these containers, both the main container and the side container, each be much simpler than if they would somehow have to be carefully finagled to been the same container. So obviously the reason I talked so much about readiness earlier is that that's going to be one example for why you would need a sidecar. So for example, with a readiness sidecar, you can check that the file exists or that the file is pretty new. You can check the contents of an HTTP response. This is not something that's very easy to do with the Kubernetes management, but you can easily send an HTTP code, and now you write your own code. So you can write it in a nicer language like Python. You can have an arbitrary logic and you don't have to start shoving in shell commands into Yaml and have them execute in these kubernetes container and make sure that they properly offer and stuff. And there's a whole thing. This is easier, right? You can just write code in Python. Python is a nice language. You can easily check the containers, do all kinds of complicated logic. It's easy. The other nice thing that you can do with side cars, collect metrics. What if the original container wrote some code and that wasn't formatted in the way that your metrics management, say Prometheus, which most of them kind of are compatible with, can read well. You can grab those metrics via whatever reformat them into promotes. You can also do synthetic checks. You can send a query to an API endpoint, measure how much time that took, and that can be like a nice gauge or a histogram, depending on exactly how you do that. You can even scan a log file, check, say how many black lines are there. And that can be your metric, right? Like how many lines were written in the last minute. That's a useful metric that you can expose. And so there's a bunch of stuff that you can do that's like that, that you can just do in a sidecar. You can also do scheduled tasks, right? Like I already gave these example of garbage collection, or you can do some sort of a re indexing operation. Again, like the sidecars shares all the knowledge that the main container has. So it can directly go with the API and kind of grab the data, do whatever indexing you need on it, and then shove it back through the API. Why write side closing Python? So there's a lot of things that make Python an ideal choice for sidecar. So if you think about it out of your examples, if you really knew that you needed that functionality early in the development process, you would already kind of but it in the design and made sure it fit right. The problem is retrofitting it. That is hard, but that also means it's late in the development process. You don't have a lot of time left in your schedule. Luckily, Python is a great prototyping language. You can whip out something that kind of works very fast. So that rewards the ability of python to quickly prototype. Now, the main thing that the sidecars will do is to look good in some sense on the dashboard, right? That means that you want a very fast feedback loop. Again, that gives you a lot of reward for fast iteration speed, right? It's not just easy to write the first version of Python programs, it's also really fast to iterate. And Python has a lot of facilities that makes iteration a lot faster and also kind of the way politics in engineering works. Often the sidecar will be built by slightly different people than the people who build the main car, right? Like they might be know kind of the deployment team or the infrastructure team or the SRE team, people who kind of adapt to that, maybe an embedded SRE, which means that the container will keep moving and the sidecar needs to keep moving along with it. It's really nice if you have something where you can adapt as changes in the container cause whatever logic you had in the sidecar to go out of date to be faster. So this means that if you can chase the container code faster than the container code moves, you're in an advantage. If you take longer for your cycles to modify. When the container code moves, you're perpetually going to be more and more behind. So I think this is kind of the fun part of the talk where I will give you an examples. The challenge I gave to myself is to feed a sidecar on a slide. That does mean that the code is optimized for the slides, not best practices. This is not how I would actually write this nodes. In practice it's mostly to show what bits and pieces would make a sidecars work. And hopefully they're going to be fun because you can kind of get all the details in one slide. This also does mean the slides are going to be a bit packed, so get ready for a fun journey. So here's like a really quick readiness sidecar. As you can see, in order to raise an exception, I just divide one by zero because that's literally the fastest code that will these shortest code that will raise an exception in Python, just three characters. So things means that I can grab the data, compare it to, let's say five, and then return the response okay, if it's not five, I raise an error. Most web framework will automatically give you 500. That complies with kubernetes. I have just enough code to hook this function into pyramid. I have routing, that's it. Other than imports things is fully functional code that will give you a readiness check. Assuming that you have a JSon that has a key called value, that has to be five. So this is fully functional readiness sidecar. Here's a metric sidecar. It takes a little bit more code because I have to integrate with the Prometheus client library. So that means I have to build these collector registry and I have to build a gauge for latency. And then I basically send a synthetic request, check how long it took, set the latency and I generate the Prometheus thing. And again, if you set everything up in kubernetes correctly, set Prometheus up with the right configuration. This will give you a gauge that shows the latency of the application, which means if the application is heavily loaded or it's like garbage collecting or something, this will take longer and you will be able to see it on a dashboard and you don't even have to count on the application actually measuring itself at any point here. Here's a simple example of a scheduler side cars. All it takes care of just periodic making sure that the application flashing bits queue. You might think, well, why not just put a Kubernetes quant job? Well, a quant job is a job, which means it is a pod. But let's say that you want this. Every single container of the main application has its own kind of internal queue that you want to flush once in a while to make sure that it flushes this is fully functional code and it will actually do the job and you actually don't have a much easier way to do that. So three lines of python. This was very easy to fit on a slide. So those are like some fun sidecars. Now I'm going to fail in my challenge because the next sidecar I have is going to be slightly longer than a slide, but we will break it down to a few slides. So it's going to be my log analyzer sidecar. To challenge myself, I gave myself kind of like some fun parameters, right? Like I said, you can actually mount a volume and so share files natively. But let's say for any kind of reason you don't want to do that, right. It just doesn't fit your model. So there can be all kind of permission issues and look, weird corner cases where it wouldn't make sense. So the main container logs to valog something. That valog something is inside the container's file system, so it wouldn't be accessible naively to other containers. And you wind these sidecar that processes has a log in this example, I think it's going to count the lines, but obviously in real life you'd do something slightly more interesting. But this almost seems like an impossible challenge. So I think the fact that I can't fit it in one slide is maybe, okay, I will fit it in like a handful of slides. So the first trick is that to configure the pod correctly. So we need to share the process namespace. Process namespace. That doesn't sound very useful. So the first step is to find out why do you have to know yourself? Because you can't know others unless you know yourself. This sounds like a weird, like mystical philosophy, but I promise you this is actually concrete technical advice, because now we can look for another process which has a different root than ours. Now we managed to get to the other process roots, right? Which means we already have access to the peer Containers operating system. So that's pretty cool. So now we have access to the file system that's in the other container. Now we can just read the log, sum the lines, and then post it to some kind of push metrics collector, let's say. And that's it, we're done. So it was a bit more than a slide, but in three slides we did something that most people would tell you is impossible. So I think that is a pretty cool thing about showing both the strengths of the kind of stuff you can do with sidecars and the kind of power that you have with python. That you can do the impossible in three slides, not even full slides worth of code. So I think that's kind of cool. So I want to leave you with a few final thoughts about containers. They're black nodes, right? You don't want to mush around with a container. Like once the container has been tested, you want to keep it as is throughout the deployment cycle. Then you want to keep your container simple. The simpler the container is, the easier it is going to be to manage. But you will have to always add something. Pods and equivalent things. On other frameworks that run containers, what they do is they allow adding stuff from the outside. At least it's a clear ish separation of responsibilities. So that's pretty useful. You can build the container to be more sidecar friendly. That's actually more important than building a container, to not need a sidecar. And one trick is to just have good APIs inside these container. You can, for example, listen on a separate port that's not exposed outside, so it's only going to be exposed to your peers, which means you can do a lot less permission checking, but still define a really well defined API. Which means you don't care about security, but you do care about portability, so that it's easier for the sidecar container to know which API to expect. So packaging stuff with containers is pretty good. This is not a controversial statement. In 2022, everybody agrees packaging stuff in separate containers is even better. You don't want to shove a container that is like the everything container or you're back to like, why even have a container? So the more sidecars you have, the better your packaging architecture is. And with that, thank you so much for listening to my talk. I hope you had fun, and I hope you're going to go and write a lot more sidecars.
...

Moshe Zadka

Principal Engineer @ Palo Alto Networks

Moshe Zadka's LinkedIn account Moshe Zadka's twitter account



Awesome tech events for

Priority access to all content

Video hallway track

Community chat

Exclusive promotions and giveaways