{"id":78543,"date":"2019-11-15T07:30:09","date_gmt":"2019-11-15T15:30:09","guid":{"rendered":"https:\/\/cloudblogs.microsoft.com\/opensource\/?p=78543"},"modified":"2025-06-27T04:40:31","modified_gmt":"2025-06-27T11:40:31","slug":"in-cluster-cnab-management-brigade","status":"publish","type":"post","link":"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/","title":{"rendered":"In-cluster CNAB management with Brigade"},"content":{"rendered":"\n<p>Next week is KubeCon North America 2019, but we wanted to give you an early preview of one of the things we\u2019ll be showing. Over the last few years, we\u2019ve been working on tools for the cloud native ecosystem. From&nbsp;<a href=\"https:\/\/helm.sh\" target=\"_blank\" rel=\"noopener noreferrer\">Helm<\/a>&nbsp;and&nbsp;<a href=\"https:\/\/brigade.sh\" target=\"_blank\" rel=\"noopener noreferrer\">Brigade<\/a>&nbsp;to&nbsp;<a href=\"https:\/\/porter.sh\" target=\"_blank\" rel=\"noopener noreferrer\">Porter<\/a>&nbsp;and&nbsp;<a href=\"https:\/\/openappmodel.io\/\" target=\"_blank\" rel=\"noopener noreferrer\">Rudr<\/a>, each tool we have built is designed to stand on its own. But our vision has always been that the tools could be combined to create things larger than the sum of their parts. In today\u2019s post, I will show how Porter and Brigade can be combined to make a Kubernetes controller for deploying CRDs.<\/p>\n\n\n\n<p><strong>Brigade<\/strong>&nbsp;is a tool for constructing worklows in Kubernetes using JavaScript. It\u2019s a limitless way to build pipelines, whether they\u2019re for CI\/CD or for larger scale data processing.<\/p>\n\n\n\n<p><strong>Porter<\/strong>&nbsp;is a cloud installer that helps you create, install and manage bundles based on the&nbsp;<a href=\"https:\/\/deislabs.io\/cnab\/\" target=\"_blank\" rel=\"noopener noreferrer\">CNAB specification<\/a>. Cloud Native Application Bundles is part of our ongoing efforts to provide a standard way for packaging and deploying cloud native applications.<\/p>\n\n\n\n<p>A moment of inspiration led us to try out an interesting experiment: Could we use Brigade to build a Kubernetes controller, and then use Porter as a backend to that controller. And in so doing, could we create a CNAB controller without writing&nbsp;<a href=\"https:\/\/github.com\/kubernetes\/sample-controller\" target=\"_blank\" rel=\"noopener noreferrer\">massive amounts of Go code<\/a>. At KubeCon, we\u2019re excited to be showing the outcome of this experiment.<\/p>\n\n\n\n<p>In the end, our effort boiled down to two parts:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Add a Brigade gateway that could act as a Kubernetes controller front-end. We call this project\u00a0<a href=\"https:\/\/github.com\/brigadecore\/buck\" target=\"_blank\" rel=\"noopener noreferrer\">Buck<\/a>, and you can use it today.<\/li>\n\n\n\n<li>Write a small JavaScript program that invokes Porter to handle individual events. The code for this is\u00a0<a href=\"https:\/\/github.com\/technosophos\/buck-cnab\" target=\"_blank\" rel=\"noopener noreferrer\">available on GitHub<\/a>, too.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Buck-The-Brigade-Universal-Controller-for-Kubernetes\">Buck: The Brigade Universal Controller for Kubernetes<\/h2>\n\n\n\n<p>Brigade follows a simple and extensible pattern. It has a central controller that handles executing workflows like this:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Listen for new events via\u00a0<em>gateways<\/em><\/li>\n\n\n\n<li>Delegate event handling to\u00a0<em>workers<\/em><\/li>\n\n\n\n<li>Let the workers spawn and manage a\u00a0<em>workflow<\/em>\u00a0of\u00a0<em>jobs<\/em>.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/11\/brigade-diagram-1024x440.webp\" alt=\"Diagram of Buck: The Brigade Universal Controller for Kubernetes\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>The typical Brigade example is its&nbsp;GitHub integration: When a new Pull Request is opened on GitHub, the Brigade GitHub gateway triggers an event. A new worker is created, and the event information is passed to it. The worker then starts up jobs to run unit tests, build binaries, and so on.<\/p>\n\n\n\n<p>We wanted to see if this pattern could be adapted to Kubernetes controllers, which are already centered on the concept of events.<\/p>\n\n\n\n<p>A Kubernetes controller frequently listens for&nbsp;<em>custom resources<\/em>&nbsp;(as created by Custom Resource Definitions, or CRDs). A custom resource is a special type that is not part of Kubernetes\u2019 core, but is user-defined.<\/p>\n\n\n\n<p>When the Kubernetes API receives a request to work with a custom resource, it sends a notification event:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>If the resource is new, a \u201cresource added\u201d event is created<\/li>\n\n\n\n<li>If the resource is a new version of an existing resource, a \u201cresource modified\u201d event is created<\/li>\n\n\n\n<li>Finally, if the request is for a deletion, Kubernetes will first delete the resource definition, and then send a \u201cresource deleted\u201d event<\/li>\n<\/ul>\n\n\n\n<p>This kind of event system is very easy to implement in Brigade.<\/p>\n\n\n\n<p>We\u2019ve recently transitioned a lot of our coding efforts from Go to&nbsp;<a href=\"https:\/\/www.rust-lang.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">Rust<\/a>. This project seemed like a great opportunity to showcase how powerful Rust\u2019s Kubernetes libraries are. So with around 100 lines of Rust code, we created a Brigade gateway that can accept a custom resource name and then listen on the Kubernetes event stream for events related to that custom resource. Since it can be used to attach to any Kubernetes resource type, we named it the&nbsp;<em>Brigade Universal Controller for Kubernetes (BUCK)<\/em>. And we built it specifically to be a tool for rapidly building Kubernetes controllers.<\/p>\n\n\n\n<p>When Buck receives an event for its custom resource, it notifies Brigade, which creates a new worker. And that worker is handed the custom resource (in JSON form) as well as the name of the event that triggered it.<\/p>\n\n\n\n<p>Implementing a new controller, then, is as simple as this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nconst { events } = require(\"brigadier\");\n\nevents.on(\"resource_added\", handle);\nevents.on(\"resource_modified\", handle);\nevents.on(\"resource_deleted\", handle);\n\nfunction handle(e, p) {\n    let obj = JSON.parse(e.payload); \/\/ <-- your Kubernetes object\n    console.log(obj);\n}\n\n<\/pre><\/div>\n\n\n<p>The above prints the custom resource that it receives for any Buck event.<\/p>\n\n\n\n<p>To round out the work on Buck, we built a&nbsp;<a href=\"https:\/\/helm.sh\" target=\"_blank\" rel=\"noopener noreferrer\">Helm<\/a>&nbsp;chart that can help you define a custom resource and deploy the appropriate Buck gateway all at once.<\/p>\n\n\n\n<p>With Buck complete, the next step was to write a CNAB controller in JavaScript, writing a more robust tool based on the script above.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Authoring-the-Porter-Brigadejs\">Authoring the Porter Brigade.js<\/h2>\n\n\n\n<p>Once we were receiving events for our custom resource, all we really wanted to do to implement a CNAB controller was pass the event data to Porter. Then Porter could manage the lifecycle of the bundle.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"Step-1-Model-a-CNAB-installation\">Step 1: Model a CNAB installation<\/h3>\n\n\n\n<p>The first step to accomplishing this goal was to define a Porter action as a Kubernetes custom resource. We ended up with a fairly simple YAML file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\napiVersion: cnab.technosophos.com\/v1\nkind: Release\nmetadata:\n  # This will be used as the name of the install\n  name: cowsay\nspec:\n  # the bundle to be pulled from an OCI repository\n  bundle: technosophos\/porter-cowsay:latest\n  # the VALUES to be supplied to parameters defined on the bundle\n  parameters:\n    - name: install_message\n      value: Moooo\n    - name: uninstall_message\n      value: Baaaah\n  credentials:\n    - name: bogo_token\n      value: bogo_value\n\n<\/pre><\/div>\n\n\n<p>The example above describes a CNAB installation:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Work with an installation named\u00a0<code>cowsay<\/code><\/li>\n\n\n\n<li>Install the CNAB called\u00a0<code>technosophos\/porter-cowsay:latest<\/code><\/li>\n\n\n\n<li>Set the\u00a0<code>install_message<\/code>\u00a0parameter to\u00a0<code>Moooo<\/code><\/li>\n\n\n\n<li>Set the\u00a0<code>uninstall_message<\/code>\u00a0parameter to\u00a0<code>Baaaah<\/code><\/li>\n\n\n\n<li>Supply a\u00a0<code>bogo_token<\/code>\u00a0credential with the value\u00a0<code>bogo_value<\/code><\/li>\n<\/ul>\n\n\n\n<p>If the above YAML is created, the appropriate bundle will be installed. When the above YAML is modified and re-submitted, the CNAB installation is upgraded. Of course, when the above is submitted as a deletion, the CNAB is uninstalled.<\/p>\n\n\n\n<p>With that done, it was time to move on to configuring Porter.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"Step-2-Package-up-Porter-in-a-Docker-image\">Step 2: Package up Porter in a Docker image<\/h3>\n\n\n\n<p>Porter is typically executed by users on the command line. But to make it accessible inside of a Brigade job, we needed to pack it inside of a Docker image.<\/p>\n\n\n\n<p>I wanted to make it possible to do more with this gateway in the future, so I made sure to build Porter's mixins as well as add some other useful tools:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nFROM docker:dind\n\nENV HELM_VER 2.12.3 \n\nRUN apk add \\\n    ca-certificates bash curl && \\\n    curl https:\/\/deislabs.blob.core.windows.net\/porter\/latest\/install-linux.sh | bash && \\\n    mkdir -p \/porter\n\nENV PATH=\"$PATH:\/root\/.porter\"\n\nWORKDIR \/porter\n\n<\/pre><\/div>\n\n\n<p>There is one&nbsp;<em>very important<\/em>&nbsp;feature of the Dockerfile above, though: It uses the base image&nbsp;<code>docker:dind<\/code>. This is the \u201cDocker in Docker\u201d image. It allows us to execute Docker operations from within a Docker image. Since Porter will be managing the installation of one or more Docker images, we need the Docker in Docker image.<\/p>\n\n\n\n<p>I pushed the image created by that Dockerfile off to Docker Hub, naming it&nbsp;<code>technosophos\/porter:latest<\/code>.<\/p>\n\n\n\n<p>The next step was to write a Brigade script that answered each event by invoking Porter.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"Step-3-Write-a-brigadejs-script\">Step 3: Write a&nbsp;<code>brigade.js<\/code>&nbsp;script<\/h3>\n\n\n\n<p>The most exciting part of our project was writing a Brigade script that could take the Porter image and execute it each time it received an event from Kubernetes. The resulting script was only about 50 lines long. Here I will break it down into a few chunks and explain what we did.<\/p>\n\n\n\n<p>First, we started by reading the parameters and credentials from the custom resource:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nconst { events, Job } = require(\"brigadier\");\n\nevents.on(\"resource_added\", handle);\nevents.on(\"resource_modified\", handle);\nevents.on(\"resource_deleted\", handle);\nevents.on(\"resource_error\", handle);\n\nfunction handle(e, p) {\n    console.log(`buck-porter for ${e.type}`)\n    let o = JSON.parse(e.payload);\n    console.log(o);\n\n    let args = [];\n    o.spec.parameters.forEach(pair => {\n        args.push(`--param ${pair.name}=\"${pair.value}\"`);\n    });\n\n    let creds = [];\n    o.spec.credentials.forEach(cred => {\n        creds.push({ name: cred.name, source: { value: cred.value } })\n    });\n    let credentials = JSON.stringify({ credentials: creds });\n    console.log(`Credentials: ${credentials}`);\n    \/\/...\n}\n\n<\/pre><\/div>\n\n\n<p>The&nbsp;<code>payload<\/code>&nbsp;holds the custom resource that we received from Brigade. We parse that, and then look through the&nbsp;<code>parameters<\/code>&nbsp;and&nbsp;<code>credentials<\/code>&nbsp;sections to get our configuration data. As a reminder of what data this script is fetching, take a look at the&nbsp;<code>parameters<\/code>&nbsp;and&nbsp;<code>credentials<\/code>&nbsp;sections in the YAML:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\napiVersion: cnab.technosophos.com\/v1\nkind: Release\nmetadata:\n  # This will be used as the name of the install\n  name: cowsay\nspec:\n  # the bundle to be pulled from an OCI repository\n  bundle: technosophos\/porter-cowsay:latest\n  # the VALUES to be supplied to parameters defined on the bundle\n  parameters:\n    - name: install_message\n      value: Moooo\n    - name: uninstall_message\n      value: Baaaah\n  credentials:\n    - name: bogo_token\n      value: bogo_value\n\n<\/pre><\/div>\n\n\n<p>Next, we needed to figure out which event type had just been triggered, and set the CNAB action accordingly. For example,&nbsp;<code>resource_added<\/code>&nbsp;needed to be translated to the&nbsp;<code>install<\/code>&nbsp;action:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nconst { events, Job } = require(\"brigadier\");\n\n\/\/...\n\nfunction handle(e, p) {\n    \/\/ ...\n    let action = \"version\";\n    switch (e.type) {\n        case \"resource_added\":\n            action = \"install\";\n            break;\n        case \"resource_modified\":\n            action = \"upgrade\";\n            break;\n        case \"resource_deleted\":\n            action = \"uninstall\";\n            break;\n        default:\n            console.log(\"no error handler registered\");\n            return;\n    }\n    \/\/ ...\n}\n\n<\/pre><\/div>\n\n\n<p>At this point, we knew which action to run, and what parameters to send. So in the last step, we just needed to run a Porter job with that information:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nconst { events, Job } = require(\"brigadier\");\n\n\/\/ ...\n\nfunction handle(e, p) {\n    \/\/ ...\n    \n    let cmd = `porter ${action} ${o.metadata.name} --tag ${o.spec.bundle} --force ${args.join(\" \")} -c buck`;\n    let porter = new Job(\"porter-run\", \"technosophos\/porter:latest\");\n    porter.tasks = [\n        \"dockerd-entrypoint.sh &\",\n        \"sleep 20\",\n        \"mkdir -p \/root\/.porter\/credentials\",\n        \"echo $CREDENTIALSET > \/root\/.porter\/credentials\/buck.yaml\",\n        `echo ${cmd}`,\n        cmd\n    ];\n    porter.privileged = true;\n    porter.timeout = 1800000; \/\/ Assume some bundles will take a long time\n    porter.cache = {\n        enabled: true,\n        size: \"20Mi\",\n        path: \"\/root\/.porter\/claims\"\n    };\n    porter.env = {\n        CREDENTIALSET: credentials\n    };\n\n    return porter.run();\n}\n\n<\/pre><\/div>\n\n\n<p>Here, we create a new job named&nbsp;<code>porter<\/code>, using the Porter Docker image we created earlier. We give it a list of tasks to run. In that list, we start the Docker-in-Docker process, create a credentials file, and then run the&nbsp;<code>porter<\/code>&nbsp;command.<\/p>\n\n\n\n<p>Before we can&nbsp;<code>run<\/code>&nbsp;the job, we have to set up a few more things:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The job has to run in\u00a0<code>privileged<\/code>\u00a0mode because it needs to execute Docker-in-Docker (which can represent a security risk in a multi-tenant cluster)<\/li>\n\n\n\n<li>Since bundles can take a long time to install if they are creating things like virtual machines or databases, we set a long timeout<\/li>\n\n\n\n<li>We want Porter to have a place to store state information, so we mount a cache that will be shared across all porter builds.<\/li>\n\n\n\n<li>Finally, we set up the\u00a0<code>CREDENTIALSET<\/code>\u00a0environment variable<\/li>\n<\/ul>\n\n\n\n<p>With all of that in place, the final step is to execute&nbsp;<code>porter.run()<\/code>.<\/p>\n\n\n\n<p>As Brigade receives new events for our custom resource, it executes this script and then waits for the script to complete. Brigade\u2019s&nbsp;<a href=\"https:\/\/cloudblogs.microsoft.com\/opensource\/2017\/12\/06\/kashti-kubernetes-open-source-microsoft\/\" target=\"_blank\" rel=\"noopener noreferrer\">Kashti UI<\/a>&nbsp;can be used to watch the status of these events in near real time.<\/p>\n\n\n\n<p>With the&nbsp;<a href=\"https:\/\/github.com\/technosophos\/buck-cnab\/blob\/master\/brigade.js\" target=\"_blank\" rel=\"noopener noreferrer\">completed script<\/a>, we had a function Kubernetes controller for CNAB written in JavaScript.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"What-Next\">What Next?<\/h2>\n\n\n\n<p>There are a couple of things we\u2019d like to do better, and will keep working on:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>It would be nice to add some code to Buck that could write a\u00a0<code>status<\/code>\u00a0object into the custom resource, as many built-in Kubernetes controllers do.<\/li>\n\n\n\n<li>Right now, credential handling is fairly minimal. It would be better to store credential data in a Kubernetes secret instead of in the custom resource.<\/li>\n\n\n\n<li>Brigade allows us to run continuous state resolution (or even automatic upgrades) by using the Cron gateway to periodically run a <code>brigade.js<\/code>. That would be a cool way to make sure our CNAB stays up-to-date.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Conclusion\">Conclusion<\/h2>\n\n\n\n<p>We set out to build Buck as a means of rapidly building Kubernetes controllers. And we\u2019ve been asked before whether it would be possible to manage CNAB bundles from within a Kubernetes cluster. So this little project was born of our desire to show how flexible tools can be assembled into interesting higher-order applications with only tiny amounts of code.<\/p>\n\n\n\n<p>While our example isn\u2019t a production-ready solution, we are excited that it demonstrates the first steps in that direction. And we hope that this example inspires others to build their own prototype Kubernetes controllers, as well as use CNAB to package their cloud native applications.<\/p>\n\n\n\n<p>To learn more about Brigade, you can head over to&nbsp;<a href=\"https:\/\/brigade.sh\" target=\"_blank\" rel=\"noopener noreferrer\">the main site<\/a>&nbsp;or visit one of our talks at KubeCon (Brigade is an official CNCF project). There is an&nbsp;<a href=\"https:\/\/kccncna19.sched.com\/event\/Uahq\/intro-brigade-matt-butcher-radu-matei-microsoft?iframe=no&amp;w=100%25&amp;sidebar=yes&amp;bg=no\" target=\"_blank\" rel=\"noopener noreferrer\">intro session<\/a>&nbsp;as well as a&nbsp;<a href=\"https:\/\/kccncna19.sched.com\/event\/Uafl\/deep-dive-brigade-matt-butcher-kent-rancourt-microsoft?iframe=no&amp;w=100%25&amp;sidebar=yes&amp;bg=no\" target=\"_blank\" rel=\"noopener noreferrer\">deep dive<\/a>&nbsp;If you would like to use Buck to create your own Kubernetes controller, the&nbsp;<a href=\"https:\/\/github.com\/brigadecore\/buck\/\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub repository<\/a>&nbsp;is a great place to start.<\/p>\n\n\n\n<p>And to learn more about CNAB, you can start with&nbsp;<a href=\"https:\/\/cnab.io\" target=\"_blank\" rel=\"noopener noreferrer\">the specification<\/a>&nbsp;or by trying the&nbsp;<a href=\"https:\/\/porter.sh\/quickstart\" target=\"_blank\" rel=\"noopener noreferrer\">Porter QuickStart<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Next week is KubeCon North America 2019, but we wanted to give you an early preview of one of the things we\u2019ll be showing. Over the last few years, we\u2019ve been working on tools for the cloud native ecosystem. From&nbsp;Helm&nbsp;and&nbsp;Brigade&nbsp;to&nbsp;Porter&nbsp;and&nbsp;Rudr, each tool we have built is designed to stand on its own.<\/p>\n","protected":false},"author":5562,"featured_media":95468,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"msxcm_post_with_no_image":false,"ep_exclude_from_search":false,"_classifai_error":"","_classifai_text_to_speech_error":"","footnotes":""},"post_tag":[158,2272],"content-type":[361,340],"topic":[2241,2242],"programming-languages":[],"coauthors":[687],"class_list":["post-78543","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-kubernetes","tag-microsoft","content-type-project-updates","content-type-tutorials-and-demos","topic-cloud","topic-containers","review-flag-1593580428-734","review-flag-1593580415-931","review-flag-1593580771-946","review-flag-1-1593580432-963","review-flag-2-1593580437-411","review-flag-3-1593580442-169","review-flag-alway-1593580310-39","review-flag-bundl-1593580256-792","review-flag-bundl-1593580260-199","review-flag-new-1593580248-669"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>In-cluster CNAB management with Brigade | Microsoft Open Source Blog<\/title>\n<meta name=\"description\" content=\"Learn more about how to use Kubernetes projects, including Porter and Brigade, together to make a Kubernetes controller for deploying CRDs.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"In-cluster CNAB management with Brigade | Microsoft Open Source Blog\" \/>\n<meta property=\"og:description\" content=\"Learn more about how to use Kubernetes projects, including Porter and Brigade, together to make a Kubernetes controller for deploying CRDs.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/\" \/>\n<meta property=\"og:site_name\" content=\"Microsoft Open Source Blog\" \/>\n<meta property=\"article:published_time\" content=\"2019-11-15T15:30:09+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-27T11:40:31+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO20b_Jayesh_office_001.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1170\" \/>\n\t<meta property=\"og:image:height\" content=\"640\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Matt Butcher\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@OpenAtMicrosoft\" \/>\n<meta name=\"twitter:site\" content=\"@OpenAtMicrosoft\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Matt Butcher\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 min read\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2019\\\/11\\\/15\\\/in-cluster-cnab-management-brigade\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2019\\\/11\\\/15\\\/in-cluster-cnab-management-brigade\\\/\"},\"author\":[{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/author\\\/matt-butcher\\\/\",\"@type\":\"Person\",\"@name\":\"Matt Butcher\"}],\"headline\":\"In-cluster CNAB management with Brigade\",\"datePublished\":\"2019-11-15T15:30:09+00:00\",\"dateModified\":\"2025-06-27T11:40:31+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2019\\\/11\\\/15\\\/in-cluster-cnab-management-brigade\\\/\"},\"wordCount\":1770,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2019\\\/11\\\/15\\\/in-cluster-cnab-management-brigade\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/wp-content\\\/uploads\\\/2024\\\/06\\\/CLO20b_Jayesh_office_001.webp\",\"keywords\":[\"Kubernetes\",\"Microsoft\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2019\\\/11\\\/15\\\/in-cluster-cnab-management-brigade\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2019\\\/11\\\/15\\\/in-cluster-cnab-management-brigade\\\/\",\"url\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2019\\\/11\\\/15\\\/in-cluster-cnab-management-brigade\\\/\",\"name\":\"In-cluster CNAB management with Brigade | Microsoft Open Source Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2019\\\/11\\\/15\\\/in-cluster-cnab-management-brigade\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2019\\\/11\\\/15\\\/in-cluster-cnab-management-brigade\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/wp-content\\\/uploads\\\/2024\\\/06\\\/CLO20b_Jayesh_office_001.webp\",\"datePublished\":\"2019-11-15T15:30:09+00:00\",\"dateModified\":\"2025-06-27T11:40:31+00:00\",\"description\":\"Learn more about how to use Kubernetes projects, including Porter and Brigade, together to make a Kubernetes controller for deploying CRDs.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2019\\\/11\\\/15\\\/in-cluster-cnab-management-brigade\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2019\\\/11\\\/15\\\/in-cluster-cnab-management-brigade\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2019\\\/11\\\/15\\\/in-cluster-cnab-management-brigade\\\/#primaryimage\",\"url\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/wp-content\\\/uploads\\\/2024\\\/06\\\/CLO20b_Jayesh_office_001.webp\",\"contentUrl\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/wp-content\\\/uploads\\\/2024\\\/06\\\/CLO20b_Jayesh_office_001.webp\",\"width\":1170,\"height\":640},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2019\\\/11\\\/15\\\/in-cluster-cnab-management-brigade\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"In-cluster CNAB management with Brigade\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/\",\"name\":\"Microsoft Open Source Blog\",\"description\":\"Open dialogue about openness at Microsoft \u2013 open source, standards, interoperability\",\"publisher\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/#organization\",\"name\":\"Microsoft Open Source Blog\",\"url\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/08\\\/Microsoft-Logo.png\",\"contentUrl\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/08\\\/Microsoft-Logo.png\",\"width\":259,\"height\":194,\"caption\":\"Microsoft Open Source Blog\"},\"image\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/OpenAtMicrosoft\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"In-cluster CNAB management with Brigade | Microsoft Open Source Blog","description":"Learn more about how to use Kubernetes projects, including Porter and Brigade, together to make a Kubernetes controller for deploying CRDs.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/","og_locale":"en_US","og_type":"article","og_title":"In-cluster CNAB management with Brigade | Microsoft Open Source Blog","og_description":"Learn more about how to use Kubernetes projects, including Porter and Brigade, together to make a Kubernetes controller for deploying CRDs.","og_url":"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/","og_site_name":"Microsoft Open Source Blog","article_published_time":"2019-11-15T15:30:09+00:00","article_modified_time":"2025-06-27T11:40:31+00:00","og_image":[{"width":1170,"height":640,"url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO20b_Jayesh_office_001.png","type":"image\/png"}],"author":"Matt Butcher","twitter_card":"summary_large_image","twitter_creator":"@OpenAtMicrosoft","twitter_site":"@OpenAtMicrosoft","twitter_misc":{"Written by":"Matt Butcher","Est. reading time":"9 min read"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/#article","isPartOf":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/"},"author":[{"@id":"https:\/\/opensource.microsoft.com\/blog\/author\/matt-butcher\/","@type":"Person","@name":"Matt Butcher"}],"headline":"In-cluster CNAB management with Brigade","datePublished":"2019-11-15T15:30:09+00:00","dateModified":"2025-06-27T11:40:31+00:00","mainEntityOfPage":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/"},"wordCount":1770,"commentCount":0,"publisher":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#organization"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/#primaryimage"},"thumbnailUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO20b_Jayesh_office_001.webp","keywords":["Kubernetes","Microsoft"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/","url":"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/","name":"In-cluster CNAB management with Brigade | Microsoft Open Source Blog","isPartOf":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/#primaryimage"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/#primaryimage"},"thumbnailUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO20b_Jayesh_office_001.webp","datePublished":"2019-11-15T15:30:09+00:00","dateModified":"2025-06-27T11:40:31+00:00","description":"Learn more about how to use Kubernetes projects, including Porter and Brigade, together to make a Kubernetes controller for deploying CRDs.","breadcrumb":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/#primaryimage","url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO20b_Jayesh_office_001.webp","contentUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO20b_Jayesh_office_001.webp","width":1170,"height":640},{"@type":"BreadcrumbList","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/11\/15\/in-cluster-cnab-management-brigade\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/opensource.microsoft.com\/blog\/"},{"@type":"ListItem","position":2,"name":"In-cluster CNAB management with Brigade"}]},{"@type":"WebSite","@id":"https:\/\/opensource.microsoft.com\/blog\/#website","url":"https:\/\/opensource.microsoft.com\/blog\/","name":"Microsoft Open Source Blog","description":"Open dialogue about openness at Microsoft \u2013 open source, standards, interoperability","publisher":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/opensource.microsoft.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/opensource.microsoft.com\/blog\/#organization","name":"Microsoft Open Source Blog","url":"https:\/\/opensource.microsoft.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png","contentUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png","width":259,"height":194,"caption":"Microsoft Open Source Blog"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/OpenAtMicrosoft"]}]}},"msxcm_animated_featured_image":null,"bloginabox_display_generated_audio":false,"distributor_meta":false,"distributor_terms":false,"distributor_media":false,"distributor_original_site_name":"Microsoft Open Source Blog","distributor_original_site_url":"https:\/\/opensource.microsoft.com\/blog","push-errors":false,"_links":{"self":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/78543","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/users\/5562"}],"replies":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/comments?post=78543"}],"version-history":[{"count":2,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/78543\/revisions"}],"predecessor-version":[{"id":97715,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/78543\/revisions\/97715"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/media\/95468"}],"wp:attachment":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/media?parent=78543"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/post_tag?post=78543"},{"taxonomy":"content-type","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/content-type?post=78543"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/topic?post=78543"},{"taxonomy":"programming-languages","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/programming-languages?post=78543"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/coauthors?post=78543"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}