<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0"><channel><title>Pulumi Blog: Alex Clemmer</title><link>https://www.pulumi.com/blog/author/alex-clemmer/</link><description>Pulumi blog posts: Alex Clemmer.</description><language>en-us</language><pubDate>Wed, 20 Nov 2019 00:00:00 +0000</pubDate><item><title>Introducing Pulumi Query for Kubernetes</title><link>https://www.pulumi.com/blog/query-kubernetes/</link><pubDate>Wed, 20 Nov 2019 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/query-kubernetes/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/query-kubernetes/index.png" /&gt;
&lt;p&gt;We often need answers to simple questions about Kubernetes resources. Questions like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How many distinct versions of MySQL are running in my cluster?&lt;/li&gt;
&lt;li&gt;Which Pods are scheduled on nodes with high memory pressure?&lt;/li&gt;
&lt;li&gt;Which Pods are publicly exposed to the internet via a load-balanced Service?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these questions would normally be answered by invoking &lt;code&gt;kubectl&lt;/code&gt; multiple times to list
resources of each type, and manually parsing the output to join it together into a single report.&lt;/p&gt;
&lt;p&gt;Examples like these motivated us to build Pulumi Query for Kubernetes, which aims to greatly simplify scenarios like the ones mentioned above.&lt;/p&gt;
&lt;p&gt;Pulumi Query is a tool and SDK for querying live Kubernetes resources. Pulumi Query supports both
batch and streaming query modes. Batch queries run to completion, generating a fixed report. Streaming queries watch Kubernetes resources in real-time and take action when specific events
occur. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Post a slack notification when we run out of disk space.&lt;/li&gt;
&lt;li&gt;Page someone when a rollout fails.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this post, we&amp;rsquo;ll look at both batch and streaming query modes by showing examples of each. The
&lt;a href="https://github.com/pulumi/pulumi-query-kubernetes"&gt;Pulumi Query for Kubernetes SDK&lt;/a&gt; also contains a set
of example queries and how to run them.&lt;/p&gt;
&lt;h2 id="batch-queries"&gt;Batch queries&lt;/h2&gt;
&lt;p&gt;A simple example is a query program that finds all distinct versions of MySQL running in the default
namespace of the active cluster of your &lt;code&gt;$KUBECONFIG&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;The core primitive of batch queries is &lt;code&gt;list&lt;/code&gt;. In this case, &lt;code&gt;list(&amp;quot;v1&amp;quot;, &amp;quot;Pod&amp;quot;)&lt;/code&gt; returns list of all
Pods in the default namespace. The rest of the query &amp;ldquo;flattens&amp;rdquo; the list of Pods into a list of all
container images, filters out all images that don&amp;rsquo;t contain the string &amp;ldquo;mysql&amp;rdquo;, and filters that
to only distinct image names.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;kq&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@pulumi/query-kubernetes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Find all distinct versions of MySQL running in your cluster.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mySqlVersions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kq&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;v1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Pod&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pod&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;containers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageName&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;imageName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;mysql&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;distinct&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;mySqlVersions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Query programs are run via the &lt;code&gt;pulumi query&lt;/code&gt; command. Note that this feature is in beta, so to run
it, you will need to prepend the command with the beta flag: &lt;code&gt;PULUMI_EXPERIMENTAL=true pulumi query&lt;/code&gt;. The output looks like the following:&lt;/p&gt;
&lt;p&gt;&lt;img src="query-list.gif" alt="Querying a list of resources"&gt;&lt;/p&gt;
&lt;p&gt;Batch query mode supports complex join operations via the &lt;code&gt;join&lt;/code&gt; and &lt;code&gt;groupJoin&lt;/code&gt; methods. This
allows us to ask complex questions about how resources relate to each other, such as &amp;ldquo;find all Pods
that reference some Secret.&amp;rdquo; For more examples, check out the examples in the SDK repository!&lt;/p&gt;
&lt;h2 id="streaming-queries"&gt;Streaming queries&lt;/h2&gt;
&lt;p&gt;The streaming query SDK continuously observes changes to any set of Kubernetes resources
and takes action as a result.&lt;/p&gt;
&lt;p&gt;The core primitive of the streaming SDK is &lt;code&gt;watch&lt;/code&gt;. In the following example, we use &lt;code&gt;watch&lt;/code&gt; to
observe all Events related to a Deployment, which we then print to the console. Unlike &lt;code&gt;list&lt;/code&gt;, which
produces a set of live Kubernetes resources, &lt;code&gt;watch&lt;/code&gt; publishes a stream of resource updates. Users
get notified if a resource is &lt;code&gt;ADDED&lt;/code&gt;, &lt;code&gt;MODIFIED&lt;/code&gt;, or &lt;code&gt;UPDATED&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here is a simple example that watches for Events related to a deployment called &lt;code&gt;alexbot&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;kq&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@pulumi/query-kubernetes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;kq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;v1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Event&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;involvedObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;apiVersion&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;apps/v1&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Deployment&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;alexbot&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; [&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;] &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When run, we can see in real-time as the Deployment controller scales up the new ReplicaSet and
scales down the old one.&lt;/p&gt;
&lt;p&gt;&lt;img src="query-watch.gif" alt="Querying a stream of resources"&gt;&lt;/p&gt;
&lt;p&gt;For streaming queries over multiple resources, we expose a &lt;code&gt;ResourceSet&lt;/code&gt;, which is a convenience
data structure that simplifies the code. &lt;code&gt;ResourceSet&lt;/code&gt; observes a sequence of resource updates
published by &lt;code&gt;watch&lt;/code&gt;, and uses those updates to keep track of which resources currently exist. It
exposes an &lt;code&gt;onUpdate&lt;/code&gt; function, which will run when this set is updated; i.e., whenever a resource
is added, modified, or deleted from the set entirely.&lt;/p&gt;
&lt;p&gt;In this case, when &lt;code&gt;onUpdate&lt;/code&gt; is called, we run a query that filters the set of events down to those
specifically having to do with a Deployment called &lt;code&gt;alexbot&lt;/code&gt;. We can rewrite the previous query
program to use &lt;code&gt;ResourceSet&lt;/code&gt; in the following way:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kr"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;kq&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@pulumi/query-kubernetes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;kq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResourceSet&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;kq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;v1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Event&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)]).&lt;/span&gt;&lt;span class="nx"&gt;onUpdate&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;involvedObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;apiVersion&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;apps/v1&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Deployment&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;alexbot&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; [&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;] &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running this code with &lt;code&gt;pulumi query&lt;/code&gt; results in an infinite stream of events telling us about this
Deployment, i.e. when it&amp;rsquo;s rolling out, when it&amp;rsquo;s scaling down a ReplicaSet, and so on.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Pulumi Query provides powerful primitives for introspecting and reacting to changes in the state
of Kubernetes resources. These primitives make it simpler to build tools that facilitate
observability and helps to understand Kubernetes applications and to integrate event-based systems
in response to them.&lt;/p&gt;
&lt;p&gt;One of our hopes for Pulumi Query is that by building these simple integrations that it will lead to a
groundswell of tooling that makes it easier to operate Kubernetes clusters by automating away
tedious tasks that would normally be scripted. With this toolkit, teams can
write linters, pre- and post-flight deployment checks, and to improve governance and security
operations with minimal effort.&lt;/p&gt;</description><author>Alex Clemmer</author><category>kubernetes</category></item><item><title>Using Helm and Pulumi to define cloud native infrastructure</title><link>https://www.pulumi.com/blog/using-helm-and-pulumi-to-define-cloud-native-infrastructure-as-code/</link><pubDate>Wed, 31 Oct 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/using-helm-and-pulumi-to-define-cloud-native-infrastructure-as-code/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/using-helm-and-pulumi-to-define-cloud-native-infrastructure-as-code/index.png" /&gt;
&lt;p&gt;The Helm community is one of the brightest spots in the infrastructure
ecosystem: collectively, it has accumulated person-decades of
operational expertise to produce Kubernetes manifests that &amp;ldquo;just work.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;But for many users, it is not feasible to run &lt;em&gt;everything&lt;/em&gt; in
Kubernetes, and the community is just starting to develop answers to
questions like: what happens when a Helm Chart needs to interface with,
for example, a managed database like AWS RDS or Azure CosmosDB?&lt;/p&gt;
&lt;p&gt;Pulumi is a cloud native development platform designed to be able to
express any cloud native infrastructure as code in a natural,
intentional manner using familiar languages. The most natural way to solve
this challenge would be to stand up an instance of AWS RDS, populate a
Kubernetes Secret with the connection details, and then simply let my
application use these newly available resources. Pulumi gives users the
primitives they need in order to achieve tasks like this most
effectively.&lt;/p&gt;
&lt;h2 id="how-to-connect-a-kubernetes-app-with-cosmosdb"&gt;How to connect a Kubernetes app with CosmosDB&lt;/h2&gt;
&lt;p&gt;In the following Pulumi program, we can manage both Azure and Kubernetes
resources, including the interconnected dependencies between the two.
Specifically, we:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create an AKS cluster,&lt;/li&gt;
&lt;li&gt;Create a MongoDB-flavored instance of Azure&amp;rsquo;s CosmosDB,&lt;/li&gt;
&lt;li&gt;Create a Kubernetes Secret from the connection string exported by
CosmosDB&lt;/li&gt;
&lt;li&gt;Deploy a Node.js Helm Chart that references it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have the Azure command line, try &lt;a href="https://github.com/pulumi/examples/tree/master/classic-azure-ts-aks-mean"&gt;running the example&lt;/a&gt;
with &lt;code&gt;pulumi up&lt;/code&gt;!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;k8s&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@pulumi/kubernetes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;azure&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@pulumi/azure&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;mongoHelpers&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;./mongoHelpers&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;./config&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Create an AKS cluster.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;k8sCluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;k8sProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;./cluster&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Create a MongoDB-flavored instance of CosmosDB.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cosmosdb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;azure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cosmosdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cosmosDb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;MongoDB&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;resourceGroupName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resourceGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;consistencyPolicy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;consistencyLevel&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;BoundedStaleness&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;maxIntervalInSeconds&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;maxStalenessPrefix&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;offerType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Standard&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;enableAutomaticFailover&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;geoLocations&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;failoverPriority&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;failoverLocation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;failoverPriority&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Create secret from MongoDB connection string.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mongoConnStrings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;k8s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;mongo-secrets&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mongoHelpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parseConnString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cosmosdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionStrings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;k8sProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Boot up nodejs Helm chart example using CosmosDB in place of in-cluster MongoDB.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;k8s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;helm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;node&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;bitnami&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;chart&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;node&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;4.0.1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;serviceType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;LoadBalancer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;mongodb&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;externaldb&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secretName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mongoConnStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;kubernetes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;k8sProvider&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;dependsOn&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mongoConnStrings&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Pulumi supports deploying resources to all the major cloud vendors, as
well as Kubernetes. Using the Pulumi programming model, it is possible
to define and deploy apps and infrastructure with an arbitrary mix of
resources from any combination of cloud providers. And, because Pulumi
uses normal &lt;code&gt;$KUBECONFIG&lt;/code&gt; files, it is compatible anywhere you would use
Helm or &lt;code&gt;kubectl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The Pulumi CLI provides rich insight into the progress a deployment
makes. When you run &lt;code&gt;pulumi up&lt;/code&gt;, the CLI will provide detailed
information about the progress we&amp;rsquo;re making as we try to deploy the
Chart. To get a sense of what this looks like, take a look at the
progress reported as we deploy the Wordpress Chart:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/using-helm-and-pulumi-to-define-cloud-native-infrastructure-as-code/helm-pulumi-deploy.gif" alt="helm-pulumi-deploy"&gt;&lt;/p&gt;
&lt;h2 id="toward-cloud-native-infrastructure-as-code"&gt;Toward Cloud Native Infrastructure as Code&lt;/h2&gt;
&lt;p&gt;As cloud native architectures mature, Pulumi can reduce the complexity
in choosing and using the available services from cloud vendors, and
combine those choices using a single, consistent programming model. This
can make the best use of existing tools such as Helm, and also reduce
the friction caused by multiple deployment tools and models across
complex architectures.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Find out more about our &lt;a href="https://www.pulumi.com/azure/"&gt;Azure&lt;/a&gt; and
&lt;a href="https://www.pulumi.com/kubernetes/"&gt;Kubernetes&lt;/a&gt; support&lt;/li&gt;
&lt;li&gt;Join the Slack community at &lt;a href="https://slack.pulumi.com"&gt;https://slack.pulumi.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><author>Alex Clemmer</author><category>kubernetes</category><category>azure</category></item><item><title>How do Kubernetes Deployments work?</title><link>https://www.pulumi.com/blog/how-do-kubernetes-deployments-work-an-adversarial-perspective/</link><pubDate>Wed, 03 Oct 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/how-do-kubernetes-deployments-work-an-adversarial-perspective/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/how-do-kubernetes-deployments-work-an-adversarial-perspective/index.png" /&gt;
&lt;p&gt;&lt;em&gt;This post is part 3 in a series on the Kubernetes API. Earlier,
&lt;a href="https://www.pulumi.com/blog/kubespy-and-the-lifecycle-of-a-kubernetes-pod-in-four-images/"&gt;Part 1&lt;/a&gt;
focused on the lifecycle of a &lt;code&gt;Pod&lt;/code&gt; and
&lt;a href="https://www.pulumi.com/blog/kubespy-trace-a-real-time-view-into-the-heart-of-a-kubernetes-service/"&gt;Part 2&lt;/a&gt;
focused on the lifecycle of a &lt;code&gt;Service&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;What is happening when a &lt;code&gt;Deployment&lt;/code&gt; rolls out a change to your app?
What does it actually do when a &lt;code&gt;Pod&lt;/code&gt; crashes or is killed? What happens
when a &lt;code&gt;Pod&lt;/code&gt; is re-labled so that it&amp;rsquo;s not targeted by the
&lt;code&gt;Deployment&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Deployment&lt;/code&gt; is probably the most complex resource type in Kubernetes
core. &lt;code&gt;Deployment&lt;/code&gt; specifies how changes should be rolled out over
&lt;code&gt;ReplicaSet&lt;/code&gt;s, which themselves specify how &lt;code&gt;Pod&lt;/code&gt;s should be replicated
in a cluster.&lt;/p&gt;
&lt;p&gt;In this post we continue our exploration of the Kubernetes API, cracking
&lt;code&gt;Deployment&lt;/code&gt; open using &lt;code&gt;kubespy&lt;/code&gt;, a small tool we developed to observe
Kubernetes resources in real-time.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;kubespy trace&lt;/code&gt;, for example, we can observe at a high level what
happens when &lt;code&gt;Deployment&lt;/code&gt; rolls out a new version of an application:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/how-do-kubernetes-deployments-work-an-adversarial-perspective/deployment-rollout.gif" alt="trace-deployment-rollout"&gt;&lt;/p&gt;
&lt;p&gt;But this post also comes with a twist, because in addition to being
complex, &lt;code&gt;Deployment&lt;/code&gt; is also the most &lt;em&gt;dynamic&lt;/em&gt; resource in Kubernetes
core. A &lt;code&gt;Pod&lt;/code&gt; can crash or be killed. A node can disappear. A user can
trigger a rollout. In each of these cases, some combination of the
&lt;code&gt;Deployment&lt;/code&gt; controller, the &lt;code&gt;ReplicaSet&lt;/code&gt; controller, and the Kubelet
have to cope.&lt;/p&gt;
&lt;p&gt;In other words, understanding &lt;code&gt;Deployment&lt;/code&gt; necessarily involves
understanding how it &lt;em&gt;does&lt;/em&gt; this coping. We therefore will also use
&lt;code&gt;kubespy trace&lt;/code&gt; to observe what happens when we kill and otherwise
bother our &lt;code&gt;Pod&lt;/code&gt;s.&lt;/p&gt;
&lt;h2 id="following-along"&gt;Following along&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;kubespy&lt;/code&gt; repository comes with a simple &lt;a href="https://github.com/pulumi/kubespy/tree/master/examples/trivial-service-trace-example"&gt;example Kubernetes app&lt;/a&gt;,
which is used in each of these examples. If you want to try it out for
yourself the README contains detailed instructions.&lt;/p&gt;
&lt;p&gt;You can use &lt;code&gt;kubectl&lt;/code&gt; or &lt;code&gt;pulumi&lt;/code&gt; &amp;mdash; &lt;code&gt;kubespy&lt;/code&gt; CLI itself is powered by
the same code that underlies the core (OSS) &lt;a href="https://www.pulumi.com/kubernetes/"&gt;Pulumi engine&lt;/a&gt;. If you like this, and would
like to see information like it in CI/CD, we hope you&amp;rsquo;ll give it a
shot! To get a flavor of what this looks like in practice, you might
also check out &lt;a href="https://twitter.com/hausdorff_space/status/1039940379301179392"&gt;my tweetstorm&lt;/a&gt;
on the subject.&lt;/p&gt;
&lt;h2 id="what-happens-during-a-rollout"&gt;What happens during a rollout?&lt;/h2&gt;
&lt;p&gt;The gif in the introduction shows what happens when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We deploy the example application.&lt;/li&gt;
&lt;li&gt;Then, later, change the image tag to &lt;code&gt;nginx:1.12-alpine&lt;/code&gt; and deploy
again.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When &lt;code&gt;kubespy trace deploy nginx&lt;/code&gt; is run, it will watch for changes to
(1) the &lt;code&gt;Deployment&lt;/code&gt; called &lt;code&gt;nginx&lt;/code&gt;, (1) the &lt;code&gt;ReplicaSet&lt;/code&gt;s it controls,
and (3) the &lt;code&gt;Pod&lt;/code&gt;s they control over time. The overall status is
aggregated and printed to the console as they change.&lt;/p&gt;
&lt;p&gt;From this gif, we can see 3 distinct phases:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First, the initial state: the &lt;code&gt;Deployment&lt;/code&gt; is in a steady state.&lt;/strong&gt; It
controls a single &lt;code&gt;ReplicaSet&lt;/code&gt;, which in turn controls three replicas of
the application &lt;code&gt;Pod&lt;/code&gt;. Each &lt;code&gt;Pod&lt;/code&gt; is marked as available. We can see
from &lt;code&gt;Deployment&lt;/code&gt;&amp;rsquo;s status that the application is on &lt;strong&gt;revision 2&lt;/strong&gt;,
which means the application has been deployed twice &amp;mdash; one initial
deployment, and one update.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/how-do-kubernetes-deployments-work-an-adversarial-perspective/deployment-found.gif" alt="1-deployment-found"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Second: the user submits a change to the &lt;code&gt;Deployment&lt;/code&gt;, which triggers
a rollout.&lt;/strong&gt; The &lt;code&gt;Deployment&lt;/code&gt; creates a new revision, &lt;strong&gt;revision 3.&lt;/strong&gt; It
creates a new &lt;code&gt;ReplicaSet&lt;/code&gt; to represent this revision, and begins to
start &lt;code&gt;Pod&lt;/code&gt;s with the new app version on it.&lt;/p&gt;
&lt;p&gt;This gif is slowed down to show each of these individual changes &amp;mdash; in
the gif above, you can see these happen in very quick succession.&lt;/p&gt;
&lt;p&gt;&lt;img src="2-replicas-created.gif" alt="2-replicaset-created"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Third: the new version of the app comes online; the old &lt;code&gt;ReplicaSet&lt;/code&gt;
is killed.&lt;/strong&gt; As the new &lt;code&gt;Pod&lt;/code&gt;s in &lt;strong&gt;revision 3&lt;/strong&gt; come online and are
marked available, the &lt;code&gt;Deployment&lt;/code&gt; controller begins killing off
replicas from &lt;strong&gt;revision 2.&lt;/strong&gt; Eventually it succeeds and the rollout is
complete.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/how-do-kubernetes-deployments-work-an-adversarial-perspective/3-rollout-succeeds.gif" alt="3-rollout-succeeds"&gt;&lt;/p&gt;
&lt;h2 id="what-happens-if-we-kill-a-pod"&gt;What happens if we kill a Pod?&lt;/h2&gt;
&lt;p&gt;Now that we understand the semantics of &lt;code&gt;Deployment&lt;/code&gt;&amp;rsquo;s rollout, we can
see what happens when we use &lt;code&gt;kubectl delete pod &amp;lt;name&amp;gt;&lt;/code&gt; on one of the
&lt;code&gt;Pod&lt;/code&gt;s that is controlled by our &lt;code&gt;Deployment&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/how-do-kubernetes-deployments-work-an-adversarial-perspective/pod-killed.gif" alt="pod-killed"&gt;&lt;/p&gt;
&lt;p&gt;As expected, we can see that the &lt;code&gt;ReplicaSet&lt;/code&gt; controller notices the
&lt;code&gt;Pod&lt;/code&gt; goes missing and spins up a new one. Note that it does &lt;em&gt;not&lt;/em&gt;
trigger a rollout, and does &lt;em&gt;not&lt;/em&gt; increment the revision.&lt;/p&gt;
&lt;p&gt;Notice also that the &lt;code&gt;Pod&lt;/code&gt; that was destroyed hangs around for a few
seconds, even though the new one has been booted up and the &lt;code&gt;ReplicaSet&lt;/code&gt;
has been marked available.&lt;/p&gt;
&lt;h2 id="what-happens-if-we-add-or-remove-labels-from-a-pod"&gt;What happens if we add or remove labels from a Pod?&lt;/h2&gt;
&lt;p&gt;Try using &lt;code&gt;kubectl edit&lt;/code&gt; to delete the labels on one of your &lt;code&gt;Pods&lt;/code&gt;.
&lt;code&gt;kubespy trace&lt;/code&gt; will show you something the following:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/how-do-kubernetes-deployments-work-an-adversarial-perspective/pod-relabeled.gif" alt="pod-relabeled"&gt;&lt;/p&gt;
&lt;p&gt;Unlike the &amp;ldquo;killing a &lt;code&gt;Pod&lt;/code&gt;&amp;rdquo; example above, the old &lt;code&gt;Pod&lt;/code&gt; seems to
&lt;em&gt;disappear&lt;/em&gt;, replaced by a new &lt;code&gt;Pod&lt;/code&gt; that, when booted up, causes the
&lt;code&gt;Deployment&lt;/code&gt; to be marked as available again.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s happening here? It turns out that if you remove the app labels
for a &lt;code&gt;Pod&lt;/code&gt;, the &lt;code&gt;ReplicaSet&lt;/code&gt; controller notices, removes itself from
&lt;code&gt;.metadata.ownerRef&lt;/code&gt;, and then treats the &lt;code&gt;Pod&lt;/code&gt; as if it&amp;rsquo;s been
deleted, spinning up a new one immediately.&lt;/p&gt;
&lt;p&gt;This is useful: if you notice one of your &lt;code&gt;Pod&lt;/code&gt;s is behaving strangely,
you can simply take it out of rotation by removing those labels, so that
you can pull it aside and test it.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The Kubernetes API is rich, packed with useful information about the
state of your cluster. It&amp;rsquo;s remarkable how little it is directly used
to make tools. Combine a little knowledge of &lt;code&gt;Deployment&lt;/code&gt;&amp;rsquo;s &lt;code&gt;.status&lt;/code&gt;
field with the Kubernetes &lt;code&gt;Watch&lt;/code&gt; API and a bit of terminal UI
programming, you&amp;rsquo;re most of your way to &lt;code&gt;kubespy trace deployment&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you enjoyed this post, or are curious to see how this lifecycle is
baked into the Pulumi CLI, please &lt;a href="https://www.pulumi.com/kubernetes/"&gt;give us a shot&lt;/a&gt;!
We&amp;rsquo;d love to hear your feedback.&lt;/p&gt;</description><author>Alex Clemmer</author><category>kubernetes</category></item><item><title>kubespy trace: a real-time view into of a Kubernetes Service</title><link>https://www.pulumi.com/blog/kubespy-trace-a-real-time-view-into-the-heart-of-a-kubernetes-service/</link><pubDate>Wed, 26 Sep 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/kubespy-trace-a-real-time-view-into-the-heart-of-a-kubernetes-service/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/kubespy-trace-a-real-time-view-into-the-heart-of-a-kubernetes-service/index.png" /&gt;
&lt;p&gt;&lt;!-- spacer --&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This post is part 3 in a series on the Kubernetes API. Earlier,
&lt;a href="https://www.pulumi.com/blog/kubespy-and-the-lifecycle-of-a-kubernetes-pod-in-four-images/"&gt;Part 1&lt;/a&gt;
focused on the lifecycle of a &lt;code&gt;Pod&lt;/code&gt;, and later
&lt;a href="https://www.pulumi.com/blog/how-do-kubernetes-deployments-work-an-adversarial-perspective/"&gt;Part 3&lt;/a&gt;
details how Kubernetes deployments work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why isn&amp;rsquo;t my &lt;code&gt;Pod&lt;/code&gt; getting any traffic?&lt;/p&gt;
&lt;p&gt;An experienced ops team running on GKE might assemble the following
checklist to help answer this question:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Does a &lt;code&gt;Service&lt;/code&gt; exist? Does that service have a &lt;code&gt;.spec.selector&lt;/code&gt;
that matches some number of &lt;code&gt;Pod&lt;/code&gt;s?&lt;/li&gt;
&lt;li&gt;Are the &lt;code&gt;Pod&lt;/code&gt;s alive and has their readiness probe passed?&lt;/li&gt;
&lt;li&gt;Did the &lt;code&gt;Service&lt;/code&gt; create an &lt;code&gt;Endpoints&lt;/code&gt; object that specifies one or
more &lt;code&gt;Pod&lt;/code&gt;s to direct traffic to?&lt;/li&gt;
&lt;li&gt;Is the &lt;code&gt;Service&lt;/code&gt; reachable via DNS? When you &lt;code&gt;kubectl ``exec&lt;/code&gt; into a
&lt;code&gt;Pod&lt;/code&gt; and you use &lt;code&gt;curl&lt;/code&gt; to poke the &lt;code&gt;Service&lt;/code&gt; hostname, do you get
a response? (If not, does &lt;em&gt;any&lt;/em&gt; &lt;code&gt;Service&lt;/code&gt; have a DNS entry?)&lt;/li&gt;
&lt;li&gt;Is the &lt;code&gt;Service&lt;/code&gt; reachable via IP? When you SSH into a &lt;code&gt;Node&lt;/code&gt; and
you use &lt;code&gt;curl&lt;/code&gt; to poke the &lt;code&gt;Service&lt;/code&gt; IP, do you get a response?&lt;/li&gt;
&lt;li&gt;Is &lt;code&gt;kube-proxy&lt;/code&gt; up? Is it writing iptables rules? Is it proxying to
the &lt;code&gt;Service&lt;/code&gt;?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This question might have the highest complexity-to-sentence-length ratio
of any question in the Kubernetes ecosystem. Unfortunately, it&amp;rsquo;s also a
question that &lt;em&gt;every&lt;/em&gt; user finds themselves asking at some point. And
when they do, it usually means their app is down.&lt;/p&gt;
&lt;p&gt;To help answer questions like this, we&amp;rsquo;ve been developing a small
diagnostic tool, &lt;code&gt;kubespy&lt;/code&gt;. In this post we&amp;rsquo;ll look at the new
&lt;code&gt;kubespy trace&lt;/code&gt; command, which is broadly aimed at automating questions
1, 2, 3, and providing &amp;ldquo;hints&amp;rdquo; about 4 and 5.&lt;/p&gt;
&lt;p&gt;Below is a gif demonstrating the CLI experience. You can watch in
real-time as the &lt;code&gt;Service&lt;/code&gt; comes online, finds pods to target, and
finally is allocated a public IP address:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/kubespy-trace-a-real-time-view-into-the-heart-of-a-kubernetes-service/trace-success-ip-allocated.gif" alt="trace-success-ip-allocated"&gt;&lt;/p&gt;
&lt;h2 id="what-is-kubespy-again"&gt;What is kubespy, again?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;kubespy&lt;/code&gt; is a simple, standalone diagnostic tool, meant to make it easy
to introspect on Kubernetes resources in real time.&lt;/p&gt;
&lt;p&gt;Before we begin, it&amp;rsquo;s worth noting that this &lt;code&gt;kubespy&lt;/code&gt; actually
re-packages the machinery we developed for Kubernetes support in
Pulumi.&lt;/p&gt;
&lt;p&gt;One of our major goals in this work was to make deploying an application
to Kubernetes as simple as possible, by presenting a concise summary of
this information in the CLI experience. See
&lt;a href="https://twitter.com/hausdorff_space/status/1039940379301179392"&gt;my tweetstorm&lt;/a&gt;
on the subject, or &lt;a href="https://www.pulumi.com/kubernetes/"&gt;try it out&lt;/a&gt; for
yourself!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/uploads/content/blog/kubespy-trace-a-real-time-view-into-the-heart-of-a-kubernetes-service/status-rich.gif" alt="status-rich"&gt;&lt;/p&gt;
&lt;h2 id="a-real-time-view-of-a-services-life"&gt;A real-time view of a Service&amp;rsquo;s life&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/pulumi/kubespy"&gt;&lt;code&gt;kubespy&lt;/code&gt; repository&lt;/a&gt; contains
the &lt;a href="https://github.com/pulumi/kubespy/tree/master/examples/trivial-service-trace-example"&gt;simple &lt;code&gt;trace&lt;/code&gt; example&lt;/a&gt;
we use in this demo. The
&lt;a href="https://github.com/pulumi/kubespy/tree/master/examples/trivial-service-trace-example"&gt;README&lt;/a&gt;
contains detailed installation instructions, as well as explaining how
to run the app (using either &lt;code&gt;kubetl&lt;/code&gt; or &lt;code&gt;pulumi&lt;/code&gt; though of course we
hope you will try Pulumi).&lt;/p&gt;
&lt;p&gt;Essentially: running &lt;code&gt;kubespy trace service nginx&lt;/code&gt; will cause &lt;code&gt;kubespy&lt;/code&gt;
to sit and wait for you to deploy a &lt;code&gt;Service&lt;/code&gt; called &lt;code&gt;nginx&lt;/code&gt;. When you
run this example, it will do just this: creating a &lt;code&gt;Deployment&lt;/code&gt; which
replicates an nginx &lt;code&gt;Pod&lt;/code&gt; 3 times and exposes it publicly to the
Internet with a &lt;code&gt;Service&lt;/code&gt;, also called &lt;code&gt;nginx&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s break down the &lt;code&gt;kubespy trace&lt;/code&gt; gif above to show that there are
actually several distinct steps in the process of booting up a
&lt;code&gt;Service&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First: &lt;code&gt;Service&lt;/code&gt; is created, the &lt;code&gt;Service&lt;/code&gt; controller creates an
&lt;code&gt;Endpoints&lt;/code&gt; object of the same name.&lt;/strong&gt; The &lt;code&gt;Endpoints&lt;/code&gt; object is to
specify which &lt;code&gt;Pod&lt;/code&gt;s get traffic &amp;mdash; their IPs, which ports to direct
traffic to, and so on. In this case, there are no &lt;code&gt;Pod&lt;/code&gt;s to target,
which &lt;code&gt;kubespy trace&lt;/code&gt; tells us:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/kubespy-trace-a-real-time-view-into-the-heart-of-a-kubernetes-service/trace-success-create-svc.gif" alt="trace-success-create-svc"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Second: &lt;code&gt;Pod&lt;/code&gt;s that match the &lt;code&gt;Service&lt;/code&gt;&amp;rsquo;s &lt;code&gt;.spec.selector&lt;/code&gt; are
created; their readiness probes immediately pass.&lt;/strong&gt; The &lt;code&gt;Endpoints&lt;/code&gt;
object is updated to reflect this. As we will see below, if the &lt;code&gt;Pods&lt;/code&gt;
failed the readiness probes, &lt;code&gt;kubespy trace&lt;/code&gt; would note this.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/kubespy-trace-a-real-time-view-into-the-heart-of-a-kubernetes-service/trace-success-pods-ready.gif" alt="trace-success-pods-ready"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Third: &lt;code&gt;Service&lt;/code&gt; is allocated a public IP address.&lt;/strong&gt; The &lt;code&gt;Service&lt;/code&gt; has
&lt;code&gt;.spec.type&lt;/code&gt; set to &lt;code&gt;LoadBalancer&lt;/code&gt;, which on most cloud platforms means
that a public IP address should be allocated for it.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/kubespy-trace-a-real-time-view-into-the-heart-of-a-kubernetes-service/trace-success-ip-allocated.gif" alt="trace-success-ip-allocated"&gt;&lt;/p&gt;
&lt;h2 id="exercise-other-service-types-watching-rollouts-deleting-services"&gt;Exercise: Other Service types, watching rollouts, deleting Services!&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;kubespy trace&lt;/code&gt; supports all the other &lt;code&gt;Service&lt;/code&gt; types, including
&lt;code&gt;ExternalName&lt;/code&gt; and &lt;code&gt;ClusterIP&lt;/code&gt;. Try both of those, and you&amp;rsquo;ll see
slightly different output. Try them! It&amp;rsquo;s also worth watching what
happens when a &lt;code&gt;Service&lt;/code&gt; is deleted.&lt;/p&gt;
&lt;p&gt;You can also use &lt;code&gt;kubespy trace&lt;/code&gt; to watch an unhealthy deployment become
healthy. In the following gif, we see a bunch of &lt;code&gt;Pod&lt;/code&gt;s that are failing
readiness checks become healthy as a new version is rolled out:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/kubespy-trace-a-real-time-view-into-the-heart-of-a-kubernetes-service/become-alive.gif" alt="become-alive"&gt;&lt;/p&gt;
&lt;h2 id="conclusions"&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;Confession time. Last time we told you we&amp;rsquo;d dig more into the lifecycle
of a &lt;code&gt;Pod&lt;/code&gt;. And we will, at some point. But we ended up deciding that it
would be easier to explain with a cohesive &lt;code&gt;trace&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;And, while this is a good start, it is only the beginning. &lt;code&gt;trace&lt;/code&gt;
currently supports only &lt;code&gt;Service&lt;/code&gt;. In our next post, we&amp;rsquo;ll extend trace
to &lt;code&gt;Deployment&lt;/code&gt; (or perhaps &lt;code&gt;ReplicaSet&lt;/code&gt;), and from there, we will have
enough tools to really dig into what is happening when you roll out your
app.&lt;/p&gt;
&lt;p&gt;In the mean time, if you enjoyed this post, or are curious to see how
this lifecycle is baked into the Pulumi CLI, &lt;a href="https://www.pulumi.com/kubernetes/"&gt;give it a spin&lt;/a&gt;!
We&amp;rsquo;d love to have your feedback.&lt;/p&gt;</description><author>Alex Clemmer</author><category>kubernetes</category></item><item><title>Kubespy, and the lifecycle of a Pod, in 4 images</title><link>https://www.pulumi.com/blog/kubespy-and-the-lifecycle-of-a-kubernetes-pod-in-four-images/</link><pubDate>Tue, 18 Sep 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/kubespy-and-the-lifecycle-of-a-kubernetes-pod-in-four-images/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/kubespy-and-the-lifecycle-of-a-kubernetes-pod-in-four-images/index.png" /&gt;
&lt;p&gt;&lt;!-- spacer --&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This post is the first part in a series on the Kubernetes API. Future installments include
&lt;a href="https://www.pulumi.com/blog/kubespy-trace-a-real-time-view-into-the-heart-of-a-kubernetes-service/"&gt;Part 2&lt;/a&gt;
focused on the lifecycle of a &lt;code&gt;Service&lt;/code&gt; and
&lt;a href="https://www.pulumi.com/blog/how-do-kubernetes-deployments-work-an-adversarial-perspective/"&gt;Part 3&lt;/a&gt;
details how Kubernetes deployments work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One of the most popular features of the recent
&lt;a href="https://www.pulumi.com/blog/cloud-native-infrastructure-with-kubernetes-and-pulumi/"&gt;v0.15.2 release&lt;/a&gt;
of Pulumi is fine-grained status updates for Kubernetes resources. On
the CLI they look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/uploads/content/blog/kubespy-and-the-lifecycle-of-a-kubernetes-pod-in-four-images/status-rich.gif" alt="status-rich"&gt;&lt;/p&gt;
&lt;p&gt;But wait &amp;mdash; how does this work exactly? What is &lt;em&gt;actually happening&lt;/em&gt;
when you deploy a &lt;code&gt;Pod&lt;/code&gt; to a cluster? How does a &lt;code&gt;Service&lt;/code&gt; go from
uninitialized, to being allocated a public IP address? How often is my
&lt;code&gt;Deployment&lt;/code&gt;&amp;rsquo;s status changing?&lt;/p&gt;
&lt;p&gt;To answer these questions and others like them, today we&amp;rsquo;re releasing
&lt;strong&gt;a small tool, &lt;a href="https://github.com/pulumi/kubespy"&gt;&lt;code&gt;kubespy&lt;/code&gt;&lt;/a&gt;,&lt;/strong&gt;
purpose-built to help you answer questions like this, by displaying the
changes made to a Kubernetes object in real time. For example, in the
following gif, we&amp;rsquo;re running &lt;code&gt;kubespy status v1 Pod nginx&lt;/code&gt; to watch the
changes to a &lt;code&gt;Pod&lt;/code&gt;&amp;rsquo;s status as it is booted up.&lt;/p&gt;
&lt;p&gt;We will spend the rest of this short post using &lt;code&gt;kubespy&lt;/code&gt; to take a
closer look at what happens to a &lt;code&gt;Pod&lt;/code&gt; when it&amp;rsquo;s deployed to the
cluster.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/kubespy-and-the-lifecycle-of-a-kubernetes-pod-in-four-images/status.gif" alt="status"&gt;&lt;/p&gt;
&lt;h2 id="what-happens-when-you-boot-up-a-pod"&gt;What happens when you boot up a Pod?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;kubespy&lt;/code&gt; comes with a &lt;a href="https://github.com/pulumi/kubespy/tree/master/examples/trivial-pulumi-example"&gt;simple example&lt;/a&gt;
that deploys nginx using a naked &lt;code&gt;Pod&lt;/code&gt;. (The example works with both the
Pulumi CLI and &lt;code&gt;kubectl&lt;/code&gt;, but we hope you&amp;rsquo;ll give Pulumi a shot!)&lt;/p&gt;
&lt;p&gt;The essential gist is that &lt;code&gt;kubespy&lt;/code&gt; will sit and watch for the example
&lt;code&gt;Pod&lt;/code&gt; to be created or changed; once you deploy the &lt;code&gt;Pod&lt;/code&gt; (either
&lt;code&gt;pulumi up&lt;/code&gt; or &lt;code&gt;kubectl apply&lt;/code&gt;), you will see something like the gif
above.&lt;/p&gt;
&lt;p&gt;If you do this yourself, you will see that there are 4 updates reported
by &lt;code&gt;kubespy&lt;/code&gt;, each of which individually gives us a fairly clear picture
of what Kubernetes is doing internally to try to run the &lt;code&gt;Pod&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First: Acknowledging the &lt;code&gt;Pod&lt;/code&gt; definition is written to etcd.&lt;/strong&gt;
The API server receives the &lt;code&gt;Pod&lt;/code&gt; definition and begins trying to schedule it.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/kubespy-and-the-lifecycle-of-a-kubernetes-pod-in-four-images/1-created.png" alt="1-created"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Second: Scheduling the &lt;code&gt;Pod&lt;/code&gt;.&lt;/strong&gt;
The scheduler successfully schedules the &lt;code&gt;Pod&lt;/code&gt; to run on a node.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/kubespy-and-the-lifecycle-of-a-kubernetes-pod-in-four-images/2-scheduled.png" alt="2-scheduled"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Third: Creating the &lt;code&gt;Pod&lt;/code&gt;.&lt;/strong&gt;
The kubelet running on the node receives the &lt;code&gt;Pod&lt;/code&gt; definition and begins &amp;ldquo;creating&amp;rdquo;
the &lt;code&gt;Pod&lt;/code&gt;. This involves pulling the
container image, adding volume mounts, &lt;em&gt;etc&lt;/em&gt;. At the end of all of this,
it will attempt to run the container.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/kubespy-and-the-lifecycle-of-a-kubernetes-pod-in-four-images/3-creating.png" alt="3-creating"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fourth: Marking the &lt;code&gt;Pod&lt;/code&gt; as &lt;code&gt;Running&lt;/code&gt;.&lt;/strong&gt;
The kubelet has now resolved the image
tag to a specific SHA, pulled it to the node, has successfully applied
the required configuration, and has successfully started the &lt;code&gt;nginx&lt;/code&gt;
container. In this case, the &lt;code&gt;Pod&lt;/code&gt; contains only one container to run,
and once it&amp;rsquo;s initialized, the whole &lt;code&gt;Pod&lt;/code&gt; gets marked running.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/kubespy-and-the-lifecycle-of-a-kubernetes-pod-in-four-images/4-running.png" alt="4-running"&gt;&lt;/p&gt;
&lt;h2 id="exercises"&gt;Exercises:&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Try inducing an error and see what &lt;code&gt;kubespy&lt;/code&gt; outputs! Make the image
tag nonsense, add a volume that doesn&amp;rsquo;t exist, and so on.&lt;/li&gt;
&lt;li&gt;Try booting up a service! See what &lt;code&gt;kubespy&lt;/code&gt; displays under various
configurations!  &lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="conclusions-and-the-subtleties-of-the-pod-api"&gt;Conclusions, and the subtleties of the Pod API.&lt;/h2&gt;
&lt;p&gt;This post shows a fairly basic, happy-path use of &lt;code&gt;Pods&lt;/code&gt;, and shows that
we can &lt;code&gt;kubespy status&lt;/code&gt; to see exactly what our containers are up to.&lt;/p&gt;
&lt;p&gt;But wait &amp;mdash; what does &lt;code&gt;Running&lt;/code&gt; mean, precisely? If a &lt;code&gt;Pod&lt;/code&gt; is
&lt;code&gt;Running&lt;/code&gt;, does that mean it&amp;rsquo;s healthy? If a &lt;code&gt;Pod&lt;/code&gt; has multiple
containers, and one crashes, does it still get marked &lt;code&gt;Running&lt;/code&gt;? Are
there any other settings that can change when a &lt;code&gt;Pod&lt;/code&gt; is marked
&lt;code&gt;Running&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;Unfortunately, though the &lt;code&gt;Pod&lt;/code&gt; is the atomic compute abstraction of
Kubernetes (or perhaps &lt;em&gt;because&lt;/em&gt; of this), the answer to these questions
turns out to be fairly subtle. But, because &lt;code&gt;Pod&lt;/code&gt;s are so important, we
should endeavor to answer them anyway!&lt;/p&gt;
&lt;p&gt;In the next post, we will &lt;code&gt;kubespy&lt;/code&gt; to observe some of the subtleties of
the &lt;code&gt;Pod&lt;/code&gt; lifecycle, and especially the effect of &lt;code&gt;restartPolicy&lt;/code&gt; on the
semantics of the &lt;code&gt;Pod&lt;/code&gt; object. We will also talk about some of the best
practices we&amp;rsquo;ve accumulated, and describe how we solved the problem of
repeatable &lt;code&gt;Pod&lt;/code&gt; deployments in Pulumi.&lt;/p&gt;
&lt;p&gt;If you enjoyed this post, or are curious to see how this lifecycle is
baked into the Pulumi CLI, &lt;a href="https://www.pulumi.com/kubernetes/"&gt;give us a shot&lt;/a&gt;!
We&amp;rsquo;d love to have your feedback.&lt;/p&gt;</description><author>Alex Clemmer</author><category>kubernetes</category></item><item><title>Simple, Reproducible Kubernetes Deployments</title><link>https://www.pulumi.com/blog/simple-reproducible-kubernetes-deployments/</link><pubDate>Fri, 24 Aug 2018 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/simple-reproducible-kubernetes-deployments/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/simple-reproducible-kubernetes-deployments/index.png" /&gt;
&lt;p&gt;Kubernetes is a powerful container orchestrator for cloud native
applications that can run on any cloud &amp;ndash; AWS, Azure, GCP &amp;ndash; in
addition to hybrid and on-premises environments. Its CLI, &lt;code&gt;kubectl&lt;/code&gt;,
offers basic built-in support for performing deployments, but
intentionally stops short here. In particular, it doesn&amp;rsquo;t offer diffs
and previews, the ability to know when a deployment has succeeded or
failed, and why, and/or sophisticated deployment orchestration.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;rsquo;ll see how Pulumi, an open source cloud native
development platform, can not only let you express Kubernetes programs
in familiar programming languages, like TypeScript, instead of endless YAML
templates, but also how Pulumi delivers simple and reproducible, yet
powerful, Kubernetes deployment workflows.&lt;/p&gt;
&lt;h2 id="less-yaml-more-robustness"&gt;Less YAML, More Robustness&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.pulumi.com/blog/announcing-pulumi-0.15-kubernetes-cicd-openstack-and-more"&gt;Pulumi 0.15&lt;/a&gt;
introduced Kubernetes support to Pulumi. When we started working on
this, we already loved being able to write in our favorite languages &amp;ndash;
with the benefits of IDEs, classes and functions, and reuse through
packages &amp;ndash; instead of YAML. But we felt just having great programming
language alone wasn&amp;rsquo;t enough.&lt;/p&gt;
&lt;p&gt;As we looked to how teams wanted to operationalize our Kubernetes
support, we set our sights on one major area: delivering simple,
reproducible application deployment workflows. When an app attempts to
roll out, it should be obvious if it succeeded. If the rollout failed,
on the other hand, errors should be clear and useful enough that most
problems are simple to fix &amp;ndash; and resuming where you left off after
fixing should be similarly just as easy. Finally, all of this should be
repeatable so that CI/CD is robust in production team environments.&lt;/p&gt;
&lt;p&gt;Like many others, we&amp;rsquo;ve used &lt;code&gt;kubectl apply&lt;/code&gt; to dump a pile of YAML into
the API server. The command returns instantly, but what happens next? Is
the app up? How does this tie back into CI/CD, so others on the team
know what&amp;rsquo;s up with a deployment? Did something fail, or get stuck?&lt;/p&gt;
&lt;p&gt;The vision in our heads, which we set out to build, was something more
like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/simple-reproducible-kubernetes-deployments/error.gif" alt="Oh no, an error!"&gt;&lt;/p&gt;
&lt;p&gt;In this example, we can clearly see the entire set of resource objects
being created, their ongoing status, and when and why something might
have failed (in this case, a missing Docker image). This is a real
screengrab of our CLI.&lt;/p&gt;
&lt;p&gt;The service works in tandem with the CLI, so we always have a history
and record of successful or failed deployments:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/simple-reproducible-kubernetes-deployments/screenshot.png" alt="deployments"&gt;&lt;/p&gt;
&lt;h2 id="lets-deploy-some-code"&gt;Let&amp;rsquo;s Deploy Some Code!&lt;/h2&gt;
&lt;p&gt;In this post, we&amp;rsquo;ll get a taste of this and several other aspects of the
Pulumi workflow by deploying a very simple app &amp;ndash; an
&lt;a href="https://www.nginx.com/"&gt;nginx&lt;/a&gt; web server &amp;ndash; to a Kubernetes cluster.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll show how to use the Pulumi &lt;code&gt;update&lt;/code&gt; command to get real-time
information about a deployment&amp;rsquo;s progress. We&amp;rsquo;ll see how to use Pulumi&amp;rsquo;s
notion of &amp;ldquo;stack outputs&amp;rdquo; to present information about successful
deployments to the user &amp;ndash; e.g. the public IP address allocated to an
application after the load balancer spins up. Finally, we&amp;rsquo;ll see how to
use Pulumi&amp;rsquo;s &lt;code&gt;diff&lt;/code&gt; command to reason about the blast radius of an
update to the application, before even attempting it.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;d like to follow along, you can find the code
&lt;a href="https://github.com/pulumi/examples/tree/master/kubernetes-ts-exposed-deployment"&gt;here&lt;/a&gt;.
In the
&lt;a href="https://github.com/pulumi/examples/tree/master/kubernetes-ts-exposed-deployment#running-the-app"&gt;README&lt;/a&gt;,
you&amp;rsquo;ll find instructions for the prerequisites &amp;ndash; installing Pulumi,
setting up a local or remote Kubernetes cluster, etc.&lt;/p&gt;
&lt;h2 id="application-config-as-code"&gt;Application Config-as-Code&lt;/h2&gt;
&lt;p&gt;Our first task in our journey is to write a Kubernetes application that
deploys nginx to the cluster and exposes it publicly to the Internet.
Kubernetes ships out of the box with useful APIs for deploying
applications, managing incremental rollouts of changes, and specifying
how traffic is directed, all of which we will use in this example.&lt;/p&gt;
&lt;p&gt;The code below as a slightly modified version of
&lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#creating-a-deployment"&gt;the &amp;ldquo;Hello World&amp;rdquo; Deployment example&lt;/a&gt;
from the Kubernetes docs. We have chosen to write this Pulumi program in
&lt;a href="https://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt;, an excellent choice for a
mix of dynamic productivity, static typing (meaning we will find errors
sooner, often at compile time!), and provides instant access to the NPM
ecosystem.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: It is also possible to directly deploy existing Kubernetes YAML
objects, as a stepping stone. See the example
&lt;a href="https://github.com/pulumi/pulumi-kubernetes/blob/master/tests/sdk/nodejs/examples/yaml-guestbook/index.ts"&gt;here&lt;/a&gt;.
This lets you reap all of the benefits of the deployment workflow
called out in this post without needing to rewrite all of your
Kubernetes YAML. It&amp;rsquo;s easy to then rewrite it piecemeal, one step at a
time, after getting up and running.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This application has two major pieces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/"&gt;Deployment&lt;/a&gt;,
which takes a template for an application, and then instantiates
some user-specified number of copies (or replicas) of that template
in the cluster.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/"&gt;Service&lt;/a&gt;,
which we will use to allocate a publicly-reachable IP address, and
to direct traffic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The code below from
&lt;a href="https://github.com/pulumi/examples/blob/master/kubernetes-ts-exposed-deployment/index.ts"&gt;index.ts&lt;/a&gt;
defines our application using these two APIs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// nginx container, replicated 3 time.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;nginx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appLabels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appName&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nginx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;k8s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v1beta1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Deployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;matchLabels&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appLabels&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;replicas&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// The application template to replicate -- just the nginx container.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;labels&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appLabels&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;containers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;nginx:1.15-alpine&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Allocate an IP to the nginx Deployment.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;frontend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;k8s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;labels&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Type `LoadBalancer` causes us to allocate a public IP address.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;LoadBalancer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;ports&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetPort&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;TCP&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appLabels&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This code creates 3 replicas of the nginx container, puts them behind a
load balancer listening on port 80, and exposes it publicly to the
Internet.&lt;/p&gt;
&lt;p&gt;Right away, we notice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pulumi does not define a new API for Kubernetes; it simply uses the
same schema as the upstream Deployment and Service
APIs. In fact, it is generated from the
&lt;a href="https://github.com/kubernetes/kubernetes/tree/master/api/openapi-spec"&gt;Kubernetes OpenAPI specification&lt;/a&gt;,
so it is always up-to-date and complete.&lt;/li&gt;
&lt;li&gt;By using TypeScript, we have the ability to eliminate repetition and
boilerplate with familiar programming language constructs, like we
did with our &lt;code&gt;appLabels&lt;/code&gt; variable that is reused multiple times.
Another example is the use of a function to capture common patterns,
as can be seen in our port of the
&lt;a href="https://github.com/pulumi/examples/blob/56ab0ab0c16f7cb1d09d748e93ef1b031a94c1c1/kubernetes-ts-guestbook/index.ts#L16"&gt;ever-familiar guestbook application&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="deploying-the-application"&gt;Deploying the Application&lt;/h2&gt;
&lt;p&gt;If we were deploying normal Kubernetes YAML, we&amp;rsquo;d run &lt;code&gt;kubectl apply&lt;/code&gt;.
This would return instantly, and it would be up to us to use ancillary
commands (e.g. &lt;code&gt;kubectl get service&lt;/code&gt;) to figure out if the application
succeeded.&lt;/p&gt;
&lt;p&gt;The Pulumi equivalent is to run &lt;code&gt;pulumi up&lt;/code&gt;. This evaluates our program,
figures out the diff and incremental update plan, shows us a preview of
what it will do, and ultimately carries it out. Under the hood, this is
using the Kubernetes Go client library, and &amp;ndash; although we will still
use &lt;code&gt;kubectl&lt;/code&gt; to inspect and interact with cluster state, the entire
deployment process is subsumed by &lt;code&gt;pulumi&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Indeed, if we take our project and simply run &lt;code&gt;pulumi up&lt;/code&gt; we&amp;rsquo;ll see
something like the following:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/simple-reproducible-kubernetes-deployments/deploy.gif" alt="deploy"&gt;&lt;/p&gt;
&lt;p&gt;Compared to &lt;code&gt;kubectl apply&lt;/code&gt;, many things are happening:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;We see that &lt;code&gt;pulumi up&lt;/code&gt; blocks until all Kubernetes resources have
completed initialization. In this case, that includes a Deployment
and a Service.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We see intermediate status messages as Kubernetes resources make
progress towards initialization. For example the Service called
&lt;code&gt;nginx&lt;/code&gt; alerts us when (1) it successfully found application
containers to direct traffic to, and (2) that an IP address has been
allocated to it. If things fail, we will see that too.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All deployment is coordinated with a state manager so that
concurrent updates are handled correctly in a team setting, rather
than potentially clobbering one another.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you look closely, at the end of the gif loop, you&amp;rsquo;ll notice some
green text that looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ---outputs:---
frontendIp: &amp;quot;35.226.79.25&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This is the IP that was allocated to the Service, which in turn
directs traffic to our nginx containers. If you run &lt;code&gt;pulumi up&lt;/code&gt;
yourself and open that IP address in your browser, easily available
with the &lt;code&gt;pulumi stack output frontendIp&lt;/code&gt; command, you will actually
see the nginx landing page.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="using-stack-outputs"&gt;Using Stack Outputs&lt;/h2&gt;
&lt;p&gt;As a brief aside, you might have wondered: how did this IP address get
here?&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;kubectl apply&lt;/code&gt; completes, it is difficult to automatically obtain
values like the IP address allocated to a Service. It can take many
minutes for the IP address to be allocated, and most users simply resort
to running &lt;code&gt;kubectl get service&lt;/code&gt; repeatedly, until it appears. This is
cumbersome and makes it hard to use reliably.&lt;/p&gt;
&lt;p&gt;In Pulumi, such values are first-class citizens &amp;ndash; called stack outputs.
Because Pulumi has a notion of &amp;ldquo;done-ness&amp;rdquo;, and because &lt;code&gt;pulumi up&lt;/code&gt; will
block until all resources are finished initializing, it is trivial to
&amp;ldquo;export&amp;rdquo; values from a fully-initialized resource. In this case,
&lt;code&gt;frontendIp&lt;/code&gt; simply contains the IP address obtained from the
fully-initialized Service that directs traffic to the nginx containers
&amp;ndash; the IP address allocated to it.&lt;/p&gt;
&lt;p&gt;We could take any value from any Kubernetes resource and put it in any
variable &amp;ndash; in this case, we just happen to have exported the IP address
using a variable &lt;code&gt;frontendIp&lt;/code&gt;. The code to do this export looks like
this (at the bottom of index.ts):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;frontendIp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;frontend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadBalancer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ingress&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Obtaining this value from the CLI is trivial using the
&lt;code&gt;pulumi stack output&lt;/code&gt; command. Here we use &lt;code&gt;curl&lt;/code&gt; to get find the title
of the nginx landing page that &lt;code&gt;frontendIp&lt;/code&gt; points at.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ curl -sL $(pulumi stack output frontendIp) | grep &amp;quot;&amp;lt;title&amp;gt;&amp;quot;
&amp;lt;title&amp;gt;Welcome to nginx!&amp;lt;/title&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="updating-our-application"&gt;Updating our Application&lt;/h2&gt;
&lt;p&gt;Pulumi makes it easy to incrementally update our application too, after
the initial creation. &lt;code&gt;pulumi up&lt;/code&gt; just handles this automatically for
us, by comparing its notion of a goal state to the cluster&amp;rsquo;s current
state, and using that to devise a plan. Because of this, we get reliable
preview diffs.&lt;/p&gt;
&lt;p&gt;When running &lt;code&gt;kubectl apply&lt;/code&gt;, it is often difficult to reason about what
changes will be made to a running application, before actually carrying
out the changes. This makes it difficult to predict the impact an update
will have, including whether there would be potential downtime. Put
simply, this can lead to accidents.&lt;/p&gt;
&lt;p&gt;In contrast, in Pulumi the idea of a &lt;code&gt;diff&lt;/code&gt; is a first-class concept,
and is front and center in the workflow. To see this in action, try
changing the container image from &lt;code&gt;nginx:1.15-alpine&lt;/code&gt; to
&lt;code&gt;nginx:alpine-1.16-alpine&lt;/code&gt; in
&lt;a href="https://github.com/pulumi/examples/blob/master/kubernetes-ts-exposed-deployment/index.ts"&gt;index.ts&lt;/a&gt;,
and then run &lt;code&gt;pulumi preview --diff&lt;/code&gt;. You will see something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/simple-reproducible-kubernetes-deployments/diff.gif" alt="diff"&gt;&lt;/p&gt;
&lt;p&gt;You can see several things happening here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The diff captures the change in the nginx image.&lt;/li&gt;
&lt;li&gt;It reports that the nginx Deployment resource will be updated
in-place. Replaces may also be replaced or deleted,
which would be evident from the diff also.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The intention here is to give users an intuition for the blast radius of
the changes to a Kubernetes application.&lt;/p&gt;
&lt;p&gt;Running diffs manually by hand isn&amp;rsquo;t necessary, despite being useful.
Unless you explicitly bypass it, running &lt;code&gt;pulumi up&lt;/code&gt; will always show
you a preview first and confirm that you&amp;rsquo;d like to proceed before
actually making any changes. And because Pulumi is always tracking old
states, rollback afterwards is easy also.&lt;/p&gt;
&lt;p&gt;This CLI workflow is great for the dev inner loop, however
&lt;a href="https://www.pulumi.com/docs/iac/packages-and-automation/continuous-delivery/github-app/"&gt;the Pulumi GitHub App&lt;/a&gt; also integrates
these previews and diffs with your CI/CD system, by enlightening your
GitHub Pull Requests with potential update impacts, while your team
still has a chance to discuss changes inside the usual PR workflow,
enabling powerful GitOps scenarios.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this article, you&amp;rsquo;ve seen that you can express Kubernetes apps in
real code, instead of YAML, and you&amp;rsquo;ve gotten a taste of the &lt;code&gt;pulumi up&lt;/code&gt;
deployment workflow, especially compared to &lt;code&gt;kubectl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Try it out now&lt;/strong&gt; by heading over to our
&lt;a href="https://www.pulumi.com/docs/get-started/"&gt;Getting Started page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We are just getting started. In the coming weeks we will be sharing more
around other deployment scenarios, including A/B traffic splitting,
canaries guarded by Prometheus metrics, Kubernetes application code
living alongside AWS infrastructure, triggering of cascading rollouts
based on ConfigMap changes&amp;hellip; And much, much, more.&lt;/p&gt;
&lt;p&gt;If you have specific use cases you&amp;rsquo;d like to see us tackle, don&amp;rsquo;t
hesitate to reach out, either
&lt;a href="https://github.com/pulumi/pulumi"&gt;on GitHub&lt;/a&gt; or in our
&lt;a href="https://slack.pulumi.com/"&gt;Pulumi Community Slack&lt;/a&gt;. We&amp;rsquo;d love to hear from you!&lt;/p&gt;</description><author>Alex Clemmer</author><category>kubernetes</category><category>typescript</category></item></channel></rss>