<?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>DEV Community: Mehdi Yedes</title>
    <description>The latest articles on DEV Community by Mehdi Yedes (@mehyedes).</description>
    <link>https://dev.to/mehyedes</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%2F342148%2F19fc8b04-6cdb-4380-b201-037f91847cfc.jpg</url>
      <title>DEV Community: Mehdi Yedes</title>
      <link>https://dev.to/mehyedes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mehyedes"/>
    <language>en</language>
    <item>
      <title>Tracking Stripe Payments with Slack and faasd</title>
      <dc:creator>Mehdi Yedes</dc:creator>
      <pubDate>Sat, 04 Apr 2020 14:49:54 +0000</pubDate>
      <link>https://dev.to/mehyedes/tracking-stripe-payments-with-slack-and-faasd-15ii</link>
      <guid>https://dev.to/mehyedes/tracking-stripe-payments-with-slack-and-faasd-15ii</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OPkehffo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1484417894907-623942c8ee29%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OPkehffo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://images.unsplash.com/photo-1484417894907-623942c8ee29%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D2000%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="Tracking Stripe Payments with Slack and faasd"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my last blog post, I have written about how to turn a Raspberry Pi into a serverless platform with the lightweight &lt;a href="https://www.openfaas.com/"&gt;OpenFaas&lt;/a&gt; implementation &lt;a href="https://github.com/openfaas/faasd"&gt;faasd&lt;/a&gt;. If you haven't checked it out already, you can find the link to the post below:&lt;br&gt;
&lt;a href="https://dev.to/mehyedes/turn-your-raspberry-pi-into-a-serverless-platform-with-faasd-14nh"&gt;Turn your Raspberry Pi into a serverless platform with faasd&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note that a Raspberry Pi is not required here.&lt;br&gt;&lt;br&gt;
faasd can be installed without issues on any other machine or cloud instance as well.&lt;br&gt;&lt;br&gt;
This blog post from Alex Ellis describes how to build a faasd serverless appliance on DigitalOcan:&lt;br&gt;&lt;br&gt;
&lt;a href="https://blog.alexellis.io/deploy-serverless-faasd-with-cloud-init/"&gt;https://blog.alexellis.io/deploy-serverless-faasd-with-cloud-init/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I did that only as a small hobby project. But then, I wanted to use this setup for a real-world use case, and also use the opportunity to try another useful open-source project which is &lt;a href="https://docs.inlets.dev/"&gt;inlets&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Inlets is a cloud native tunnel that allows you to expose private endpoints to the Internet. In this case, it will be used to expose the OpenFaas gateway running on my Raspberry Pi board to receive incoming Stripe Webhook events and dispatch them to the relevant function.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For more details about inlets, check the official docs and resources available at &lt;a href="https://docs.inlets.dev/"&gt;https://docs.inlets.dev/&lt;/a&gt;  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So in this post, I will describe how I built a local OpenFaas function that serves as a Stripe webhook. This function  simply sends a Slack notification for every new &lt;code&gt;charge.succeeded&lt;/code&gt; event.&lt;/p&gt;
&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fHnVgKco--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/03/stripe-slack-notifier-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fHnVgKco--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/03/stripe-slack-notifier-2.png" alt="Tracking Stripe Payments with Slack and faasd"&gt;&lt;/a&gt;Pictured: conceptual architecture with faasd for functions, inlets for the tunnel and stripe to generate webhooks.&lt;/p&gt;

&lt;p&gt;Inlets is an open source HTTP tunnel which we can use to get a public IP address to receive webhooks. The inlets client will be running on the Raspberry Pi and connects to the inlets server using a websocket connection to listen for any incoming requests.&lt;/p&gt;

&lt;p&gt;In our setup, every Stripe webhook event that is received by the inlets client will be forwarded to the OpenFaaS gateway on the Raspberry Pi, which will in turn trigger the &lt;code&gt;stripe-slack-notifier&lt;/code&gt; function to send a new Slack notification.&lt;/p&gt;
&lt;h2&gt;
  
  
  Preparing the Setup
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Obtain an exit node with inlets
&lt;/h3&gt;

&lt;p&gt;First, make sure you have created an inlets exit server following this nice guide by Alex Ellis:&lt;br&gt;
&lt;a href="https://blog.alexellis.io/https-inlets-local-endpoints/"&gt;Get HTTPS for your local endpoints with inlets and Caddy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This would allow you to have an exit node with a public domain that can be used for receiving webhook events. In the setup described earlier, the inlets client is running on the Raspberry Pi, so I have chosen the upstream to be &lt;a href="http://127.0.0.1:8080"&gt;http://127.0.0.1:8080&lt;/a&gt;, which is the OpenFaaS Gateway URL.&lt;/p&gt;

&lt;p&gt;Using the token provided by the inlets server, the command below exposes the OpenFaaS Gateway to the Internet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;inlets client &lt;span class="nt"&gt;--remote&lt;/span&gt; wss://&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REMOTE_EXIT_NODE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--upstream&lt;/span&gt; http://127.0.0.1:8080 &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;INLETS_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our function will then be available from the Internet using the &lt;code&gt;https://${REMOTE_EXIT_NODE}/function/stripe-slack-notifier&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a Stripe Webhook
&lt;/h3&gt;

&lt;p&gt;We need to add a new Stripe webhook using the&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nHSNwIby--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/03/stripe-webhook-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nHSNwIby--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/03/stripe-webhook-1.png" alt="Tracking Stripe Payments with Slack and faasd"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Slack App
&lt;/h3&gt;

&lt;p&gt;To receive incoming webhooks we need to create an App in the Slack dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uxDPAAcw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/03/Screenshot-from-2020-03-31-19-49-49.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uxDPAAcw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/03/Screenshot-from-2020-03-31-19-49-49.png" alt="Tracking Stripe Payments with Slack and faasd"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a function to receive webhooks from Stripe
&lt;/h2&gt;

&lt;p&gt;The function &lt;code&gt;stripe-slack-notifier&lt;/code&gt; was built using the &lt;code&gt;python3-http&lt;/code&gt; template that is available in the OpenFaaS template store.&lt;/p&gt;

&lt;p&gt;Creating a new function using &lt;code&gt;faas-cli new&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;faas-cli template store list | &lt;span class="nb"&gt;grep &lt;/span&gt;python3-http
python3-http openfaas-incubator Python 3.6 with Flask and HTTP
&lt;span class="nv"&gt;$ &lt;/span&gt;faas-cli template store pull python3-http

&lt;span class="c"&gt;# Make sure to set this to you Docker hub username&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DOCKER_REGISTRY_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;Docker hub username&amp;gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;faas-cli new stripe-slack-notifier &lt;span class="nt"&gt;--lang&lt;/span&gt; python3-http &lt;span class="nt"&gt;--prefix&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOCKER_REGISTRY_PREFIX&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
Folder: stripe-slack-notifier created.
  ________  ____
 / _ &lt;span class="se"&gt;\ &lt;/span&gt;_ _____ _ __|___ |_ _ ___/___ |
| | | | &lt;span class="s1"&gt;'_ \ / _ \ '&lt;/span&gt;_ &lt;span class="se"&gt;\|&lt;/span&gt; |_ / _&lt;span class="sb"&gt;`&lt;/span&gt; |/ _&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="se"&gt;\_&lt;/span&gt;__ &lt;span class="se"&gt;\&lt;/span&gt;
| |_| | |_&lt;span class="o"&gt;)&lt;/span&gt; | __/ | | | _| &lt;span class="o"&gt;(&lt;/span&gt;_| | &lt;span class="o"&gt;(&lt;/span&gt;_| |___&lt;span class="o"&gt;)&lt;/span&gt; |
 &lt;span class="se"&gt;\ &lt;/span&gt;___/| .__ / &lt;span class="se"&gt;\ &lt;/span&gt;___|_| |_|_| &lt;span class="se"&gt;\_&lt;/span&gt;_ ,_|&lt;span class="se"&gt;\ &lt;/span&gt;__,_|____ /
      |_|

Function created &lt;span class="k"&gt;in &lt;/span&gt;folder: stripe-slack-notifier
Stack file written: stripe-slack-notifier.yml
&lt;span class="nv"&gt;$ &lt;/span&gt;tree &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
├── stripe-slack-notifier
│   ├── handler.py
│   └── requirements.txt
└── stripe-slack-notifier.yml

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



&lt;p&gt;The final source code for the function can be found in the &lt;a href="https://github.com/mehyedes/stripe-slack-notifier"&gt;mehyedes/stripe-slack-notifier&lt;/a&gt;Github repository.&lt;/p&gt;

&lt;p&gt;The function is written in python. It will verify the received payload using the Stripe API key and the webhook singing secret, and then send a slack notification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;stripe&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;babel&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;slack_webhook&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Slack&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;secret_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;f"/var/openfaas/secrets/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;secret_name&lt;/span&gt;&lt;span class="si"&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;'r'&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;secret_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&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="c1"&gt;# Make sure to create the secrets below
&lt;/span&gt;    &lt;span class="n"&gt;webhook_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetch_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"slack-webhook-url"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;stripe&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;fetch_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"stripe-secret-key"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;webhook_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetch_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"webhook-secret"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&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;body&lt;/span&gt;
    &lt;span class="n"&gt;received_sig&lt;/span&gt; &lt;span class="o"&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;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Stripe-Signature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Webhook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;construct_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;received_sig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;webhook_secret&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error while decoding event!"&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;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Bad payload"&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;400&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SignatureVerificationError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid signature!"&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;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Bad signature"&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;400&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Fail for all other event types  
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"charge.succeeded"&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;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;"Unsupported event type"&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;422&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format_currency&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
      &lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'en'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;slack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Slack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;webhook_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;slack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;f"You have a received a new payment of &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; :moneybag: :tada:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"An error occured when trying to send slack message."&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;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Could not send slack message"&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;500&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;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Notification was sent successfully to Slack"&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
stripe-slack-notifier/handler.py





&lt;p&gt;OpenFaaS &lt;a href="https://docs.openfaas.com/reference/secrets/#deploy-a-function-with-secrets"&gt;secrets&lt;/a&gt; are used to pass the following data to the function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stripe API key&lt;/li&gt;
&lt;li&gt;Webhook signing secret&lt;/li&gt;
&lt;li&gt;Slack Webhook URL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So they need to be created manually with &lt;code&gt;faas-cli&lt;/code&gt; before deploying the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;faas-cli secret create slack-webhook-url &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--gateway&lt;/span&gt; http://raspberrypi.loc:8080
&lt;span class="nv"&gt;$ &lt;/span&gt;faas-cli secret create stripe-secret-key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;STRIPE_API_KEY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--gateway&lt;/span&gt; http://raspberrypi.loc:8080
&lt;span class="nv"&gt;$ &lt;/span&gt;faas-cli secret create webhook-secret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WEBHOOK_SIGNING_SECRET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--gateway&lt;/span&gt; http://raspberrypi.loc:8080
&lt;span class="nv"&gt;$ &lt;/span&gt;faas-cli secret list &lt;span class="nt"&gt;--gateway&lt;/span&gt; http://raspberrypi.loc:8080  

NAME
slack-webhook-url
stripe-secret-key
webhook-secret
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Building &amp;amp; deploying the function
&lt;/h2&gt;

&lt;p&gt;Since the function will be running on Raspberry Pi, we need to build the Docker image for &lt;code&gt;armv7&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
Fortunately, this has become easier with the multi platform builds feature by &lt;a href="https://github.com/docker/buildx/"&gt;buildx&lt;/a&gt; available in docker &lt;strong&gt;v19.03.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But since we have already exposed our OpenFaaS gateway using inlets, we can use Github Actions to build and deploy the latest version of our function to the Raspberry Pi with every git push. The workflow file used to automate this can be found &lt;a href="https://github.com/mehyedes/stripe-slack-notifier/blob/master/.github/workflows/main.yml"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Manually, this can be achieved with the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate the Docker build context for the function
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;faas-cli build &lt;span class="nt"&gt;--shrinkwrap&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; stripe-slack-notifier.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;  This step is not needed if using Docker for desktop as this would be done automatically for you. On Linux, we need to register other platforms like &lt;code&gt;armv7&lt;/code&gt; with the kernel using the &lt;code&gt;docker/binfmt&lt;/code&gt; image:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;  Create a new builder for buildx
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker buildx create &lt;span class="nt"&gt;--name&lt;/span&gt; armhf &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/arm/v7 
armhf

&lt;span class="nv"&gt;$ &lt;/span&gt;docker buildx &lt;span class="nb"&gt;ls 
&lt;/span&gt;NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
armhf &lt;span class="k"&gt;*&lt;/span&gt; docker-container                    
  armhf0 unix:///var/run/docker.sock running linux/arm/v7, linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
default docker                              
  default default running linux/amd64, linux/386
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;  Finally build and push the image for both &lt;code&gt;armv7&lt;/code&gt; and &lt;code&gt;amd64&lt;/code&gt; platforms:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ DOCKER_BUILD_KIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;DOCKER_CLI_EXPERIMENTAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;enabled docker buildx build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/arm/v7,linux/amd64 &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOCKER_REGISTRY_PREFIX&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/stripe-slack-notifier:latest &lt;span class="nt"&gt;--push&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;A new Docker image is now available on the Docker registry for both &lt;code&gt;amd64&lt;/code&gt; and &lt;code&gt;armv7&lt;/code&gt; architectures:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4PDHcQdo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/03/Screenshot-from-2020-03-31-20-34-52.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4PDHcQdo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/03/Screenshot-from-2020-03-31-20-34-52.png" alt="Tracking Stripe Payments with Slack and faasd"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To deploy the function to the Raspberry Pi, we can finally run the command below from our laptop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;faas-cli deploy &lt;span class="nt"&gt;-f&lt;/span&gt; stripe-slack-notifier.yml &lt;span class="nt"&gt;-g&lt;/span&gt; https://raspberrypi.loc:8080 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Test the function
&lt;/h2&gt;

&lt;p&gt;Check the status of the function with &lt;code&gt;faas-cli describe&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;faas-cli describe stripe-slack-notifier &lt;span class="nt"&gt;--gateway&lt;/span&gt; http://raspberrypi.loc:8080
Name: stripe-slack-notifier
Status: Ready
Replicas: 1
Available replicas: 1
Invocations: 0
Image:               
Function process:    
URL: http://raspberrypi.loc:8080/function/stripe-slack-notifier
Async URL: http://raspberrypi.loc:8080/async-function/stripe-slack-notifier

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



&lt;p&gt;We can test that it's now by triggering some test events from the Stripe dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BjY_ZiWY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/03/Screenshot-from-2020-03-31-20-53-24.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BjY_ZiWY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/03/Screenshot-from-2020-03-31-20-53-24.png" alt="Tracking Stripe Payments with Slack and faasd"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a few moments, we can see a similar message in Slack:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QMc3jjCY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/03/Screenshot-from-2020-03-31-20-55-28.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QMc3jjCY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/03/Screenshot-from-2020-03-31-20-55-28.png" alt="Tracking Stripe Payments with Slack and faasd"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The function only supports &lt;code&gt;charge.succeeded&lt;/code&gt; Stripe events for now, so any other type of event will receive &lt;code&gt;Unsupported event type&lt;/code&gt; error from the function, and no Slack messages will be sent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog post, I have described how we can create a serverless &lt;a href="https://stripe.com/docs/webhooks"&gt;webhook&lt;/a&gt; handler for Stripe API events using &lt;a href="https://github.com/openfaas/faasd"&gt;faasd&lt;/a&gt; and &lt;a href="https://docs.inlets.dev/#/"&gt;inlets&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As we have seen in this post, faasd can be a great alternative to running OpenFaaS without the need for managing a Kubernetes or Docker Swarm cluster. Combined with inlets, they can be used together for running and exposing functions for real-world use cases.&lt;/p&gt;

&lt;p&gt;Things that you might want to do next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Join the OpenFaaS community on &lt;a href="https://openfaas.slack.com"&gt;slack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Extend the &lt;a href="https://github.com/mehyedes/stripe-slack-notifier"&gt;mehyedes/stripe-slack-notifier&lt;/a&gt; function to support other webhook event types&lt;/li&gt;
&lt;li&gt;Build a faasd serverless appliance on a cloud instance using cloud-init (see &lt;a href="https://blog.alexellis.io/deploy-serverless-faasd-with-cloud-init/"&gt;deploy-serverless-faasd-with-cloud-init&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Try out the &lt;a href="https://github.com/inlets/inlets-operator"&gt;inlets-operator&lt;/a&gt; for getting public IP addresses for your local kubernetes cluster&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>serverless</category>
      <category>openfaas</category>
      <category>raspberrypi</category>
      <category>faasd</category>
    </item>
    <item>
      <title>Turn your Raspberry Pi into a serverless platform with faasd</title>
      <dc:creator>Mehdi Yedes</dc:creator>
      <pubDate>Sun, 01 Mar 2020 16:29:26 +0000</pubDate>
      <link>https://dev.to/mehyedes/turn-your-raspberry-pi-into-a-serverless-platform-with-faasd-14nh</link>
      <guid>https://dev.to/mehyedes/turn-your-raspberry-pi-into-a-serverless-platform-with-faasd-14nh</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZPDd1-Zl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/02/faasd-post-image-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZPDd1-Zl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/02/faasd-post-image-1.jpg" alt="Turn your Raspberry Pi into a serverless platform with faasd"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Function-as-a-Service (FaaS) is a serverless computing method that consists on running applications' logic in stateless, ephemeral containers that are triggered by specific events which may last only for one invocation; &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt; and &lt;a href="https://cloud.google.com/run"&gt;Google Cloud Run&lt;/a&gt; are popular implementations of FaaS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.openfaas.com/"&gt;OpenFaaS&lt;/a&gt; is a serverless computing framework has been getting a lot of traction in the Open Source community. It allows you to easily build your own FaaS serverless computing platform on top of &lt;a href="https://docs.docker.com/engine/swarm/"&gt;Docker Swarm&lt;/a&gt; or &lt;a href="https://kubernetes.io/"&gt;Kubernetes&lt;/a&gt;, while also providing you with the tools necessary for building your functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/openfaas/faasd"&gt;faasd&lt;/a&gt; makes it possible to run OpenFaaS without the need for a container orchestration engine by relying on &lt;a href="https://github.com/containerd/containerd"&gt;containerd&lt;/a&gt;, which makes it ideal for a building a serverless home lab that doesn't require much computing resources; perfect for single board computers like Raspberry Pi.&lt;/p&gt;

&lt;p&gt;In this blog post, I am going to describe how you can build your own OpenFaaS serverless platform by installing faasd on a single Raspberry Pi, and how you can build and deploy you first function to OpenFaas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing dependencies
&lt;/h2&gt;

&lt;p&gt;First, &lt;code&gt;ssh&lt;/code&gt; into your Raspberry Pi to install a few dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update \
    &amp;amp;&amp;amp; sudo apt install -qy git runc bridge-utils
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Install containerd
&lt;/h3&gt;

&lt;p&gt;Since the Rapsberry Pi has an &lt;code&gt;armv7&lt;/code&gt; architecture&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ uname -m
armv7l
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;we cannot use the &lt;a href="https://github.com/containerd/containerd/releases"&gt;binaries&lt;/a&gt; released by the containerd maintainers as they are only compatible with the &lt;code&gt;x86_64&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So in order to install containerd, we can either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build the binaries on the Raspberry Pi by following the instructions available here
OR&lt;/li&gt;
&lt;li&gt;use the pre-built binaries that &lt;a href="https://twitter.com/alexellisuk"&gt;@alexellisuk&lt;/a&gt; was kind enough to provide in his &lt;a href="https://github.com/alexellis/containerd-armhf/releases"&gt;Github repo&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's start by downloading the containerd binaries&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ curl -sSL https://github.com/alexellis/containerd-armhf/releases/download/v1.3.2/containerd.tgz | sudo tar -xvz --strip-components=2 -C /usr/local/bin/
./bin/containerd-shim-runc-v1
./bin/containerd-stress
./bin/ctr
./bin/containerd
./bin/containerd-shim-runc-v2
./bin/containerd-shim
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Get the containerd systemd unit file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ sudo wget --output-document=/etc/systemd/system/containerd.service https://raw.githubusercontent.com/containerd/containerd/v1.3.2/containerd.service--2020-02-09 16:44:04-- https://raw.githubusercontent.com/containerd/containerd/v1.3.2/containerd.service

2020-02-09 16:44:04 (6.45 MB/s) - ‘/etc/systemd/system/containerd.service’ saved [641/641]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Start containerd and enable it at system startup&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ sudo systemctl enable containerd
Created symlink /etc/systemd/system/multi-user.target.wants/containerd.service → /etc/systemd/system/containerd.service.
pi@raspberrypi:~ $ sudo systemctl start containerd.service
pi@raspberrypi:~ $ systemctl status containerd.service 
● containerd.service - containerd container runtime
   Loaded: loaded (/etc/systemd/system/containerd.service; enabled; vendor preset: enabled)
   Active: active (running) since Sun 2020-02-09 16:45:32 CET; 47s ago
     Docs: https://containerd.io
  Process: 2763 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)
 Main PID: 2764 (containerd)
    Tasks: 13
   Memory: 19.2M
   CGroup: /system.slice/containerd.service
           └─2764 /usr/local/bin/containerd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Setup container networking
&lt;/h3&gt;

&lt;p&gt;We need to enable the Linux kernel bridge modules and IPv4 forwarding as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ sudo modprobe br_netfilter
pi@raspberrypi:~ $ sudo sysctl net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-iptables = 1
pi@raspberrypi:~ $ sudo /sbin/sysctl -w net.ipv4.conf.all.forwarding=1

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



&lt;p&gt;We also need to install the CNI networking plugins using the commands below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ sudo mkdir -p /opt/cni/bin
pi@raspberrypi:~ $ curl -sSL https://github.com/containernetworking/plugins/releases/download/v0.8.5/cni-plugins-linux-arm-v0.8.5.tgz | sudo tar -xz -C /opt/cni/bin
pi@raspberrypi:~ $ ls -l /opt/cni/bin/
total 64436
-rwxr-xr-x 1 root root 3775719 Jan 22 19:52 bandwidth
-rwxr-xr-x 1 root root 4255875 Jan 22 19:52 bridge
-rwxr-xr-x 1 root root 10706922 Jan 22 19:52 dhcp
-rwxr-xr-x 1 root root 5394554 Jan 22 19:52 firewall
-rwxr-xr-x 1 root root 2872015 Jan 22 19:52 flannel
-rwxr-xr-x 1 root root 3843695 Jan 22 19:52 host-device
-rwxr-xr-x 1 root root 3359276 Jan 22 19:52 host-local
-rwxr-xr-x 1 root root 3976434 Jan 22 19:52 ipvlan
-rwxr-xr-x 1 root root 3015277 Jan 22 19:52 loopback
-rwxr-xr-x 1 root root 4046458 Jan 22 19:52 macvlan
-rwxr-xr-x 1 root root 3637166 Jan 22 19:52 portmap
-rwxr-xr-x 1 root root 4187702 Jan 22 19:52 ptp
-rwxr-xr-x 1 root root 3152425 Jan 22 19:52 sbr
-rwxr-xr-x 1 root root 2665626 Jan 22 19:52 static
-rwxr-xr-x 1 root root 3087310 Jan 22 19:52 tuning
-rwxr-xr-x 1 root root 3976306 Jan 22 19:52 vlan
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Install faasd
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Install &lt;code&gt;faas-cli&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Before installing faasd, let's install &lt;a href="https://github.com/openfaas/faas-cli"&gt;faas-cli&lt;/a&gt;. &lt;code&gt;faas-cli&lt;/code&gt; is the command line utility that can be used to interact with OpenFaaS and allows us to build and deploy functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ curl -sLfS https://cli.openfaas.com | sudo sh
armv7l
Downloading package https://github.com/openfaas/faas-cli/releases/download/0.11.7/faas-cli-armhf as /tmp/faas-cli-armhf
Download complete.

Running with sufficient permissions to attempt to move faas-cli to /usr/local/bin
New version of faas-cli installed to /usr/local/bin
Creating alias 'faas' for 'faas-cli'.
  ________  ____
 / _ \ _ _____ _ __|___ |_ _ ___/___ |
| | | | '_ \ / _ \ '_ \| |_ / _` |/ _` \___ \
| |_| | |_) | __/ | | | _| (_| | (_| |___) |
 \ ___/| .__ / \ ___|_| |_|_| \__ ,_|\ __,_|____ /
      |_|

CLI:
 commit: 30b7cec9634c708679cf5b4d2884cf597b431401
 version: 0.11.7
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can also enable bash-completion for &lt;code&gt;faas-cli&lt;/code&gt; using the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ source &amp;lt;(faas-cli completion --shell bash)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Install &lt;code&gt;faasd&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Let's fetch the latest &lt;code&gt;faasd&lt;/code&gt; binary using the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ sudo wget --output-document=/usr/local/bin/faasd https://github.com/openfaas/faasd/releases/download/0.7.4/faasd-armhf &amp;amp;&amp;amp; sudo chmod +x /usr/local/bin/faasd

2020-02-09 17:10:19 (662 KB/s) - ‘/usr/local/bin/faasd’ saved [14548992/14548992]

pi@raspberrypi:~ $ faasd version 
  __ _ 
 / _| _____ _ _____ | |
| |_ / _` |/ _` / __|/ _` |
| _| (_| | (_| \__ \ (_| |
|_| \ __,_|\__ ,_| ___/\__ ,_|

faasd
Commit: 592f3d3cc073ca6af83fac3013cc2f4743d05e52
Version: 0.7.4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we just need to run the faasd installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ export GOPATH=$HOME/go/
pi@raspberrypi:~ $ mkdir -p $GOPATH/src/github.com/openfaas
pi@raspberrypi:~ $ cd $GOPATH/src/github.com/openfaas 
pi@raspberrypi:~/go/src/github.com/openfaas $ git clone https://github.com/openfaas/faasd.git
pi@raspberrypi:~/go/src/github.com/openfaas $ cd faasd/
pi@raspberrypi:~/go/src/github.com/openfaas/faasd $ sudo faasd install 
Login with:
  sudo cat /var/lib/faasd/secrets/basic-auth-password | faas-cli login -s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And finally, as mentioned in the command output, login using &lt;code&gt;faas-cli&lt;/code&gt; to be able to interact with your new OpenFaaS installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ sudo cat /var/lib/faasd/secrets/basic-auth-password | faas-cli login -s
Calling the OpenFaaS server to validate the credentials...
WARNING! Communication is not secure, please consider using HTTPS. Letsencrypt.org offers free SSL/TLS certificates.
credentials saved for admin http://127.0.0.1:8080
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Access the OpenFaaS interface
&lt;/h2&gt;

&lt;p&gt;Once &lt;code&gt;faasd&lt;/code&gt; is setup and running, the OpenFaaS user interface can be accessed on your browser at &lt;code&gt;http://RASPBERRYPI_IP:8080&lt;/code&gt;. Since it is protected by basic auth, you would need to use the username and password available under &lt;code&gt;/var/lib/faasd/secrets/basic-auth-password&lt;/code&gt; and &lt;code&gt;/var/lib/faasd/secrets/basic-auth-user&lt;/code&gt; to login.&lt;/p&gt;

&lt;p&gt;Once logged in, you will be greeted with the following interface:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iYa6sQth--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/02/Screenshot-from-2020-02-29-18-55-24.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iYa6sQth--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/02/Screenshot-from-2020-02-29-18-55-24.png" alt="Turn your Raspberry Pi into a serverless platform with faasd"&gt;&lt;/a&gt;OpenFaaS web interface&lt;/p&gt;

&lt;p&gt;There are a few functions already available at the OpenFaas store, that you can easily deploy directly from the web user interface. Let's try deploying the &lt;code&gt;figlet&lt;/code&gt; function:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--foilppXg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/02/Screenshot-from-2020-02-29-19-03-43.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--foilppXg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/02/Screenshot-from-2020-02-29-19-03-43.png" alt="Turn your Raspberry Pi into a serverless platform with faasd"&gt;&lt;/a&gt;Deploy the figlet function&lt;/p&gt;

&lt;p&gt;Once the status of the function is &lt;code&gt;Ready&lt;/code&gt;, let's try to invoke it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0vkG5ixy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/02/Screenshot-from-2020-02-29-19-07-25.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0vkG5ixy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/02/Screenshot-from-2020-02-29-19-07-25.png" alt="Turn your Raspberry Pi into a serverless platform with faasd"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using faas-cli
&lt;/h2&gt;

&lt;p&gt;Let's use &lt;code&gt;faas-cli&lt;/code&gt; now to interact with the OpenFaas gateway. First, we need to login to the OpenFaas gateway using the &lt;code&gt;faas login&lt;/code&gt; command from our workstation.&lt;br&gt;&lt;br&gt;
Fetch the password from the &lt;code&gt;/var/lib/faasd/secrets/basic-auth-password&lt;/code&gt; file on the Raspberry Pi and store it in a file on your workstation because it's required for the login. Here, I have stored the password in the &lt;code&gt;~/.faas_pass&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat ~/.faas_pass | faas login -s --gateway http://raspberrypi.loc:8080  
Calling the OpenFaaS server to validate the credentials...
WARNING! Communication is not secure, please consider using HTTPS. Letsencrypt.org offers free SSL/TLS certificates.
credentials saved for admin http://raspberrypi.loc:8080
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can list and inspect the deployed functions using the commands below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ faas list --gateway http://raspberrypi.loc:8080
Function Invocations Replicas
figlet 1 1    

$ faas describe --gateway http://raspberrypi.loc:8080 figlet
Name: figlet
Status: Ready
Replicas: 1
Available replicas: 1
Invocations: 1
Image:               
Function process:    
URL: http://raspberrypi.loc:8080/function/figlet
Async URL: http://raspberrypi.loc:8080/async-function/figlet
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Using &lt;code&gt;faas-cli&lt;/code&gt; to deploy a function from the store
&lt;/h3&gt;

&lt;p&gt;We can list the available function in the OpenFaas store for the &lt;code&gt;armhf&lt;/code&gt; platform using the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ faas store list --platform armhf

FUNCTION DESCRIPTION
NodeInfo Get info about the machine that you...
Figlet Generate ASCII logos with the figlet CLI
SSL/TLS cert info Returns SSL/TLS certificate informati...
YouTube Video Downloader Download YouTube videos as a function
OpenFaaS Text-to-Speech Generate an MP3 of text using Google'...
nslookup Uses nslookup to return any IP addres...
Docker Image Manifest Query Query an image on the Docker Hub for ...
Left-Pad left-pad on OpenFaaS
Identicon Generator Create an identicon from a provided s...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's deploy the &lt;code&gt;nslookup&lt;/code&gt; function using the &lt;code&gt;faas store deploy&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ faas store deploy --platform armhf --gateway http://raspberrypi.loc:8080 nslookup
WARNING! Communication is not secure, please consider using HTTPS. Letsencrypt.org offers free SSL/TLS certificates.

Deployed. 200 OK.
URL: http://raspberrypi.loc:8080/function/nslookup

$ faas describe nslookup --gateway http://raspberrypi.loc:8080
Name: nslookup
Status: Ready
Replicas: 1
Available replicas: 1
Invocations: 0
Image:               
Function process:    
URL: http://raspberrypi.loc:8080/function/nslookup
Async URL: http://raspberrypi.loc:8080/async-function/nslookup

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



&lt;p&gt;Let's invoke our new function from the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ echo "openfaas.com" | faas invoke nslookup --gateway http://raspberrypi.loc:8080 
nslookup: can't resolve '(null)': Name does not resolve

Name: openfaas.com
Address 1: 185.199.108.153
Address 2: 185.199.111.153
Address 3: 185.199.109.153
Address 4: 185.199.110.153

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



&lt;p&gt;Voila! We have deployed and tested our first function from the command line. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Build your own function
&lt;/h2&gt;

&lt;p&gt;What if we want to build a new function ourserlves, and deploy it to OpenFaas on the Raspberry PI?&lt;br&gt;&lt;br&gt;
&lt;code&gt;faas-cli&lt;/code&gt; offers a convenient way to achieve this, as it provides templates for multiple programming languages and commands that allow us to &lt;a href="https://docs.openfaas.com/cli/build/"&gt;build&lt;/a&gt; and deploy new functions to OpenFaas.&lt;br&gt;&lt;br&gt;
However, since we don't have Docker installed on the Rapsberry Pi, we would need to build our functions using another tool: &lt;a href="https://github.com/moby/buildkit"&gt;&lt;strong&gt;buildkit&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Download buildkit binaries
&lt;/h3&gt;

&lt;p&gt;Since &lt;code&gt;buildkit&lt;/code&gt; binaries for &lt;code&gt;armv7&lt;/code&gt; are already available on the project's Github repository, we won't need to compile them by ourselves. Let's fetch the latest &lt;code&gt;buildkit&lt;/code&gt; binaries on the RPi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ wget -qO- https://github.com/moby/buildkit/releases/download/v0.6.4/buildkit-v0.6.4.linux-arm-v7.tar.gz | sudo tar -xz -C /usr/local/bin/ --strip-components=1 
pi@raspberrypi:~ $ /usr/local/bin/buildkitd --version 
buildkitd github.com/moby/buildkit v0.6.4 ebcef1f69af0bbca077efa9a960a481e579a0e89
pi@raspberrypi:~ $ /usr/local/bin/buildctl --version 
buildctl github.com/moby/buildkit v0.6.4 ebcef1f69af0bbca077efa9a960a481e579a0e89
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Build a new function
&lt;/h3&gt;

&lt;p&gt;We are going to build a small function in &lt;code&gt;golang&lt;/code&gt; and deploy it to OpenFaas.&lt;br&gt;&lt;br&gt;
Luckily, we don't have to do everything from scratch. We can use one of the already available templates from the OpenFaas template store.&lt;br&gt;&lt;br&gt;
We can list the templates available for the &lt;code&gt;armhf&lt;/code&gt; platform using the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ faas template store list --platform armhf 

NAME SOURCE DESCRIPTION
dockerfile-armhf openfaas Classic Dockerfile armhf template
go-armhf openfaas Classic Golang armhf template
node-armhf openfaas Classic NodeJS 8 armhf template
python-armhf openfaas Classic Python 2.7 armhf template
python3-armhf openfaas Classic Python 3.6 armhf template
node10-express-armhf openfaas-incubator Node.js 10 powered by express armhf template
python3-flask-armhf openfaas-incubator Python 3.6 Flask armhf template
python3-http-armhf openfaas-incubator Python 3.6 with Flask and HTTP for ARMHF
node8-express-armhf openfaas-incubator Node.js 8 powered by express armhf template
golang-http-armhf openfaas-incubator Golang HTTP armhf template
golang-middleware-armhf openfaas-incubator Golang Middleware armhf template

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



&lt;p&gt;We're going to use the classic golang template here, so let's create a new function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ mkdir ~/openfaas &amp;amp;&amp;amp; cd ~/openfaas
pi@raspberrypi:~/openfaas $ faas new hello-go --lang go-armhf --prefix myedes
Folder: hello-go created.
  ________  ____
 / _ \ _ _____ _ __|___ |_ _ ___/___ |
| | | | '_ \ / _ \ '_ \| |_ / _` |/ _` \___ \
| |_| | |_) | __/ | | | _| (_| | (_| |___) |
 \ ___/| .__ / \ ___|_| |_|_| \__ ,_|\ __,_|____ /
      |_|

Function created in folder: hello-go
Stack file written: hello-go.yml

Notes:
You have created a new function which uses Golang 1.11
To include third-party dependencies, use a vendoring tool like dep:
dep documentation: https://github.com/golang/dep#installation

You may also like the golang-middleware and golang-http templates
available via "faas-cli template store"

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



&lt;p&gt;The &lt;code&gt;--prefix&lt;/code&gt; option allows you to specify a custom docker registry used to pushing and pulling the function's image.&lt;/p&gt;

&lt;p&gt;The template we have used consists of a simple go function that returns a text message along with the request body. The logic can be found inside the &lt;code&gt;hello-go/handler.go&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~/openfaas $ cat hello-go/handler.go 
package function

import (
    "fmt"
)

// Handle a serverless request
func Handle(req []byte) string {
    return fmt.Sprintf("Hello, Go. You said: %s", string(req))
}

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



&lt;p&gt;Using the &lt;code&gt;--shrinkwrap&lt;/code&gt; option, we can generate the build context for our function without actually building our function's image. This is useful when we want to use a different tool for building our image, in our case &lt;code&gt;buildkit&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~/openfaas $ faas build -f hello-go.yml --shrinkwrap 
[0] &amp;gt; Building hello-go.
Clearing temporary build folder: ./build/hello-go/
Preparing: ./hello-go/ build/hello-go/function
Building: docker.io/myedes/hello-go:latest with go-armhf template. Please wait..
hello-go shrink-wrapped to ./build/hello-go/
[0] &amp;lt; Building hello-go done in 0.01s.
[0] Worker done.

Total build time: 0.01s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As we can see, the command generated a few files inside the &lt;code&gt;build/&lt;/code&gt; folder; our build context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build/
└── hello-go
    ├── Dockerfile
    ├── function
    │   └── handler.go
    ├── go.mod
    ├── main.go
    └── template.yml

2 directories, 5 files
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Before building our image, we need to configure the authentication to be able to push to the docker registry. However, we cannot use the &lt;code&gt;docker login&lt;/code&gt; command since we don't have Docker installed, so we will have to create the &lt;code&gt;~/.docker/config.json&lt;/code&gt; file manually.&lt;br&gt;&lt;br&gt;
For &lt;a href="https://hub.docker.com/"&gt;Docker Hub&lt;/a&gt; , we need to generate a token from the user interface and then create the &lt;code&gt;~/.docker/config.json&lt;/code&gt; on the RPi as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~/openfaas $ export DOCKERHUB_USERNAME=myedes
pi@raspberrypi:~/openfaas $ export DOCKERHUB_TOKEN=&amp;lt;PERSONAL ACCESS TOKEN&amp;gt;
pi@raspberrypi:~/openfaas $ export DOCKER_AUTH=$(echo -n "$DOCKERHUB_USERNAME:$DOCKERHUB_TOKEN" | base64)

pi@raspberrypi:~/openfaas $ cat &amp;gt; ~/.docker/config.json &amp;lt;&amp;lt; EOF 
{
    "auths": {
        "https://index.docker.io/v1/": {
            "auth": "$DOCKER_AUTH"
        }
    }
}
EOF

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



&lt;p&gt;Let's start the &lt;code&gt;buildkitd&lt;/code&gt; daemon in the background, and then start the build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Run the buildkitd daemon
pi@raspberrypi:~/openfaas $ sudo /usr/local/bin/buildkitd &amp;amp; 
[1] 15587

# Build and push the function image to hub.docker.com
pi@raspberrypi:~/openfaas $ sudo buildctl build \
    --frontend dockerfile.v0 \
    --local context=build/hello-go/ \
    --local dockerfile=build/hello-go/ \
    --output type=image,name=docker.io/myedes/hello-go:latest,push=true

[+] Building 81.3s (26/26) FINISHED
...
 =&amp;gt; =&amp;gt; pushing layers
 =&amp;gt; =&amp;gt; pushing manifest for docker.io/myedes/hello-go:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;For more information about how to use &lt;code&gt;buildkit&lt;/code&gt;, make sure to check out the &lt;a href="https://github.com/moby/buildkit"&gt;moby/buildkit&lt;/a&gt; Github repository.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Deploy the function
&lt;/h3&gt;

&lt;p&gt;As we have built and pushed the function's image to the registry, we can now go ahead and deploy our new function to our OpenFaas gateway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~/openfaas $ faas deploy -f hello-go.yml 
Deploying: hello-go.
WARNING! Communication is not secure, please consider using HTTPS. Letsencrypt.org offers free SSL/TLS certificates.

Deployed. 200 OK.
URL: http://127.0.0.1:8080/function/hello-go

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



&lt;p&gt;By inspecting our new function, we can see that it's in a "Ready" state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~/openfaas $ faas describe hello-go 
Name: hello-go
Status: Ready
Replicas: 1
Available replicas: 1
Invocations: 0
Image:               
Function process:    
URL: http://127.0.0.1:8080/function/hello-go
Async URL: http://127.0.0.1:8080/async-function/hello-go
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can now invoke it using the &lt;code&gt;faas invoke&lt;/code&gt; command from the Raspberry Pi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~/openfaas $ echo "Hello OpenFaas" | faas invoke hello-go 
Hello, Go. You said: Hello OpenFaas
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;or from our workstation, but we need to specify the gateway using the &lt;code&gt;--gateway&lt;/code&gt; parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ echo "Hello from laptop" | faas invoke --gateway http://raspberrypi.loc:8080 hello-go

Hello, Go. You said: Hello from laptop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And that's it, we have built and deployed our first function to OpenFaas running on a single Raspberry PI 🎉 Pretty cool isn't it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog post, we have gone through how we can run a FaaS serverless platform based on OpenFaas on a Raspberry Pi board, and how to build and deploy a simple function.&lt;br&gt;&lt;br&gt;
Although faasd is still a bit limited in its features compared to OpenFaas, it is very useful for small setups like home labs especially that there is no need to maintain a Kubernetes or Docker Swarm cluster&lt;/p&gt;

&lt;p&gt;Finally, kudos to &lt;a href="https://twitter.com/alexellisuk?s=20"&gt;@&lt;/a&gt;&lt;a href="https://twitter.com/alexellisuk"&gt;alexellisuk&lt;/a&gt; and the OpenFaas team for this awesome project!&lt;/p&gt;

&lt;h4&gt;
  
  
  References:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.alexellis.io/faasd-for-lightweight-serverless/"&gt;https://blog.alexellis.io/faasd-for-lightweight-serverless/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.openfaas.com/"&gt;https://docs.openfaas.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.alexellis.io/quickstart-openfaas-cli/"&gt;https://blog.alexellis.io/quickstart-openfaas-cli/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openfaas/faasd"&gt;https://github.com/openfaas/faasd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/moby/buildkit"&gt;https://github.com/moby/buildkit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>serverless</category>
      <category>openfaas</category>
      <category>raspberrypi</category>
      <category>containerd</category>
    </item>
    <item>
      <title>How I prepared &amp; passed the Certified Kubernetes Administrator (CKA) Exam</title>
      <dc:creator>Mehdi Yedes</dc:creator>
      <pubDate>Wed, 29 Jan 2020 09:45:23 +0000</pubDate>
      <link>https://dev.to/mehyedes/how-i-prepared-passed-the-certified-kubernetes-administrator-cka-exam-4nk7</link>
      <guid>https://dev.to/mehyedes/how-i-prepared-passed-the-certified-kubernetes-administrator-cka-exam-4nk7</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zaspSQZ7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/01/cert_anon.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zaspSQZ7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://myedes.io/content/images/2020/01/cert_anon.jpeg" alt="How I prepared &amp;amp; passed the Certified Kubernetes Administrator (CKA) Exam"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finishing the year as a Certified Kubernetes Administrator(CKA) was my personal goal for 2019, and I was able to take the exam around the end of the year and pass with a score of &lt;strong&gt;91%&lt;/strong&gt;. 🎉&lt;/p&gt;

&lt;p&gt;In this blog post, I wanted to share some useful resources that helped me pass the CKA exam, and a few tips that can help you prepare and hopefully pass if you are also planning to take it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;DISCLAIMER:&lt;/strong&gt; This post is a bit long because I tried to dump all the knowledge and experience I gathered when preparing for the CKA exam. So brace yourself 😀&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  The Certified Kubernetes Administrator Exam
&lt;/h1&gt;

&lt;p&gt;With the exploding adoption of Kubernetes, the &lt;a href="https://www.cncf.io/certification/cka/"&gt;Certified Kubernetes Administrator&lt;/a&gt; program was created by the &lt;a href="https://www.cncf.io/"&gt;Cloud Native Computing Foundation&lt;/a&gt;(CNCF) in collaboration with the Linux Foundation to allow Kubernetes users to demonstrate that they have the necessary skills and knowledge to perform the tasks and responsibilities of a Kubernetes administrator.&lt;/p&gt;

&lt;h2&gt;
  
  
  The exam's format
&lt;/h2&gt;

&lt;p&gt;The good thing about it is that it's 100% hands-on. It's an online proctored exam where you are asked to perform certain tasks on the command line.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://training.linuxfoundation.org/go/cka-ckad-candidate-handbook"&gt;Candidate Handbook&lt;/a&gt; is your definitive source for any details about the exam. So make sure to read it &lt;strong&gt;thoroughly&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Here is a short list of points worth mentioning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need a &lt;strong&gt;steady&lt;/strong&gt; internet connection.&lt;/li&gt;
&lt;li&gt;You would need a &lt;strong&gt;webcam&lt;/strong&gt; and a &lt;strong&gt;microphone&lt;/strong&gt; which are required by the proctor.&lt;/li&gt;
&lt;li&gt;You would need a government issued &lt;strong&gt;ID&lt;/strong&gt; , or a passport.&lt;/li&gt;
&lt;li&gt;The exam consists of &lt;strong&gt;24 questions&lt;/strong&gt; that you can solve in no specific order.&lt;/li&gt;
&lt;li&gt;The duration of the exam is &lt;strong&gt;3 hours&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The pass mark is &lt;strong&gt;74%.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You need to use the &lt;strong&gt;Chrome browser&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You have &lt;strong&gt;one free retake&lt;/strong&gt; in case you don't pass on your first try 🎉&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The curriculum
&lt;/h2&gt;

&lt;p&gt;Unlike the Certified &lt;a href="https://www.cncf.io/certification/ckad/"&gt;Kubernetes Application Developer&lt;/a&gt;(CKAD) exam, the CKA exam focuses more on cluster administration rather than deploying and managing applications on Kubernetes.&lt;/p&gt;

&lt;p&gt;The exam's curriculum is usually updated quarterly, you can always find the latest version on Github at &lt;a href="https://github.com/cncf/curriculum"&gt;cncf/curriculum&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The CKA exam covers the following topics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application Lifecycle Management – 8%&lt;/li&gt;
&lt;li&gt;Installation, Configuration &amp;amp; Validation – 12%&lt;/li&gt;
&lt;li&gt;Core Concepts – 19%&lt;/li&gt;
&lt;li&gt;Networking – 11%&lt;/li&gt;
&lt;li&gt;Scheduling – 5%&lt;/li&gt;
&lt;li&gt;Security – 12%&lt;/li&gt;
&lt;li&gt;Cluster Maintenance – 11%&lt;/li&gt;
&lt;li&gt;Logging / Monitoring – 5%&lt;/li&gt;
&lt;li&gt;Storage – 7% &lt;/li&gt;
&lt;li&gt;Troubleshooting – 10%&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The exam environment
&lt;/h2&gt;

&lt;p&gt;The day of the exam, you will have multiple cluster presented for you, and with each question you will be provided with name of the cluster where you should try to solve the question.&lt;/p&gt;

&lt;p&gt;Below is the list of the clusters provided to the candidate from the latest &lt;a href="http://training.linuxfoundation.org/go//Important-Tips-CKA-CKAD"&gt;Exam Tips&lt;/a&gt; document available at the CKA CNCF page at the time of writing this post:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cluster&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Members&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;CNI&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;k8s&lt;/td&gt;
&lt;td&gt;1 master, 2 workers&lt;/td&gt;
&lt;td&gt;flannel&lt;/td&gt;
&lt;td&gt;k8s cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hk8s&lt;/td&gt;
&lt;td&gt;1 master, 2 workers&lt;/td&gt;
&lt;td&gt;calico&lt;/td&gt;
&lt;td&gt;k8s cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bk8s&lt;/td&gt;
&lt;td&gt;1 master, 1 worker&lt;/td&gt;
&lt;td&gt;flannel&lt;/td&gt;
&lt;td&gt;k8s cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wk8s&lt;/td&gt;
&lt;td&gt;1 master, 2 workers&lt;/td&gt;
&lt;td&gt;flannel&lt;/td&gt;
&lt;td&gt;k8s cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ek8s&lt;/td&gt;
&lt;td&gt;1 master, 2 workers&lt;/td&gt;
&lt;td&gt;flannel&lt;/td&gt;
&lt;td&gt;k8s cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ik8s&lt;/td&gt;
&lt;td&gt;1 master, 1 base node&lt;/td&gt;
&lt;td&gt;loopback&lt;/td&gt;
&lt;td&gt;k8s cluster - missing worker node&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Kubernetes version running on the exam environment is currently &lt;strong&gt;v1.16&lt;/strong&gt; at the time of writing this post, and the Linux distribution is &lt;strong&gt;Ubuntu 16&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Preparing for the exam
&lt;/h1&gt;

&lt;p&gt;The first step in preparing for the CKA exam(or any exam) is understanding what it is about.&lt;br&gt;&lt;br&gt;
So make sure to read all the documents provided in the CKA Program page at &lt;a href="https://www.cncf.io/certification/cka/"&gt;https://www.cncf.io/certification/cka/&lt;/a&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://training.linuxfoundation.org/go/cka-ckad-candidate-handbook"&gt;Candidate Handbook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cncf/curriculum"&gt;Curriculum Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://training.linuxfoundation.org/go//Important-Tips-CKA-CKAD"&gt;Exam Tips&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://training.linuxfoundation.org/go/cka-ckad-faq"&gt;Frequently Asked Questions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pre-requisites
&lt;/h2&gt;

&lt;p&gt;Although the CKA exam is about Kubernetes, it also requires some basic sysadmin skills. So, you need be comfortable with the Linux command line and have a minimum knowledge on how to use the following tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;systemd&lt;/code&gt; for managing system services. Basic knowledge would be enough IMHO, but very important especially for troubleshooting cluster components. There is a nice tutorial series for that provided by the DigitalOcean people: &lt;a href="https://www.digitalocean.com/community/tutorials/systemd-essentials-working-with-services-units-and-the-journal"&gt;Systemd Essentials: Working with Services, Units, and the Journal&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;vim&lt;/code&gt; for editing files on the command line. Although you could change the default text editor by setting the value of &lt;code&gt;$EDITOR&lt;/code&gt; to nano if that's what you are most comfortable with, vim can give you a productive boost during the exam. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;tmux&lt;/code&gt; since you only get one console during the exam, being able to have multiple panes open at the same time might be helpful. Personally, I didn't really need or use tmux during the exam, so if you don't use it already in your day to day work, I don't recommend learning it for the sake of the exam.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;openssl&lt;/code&gt; for generating keys, CSRs, certificates etc.. You will probably need it during the exam for security related questions. So make sure you train yourself to use it at least for those basic use cases.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting ready for the exam
&lt;/h2&gt;

&lt;p&gt;In this section, I am going to provide some tips on how to prepare for the exam and also list some useful resources that helped me and might help you get fit for the exam day.&lt;/p&gt;

&lt;h3&gt;
  
  
  kubectl
&lt;/h3&gt;

&lt;p&gt;Since the CKA exam is 100% practical, you need to make sure you are confident enough with &lt;code&gt;kubectl&lt;/code&gt;. That's mostly what you will be using during the exam, and since you are already reading this post, chances are you are already using kubectl or at least experimenting with it.&lt;/p&gt;

&lt;p&gt;You need to be quick on the command line since you will have limited time for solving the questions during the exam, so knowing how to perform the following quickly with kubectl is crucial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checking the config, switching and creating contexts&lt;/li&gt;
&lt;li&gt;Creating, editing and deleting kubernetes resources&lt;/li&gt;
&lt;li&gt;Viewing, finding and inspecting resources&lt;/li&gt;
&lt;li&gt;Updating and patching resources&lt;/li&gt;
&lt;li&gt;Interacting with pods, nodes and cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of useful &lt;code&gt;kubectl&lt;/code&gt; command examples can be found in the &lt;a href="https://kubernetes.io/docs/reference/kubectl/cheatsheet/"&gt;kubectl cheatsheet&lt;/a&gt; available in the official kubernetes documentation.&lt;/p&gt;

&lt;p&gt;It is also very useful to know how to use the &lt;code&gt;kubectl run&lt;/code&gt; command to create resources quickly, saving time by avoiding to write yaml files(who likes that, right?). You can also use it to generate yaml files if you need to edit something before actually creating the kubernetes object by using the &lt;code&gt;--dry-run&lt;/code&gt;  and the &lt;code&gt;-o yaml&lt;/code&gt; options combined. Some details about the &lt;code&gt;kubectl run&lt;/code&gt; usage can be found &lt;a href="https://kubernetes.io/docs/reference/kubectl/conventions/#kubectl-run"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you come from the Docker world and still starting with Kubernetes, then the &lt;strong&gt;&lt;a href="https://kubernetes.io/docs/reference/kubectl/docker-cli-to-kubectl/"&gt;kubectl for Docker Users&lt;/a&gt;&lt;/strong&gt; documentation page is definitely worth checking out.&lt;/p&gt;

&lt;p&gt;Getting familiar with &lt;strong&gt;&lt;a href="https://kubernetes.io/docs/reference/kubectl/jsonpath/"&gt;JSONPath&lt;/a&gt;&lt;/strong&gt; template would be also helpful. Combining kubectl and jsonpath enables you to easily extract resource information in a format that you can specify.&lt;/p&gt;

&lt;p&gt;Finally, make sure to practice a lot with kubectl, whether it is on local kubernetes clusters with minikube, docker-desktop or on the cloud. That is very crucial for the exam.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learning resources
&lt;/h3&gt;

&lt;p&gt;If you are planning to take the CKA exam, them you probably have already searched around the internet for some resources and found plenty. So in this section, I am only going to list the resources that I have found most informative and helpful for me to pass the exam.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;The Kubernetes Documentation&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most important resource is the &lt;a href="https://kubernetes.io/docs/home/"&gt;official kubernetes documentation&lt;/a&gt;; that's your definitive source of information. And since you are &lt;strong&gt;allowed&lt;/strong&gt; to access it during the exam, it's really important that you know how to easily navigate it and quickly search for what you need. Make sure to get well accustomed to it.&lt;/p&gt;

&lt;p&gt;Also, make sure you get to do most if not all of the &lt;strong&gt;tasks&lt;/strong&gt; listed &lt;a href="https://kubernetes.io/docs/tasks/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's really useful to join the kubernetes slack community at &lt;a href="https://slack.k8s.io/"&gt;https://slack.k8s.io/&lt;/a&gt;. There is a slack channel dedicated to CKA exam questions named &lt;strong&gt;#cka-exam-prep.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The members there are really nice and helpful and would answer any questions you have.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Kubernetes The Hard Way(KTHW)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/kelseyhightower/kubernetes-the-hard-way"&gt;kubernetes-the-hard-way&lt;/a&gt; repo was created by Kelsey Hightower to provide a guide for bootstrapping a Kubernetes cluster on Google Cloud Platform. It helps you understand the internals of a Kubernetes cluster, which would be really important especially for troubleshooting.&lt;br&gt;&lt;br&gt;
Make sure to go through it at least once while trying to understand &lt;strong&gt;every&lt;/strong&gt; step on the way.&lt;/p&gt;

&lt;p&gt;If you don't want to use GCP, there is another fork that relies on vagrant and can be found here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mmumshad/kubernetes-the-hard-way"&gt;Bootstrap Kubernetes the hard way on Vagrant on Local Machine. No scripts.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Online Courses&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are a couple of online course available that can help you prepare for the CKA exam.&lt;br&gt;&lt;br&gt;
I was able to try 3 of them while preparing for the exam:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://training.linuxfoundation.org/training/kubernetes-fundamentals/"&gt;Kubernetes Fundamentals (LFS258)&lt;/a&gt; which was part of the CKA exam bundle I purchased.
I only liked the practice labs, otherwise the course was very boring and you'd better read the kubernetes documentation rather than reading slides. So &lt;em&gt;IMHO&lt;/em&gt; totally not worth taking.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://linuxacademy.com/course/cloud-native-certified-kubernetes-administrator-cka/"&gt;Linuxacademy: Cloud Native Certified Kubernetes Administrator (CKA)&lt;/a&gt;:
I found this course really good at first. However, after a while I found myself watching the instructor mostly typing commands in the terminal so I got disconnected and stopped following the course. I also tried the mock exams, but I found them a bit limited.&lt;/li&gt;
&lt;li&gt;Udemy: &lt;a href="https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests/"&gt;Certified Kubernetes Administrator (CKA) with Practice Tests&lt;/a&gt;
This was the most comprehensive course for me in this list. It covered all the topics, and the instructor made sure to explain all the Kubernetes concepts(and also other concepts) thouroughly.
The practice labs are really good since you are provided with an environment and your answers are checked there automatically.
The mock exams were also a great preparation for the exam.
&lt;strong&gt;I cannot &lt;em&gt;recommend&lt;/em&gt; this course enough!&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Additional Resources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.manning.com/books/kubernetes-in-action"&gt;Kubernetes in Action&lt;/a&gt; book by Marko Lukša is definitely worth reading to have a good understanding of Kubernetes.&lt;/p&gt;

&lt;p&gt;There is a google spreadsheet created by the community that compiles a lot of useful resources that can be found &lt;a href="https://bit.ly/2IdKwIc"&gt;here&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
Some additional useful Github repositories:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/stretchcloud/cka-lab-practice"&gt;https://github.com/stretchcloud/cka-lab-practice&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/walidshaari/Kubernetes-Certified-Administrator"&gt;https://github.com/walidshaari/Kubernetes-Certified-Administrator&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/krzko/awesome-cka"&gt;https://github.com/krzko/awesome-cka&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/David-VTUK/CKA-StudyGuide"&gt;https://github.com/David-VTUK/CKA-StudyGuide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tips for the exam day
&lt;/h2&gt;

&lt;p&gt;In this section, I am going to provide a few tips for the day of the exam:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are allowed to open one additional browser tab in addition to the exam interface and you can use it to browse the kubernetes documentation. &lt;em&gt;Bookmarks&lt;/em&gt; are also allowed, so make sure to create some bookmarks in chromes for the documentation sections that you think you would need in the exam beforehand.&lt;/li&gt;
&lt;li&gt;You don't have to solve the questions in a specific order. So you can start with the easiest to build some confidence, but that's a matter of personal preference.&lt;/li&gt;
&lt;li&gt;There is built-in notepad in the exam interface which might be handy since you're not allowed to write on paper during the exam. You can use it to write the questions' numbers so that you keep track of the ones you didn't solve and get back to them later.&lt;/li&gt;
&lt;li&gt;If you are taking the exam with a laptop, use an external monitor if your laptop screen is tiny. You would need all the space you can get for the terminal.&lt;/li&gt;
&lt;li&gt;Make sure to go to the restroom before starting the exam. During the 3 hours, you would only be able to take a break if your proctor allows it but the timer would never stop for you.&lt;/li&gt;
&lt;li&gt;Have some water in a bottle without a label, or a transparent glass. Anything other than that is not allowed.&lt;/li&gt;
&lt;li&gt;Take the exam in a quiet room, on a clean desk. Remove any electronics from the desk and make sure that absolutely no one enters the room during the exam.
You will be asked by the proctor to show him around the room using the webcam.&lt;/li&gt;
&lt;li&gt;Finally: &lt;strong&gt;&lt;em&gt;GOOD LUCK!&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;In this post, I tried to provide some tips and resources for preparing the CKA exam based on my experience.&lt;/p&gt;

&lt;p&gt;I hope this article would be useful for you and please let me know in the comments if it somehow helped you to pass the exam.&lt;/p&gt;




&lt;p&gt;This post was originally published at: &lt;a href="https://myedes.io/cka-exam-tips/"&gt;https://myedes.io/cka-exam-tips/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cka</category>
      <category>certification</category>
      <category>cncf</category>
    </item>
  </channel>
</rss>
