 My name is Ron Schneider. I'm CTO co-founder of Diagrid and a Dapper Maintainer and Stream Committee member. Hello, can you hear me? Okay. My name is Arthur. I'm an engineering manager for the Dapper team at Microsoft. I'm also a Maintainer and SCC member. And today we're going to talk to you about a few updates that we've been seeing for the Dapper project that we'd like to share with the broader community and everyone and also talk about Plugable Components in Dapper, which we recently introduced in Dapper 1.9. And before we go into all the details, I really want to thank our amazing community contributors who really helped drive Plugable Components into Dapper. This is something we've been talking about, I think, since the very first versions of Dapper at Arthur, how we want to basically extend Dapper and make it Plugable so that people can bring in their own components. So thank you to all of our amazing contributors. And so let's talk a little bit about what Dapper is and what Dapper does. As an identification developer, writing a distributed system or a microservice architecture on top of Kubernetes or anywhere really, you might be writing something like this, right? You might have a bunch of services that communicate with each other. They might have a queue or a database they're writing to. You might have a secret store, a configuration store, you know, the underlying infrastructure for all of your applications. And it looks simple on the surface, but when you get into the integrated details of it, there are many, many distributed systems challenges that developers have to solve each and every day, really, with every new feature also. So things like how do you do state management from multiple replicas writing to the same record? How do you do conflict management at scale? How do you do error handling and fault resiliency? When you talk to your message bus, how do you delay messages being sent over an event-driven system? How do you secure messages not just between your service to service calls, but to your database and encrypt those connections and rotate these keys? So, lots of these distributed systems challenges are meeting developers. And as complexity rises, these challenges rise too. And so, the apper comes in as these set of APIs, distributed systems APIs, that allow developers to focus on their code, on their business logic, and really remove the boilerplate code associated with all of these hard problems. So, the apper can be invoked by HTTP or JRPC, so it's very inclusive to developers everywhere, if you're using a language that knows how to talk to HTTP or JRPC, you can use the apper. And we have a bunch of these APIs. We're not going to cover all of them today, but some things you can see here are service-to-service invocation, which is a way to find discover and secure communication calls, state management, which allow you to save state and get state in a reliable way, PubSub, which allows you to very easily create an event-driven system without all the boilerplate code and issues associated with it, resource bindings, which is a way to trigger your application based on events coming in from external systems. Actors, this is a very specific program model that distributes compute and state at a high granular rate. Observability and secrets are ways to basically observe everything that's going on in the apper, because the apper is at the intersection of all of your application communications. The apper basically observes everything for you, whether it reverses service-to-service calls or whether it's coming in from something like Kafka or AWS SQS, without you as a developer needing to take any SDKs and defenses into your code. And these APIs are being added by the community at a very active pace. And of course, the apper is not only tied to Kubernetes. Kubernetes is, of course, the best way to run the apper today, but the apper runs as a single binary on your laptop, whether you're using a Mac, a Linux, or a Windows machine. So really, the apper doesn't have you lifting for you, and this is how you use the apper to have your application process, the apper process, called a bunch of examples of how, for example, you would use the apper to discover a different application wherever it's running, just by its name, and invoke a method or get some state or publish a message to the orders topic, or get a secret from some secret store called Apple. So as a developer, you get these consistent APIs, and the apper basically makes sure that everything's secure, reliable, and traceable under the hoods. So at this point, you might think, well, does the apper replace it? Well, the answer is no, it doesn't. It integrates with over 105 components that represent the majority of services that you will find in clouds like AWS, Google Cloud, and Azure, and also open source ones. So these are just a few here on the right, but the apper has a very extensible model to it where you can come in to our repository, you can add in a different component, and when we launch the apper in 1.0, how many components do we have, like seven or eight, I think, and since then we, maintainers, did not contribute a single component. All of these components were contributed by our amazing community. So the apper really integrates, so as a developer, you let your developers talk to a very consistent and portable API, but as an operator, infrastructure person, you can decide what the underlying implementation is based on the environment that you're running in, and it's basically a YAML swap. But the apper is not only just a play in many cases, the apper will add features on top of the implementations that you won't find on their open source versions, or even their cloud versions. This is an update for the community traction that we're seeing. The apper today is the ninth project in CNCF based on the report. There was a release based in CNCF a month ago, I think it was. We're seeing lots of amazing companies contributing to the project. Yeah, we have over 2200 contributors today, and our Discord community is growing. Please join it after the talk. It'd be great to get your thoughts and feedback on the project. So this is really how apper components are plugged into our ontem today. So we have the apper binary, apper is written in Go, which means that every time you want to add a binary, sorry, a component to the apper binary, you need to go to our repository, you need to basically write the implementation in Go. It has to be Go because apper is written in Go. We will accept the PR, we'll approve it, we'll LGTM it, and then when the next version of apper ships, it'll get compiled into the apper binary. That's where we have over 100 different components. But many organizations actually need to extend apper for apper to be able to talk to systems that are proprietary to their own organizations, or if they have some kind of business logic that's NIP to their company and they can't actually contribute it upstream. So this is a use case that we're seeing and today it's very difficult adding these private components before version 1.9. So you would have to fork the main apper repository, you would need to fork the components contrib repository, get those local versions, you would need to write your custom component in Go, which means if you're an organization that doesn't have knowledge in Go, you need to learn Go, it's iLoveGo, it's my favorite language. No, that's not really more typescript, it's not my favorite language, stuff and you need to become an expert in it, it's kind of easy but you still need to learn Go, which is a major hurdle for many organizations and then once you have your component set up, you basically need to integrate it into dapper, build your custom binary, if you're building a custom container, you need to go to the dapper settings and basically pull off your version of the image. So this is the configuration of the environment really. So it's kind of a convoluted process but now in 1.9 we have a concept of plugable components. You can write a component in any language that supports VRPC and then you just need to package that into a container or a process depending on whether or not you're running dapper in a containerized environment or not and then you just deploy your application code so this becomes really easy. The core design plans for us are of course to be secure in performance, so we use Unix domain sockets so you can't invoke these components from outside of your local network namespace whether you know it's your VM or a pod on Kubernetes. We're leveraging RPC standards so there's no new RPC framework here, we're just leveraging Google DRPC which is fully supported, no limitations there on any DRPC features. There's very low operational overhead because we are leveraging existing dapper CRDs. So if you're familiar with the concept of CRD, it's basically a Kubernetes resource that allows you to extend Kubernetes and many projects that you will find in Kubernetes have 10, 20, 30, some of them even have 80 CRDs and that adds a lot of operational overhead. In dapper today we have four CRDs and so we did not add a new one to add pluggable components, we're basically reusing our same component CRD. And then it's platform agnostic, we make sure that everything we do in dapper is compliant both to Kubernetes and outside Kubernetes and so yes, pluggable components run inside of Kubernetes and outside of it. So the thing we ruled out was to use Go plugins because it requires the use of Sego and one of the founders and creators of the Go project, the Go runtime, basically said that Sego is not Go. If you Google Sego is not Go, you will find the article that explains why I'm not going to go into details here, but it's basically causing lots of cross-compilation issues. We'd basically be telling every Windows developer out there, typically, or on Windows machines, and there's low performance. So it takes the performance in. And then there's also some dependency management in your host processing, the plugin. If you're referencing other packages, they must have the same version and that creates version and conflict management and that becomes a real issue and then of course, the language support they talked about earlier. So this is the user experience, basically. This is 80% of the user experience. So this is what a component CRD in dapper looks like. You have that type there, which is statemydb. That's a custom pluggable component. I don't know if you know how dapper CRDs look like. We probably should have put an image here. But it would basically change type state.mydb to something like state.aws.sqs. If this was a component that's built into dapper that you get with the dapper stable release. And so, just by putting statemydb here, we're basically telling dapper, hey, this is my own custom component and you can have your own metadata which really targets whatever underlying system you might be writing this component for. And we use a domain socket. So we basically, the dapper runtime looks at the local file system. It searches for a local domain socket and then it basically looks for other processes that register to it, which would be the pluggable components. And this is really what it looks like. So you have the dapper D binary here on the left and there is a discovery mechanism which will basically listen to sockets on the file system. And this file system can be mounted on Kubernetes. It can be an in-memory file system too, by the way. And then you have the pluggable component process, which is a totally separate process to the dapper D one. It can be a separate container inside of your deployment or if running dapper outside of Kubernetes, it can just be a process co-located inside of your VM. And the important thing is that they both have access to the mounted file system that you're giving it so that the dapper can basically discover these pluggable components. Once the dapper has discovered the socket and the pluggable component has listened to the socket, they will basically connect and the pluggable component will connect to the dapper by a JRPC. So the dapper will basically use JRPC reflection, the JRPC reflection API to talk to the component and discover all of its different properties and make sure that it's actually a dapper pluggable component to not just something that's not compliant to the interface that the dapper expects. So this is basically how it works. And adding private components now in stand-alone mode is very simple. You basically just give your pluggable component the unix domain socket patch. You can see it here at unix temp dapper component socket and then the component will basically just listen on the JRPC socket there. It's very simple. This is how it would look like in Kubernetes. This is what the dapper D site will show once it discovered the pluggable component and successfully register it. That's the line right there. The line at the top is basically a dapper CLI command. We have developer tooling. A dapper will take basically your application, the dapper site can run it together and dapper will successfully register the pluggable component if it was able to discover it via JRPC reflection as I mentioned earlier. Adding components in Kubernetes looks like this. So the way to inject dapper into a deployment YAML is basically to annotate it with a bunch of annotations which you can see on the bottom there. You go dapper IO enable true which means dapper hey I want you to inject dapper sidecar using your mutating webhook and then you have all other every other annotation that you can think of like an app ID to give your application an ID and app port. And the very last one at the bottom is the unix domain socket path. So inside of your application you will mount a volume which will be used to host the socket that both the pluggable component and dapper listen on. So it's a very nice user experience. Inside of your deployment YAML that's something that's a little less nice but we're working on improving that experience. That's what you would need to do on your deployment side. You of course have your pluggable components. You built it into a Docker image, you pushed it into registry and so you're adding it as an additional container inside of your deployment inside of your pod spec. So you're mounting the volume. It's all pretty easy stuff. Standard here. Nothing special that you need to do. It's all documented on our website and this makes up the user experience of how you would go about communicating and adding these pluggable components. Now I'm going to hand it over to Arthur. Thank you. You can hear me? Awesome. So for probably this demo we're going to use Discord. And if you have Discord on your phone you'll be able to interact with the demo. If you don't have it when it's installed, you can take your time to install it out. So let's get started with the demo. First of all, the code is available online. So you can go to GitHub with this URL here. It might not be so readable but basically it's called NA. Can you zoom in a little? Oh, I can zoom in right here. There you go. So you can go in and it's public and you can go through and try the demo yourself. Okay? And we're going to actually do this demo together here right now. So to do this demo you have to use basically havemake to compile and install it up for CLI if you don't have one. And you can also use to install the gRPC tool for local service invocation. And there's some other dependencies here depending on which part of the demo you're going to take. So I have to check out the code already and then there's existing code for the mem store. So the same example that we saw on this slide, there's some echo corner here. We'll just simply do a .NET run but before we do that, let's look at the code. So let me zoom in one more time. There you go. So if I look at our Plugable Components folder, you're going to see the mem store which is basically a gRPC service in .NET. Program.cs basically handles the sockets. So in this case we're going to put the socket in slash dmp, handle the socket creation. And then we're going to configure the socket with adding the gRPC listening and in this we also need the gRPC reflection. So whatever language you use to implement a Plugable Component for the upper, we require the gRPC reflection because that's the way we found to not require a new CRD and make damper, sci-car automatically discover the components that are listed in the socket directory. So every socket in the directory will be scanned by the sci-car and are going to invoke the reflection API for each one of them to auto discover what type of component that is. So that's how we could bring in that simplicity and put us through Marcos and Yadam for having that idea and working on that change. Okay? And then this is just handling the socket file. One thing that I want to call out is you can see this as an example but as we work on that we want to make that even easier and we are now writing new SDKs for damper to compose components. So now you're not only going to have SDKs to use the damper APIs but you're also writing SDKs for it to make it very easy for you to write any component in the languages in the most popular languages that we have which are .NET, Java, Python and Go. These are the SDKs that are going to come up. And now I'm going to look at and show you how the service is implemented. So the mem store basically this interface Sorry, Arthur, just one comment there. Although we are writing these SDKs to make it easier for you to write plugable components it's important for me to call out that you can write them in any language even without the SDKs you just need to do a little bit of JRPCE plumbing which isn't really a problem. Yeah, thanks for the call out. So these are the interface that you're going to implement based on the JRPCE service you're offering and you can do like the get to get a key set to set a key both set to handle a bulk request so init method is the one that the sidecar will invoke passing the metadata that you had on show previously on the slide. So all those metadata attributes is per component some components like the mem store for example that I've implemented doesn't require any metadata attribute you could pass like a URL you could even pass a secret if you want and you use a secret reference a feature that DAPR has if you're not familiar with that I recommend taking a look later and of course ping for the health check of your components so with that we can go to the folder plugable components mem store and then we do dotnet run there we go one thing I'm going to show is I'm going to show that in the DAPR CLI it stores a folder for you with components and we change the mem store YAML file to handle the mem store so like you can see it's as simple as that doesn't require any metadata and in this case I'm not going to be using radius we're going to be using the mem store okay now if I go back to our demo list we can go now to the hello word on quick starts and we're going to run the same quick start that I don't know if you have tried DAPR already but this is a usual quick start for hello word to make invocations so we're going to go to different folder and just going to do npm install actually I've done this before it should be fast and then we're going to be able to run the Node.js application so the Node.js application if you haven't seen this example before all it does is receive the call from the Python application that we're going to run in a minute and save that to a stage store as simple as that so I'm running this in the new terminal and we see the mem store detected okay and I'm trying to look for the message now but let's just proceed to the next step of the demo where we're going to run now we can invoke actually this service and see okay what Arthur is showing right now are two applications the Python app invoking a Node.js app through DAPR so it's invoking with DAPR directly and so you get MTLS observability to entry all of the DAPR features so we're going to get that request from the Python app through DAPR and save it to the stage store which is the custom plugable component which isn't compiled in the DAPR bindings so in this example I ended up running the Python bindings but I did use the DAPR invoke feature to make the invocation and you can see here that the state was saved got a new order there you go and the state was persisted so now let's look at a more cool demo this is just for you to get started on local development so this is I'll show the other demo now so if you have seen the Twitter processing demo in the past, there are multiple demos online you can see that it basically uses DAPR with the Twitter input binding to receive tweets you make a service invocation to the settlement processor to the settlement analysis you annotate the payload with that extra information saves to a stage store publishes to a PubSub topic and there's another application with a dashboard that consumes those tweets with the settlement analysis results and displays on the dashboard so we're going to do this we did the demo with a ready running but I'm going to go through some of the steps with you guys so this is also a time if you want to interact download to connect to our Discord server for the demo so if you have Discord I'll give you a few minutes so what we did is we took the Twitter binding and we wrote a custom component in Java that would instead take messages from Discord so I don't know if you follow TechCrunch but today was announced that Twitter has a new owner so how convenient my demo to replace Twitter by True Discord so some news there so the Twitter is going to be basically Discord so we're going to show the QR code one more time anybody else need to scan this? okay so I have this demo running for a few days now as you can see and I'm going to do pawns without getting all namespaces so you can have a clear view of what's going on so what you will see is that the processor is the one that does the settlement analysis and still only has two sidecars but if you look at the provider it now has three sidecars three containers two of them are different sidecars one sidecar for dapper and one for the pluggable component so if I do logs on this particular pod you're going to show that you have provider which is a name of the application Discord and dapperd and then you can put Discord here and you're going to see that we already got some messages but thanks for that so the demo is already working as we can all see and dapperd are you going to see anything interesting here? yeah it shows that we detected one binding component okay awesome so we are all up and running but also going to do a describe so we're going to describe the deployment and one thing you will see is that you have the same annotations that Yauta mentioned in this case we'll use a custom image but that's just a detail for this demo and don't really require that and you have the Unix domain socket path the same way that the application requires okay now that we have done that let's go to the demo before I show you more of the code so let's do a port forward and then we're going to do port 8080 didn't make any typo there you go now I'm going to open my browser I keep forgetting when my browser is that's my usual problem there you go there you go so now if people want to try it out send some messages we can see them here so please be nice and don't send angry messages there you go so you still see the Twitter logo because that's how the demo was originally built for but it's all coming from this cord now awesome thank you we're not done yet there's more yeah I didn't thank you we're engaging them okay and now we're going to see some of the code that we did to get this done so you saw in .NET memstore this one's a little bit more complicated it uses the input binding so we have to stream the messages from this cord API into the sidecar this code a little bit so the server does the same thing as the .NET equivalent where it basically manages the socket for you like how do you start this on the socket how you change the permission to make sure another process can read and write to it so this code is what we think it should belong in an SDK that makes things easier for you to get a specific forest to handle here but then the core of the code is actually in the discord binding implementation where you basically have the .NET that we discussed and in this example we do get a token so I'm going to show you in a minute so we do actually get the token to connect to discord through the metadata and it's not hard coded at all but you still get it here for in your code so and then we start the client and then we respond to the message we also have the ping and we also have the read so the read we basically starts in this case is using the discord SDK behind the scenes I don't even know if there's a discord maybe there is but and then we're going to keep forward in the messages to make sure it's working and then we're going to create a payload following the same structure as the Twitter binding was working before to make sure we keep the compatibility so we can remove one and put the other one in place with no code change in the rest of the code and then those messages are going to be sent in the response observer so this is how we wrote a binding component in Java and I'm going to show you in a moment right now how does the component actually look like let me just move here so all the Kubernetes artifacts you need for this demo are available in the repository as well so I'm going to open up the discord example but even the mem store example you can run the Kubernetes as well if you want a simpler easier way to get it working so as you can see we have the metadata it has the token and this is a nice feature that DAPR has for those not familiar with that DAPR has a secret store and our components are integrated natively with that and also in this case I'm using the Kubernetes secret itself and you can see how I referenced that without having to hard code the token on my demo so you can these are examples of how to get the discord token if you want to redo this yourself but that's how we manage secrets the configuration I added and you use a new feature here but that's a different conversation the processor this is the same way we use for the original Twitter demo and in this case I'm using Azure using Azure Cognitive Services and you can use that yourself or if you want you can change the code to not use Azure and just do a random settlement analysis in the code the provider that will actually use the Plugable Component and like we saw on Kubernetes it has the cluster image but it has also the domain socket path and it has the container for the provider and the container for discord and you can use these images as well from Docker Hub the images are built with this same code and then PubSub has no difference is the usual PubSub again using the logic reference in this case there's nothing different in particular for this demo for that the state store also Redis and the view application also just the same as we had before there was no change here so yeah, that's all I have thank you everyone thank you for coming and we are here for questions oh that's a good idea you're gonna get a dapper mug now for asking questions well thank you that was an excellent talk I appreciated the description of how dapper provides APIs I was wondering for those who aren't familiar could you go over the merits of the PubSub architecture and then as well could you briefly describe the GRPC reflection I'm unfamiliar describe the what, sorry the GRPC reflection API so I'll start with the PubSub question so your question was what are the merits of the PubSub API yeah, so many times you would want to create an Avenger of an architecture where you publish a message and these publishers are decoupled from subscribers and it's very hard to create it on your own because if you have multiple subscribers you need to basically handle scale how do you handle error handling or throttling from the publisher if you're a subscriber you need to start learning about consumer groups and partitions and how you listen on these and handle errors and faults coming in from these concepts and so dapper really encapsulates all of that for you it creates consumer groups, it creates partitions it configures them, the only thing you need to do as a publisher is just reach through the dapper API and say hey dapper publish this message and as a subscriber you have an HTTP endpoint that you put in your app dapper will send you the message on your application code or if you're using gRPC it'll send it back the gRPC Reflection API is something that we only use for plugable components and that's basically a way for gRPC services to discover properties or characteristics of other gRPC services and so dapper will basically use this reflection API to reflect on the properties of the plugable component and see that it answers all of its requirements Alright, more questions? There's one over there Hi, I want to know how long a particular message can stay in the queue before it is consumed by another component That depends on the component so that's a configuration that you can have in your Redis or Azure Service Bus but dapper can also add a feature on top which is the message TTL you can put a metadata parameter that says this message is in the queue only for, let's say, one day and then the dapper side card that consumes that we understand that and don't send that to the application and you can also bundle that feature with that letter Q that letter Q via dapper which is basically another pop-up component so you can have that done all within the dapper layer itself or if you want you can also do the same with the broker that you chose if it's a native feature There's one more follow-up question so I'm thinking about this in the context of workflows so is there a way to cancel a job which is running for too long for example, a dapper endpoint which is processing for a very long time? Not right now but we are going to add workflows as code to dapper it's a community proposal that's being discussed right now and so you will get these types of APIs and operations in dapper Thank you Great question Come get your mug after Oh yeah, you asked a question, get a mug Actually everyone will get mugged if you come fast enough after we're done I have four mugs total so the first four questions only I have one that thinks a basic question but I might have missed it to consume the dapper API in your application do you have client libraries to include that packet and then locally you're just doing REST or GRPC to the sidecar? Yes, actually to SDK most of them I think you use GRPC Awesome which is faster Thank you Great, thank you everyone Thanks again