 Hello, and welcome to KubeCon Paris. Nice to see so many people here. And that you'd like to join us to start of your KubeCon journey today with 10 years of Kube and its pedals evolution. That's really awesome. So actually, my name is Roland Huss. I'm from Red Hat. And we have written a book together with Bill Jean. Yeah. Hello, everybody. My name is Bill Guinea-Briam. I'm product manager at Tigrid, where we are working with a project called Tupper. OK, so let's get started. We have a lot of stuff for you in the next 35 minutes. For that, just my presenters here. OK. So what we have done, so we've written that book. We have collected a certain set of patterns that we think are useful for you if you are a user of Kubernetes. And yeah, so you can take this URL. You find a free download of the book if you like. It's a PDF format. It's kindly sponsored by Red Hat. So yeah, on everything that you find here, you find also, of course, the PDF and much, much more. So we have 26 or 7 patterns, I know. And today, we are going to present around 6 of them. But before I start, some quick introductions, what we mean with patterns or design patterns, and also a very short introduction to Kubernetes. But actually, this will become very short. So this is a very famous book. It's coming from 1977. And actually, the fun thing, it's not kind of a computer book. It's a book coming from the field of architecture. And it has been written by Christopher Alexander. And he kind of invented the idea of design patterns. This means repeatable solution for common problems. And they had in the books things like how do you have to design beer halls so that people meet each other and whatnot. But actually, when we talk as developers or IT experts, of design patterns, we typically think about that book. And most of us probably know the book already. So this is design patterns for object-oriented language software. And they kind of really transform this idea to the field of IT. So when we are going to try to have the same form, so the patterns are kind of self-contained. They have all our name. They are interconnected. And they describe kind of a blueprint for class of problems. Now, as I said, quick introduction to Kubernetes. Of course, I hope that everybody knows already what Kubernetes is in some sense. But because we'll be talking about 10 years of evolution of Kubernetes, Kubernetes itself has been started in 2014 and was created by Google where they kind of make open source their internal way, how to orchestrate their containers with their internal system called Borg. And Kubernetes provides everything what you need for this kind of orchestration like scheduling, self-healing by, and also automatic scaling horizontally, which means duplicating your applications or also vertically, which means to increase the size of your memory or CPU, but also tons of other stuff that we see and you probably know that already. I think that the most important thing that you need to know about Kubernetes also that it's a declarative resource-centric API, which means that you kind of declare your target state and Kubernetes task is to actually get the current state more closer to the target state. And here's a 10,000 feet overview how you interact with Kubernetes. So you have a CLI or any other UI, you have a kind of API server or the control plane, and this kind of talks with many nodes. These nodes are kind of the compute thing in your cluster and the controller is also responsible to distribute all this workload to the different nodes. And the central element in Kubernetes, you know that it's a pod. A pod is kind of a higher level of abstraction of a container, so a pod can contain one or more containers, and these containers can talk to each other, either via network or if you configure it, also via disk or volume, and these containers are kind of opaque, so everything that's running in the container stays in the container and you can put anything what you want in these containers. And around these abstractions of pod, you can describe the pod with the YAML files and with the resource descriptor. There are tons of other resources that are going around that we are not going on, of course, in the detail of that one, but at the end everything goes around your code. So your code is running within the container. You describe how your code should be run and how it interconnects to the outside world. And this kind of concludes our very quick crash tour through Kubernetes. And now let's start with our patterns. So we tried to make kind of a categorization of those patterns where we put together all the patterns that we think belong together and we start with foundational patterns with more or less direct reflections of the core Kubernetes ideas. I'll have to say that Kubernetes itself is kind of a pattern machine, so to say, so the Kubernetes tries to implement certain patterns that has been evolved when running on clusters together. So if some of the patterns really looks like a part of the documentation of Kubernetes, this is not by accident. This is really kind of intended. So yeah, let's start our journey. So we have these six categories. We will go through, we have one example of each of these category and try to give you a preview overview. So. Right, thanks, Ron. So we'll start with the foundational patterns and really these patterns, I would say, are so important that you can think of those as the foundational principles that every containerized application must implement in order to become a good cloud native citizens, right? And we'll start with the health probe pattern. Really the idea of the health probe is how can an application communicate its health and status to the platform without exposing its internal implementation, right? When you run an application on Kubernetes, right? Kubernetes will perform process health checks on your application. So if the process dies, the Kubelet will make sure the process is started on a note where the port was assigned to. So just by running your application on Kubernetes, you basically make your application more resilient. But typically that's not enough, right? From practice we know there are many cases when the process might be up but the application might not be doing what you want it to do. So that's why we need additional APIs so that the platform can find out what's the state of your application and perform some corrective actions, right? There can be cases when your application is in a deadlock or your application has not started yet or it has run out of memory. So the process might be up but it's not functioning as intended. And there are different corrective actions the platform can perform in this case. In some cases it can restart your application or it can wait for it to recover on its own with a transient error. So what are those health probes? There are three of those. Lightness probe is basically supposed to detect when your application is in some kind of deadlock, the process is up but it's not functioning. And the corrective action in this case is restarting your container. Now this might not work in all kind of scenarios. For example, if your application is just starting up, right? The process might be up but the application is not ready or if your application is waiting for some kind of external dependency like a database to come online. In this scenario, restarting your application will not help. And that's why there is the second probe which is the readiness probe. And the idea of readiness probe is that when that probe fails, the application endpoint is removed from the list of endpoints from the service. That means no further traffic is directed to your application. Now that is useful if your application is processing incoming requests to the service abstraction. But if your application is consuming messages from a message broker, for example, that will not help. So you have to really think whether the corrective action is what you want. And in some cases, like when I used to work in the Java world, the application used to take a long time to start up, right? If you wanna handle those kind of cases, there is the new startup probe. That was added, I would say, quite recently compared to other probes. And the idea in this case is you can have more control how to detect that application is starting. And only when a startup probe passes, then the readiness and lameness probes take effect. There are different ways to implement these probes, right? I have a code sample here. This is a pseudo code, so it's not meant to work if you copy and try to run it. But it shows you how easy it is to implement these probes in your YAML file. And you can do the probes using, you know, HTTP, GRPC, which is also a new addition. You can execute a command or you can try to do a TCP connection to your application. The whole point is if any of those different methods fail, the probe fails. And I would say the health probe was such a fundamental when it was introduced in Kubernetes now 10 years ago that most application runtime frameworks implement this kind of health and probes out of the box. So you just have to plug your custom logic. And it's also implemented outside of Kubernetes as well. If you're running container as a service, you have the concept of health probes. And finally, just to wrap up the foundational patterns, these are also, you know, interrelated you kind of need to implement these in order to have your application work properly in Kubernetes. For example, if you want the automatic placement to work properly, that's one of the patterns, right? You want to declare what kind of resources your application needs. That's the predictable demand. Not only that, you wanna declare how the application should replace the old version with the new version of that, right? That's the declarative deployment. And for the declarative deployment to work, you want the application to listen to events coming from Kubernetes, which is the managed life cycle and the health probe we just covered. So if you implement those, you will make sure that your containerized application is running fine on Kubernetes. So next stop on our quick crash course on patterns. Let's talk about structural patterns. So the first thing that you're seeing, these are really kind of core concepts of Kubernetes. So you probably already kind of know them maybe, but there's also some higher level patterns which are really based on those core concepts that you typically do not find directly in Kubernetes. But you can easily implement them so. So we start with structural ones. And from that list, we picked the sidecar, which looks like, I think it's one of the most famous patterns that you probably also know about from Kubernetes. And let's talk about this one. It's really about how you can enhance the functionality of your application in a container without touching directly the application that is in the container. So in some sense, if you're coming from the programming world, it's a little bit like aspect-oriented programming where you're also kind of put on orthogonal aspects to your application just by reconfiguring and combining two containers in there. The nice thing is, of course, that they are only loosely coupled, but these containers actually can communicate with each other via network and also via file system, via volumes. And you can easily use separation of concerns. Examples of those sidecars are, of course, service meshes like Istio and other service meshes that really add functionality, network-related functionality, typically, to your application. So how does it look like? So here you see the port outside and inside the port. As I mentioned, you can have multiple containers. And in this case, you have a main container which just is an HTTP server and this HTTP server serves static files. So it doesn't know anything about the content that it serves. And just by attaching a sidecar container that periodically pulls a Git repository, you can add a new functionality, namely updating the files when something changes on your Git repository. Only via configuration adding the sidecar container. So you can really compose your applications also and give them more functionality than it originally had. So there's some things that we already talked, so let's come a little bit back to the topic of evolution of patterns because Kubernetes itself didn't had any, all of the features at once when it started. And the sidecar container actually turned out to be very useful. And it's so useful that it nowadays, actually in one of the least Kubernetes versions, you can have it as a high-level contact also within Kubernetes itself. There's one problem with sidecar containers is that if you start the sidecar containers in the startup, the startup order is not really well-defined because all the application containers really start in parallel, whereas the init containers, which is another pattern, but init containers are run before the application container bay are run sequentially, so one after each other. And with Kubernetes 1.29, you have a PETA feature, so you have to enable it with a feature flag that you can specify a so-called restart policy to an init container. And this init container means if the policy is always, it means that it just keeps running even if the init containers has been finished. And if it stops, it just behaves like a regular application container, but it's a dedicated sidecar container. So it supports all the health probe that Belgium just mentioned, including our startup probe. So it also ensures that the other application container only starts when, for example, this init sidecar has been started. So this is a good example of really also the internal features that of Kubernetes evolves over time. And of course, we can expect much more later on. Okay, up to the next patterns. All right. So we've seen how foundational patterns help you create containers that are good cloud native citizens. The structural patterns help you combine the containers in different formats within the port with init containers, with a sidecar, et cetera. And the idea of behavioral patterns is that they dictate how the port communicates with the Kubernetes platform. Typically that's around the life cycle, whether your application is running as a long running process, whether it's running as a stateful application, stateless application, et cetera. And it's also about directing traffic to your application or metadata to your application, right? These are the behavioral patterns. What we'll look at is one of those, the singleton pattern. And really in essence, the idea of singleton patterns is how can you make sure that there is a single instance or a single active component within the instances of a service, right? Kubernetes is really great in running multiple replicas of your stateless applications, right? It can scale them quite easily. It can scale them automatically, et cetera. But in some scenarios, you may wanna have a control over how many instances you wanna have of an application. For example, if you have an application that is pulling a file system or it's pulling a database, you wanna have a control how many instances at the same time can pull that instance. And the example would be if you have a service that is doing a regular calls to an endpoint. If you start multiple replicas, you will have a duplicate request, right? You wanna have a control over that. A third example would be if you wanna control, if you wanna have a sequential processing consuming messages from a message queue, right? In that case, you wanna have a single consumer that is single-threaded. That's the only way to guarantee ordering of messages. And the easiest way to do that would be just to tell, you know, Kubernetes give me a deployment, give me a replica set with one replica and you have a singleton, right? That's not quite right. So typically singletons prefer consistency over availability. So they prefer to have a single replica or single instance than being all the time available. And the replica set doesn't favor that with a replica set. There are cases where you may have more than one instance or a replica set will favor availability. And if you have a node that's disconnected, for example, Kubernetes will try to start another port on a healthy node if there is a capacity. And you may have scenarios where there are multiple ports running even if that's not what you want. In this kind of scenarios, you can use a stateful set, right? Which favors consistency over availability. And if a node is disconnected or if you have some other scenarios when you're moving a port between the nodes, et cetera, you will never have more than the desired number of instances. You may have less but not more, right? So this is an example of how you can have a singleton without the application being aware, right? It's handled at the Kubernetes level. But that may not work always. For example, what about if somebody from the operations team decided at some point in the application lifecycle to scale or add an auto scaler and break the constraints that your application wants? In that case, you wanna do a locking between your application, right? Here I have on the diagram an application with two replicas. It doesn't matter whether it's stateful set or replica set. You have containers, you have some kind of business logic and you want only one of those to be active. And the way to do that is to acquire a lock on some kind of distributed lock, right? And different frameworks where you can do that. For example, I've used in the Java world, I used Apache Camel for that. And there are also things that Kubernetes can help you. For example, you can use Apache Camel to connect to Kubernetes at CD. You can use ConfigMaps as a shared lock. And make sure that only the first app that starts acquires the lock and the other apps are waiting in a passive state. This is an example of an in application locking, right? It's an active passive topology that is only like a set number of instances that are active and you can use different ways. One of the new additions here was the lease object in Kubernetes, which is used by the nodes to communicate that they're up and healthy. It's used by the control plane components of Kubernetes. So you can use that but you have to implement your own operator to implement the singleton. I would say a third way would be to use a side card, a pattern that Roland just covered, right? So rather than having a library within your application to acquire a lock, you can use a side card with the Dapper project. So Dapper is deployed as a side card and it exposes various APIs for your application to call. One of those APIs is a distributed lock. In that case, your application can call the side card on localhost to acquire the lock and the first instance that acquires the lock becomes the leader. The other instances will be waiting on that. So this will obstruct the backing infrastructure such as Redis and it also makes it available to all kind of applications in every language. That would be like an evolution of the singleton pattern with the side card. So the next one. Yo, up to the next category, which is about configuration. So this category all is all about how you can configure your applications or of course there are different possibilities that you can use. Some are really at the heart of Kubernetes like configuring with environment variables or config maps. But some are also a little bit more advanced which goes beyond the core concepts. And I would like to present the pattern of the immutable configuration which means is all about how you can configure your application with immutable container images. So where you put all your container data into an image and then bring this to your application. If you're a Docker user, you know this is quite easy. You just make a volume mount into the container itself. Unfortunately, this core feature is not really directly possible with Kubernetes. So you cannot directly, even with the sidecars directly access. I will show you a possibility later on but normally you cannot access the data within the container image without copying it over into something else. And this is what the pattern is actually. But before I show you the solution the idea is really about the configuration characteristic that it cannot be changed during the runtime. So every time you make a configuration change you have kind of a redeployment or an update of your deployment which allows you to, for versioning and also auditing so you can really reason about your configuration quite easily and avoid snowflake systems for example. Also you can prepare your containers for different environments like development, staging or production. And that is really that you put in container images and OCI images that you can benefit from all the OCI image machinery like versioning, tacking and distributing all the images via registries. This looks like, and the technique how to implement this is going with an init container. So init container is other pattern that we have not shown but actually as I mentioned it's something that runs before your main containers are running. And this init container would just pick up some data out of its own container image and copy it over into an so-called MTDir volume which is just an empty directory that you can configure in Kubernetes. And then when the init container finishes the application container starts and just picks up the data from this MTDir volume. So it goes over this shared directory. However, it involves copying over of certain data which usually is not a big issue but if the configuration data is very large then this can become an issue. And so configuration data, of course if you think about YAML files configuration data is probably very misfeasible but if you think for example about large language models and you want to run really big things like multi gigabytes of size data then this becomes an issue. And for that luckily there's another maybe not so well-known possibility that you can use actually this goes over this little innocent share process namespace fields equals to true. This allows you if you put this into the pod spec this allows you that your containers can share the process space, each other so they are not fully isolated anymore. So if you make a PS in the container you see all the other containers as well. And the trick is that you also can access the root file system of each other container via the proc file system. So actually you see that you can go over proc then the PID of the target containers main process and then slash root and you can access directly the data without copying it over into an empty data volume previously. And it's used actually directly for example in the model class feature of K-Serve. So K-Serve is an add-on on Kibir Lattice for inference services of machine learning models including large language models and of course this allows you to save this copy step and makes startups much quicker for example. So this is the idea. And yeah, if you want to know more about the just check out model class for K-Serve there's a detailed description of how this really works. Okay, now up to the next panel. Yeah, next one is one from the security category. Now security is really a broad topic, right? It touches the all the full software lifecycle from development to build to deployment, right? It also touches the full application stack starting with securing the cloud to your Kubernetes cluster, the containers, the application code, et cetera. But in our patterns book we look into very specific angles. So we look at the interaction of the application of the pod with the Kubernetes resources at runtime. So the four patterns here are really when you run a pod on the nodes, you know one of the things it always does is it interacts with that node. How can you constrain that interaction with the process containment pattern? The other thing a pod will most likely need is it will need a secure configuration. So secrets, passwords to databases, et cetera. That's how it gets some configurations in a secure manner to the pod. That's the secure configuration pattern. The other thing a pod may do is it may interact with other pods, right? It can network calls, it may receive network calls. So how do you secure that interaction? And the final one is in some cases the application pod also needs to interact with the Kubernetes API server. So you also have to secure that. So this I think is a really good way to look into application security on Kubernetes. What we'll look at here is just one of those patterns is the process containment pattern. And really the idea is how can we protect the node, the Kubernetes cluster from your deployed code. Actually that's when it's exploited, right? And there are many ways to secure an application. You can do static code analysis, you can do dynamic scanning, you can do image scanning, et cetera. But whatever you do, there is no guarantee that your application is risk free. So one of the common exploits on Kubernetes is to the application workload. And really this pattern says that you can use the container and pod abstraction to constrain what an application, what can a process do on Kubernetes. And three concrete things. I'll give an example of what you can do to do that. The first thing is you can make sure that your containers are not running as a root user. For example, if you are using Docker images that don't have the user specified, they will run with the user ID zero, which means they'll become a root user. So that container, that process gets root access to the node. One thing you can do is you can specify user ID on the node. For example, in this case, at runtime, I am overriding the user ID 2000. That may work in some cases, but some containers still rely on a specific user ID in order to work, so this can break at runtime your container. A safer way and less intrusive way is just enforced with run as non-root parameter that the container is not running as a root. That would be a less intrusive way. Now once you secure the user, I'll say the next thing is to lock down the capabilities that user has. And one of the things you can do here is easily to prevent privilege escalation, right? User, that would be to preventing something like running a pseudo command in your container and becoming a root user. So with this flag, you can do that. That's a kind of safe option unless you're doing something very advanced and building images and you need a specific access. So that's a good option to the node. And the other thing you can do is, these users, the user that your container process is running as by default, it has a set of capabilities that usually are quite open and wide. You might be surprised how many capabilities are added by your runtime. So one thing you can do there is you can clean up those capabilities and only add the ones your application need. In this case, I have a container image server and in order for it to start on port 80, I'm giving it access with the net buying service so that it can start below 10, 24 port number. It's not easy to find out what are those capabilities. You may have to run your app and check the logs with some specialized security tooling. And the third thing, last thing you can do is, once you secure the user, once you secure what capabilities it can do is to secure the file system. If you are doing cloud native application, you shouldn't have to write to the container file system. And a way to protect that is by setting this read-only flag. If you don't do that, if your application is exploited, there is a chance that they install something on that container and get more capabilities to attack the node, right? You can enforce that with this flag. And on the right-hand side, you see what we have discussed. Because this is in the application port file, this becomes a developer responsibility and it's quite hard to enforce that. That's why it's more often than done through the administrator's true policies, which we are not covering here. That's the security pattern. Yeah, so now for the last five minutes, let's talk about some advanced patterns. And actually advanced patterns are just patterns that do not fit in any other category and really kind of really complex things. And we will focus at this time on the controller pattern and the operator pattern. So first about on the controller pattern, to understand the controller pattern or to think about what the question actually, controller pattern helps you to get from a current state to a declared target state. So this is, as I mentioned at the beginning, this is kind of at the heart of Kubernetes to exactly make this reconciliation. And if you think, really, so Kubernetes is really a distributed state manager, so it really needs to manage the state on multiple distributed nodes in the cluster. And actually all Kubernetes, not all, but actually the main thing that Kubernetes does is really to make the actual state of the state that is observed more closer to the declared target state. So the target state you declare in these resource files that you have seen. So the YAML files here. And this is a kind of an endless cycle that goes on and on. So first of all, you observe. Observe means that your controller is listening on events that the API server distributes to everybody who was interested in certain type of events. Then these events are analyzed and compared to the configuration in the resource files. And if they are the same and if they fit, then it's everything's fine, nothing needs to be done. But if there's a delta, then some action needs to be done. And this works like, as I said, the API server sends over events to a controller, the controller analyzes and then acts also via the API server. The API server then is responsible for talking to the node components. So these, every node has a certain component running or multiple components running on them, like KubeLabs and KubeProxy. And these are really acting on the actual data plane. So on the workloads there. So the application runtime is then managed by these node components. And this kind of is what Kubernetes does very well. And you also can write your own controllers. This is what I mean. You can also write an application and perform exactly the same loop. And this is quite nice, but there's even more advanced concept that is based on the controller pattern, which is the operator pattern. And the question actually is really here, how you can encapsulate your operational knowledge into something which is executable. And this works, I like this quote here, is that actually an operator is a Kubernetes controller that really understands two domains. So one is of course Kubernetes. But the other one is kind of a specialized maybe business domain, something like for example, Prometheus knows about the observability domain and operator combines both of them. So actually he has the knowledge, but actually tells then Kubernetes actually what to be done and can automate all these tasks that are actually not at the heart of Kubernetes, but outside, but you can benefit from the whole machinery that Kubernetes offers. So actually for our task here, this operator is just a controller plus a so-called custom resource definition. What a custom resource definition is we see in a second. Actually this kind of definition is a little bit fuzzy, of course, some people don't agree with that, but actually this is the working assumption for our book here. And a custom resource definition actually is, actually encapsulates or offers the configuration of your specific domains. Actually you, if you're a Java developer, for example, you can think about a custom resource definition as a Java class. So it's a type definition which specifies the schema of the resource that you can apply and you install the custom resource definition cluster wide on your cluster. So there's a single instance of the custom resource definition for a certain type. In this case, example, it's a config watcher and the target of this, or the goal of this config watcher is to watch configuration and restart pods if the conversion changes. So this is a very simple example operator you find the full example in the book there, but the idea that you define the type and then you can create an instance of the type by just creating a resource that looks like any other Kubernetes resource but has a different kind. So the type is here the kind and this type has been introduced by the custom resource definition here. And then you can specify here, you see there are very specific keys like config map and pod selector. And these schema of the spec here is what you have defined in your custom resource definition as a schema. Okay, you can of course try to categorize all the operator and the components and what it's good for. Actually, here in a simple graph, I don't want to go into detail here much, but actually there's a rough classification that you have. You can classify installation CRDs and application CRDs. One is those which are installing full application sets so they're at the global configuration of your application. Like for example, if you want to install Prometheus in your cluster, there's a Prometheus CRD and then you create instance and then a controller watches whether somebody creates such an instance and if so then it just installs Prometheus in your cluster. And the other one is more like for configuring these installations which are called application CRDs. Like in the Prometheus example, it's a service monitor CRD that configures which metrics to monitor for Prometheus. If you look for operators, there's also the concept of an operator hub. So there's like a catalog where you can find pre-configured and or published operators that it directly can use. We are installations. So this is kind of an alternative to Helm charts. For example, you can use this directly with operators. And yeah, so this is kind of, okay, now we come to an end. You see, of course, sorry, this was very quick. I apologize for that. But you find all the details in the book itself if you like. And there will be two book signing sessions today. So one is in one hour approximately at the O'Reilly booth. And the other one is at the Reddit booth and you get your free book for that signed by us if you like. And as I said, if you want more to know, you go to the site and download the PDF. That's it. By the way, we are hanging around. So, Vigil will be the direct booth mostly. And I'm also available in the Knative kiosk if you like to. And also in the Reddit booth. So grab us and ask questions. Thank you very much.