<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Gahl Saraf</title>
    <description>The latest articles on Forem by Gahl Saraf (@gsaraf).</description>
    <link>https://forem.com/gsaraf</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1116873%2F1cbd31cb-cc2a-450e-9546-55d6684c33ca.png</url>
      <title>Forem: Gahl Saraf</title>
      <link>https://forem.com/gsaraf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gsaraf"/>
    <language>en</language>
    <item>
      <title>Creating Kube or Fake with ChatGPT</title>
      <dc:creator>Gahl Saraf</dc:creator>
      <pubDate>Tue, 01 Aug 2023 16:37:03 +0000</pubDate>
      <link>https://forem.com/gsaraf/creating-kube-or-fake-with-chatgpt-1noe</link>
      <guid>https://forem.com/gsaraf/creating-kube-or-fake-with-chatgpt-1noe</guid>
      <description>&lt;p&gt;&lt;strong&gt;Guest post by Assaf Avital, the mastermind who developed &lt;a href="//kube-or-fake.raftt.io"&gt;Kube or Fake!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hi all! In this blog post, I’ll guide you through creating your own &lt;em&gt;Kube or Fake?&lt;/em&gt; mini-game using ChatGPT. For those of you who joined late, &lt;em&gt;Kube or Fake?&lt;/em&gt; is a Kubernetes/ChatGPT mini-game created by Raftt, where the player must distinguish between real Kubernetes terms and fake ChatGPT-generated ones (and it all happens live 💪). If you haven’t already tried it, &lt;a href="https://kube-or-fake.raftt.io"&gt;kick back and enjoy&lt;/a&gt;.&lt;br&gt;
First, we will get familiar with the ChatGPT API and see how we can use it to generate text. We will then see how we can integrate it into a small funky app, wrap it nicely enough, and publish it for the whole world! (or a couple of friends).&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1 - Using ChatGPT to Generate K8s Terms
&lt;/h2&gt;

&lt;p&gt;The most important part of our mini-game is having ChatGPT generate Kubernetes terms (either real or fake). Since we want to use ChatGPT to power our app, let’s have it return the result in a structured syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;generated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;term&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isReal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;*True*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;term&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;real&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;*False*&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;term&amp;gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Attempt #1: Making ChatGPT flip a coin
&lt;/h3&gt;

&lt;p&gt;Our initial approach was letting ChatGPT decide whether the generated Kubernetes term should be real or fake, using a prompt as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;openai&lt;/span&gt;
&lt;span class="err"&gt;‍&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""You are a Kubernetes expert, and also a game master.
You are 50% likely to respond with a real Kubernetes term (either a resource kind or field name), and 50% likely to make up a fake term which resembles a real one, in order to confuse the player.
Your response syntax is:
{
  “term”: your generated term,
  “isReal”: true if the term is real, else false,
  “description”: a short description of the term. If it’s fake, don’t mention it
}"""&lt;/span&gt;
  &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChatCompletion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"gpt-3.5-turbo-16k-0613"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the API call &lt;code&gt;openai.ChatCompletion.create(...)&lt;/code&gt; which requires choosing a specific GPT model to use, and the structure of messages we pass to it.&lt;br&gt;
When getting the response back, we retrieve its content as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="err"&gt;‍&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'choices'&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="s"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;'content'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;term&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Since we instructed ChatGPT to respond with a JSON string
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Overall, this attempt worked pretty well, however the perceived probability of generating real terms vs. fake ones wasn’t 50/50. After running the same prompt for a few times, it became pretty obvious that ChatGPT was a bit biased towards real Kubernetes terms. We can overcome this by either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fine-tuning the probabilities in the prompt (&lt;em&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/em&gt;&lt;strong&gt;&lt;/strong&gt;e.g.&lt;strong&gt;&lt;/strong&gt; 30% real / 70% term) such that the actual probability is closer to 50/50.&lt;/li&gt;
&lt;li&gt;Extracting the coin flip to the code, and writing a different prompt for each side of the coin.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Attempt #2: Flip the coin before invoking ChatGPT API
&lt;/h3&gt;

&lt;p&gt;We’ll need two different prompts for this approach, depending on the coin flip result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt #1&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a Kubernetes expert.
Generate a real Kubernetes term, which is either a resource kind or a field name.
Response syntax:
{
  “term”: your generated term,
  “isReal”: true,
  “description”: a short description of the term
 }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prompt #2&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a Kubernetes expert.
Generate a random, fake Kubernetes term, which resembles a resource kind or a field name.
An average software developer should not be able to immediately realize this is a fake term.
Make sure that this is in fact, not a real Kubernetes term. 
Response syntax:
{
 “term”: your generated term,
 “isReal”: false,
 “description”: a short description of the term, don’t mention it is fake
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll make the necessary adjustments to our code, and add the coin flip logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;openai&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt;
&lt;span class="err"&gt;‍&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="n"&gt;prompts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;REAL_PROMPT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FAKE_PROMPT&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompts&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="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChatCompletion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"gpt-3.5-turbo-16k-0613"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach worked &lt;em&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/em&gt;much&lt;strong&gt;&lt;/strong&gt; better, and the generated probability was lot closer to 50/50 😛&lt;/p&gt;

&lt;h3&gt;
  
  
  Attempt #3: Adding a 3rd side to the coin
&lt;/h3&gt;

&lt;p&gt;The previous approach was pretty good output-wise, and met our requirements almost perfectly. Now, the only thing missing from this game is the “oh-my-god-chatgpt-is-so-random” funny part!&lt;br&gt;
To spice it up just a bit, let’s add a &lt;em&gt;&lt;em&gt;third&lt;/em&gt;&lt;/em&gt; prompt and let ChatGPT run wild:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a Kubernetes expert.
Generate a random, obviously fake Kubernetes term, which resembles a resource kind or a field name.
An average software developer should be able to immediately realize this is a fake term,
Make sure that this is in fact, not a real Kubernetes term.
Extra points for coming up with a funny term.
Response syntax:
{
 “term”: your generated term,
 “isReal”: false,
 “description”: a short description of the term, don’t mention it is fake
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can either keep the prompts equally likely (~33% chance for each), or play with the probability values as we see fit. “Prompt engineering” often leads to add-on sentences like “Make sure that this is in fact, not a real Kubernetes term”, because the model makes many mistakes and this helps keep it on track.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying the term generator as an AWS Lambda
&lt;/h2&gt;

&lt;p&gt;We’ll soon get to the code of the game itself, but first let’s publish our term-generating code so it is publicly available. For this purpose, let’s use AWS Lambda.&lt;/p&gt;

&lt;p&gt;We want the Lambda handler to do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flip a (3-sided 🙃) coin (using &lt;code&gt;random&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Depending on the flip result, either:

&lt;ul&gt;
&lt;li&gt;Generate a real Kubernetes term&lt;/li&gt;
&lt;li&gt;Generate a not-so-obviously-fake Kubernetes term, to make the game a bit more difficult&lt;/li&gt;
&lt;li&gt;Generate an obviously-fake Kubernetes term, to make this game a bit more funny&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Respond with the JSON generated by ChatGPT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ll be using Python as my Lambda runtime, but this is easily achievable with other runtimes as well.&lt;/p&gt;

&lt;p&gt;The general structure of our Lambda code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;openai&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt;

&lt;span class="err"&gt;‍&lt;/span&gt;
&lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OPENAI_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;OPENAI_MODEL_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OPENAI_MODEL_NAME"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;‍&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prompts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;REAL_PROMPT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FAKE_PROMPT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OBVIOUSLY_FAKE_PROMPT&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompts&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="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'choices'&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="s"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;'content'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;'statusCode'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;'headers'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"X-Requested-With"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Access-Control-Allow-Headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Requested-With'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Access-Control-Allow-Origin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Access-Control-Allow-Methods"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'GET, OPTIONS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Access-Control-Allow-Credentials"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt; &lt;span class="c1"&gt;# Required for cookies, authorization headers with HTTPS 
&lt;/span&gt;        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;'body'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll cover two easy ways to deploy the lambda code to AWS. Use whichever is more convenient for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Method 1: AWS Console
&lt;/h3&gt;

&lt;p&gt;One approach to creating the Lambda function is via AWS Console. Using this approach we don’t bind the ChatGPT logic to our game repo, which allows us to quickly make changes to the Lambda without having to commit, push, and re-deploy our website.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating the OpenAI Lambda Layer
&lt;/h4&gt;

&lt;p&gt;AWS Lambda Layers are a way to manage common code and libraries across multiple Lambda functions. A layer is made up of code and libraries that are organized into a package that can be reused across multiple functions.&lt;/p&gt;

&lt;p&gt;But why do we need them? Well, unfortunately, AWS Lambda’s Python runtimes do not include the &lt;code&gt;openai&lt;/code&gt; package by default. Hence, we must provide it as a layer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the &lt;code&gt;openai&lt;/code&gt; package locally, in a folder called ‘python’:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;python
pip &lt;span class="nb"&gt;install &lt;/span&gt;openai &lt;span class="nt"&gt;-t&lt;/span&gt; python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;After the installation is complete, zip the folder:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zip &lt;span class="nt"&gt;-r&lt;/span&gt; openai.zip python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create the Lambda layer by going to the Lambda console → Layers → Create Layer. Provide a name for the layer, and select “Upload a .zip file”. Under “Compatible runtimes”, select Python 3.9. The final screen should look like this:
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jOqkiYNa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://uploads-ssl.webflow.com/627bf36ecef36c976239c7b6/64c8f484e7f4e8715fd420f5_layer-configuration.png" alt="" width="768" height="761"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Creating the Lambda Function
&lt;/h4&gt;

&lt;p&gt;Now we’re ready to create the actual Lambda! Go to Functions → Create Function, and choose Python 3.9 as your runtime. The code editor should come up shortly.&lt;br&gt;
To use our newly created layer, click Layers → Add a layer. Choose “Custom layers” as the source and pick the layer from the dropdown. When finished, click “Add”.&lt;/p&gt;

&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g1Jb-Nsl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://uploads-ssl.webflow.com/627bf36ecef36c976239c7b6/64c8f49506d27603cb236f99_layers.png" alt="" width="786" height="396"&gt;

&lt;p&gt;Next, we’ll define our environment variables. Go to Configuration → Environment variables and set the following env vars:&lt;/p&gt;

&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2-hIoOwv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://uploads-ssl.webflow.com/627bf36ecef36c976239c7b6/64c8f4ae76f82867df1ff32c_env-vars.jpg" alt="" width="800" height="227"&gt;

&lt;p&gt;You can use any model you’d like, I’ve chosen &lt;strong&gt;gpt-3.5-turbo-16k-0613&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now we’re ready to code! Go to “Code”,  open up the web IDE and paste the code snippet above. Click “Deploy” to publish the function. You can set up a function URL by going to Configuration → Function URL.&lt;/p&gt;
&lt;h3&gt;
  
  
  Method 2: SAM
&lt;/h3&gt;

&lt;p&gt;Another approach to AWS Lambdas is using &lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html"&gt;SAM&lt;/a&gt;, which allows us to deploy Lambdas either locally or to our AWS account.&lt;/p&gt;
&lt;h4&gt;
  
  
  Writing the Lambda handler
&lt;/h4&gt;

&lt;p&gt;We’ll create a file &lt;code&gt;lambda/lambda.py&lt;/code&gt; in our repository, and have the handler code written there.&lt;/p&gt;
&lt;h4&gt;
  
  
  Creating a local Lambda layer
&lt;/h4&gt;

&lt;p&gt;In order to create the &lt;code&gt;openai&lt;/code&gt; layer locally, we should install it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt &lt;span class="nt"&gt;-t&lt;/span&gt; libs/python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Creating a CloudFormation template
&lt;/h4&gt;

&lt;p&gt;To deploy the Lambda, we first must set up a &lt;a href="https://aws.amazon.com/cloudformation/"&gt;CloudFormation&lt;/a&gt; template in our project root directory, named &lt;code&gt;template.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
&lt;span class="na"&gt;Transform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless-2016-10-31&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;OpenAILambdaLayer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::LayerVersion&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;LayerName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai&lt;/span&gt;
      &lt;span class="na"&gt;ContentUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;libs&lt;/span&gt;
  &lt;span class="na"&gt;GenerateKubernetesTermFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;lt&lt;/span&gt;&lt;span class="s"&gt;;&amp;amp;lt; replace &amp;amp;gt;&amp;amp;gt;&lt;/span&gt;
          &lt;span class="na"&gt;OPENAI_MODEL_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;lt&lt;/span&gt;&lt;span class="s"&gt;;&amp;amp;lt; replace &amp;amp;gt;&amp;amp;gt;&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda/&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda.lambda_handler&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3.10&lt;/span&gt;
      &lt;span class="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
      &lt;span class="na"&gt;FunctionUrlConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AuthType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NONE&lt;/span&gt;
      &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;GenerateTerm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Api&lt;/span&gt;
          &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/generate&lt;/span&gt;
            &lt;span class="na"&gt;Method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;get&lt;/span&gt;
      &lt;span class="na"&gt;Layers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;OpenAILambdaLayer&lt;/span&gt;

&lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;GenerateKubernetesTermFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;GenerateKubernetesTermFunction.Arn&lt;/span&gt;
  &lt;span class="na"&gt;GenerateKubernetesTermFunctionIAMRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;GenerateKubernetesTermFunctionRole.Arn&lt;/span&gt;
  &lt;span class="na"&gt;GenerateKubernetesTermFunctionURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;GenerateKubernetesTermFunctionUrl.FunctionUrl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The template above defines the resources that will be deployed as part of the CloudFormation stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda function + layer&lt;/li&gt;
&lt;li&gt;IAM Role associated with the Lambda function&lt;/li&gt;
&lt;li&gt;API Gateway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From here we can deploy the Lambda function either locally, or to AWS.&lt;/p&gt;

&lt;h4&gt;
  
  
  Local Lambda deployment
&lt;/h4&gt;

&lt;p&gt;The Lambda can be run locally with &lt;code&gt;sam&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sam &lt;span class="nb"&gt;local &lt;/span&gt;start-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command starts a server running in &lt;code&gt;localhost:3000&lt;/code&gt;. The command output should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mounting GenerateKubernetesTermFunction at &amp;amp;lt;http://127.0.0.1:3000/generate&amp;amp;gt; [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. If you used sam
build before running local commands, you will need to re-run sam build for the changes to be picked up. You only need to restart SAM CLI if you update your AWS SAM template
2023-07-20 11:58:51 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on &amp;amp;lt;http://127.0.0.1:3000&amp;amp;gt;
2023-07-20 11:58:51 Press CTRL+C to quit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the Lambda is invoked via &lt;code&gt;localhost:3000/generate&lt;/code&gt;, some more logs are shown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Invoking lambda.lambda_handler (python3.10)
OpenAILambdaLayer is a local Layer in the template
Local image is up-to-date
Building image.....................
Using local image: samcli/lambda-python:3.10-x86_64-b22538ac72603f4028703c3d1.
‍
Mounting kube-or-fake/lambda as /var/task:ro,delegated, inside runtime container
START RequestId: b1c733b3-8449-421b-ae6a-fe9ac2c86022 Version: $LATEST
END RequestId: b1c733b3-8449-421b-ae6a-fe9ac2c86022
REPORT RequestId: b1c733b3-8449-421b-ae6a-fe9ac2c86022
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
&lt;em&gt;‍&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/em&gt; You may be requested to provide your local machine credentials to allow &lt;code&gt;sam&lt;/code&gt; interacting with your local docker daemon.&lt;/p&gt;

&lt;p&gt;Be aware that the Lambda docker image will be built upon each invocation, and that there is no need to re-run &lt;code&gt;sam local start-api&lt;/code&gt; when making changes to the Lambda code (changes to &lt;code&gt;template.yml&lt;/code&gt; &lt;em&gt;&lt;em&gt;do&lt;/em&gt;&lt;/em&gt; require a re-run though).&lt;/p&gt;
&lt;h4&gt;
  
  
  Deploying to AWS
&lt;/h4&gt;

&lt;p&gt;We do this using &lt;code&gt;sam&lt;/code&gt; as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sam deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the command's output to see where your new Lambda is created (&lt;code&gt;GenerateKubernetesTermFunctionURL&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bqAKrchf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://uploads-ssl.webflow.com/627bf36ecef36c976239c7b6/64c8f89c6c53b95f97bc8692_cloudformation-output.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bqAKrchf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://uploads-ssl.webflow.com/627bf36ecef36c976239c7b6/64c8f89c6c53b95f97bc8692_cloudformation-output.png" alt="" width="800" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 - Getting JSy With It
&lt;/h2&gt;

&lt;p&gt;Our generator’s already up, all we have to do is create the game’s UX.&lt;br&gt;
There are many ways to implement this, but for the purpose of this guide I’ll be focusing on the raw JS script we’ll be using.&lt;/p&gt;
&lt;h3&gt;
  
  
  Generate a Term by Invoking Lambda
&lt;/h3&gt;

&lt;p&gt;We want the browser to make the call to our Lambda and generate a term. This can be done with &lt;code&gt;fetch&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GENERATOR_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{your generated function url}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;generateWord&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GENERATOR_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Implement “Guess” Logic
&lt;/h3&gt;

&lt;p&gt;We want to show a result according to the user’s choice (”KUBE” or “FAKE”).&lt;/p&gt;

&lt;p&gt;Here’s the relevant code, assuming we’ve created the buttons in the HTML already:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;checkGuess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;guess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;correctGuess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;guess&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isReal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;correctGuess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// user is correct - show good message&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// user is wrong - show bad message&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;‍&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isReal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// show description&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is an AI hallucination.`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;‍&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game-board&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetClass&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;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kube-button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;checkGuess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;checkGuess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And… that’s basically it! Of course you can add a lot more (score-keeping, animations, share buttons, etc.) but that’s completely up to you.&lt;/p&gt;

&lt;p&gt;This may be the place to note that there are all kinds of fancy UI libraries for web. React, Angular, Vue, … As you might have been able to tell from the website itself, we know none of these. 😄&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 - Deploy to GitHub Pages
&lt;/h2&gt;

&lt;p&gt;We want the game to be publicly accessible, right? Let’s use GitHub pages to make it work. In your repo, go to Settings → Pages. Select a branch to deploy from, and choose your root folder as the source → Click “Save”. A workflow called “pages build and deployment” should start instantly:&lt;br&gt;
‍&lt;br&gt;
This workflow will now be triggered by every push to your selected branch!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;This was a blast to create, and [I hope] to play with. Don’t forget to share your results - we’d love to see forks of the game with changed UX or logic 🙂&lt;br&gt;
Check out all the code in the repo - &lt;a href="https://github.com/rafttio/kube-or-fake"&gt;https://github.com/rafttio/kube-or-fake&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>kubernetes</category>
      <category>devops</category>
    </item>
    <item>
      <title>Kube or Fake: A Kubernetes Minigame</title>
      <dc:creator>Gahl Saraf</dc:creator>
      <pubDate>Wed, 26 Jul 2023 12:15:02 +0000</pubDate>
      <link>https://forem.com/gsaraf/kube-or-fake-a-kubernetes-minigame-52jf</link>
      <guid>https://forem.com/gsaraf/kube-or-fake-a-kubernetes-minigame-52jf</guid>
      <description>&lt;p&gt;Raftt is excited to announce the launch of &lt;em&gt;Kube or Fake?&lt;/em&gt;, our latest web mini-game. In this game, you will be presented with a series of Kubernetes terms generated in real-time by ChatGPT, and will need to determine whether they are real or AI hallucinations. Be warned, the descriptions accompanying each term may be tricky.&lt;/p&gt;

&lt;p&gt;We built &lt;em&gt;Kube or Fake?&lt;/em&gt; with ChatGPT API, AWS Lambda, and some JavaScript. Our goal was to create a fun and interactive way to test your knowledge of Kubernetes terminology. This game is perfect for Kubernetes enthusiasts and newcomers alike.&lt;/p&gt;

&lt;p&gt;So, how do you play? Simply visit &lt;a href="https://kube-or-fake.raftt.io"&gt;the game&lt;/a&gt; and start playing! Try to correctly identify each of the terms as either “Kube” or “Fake”. Challenge your friends and see who can get the highest score!&lt;/p&gt;

&lt;p&gt;We hope you enjoy playing &lt;em&gt;Kube or Fake?&lt;/em&gt; as much as we enjoyed building it. Let us know what you think, and stay tuned for the Making Of, where we dig into how we built this and why :).&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>gpt3</category>
      <category>ai</category>
      <category>devops</category>
    </item>
    <item>
      <title>Develop Right on Top of Your Kubernetes Cluster</title>
      <dc:creator>Gahl Saraf</dc:creator>
      <pubDate>Mon, 24 Jul 2023 07:20:00 +0000</pubDate>
      <link>https://forem.com/gsaraf/develop-right-on-top-of-your-kubernetes-cluster-2id0</link>
      <guid>https://forem.com/gsaraf/develop-right-on-top-of-your-kubernetes-cluster-2id0</guid>
      <description>&lt;p&gt;In recent years, Kubernetes has emerged as the de facto standard for container orchestration, providing an efficient and scalable platform for deploying and managing applications. As developers strive to streamline their workflows and optimize productivity, an emerging trend is developing applications directly on top of the Kubernetes cluster itself. In this blog post, we will explore the benefits of developing right on top of your Kubernetes cluster, and dive into the challenges that make this a difficult reality to achieve.&lt;/p&gt;

&lt;p&gt;Starting with the benefits - &lt;/p&gt;

&lt;h3&gt;
  
  
  Reduced env variance
&lt;/h3&gt;

&lt;p&gt;By developing directly on your Kubernetes cluster, you eliminate the need for a separate development environment or local setup. This streamlines the development process, reducing the overhead of replicating the cluster setup locally, and therefore the time spent on setting up and maintaining multiple environments.&lt;/p&gt;

&lt;p&gt;In a complex project there may be multiple flavors of production environments along with a host of staging, test, and preview environments. Having to maintain yet another environment type (which is used by the entire dev team!) is a huge time and attention sink for both developers and devops.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consistent development and production envs
&lt;/h3&gt;

&lt;p&gt;Catch and resolve issues early on, and reduce the risk of surprises during deployment by using the same tools, libraries, and configurations as the production environment. Developing on top of your Kubernetes cluster ensures that your development environment is aligned with the production environment. This consistency eliminates the "works on my machine" problem, where code behaves differently in different environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Seamless integration with cluster services
&lt;/h3&gt;

&lt;p&gt;Developing on top of your Kubernetes cluster gives you direct access to cluster services and features. You can easily integrate with monitoring tools, logging systems, service meshes, and other cluster-specific resources. This allows you to develop applications that are tightly coupled with the underlying infrastructure, leveraging the full capabilities of Kubernetes and maximizing the efficiency of your applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Improved collaboration and teamwork
&lt;/h3&gt;

&lt;p&gt;Kubernetes provides powerful features for collaboration, allowing multiple developers to work simultaneously on the same cluster. With development happening directly on the cluster, team members can easily share links to environments (using Kubernetes Ingress objects). This enhances teamwork and promotes knowledge sharing, as developers can observe and learn from each other's work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simplified testing
&lt;/h3&gt;

&lt;p&gt;Developing directly on your Kubernetes cluster simplifies the testing process. Since you are working on the same environment where your application will run, you can easily reproduce and analyze issues that might arise during deployment. This reduces the time spent on recreating issues locally and provides a more accurate representation of how the application behaves in the production environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O0_gzFR1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3oqqnt3nti584nmohxgq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O0_gzFR1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3oqqnt3nti584nmohxgq.png" alt="Clusters in the cloud" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So why isn’t everyone developing on top of their Kubernetes cluster?
&lt;/h2&gt;

&lt;p&gt;Unfortunately, there are a couple of significant challenges that make developing on top of a cluster difficult - &lt;/p&gt;

&lt;h3&gt;
  
  
  Container image immutability
&lt;/h3&gt;

&lt;p&gt;Containers are designed to be immutable, meaning they can not be modified once built. While this immutability promotes scalability and consistency, it poses a big challenge for developers - it is no longer possible to make code changes and see fast feedback! Instead, developers rely on their CI pipeline to deploy new images, which extends the feedback cycle from seconds to 30+ minutes.&lt;/p&gt;

&lt;p&gt;Long cycles leads to bundling many changes together, which in turn causes each cycle’s success rate to drop, and inevitably dev speed is slowed significantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inability to debug
&lt;/h3&gt;

&lt;p&gt;To make matters worse, it is difficult to set up proper debugging for code running in a Kubernetes cluster. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The container image does not contain the required debugging toolchain (&lt;code&gt;debugpy&lt;/code&gt; for Python, &lt;code&gt;dlv&lt;/code&gt; for Go, etc.).&lt;/li&gt;
&lt;li&gt;The container may not have the required security configuration (read only root FS / missing capabilities / …)&lt;/li&gt;
&lt;li&gt;Ports need to be opened between the IDE and the container running in the cluster.&lt;/li&gt;
&lt;li&gt;The IDE needs to be configured with the correct debugging configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these mean that practically - developers cease debugging once their env is running on Kubernetes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Complexity
&lt;/h3&gt;

&lt;p&gt;Kubernetes is a highly sophisticated and complex system. While some developers have a strong grasp of the basic Kubernetes concepts, the learning curve can be steep and it can slow down developers significantly. &lt;/p&gt;

&lt;h3&gt;
  
  
  Lack of access
&lt;/h3&gt;

&lt;p&gt;In most organizations, access to Kubernetes clusters (even those intended for development) is not available to all developers. For good reason too - it is easy to make mistakes and cause damage that takes time and resources to repair, and the teams maintaining the clusters are wise to take proactive measures to minimize the chance of that. The larger the organization, and the more complex the deployment, the higher the chance of limitations being in place for its clusters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost and environment management
&lt;/h3&gt;

&lt;p&gt;Managing the cost of a Kubernetes cluster is &lt;a href="https://dev.to/gsaraf/reducing-cloud-costs-on-kubernetes-dev-envs-318"&gt;an art in itself&lt;/a&gt;. Significantly increasing the number of environments due to their use in development can increase costs significantly. Managing the large number of dev environments can add a lot of overhead to already overextended DevOps or platform teams. Existing tooling intended to provide visibility into a limited number of environments may not scale well to the higher usage. And differences in configuration between environment types can cause overhead and false-positives.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SbsOlh5H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6lc9uem8j43segam4btd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SbsOlh5H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6lc9uem8j43segam4btd.png" alt="Touch the cluster" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait - how is &lt;strong&gt;anyone&lt;/strong&gt; developing on their Kubernetes cluster?
&lt;/h2&gt;

&lt;p&gt;Luckily, there are all kinds of tools that can be used to manage and develop on dev environments on Kubernetes. Everything from Spinnaker to ArgoCD and FluxCD for orchestration, and various solutions for the developer experience layer.&lt;/p&gt;

&lt;p&gt;We use our own product (&lt;a href="https://raftt.io"&gt;Raftt&lt;/a&gt;), which handles all of this complexity [and more]. Raftt adapts the Kubernetes environment for development, enabling out of the box hot-reloading of code and debugging, connected directly to your local machine and IDE. Raftt’s optional cluster-level controller handles environment provisioning and lifecycle, minimizing maintenance and additional costs.&lt;/p&gt;

&lt;p&gt;You are left with a great development experience (including code hot reloading, debugging, collaboration…) that is easy to manage and deploy. You can try it yourself - deploy Raftt for development on your Kubernetes cluster, with &lt;a href="https://docs.raftt.io/basics/tutorials/connect_mode"&gt;our demo project&lt;/a&gt; or with &lt;a href="https://docs.raftt.io/basics/onboard_project"&gt;your own code&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>cloudnative</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Reducing Cloud Costs on Kubernetes Dev Envs</title>
      <dc:creator>Gahl Saraf</dc:creator>
      <pubDate>Wed, 19 Jul 2023 06:57:09 +0000</pubDate>
      <link>https://forem.com/gsaraf/reducing-cloud-costs-on-kubernetes-dev-envs-318</link>
      <guid>https://forem.com/gsaraf/reducing-cloud-costs-on-kubernetes-dev-envs-318</guid>
      <description>&lt;p&gt;At &lt;a href="http://raftt.io"&gt;Raftt&lt;/a&gt;, we’ve gone to great lengths to reduce the cloud costs for environments running on our development cluster, both for our internal usage and for our customers. In this blog post I’ll cover all the adaptations we made, so you can apply them to your own infrastructure. For each, I’ll add the approximate savings, and in the end I’ve attached a summary. &lt;/p&gt;

&lt;p&gt;We primarily use AWS and EKS for our managed cloud clusters, so there will be certain parts of the post that will be a bit more relevant if you are using those (though the concepts carry over to any other cloud provider). We’ve covered the most significant factors, and through these were able to save over 95%, but there is lots more you can do. For instance - reduce persistent volume sizes or remove some of CloudWatch logging. I’ll start with infrastructure-level modifications - sharing clusters, autoscaling, right-sizing nodes, and using spot instances:&lt;/p&gt;

&lt;h2&gt;
  
  
  Infrastructure Adaptations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Shared clusters
&lt;/h3&gt;

&lt;p&gt;This one may be a bit obvious, but it has to be said - while our production runs on its own Kubernetes cluster, we do not want a cluster for each preview or dev environment. This is because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It takes a long time to create Kubernetes clusters. In EKS, this is 10-15 minutes, including the time it takes for nodes to spin up. Other distributions are faster, but it still slows down development.&lt;/li&gt;
&lt;li&gt;There are a lot of static costs associated with each cluster. For our EKS setup, this ends up being around $100 / month, including the EKS backend (~$70), an NLB load balancer (~$21), some CloudWatch logs (~$10).&lt;/li&gt;
&lt;li&gt;Different clusters cannot share the same nodes, so resource sharing is impossible, and we end up spending much more on EC2 instances.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, we will create a single long-lived cluster, and deploy our application in different namespaces. There are a bunch of ways to do that - see &lt;a href="https://argoproj.github.io/cd"&gt;ArgoCD&lt;/a&gt;, &lt;a href="https://fluxcd.io/"&gt;Flux&lt;/a&gt;, custom internal tooling, or other solutions (we use our own product). That way, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only setup the cluster and infra once, and only incur the costs once.&lt;/li&gt;
&lt;li&gt;Are able to share the underlying resources (more on that below).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Autoscaling
&lt;/h3&gt;

&lt;p&gt;The most significant single factor in the cost of most Kubernetes clusters is the compute powering the cluster’s nodes. Several factors affect their cost, and the first we will cover is autoscaling. In cloud-based clusters with different levels of utilization, autoscaling is a must. It can reduce costs by an order of magnitude. Specifically for a cluster used for development purposes, and assuming we have infrastructure that can bring up and scale down environments as needed, this means cloud instances can be taken down (automatically):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Over the weekends, saving 48 hours a week&lt;/li&gt;
&lt;li&gt;Outside of working hours, saving 14 hours a day for working days (another 70 hours a week)&lt;/li&gt;
&lt;li&gt;Holidays, and other off days - another 15 days a year&lt;/li&gt;
&lt;li&gt;Since we are talking about environments used for developments, we can also scale down on days where people are on personal leave - another 20ish days a year.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All told, we get to &lt;code&gt;(365-(52*2)-15-20)=226&lt;/code&gt; working days per engineer, and with 10ish hours of work per day - around 2260 hours, or around &lt;code&gt;2260/(365*24)=1/4&lt;/code&gt; of the total yearly time.&lt;/p&gt;

&lt;p&gt;Autoscaling over EKS can be accomplished using either the &lt;a href="https://github.com/kubernetes/autoscaler"&gt;cluster-autoscaler&lt;/a&gt; project or &lt;a href="https://karpenter.sh/"&gt;Karpenter&lt;/a&gt;. If you want to use Spot instances, consider using Karpenter, as it has better integrations with AWS for optimizing spot pricing and availability, minimizing interruptions, and falling back to on-demand nodes if no spot instances are available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Node selection
&lt;/h3&gt;

&lt;p&gt;Autoscaling applies on the node types we have chosen, and there are several important factors to consider when choosing them:&lt;/p&gt;

&lt;p&gt;The most important consideration is the node size. Kubernetes works well with clusters that have many smaller nodes, rather than a few large ones. This provides finer-grained autoscaling and reduces the impact of single nodes becoming unavailable. However, there is a set overhead per node consisting of daemons running on the VM itself (Kubelet, containerd, …), and DaemonSet pods that run on each node. Make sure to take those into account, and choose a node such that those services won’t be a significant percentage of its compute. We recommend working with nodes in the &lt;code&gt;[cm].large-2xlarge&lt;/code&gt; range.&lt;/p&gt;

&lt;p&gt;It is possible to further optimize your node size by creating multiple node groups. if your workloads have diverse resource requirements (some need high memory while others need high CPU), this might be a worthwhile optimization to allow flexibility and maximum resource utilization. For example, if you have a workload that requests 8 GiB memory and 1 vCPU, it may make sense to utilize &lt;code&gt;r&lt;/code&gt;-type (high memory) instances. That said, for smaller clusters (less than 10 nodes), this can be a hassle to maintain.&lt;/p&gt;

&lt;p&gt;When using a rare node type (for example, X1 super high memory instances), you may occasionally run into problems when scaling up. This is especially true if you are limited in your availability zones, or trying to use Spot instances. Our solution was to specify a wide range of similar instances, and allow Karpenter to choose between them. For different clusters we use the &lt;code&gt;c&lt;/code&gt; or &lt;code&gt;m&lt;/code&gt; instance families, and provide Karpenter with a list such as: &lt;code&gt;c5a, c5, c5ad, c5d, c5n, c6a, c6i, c6id, and c6in&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, since dev environments are bursty and can tolerate cpu disruption, you can choose to use the burstable node types (in AWS - t3, t4), and save some more. At time of writing, in AWS in Frankfurt, a &lt;code&gt;t3a.2xlarge&lt;/code&gt; costs $252 per month, while a &lt;a href="https://instances.vantage.sh/aws/ec2/m5a.2xlarge"&gt;&lt;code&gt;m5a.2xlarge&lt;/code&gt;&lt;/a&gt; costs $303, so a 17% discount. This is not super significant, and has some actual downsides, so we will not assume you’ve made this jump.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spot Instances
&lt;/h3&gt;

&lt;p&gt;One of the best ways to save money on cloud instances is to use them through the Spot or interruptible program through your cloud provider. This can save anywhere from 45%-85% of the cost, without requiring any commitment.&lt;/p&gt;

&lt;p&gt;Not all workloads perform well on spot instances, since the chance of a node needing to be replaced is significant higher. It is very worthwhile to check though, since the savings are so significant.&lt;/p&gt;

&lt;p&gt;We use Karpenter with a Provisioner that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;karpenter.sh/v1alpha5&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Provisioner&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="nl"&gt;&amp;amp;nbsp&lt;/span&gt;&lt;span class="na"&gt;;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="nl"&gt;&amp;amp;nbsp&lt;/span&gt;&lt;span class="na"&gt;;requirements&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="nl"&gt;&amp;amp;nbsp&lt;/span&gt;&lt;span class="na"&gt;; &amp;amp;nbsp;- key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node.kubernetes.io/instance-type&lt;/span&gt;
 &lt;span class="nl"&gt;&amp;amp;nbsp&lt;/span&gt;&lt;span class="na"&gt;; &amp;amp;nbsp; &amp;amp;nbsp;operator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;In&lt;/span&gt;
 &lt;span class="nl"&gt;&amp;amp;nbsp&lt;/span&gt;&lt;span class="na"&gt;; &amp;amp;nbsp; &amp;amp;nbsp;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# List of node types as described above&lt;/span&gt;
 &lt;span class="nl"&gt;&amp;amp;nbsp&lt;/span&gt;&lt;span class="na"&gt;; &amp;amp;nbsp;- key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;karpenter.sh/capacity-type&lt;/span&gt;
 &lt;span class="nl"&gt;&amp;amp;nbsp&lt;/span&gt;&lt;span class="na"&gt;; &amp;amp;nbsp; &amp;amp;nbsp;operator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;In&lt;/span&gt;
 &lt;span class="nl"&gt;&amp;amp;nbsp&lt;/span&gt;&lt;span class="na"&gt;; &amp;amp;nbsp; &amp;amp;nbsp;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="nl"&gt;&amp;amp;nbsp&lt;/span&gt;&lt;span class="s"&gt;; &amp;amp;nbsp; &amp;amp;nbsp; &amp;amp;nbsp;- "spot"&lt;/span&gt;
 &lt;span class="nl"&gt;&amp;amp;nbsp&lt;/span&gt;&lt;span class="s"&gt;; &amp;amp;nbsp; &amp;amp;nbsp; &amp;amp;nbsp;- "on-demand"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which allows Karpenter to choose the most ideal instances for us, and if none are available as Spot instances, use on-demand. Since Karpenter itself does not like to run on the instances it manages, we spin up a small &lt;code&gt;t3.medium&lt;/code&gt; instance for Karpenter and a select few other services that don’t tolerate interruptions well enough.&lt;/p&gt;

&lt;p&gt;We have seen an average reduction in EC2 cost of about 60%.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Adaptations
&lt;/h2&gt;

&lt;p&gt;When using the same Infrastructure as Code (IaC) definitions for production, staging, preview and dev (highly recommended!) it is important to remember to modify them for use for dev environments. There are two main differences between production and dev that are relevant for our purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;That dev environments are mostly idle&lt;/li&gt;
&lt;li&gt;That we don’t necessarily need the entire environment&lt;/li&gt;
&lt;li&gt;That we don’t need to be as resilient&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the following sections we’ll discuss applying these assumptions to the number of replicas, the resource requests and the environment&lt;/p&gt;

&lt;h3&gt;
  
  
  Workload Replicas
&lt;/h3&gt;

&lt;p&gt;Since we don’t need to be resilient, and the environment is expected to be mostly idle, we can scale down deployments to a single replica. This is true both for regular deployments, but also for stateful sets, where the scale down translates to storage savings as well.&lt;/p&gt;

&lt;p&gt;Note that for stateful sets in particular, there may be business logic that might need to change to handle the different replica count, or edge cases that won’t replicate with fewer replicas. Think things like consistent routing of requests to the same stateful services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workload Resource Requests
&lt;/h3&gt;

&lt;p&gt;The main difference is that dev environments are idle 99% of the time. In our case, for one of our services, we are seeing the pods use 1-5 mCPU and 50MiB memory, down from abut 100 milli CPU and 160 MiB memory. So around 1/20th the CPU and 1/3 the memory. In this case, we will be able to pack 3 times as many dev environments if we modify the resource requests accordingly.&lt;/p&gt;

&lt;p&gt;One of the difficulties with developer environments is that their usage can be “bursty” - idle 99% of the time, but suddenly busy for a short while while the developer is working with them. These spikes can cause an increase in CPU and in memory. Because CPU is a compressible resource, nothing significantly bad will happen if there is some contention, though things may run a bit more slowly. Memory, however, is a different matter. If your application takes a lot more memory when in use, you may need to increase the requests to match, or be susceptible to OOMs and evictions.&lt;/p&gt;

&lt;p&gt;Kubernetes is reasonably good at dispersing pods across nodes, and one thing that could happen is that some nodes will end up with the “burstier” pods, while others are quiet. You could try to bind the entire environment to a single node so all nodes behave similarly, though that adds more complexity to the deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Partial environments
&lt;/h3&gt;

&lt;p&gt;A final possibility for resource reduction is to intelligently choose subsets of environments to bring up for dev purposes. For example, instead of bringing up all 20 microservices, you could choose a part of the environment to bring up. There are two main ways to accomplish that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatically Identify which code was changed in the branch, to which services it belongs, and bring up those + their dependencies. Unfortunately, this is very hard to do well, and mistakes mean broken environments.&lt;/li&gt;
&lt;li&gt;Pre-define several environment subsets, and bring those up depending on certain criteria or developer request. This is easier, but requires maintenance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From our experience, investing in partial environments only makes sense if it is easy to do for your system, and you have a relatively large number of services - let’s say more than 20. If you do go this route, it should be possible to save an additional 30-60% of the resources in the environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;We’ve gone through 6 complimentary strategies for reducing the cloud resource cost of running developer environments. Together, they can drastically reduce your developer-related cloud bill. Let’s bring this down to actual money. Let’s say we have a project that has 15 microservices, with 3 replicas in production, and an overall resource utilization in production of 80GiB memory and 20 CPUs. This costs us (assuming AWS in Frankfurt):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt; &lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Subtotal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;EKS Cluster&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;$70&lt;/td&gt;
&lt;td&gt;$70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load Balancer&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;$21&lt;/td&gt;
&lt;td&gt;$21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudWatch&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;$10&lt;/td&gt;
&lt;td&gt;$10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static management node (t3.medium)&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;$35&lt;/td&gt;
&lt;td&gt;$35&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Main EC2 Nodes (m5a.2xlarge)&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;$303&lt;/td&gt;
&lt;td&gt;$909&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;$1045&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Our comparison state is one where we deploy a full copy of our infrastructure for each environment. If you already have some of this implemented, take a look at the difference. Lets take a look at the savings we get from applying all the above techniques.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Shared cluster - so we have 0 incremental costs for each environment. Assuming 10 environments on average using the cluster, let’s amortize the cost - &lt;code&gt;(70+21+10+35)/10&lt;/code&gt; = $13.6&lt;/li&gt;
&lt;li&gt;Autoscaling - Instead of keeping our 3 main nodes up all the time, we enable autoscaling and reduce their cost to &lt;code&gt;(3*303)/4&lt;/code&gt; = $227.25&lt;/li&gt;
&lt;li&gt;Node selection - as discussed in that section, while we could save another 17% and switch to t3 instances, we’ll skip this optimization for now.&lt;/li&gt;
&lt;li&gt;Spot instances - implementing spot instances, and using the up to date pricing, we reduce our cost per node to $134.46, and taking into account the autoscaling and node count, a total of &lt;code&gt;(3*134.46)/4&lt;/code&gt; = $100.845&lt;/li&gt;
&lt;li&gt;Workload replicas - reducing our replica count to 1 cuts our resource usage by 1/3, and leaves us with 1 instead of 3 nodes. At this point it may make sense to choose smaller nodes, but since pricing is generally linear with node resources, let’s ignore that. Our updated price for the EC2 instances is &lt;code&gt;134.46/4&lt;/code&gt; = $33.6&lt;/li&gt;
&lt;li&gt;Workload resource requests - cutting our resource requests to around one-third translates directly to fewer EC2 instances, so - &lt;code&gt;33.6/3&lt;/code&gt; = $11.2&lt;/li&gt;
&lt;li&gt;Partial environments - if we can easily cut our environment and bring up only what is necessary, we can save another ~50%, and get to &lt;code&gt;11.2/2&lt;/code&gt; = $5.1&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To sum up, we have $13.6 static costs and $5.1 usage costs per env. We’ve gone down more than 50x, from $1045 for our production to $18.7 for each dev env. And since our largest portion is the cluster static costs, we scale really well - increasing this to 20 environments means the price per env drops to $11.9.&lt;/p&gt;

&lt;p&gt;This is, of course, a rough approximation, and doesn’t count things like EBS volumes, increased CloudWatch usage or less-than-optimal node utilization. But even if we are off by 20%, we still end up over 40x cheaper.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sounds great, right? What do I need to do to make this happen?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Make sure you have some declarative way to define the infrastructure - Terraform, CloudFormation, CDK, Pulumi - whatever. This is crucial because making incremental improvements is &lt;em&gt;significantly&lt;/em&gt; easier if you can see the exact effects of the changes you are making and roll back as needed. &lt;/li&gt;
&lt;li&gt;Next, adopt some kind of environment orchestration solution - while we don’t use Argo or Flux internally, I’ve heard great things about either of them.&lt;/li&gt;
&lt;li&gt;Once you have a working setup, start with the infrastructure changes, and adopt them one by one - autoscaling, right-sizing nodes, and switching to spot instances.&lt;/li&gt;
&lt;li&gt;Next, modify your application deployment (through Helm / Kustomize / whatever you are using) to reduce replicas and resource requests, and create partial configurations if possible.&lt;/li&gt;
&lt;li&gt;Finally, wait a day and go to your AWS Cost Explorer page and see how far you got 🙂.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To actually be able to use these environments for development, you will want a solution that gives developers access to these environments, and provides fast iterations, hot reloading and debugging. For that, (or if all of this seems like a &lt;em&gt;lot&lt;/em&gt; of work), &lt;a href="http://raftt.io"&gt;let’s get in touch&lt;/a&gt; 😉.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
