<?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: Dunith Danushka</title>
    <description>The latest articles on DEV Community by Dunith Danushka (@dunithd).</description>
    <link>https://dev.to/dunithd</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%2F43055%2Fc02e183b-b9af-49b2-b159-95dc47f5539b.jpg</url>
      <title>DEV Community: Dunith Danushka</title>
      <link>https://dev.to/dunithd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dunithd"/>
    <language>en</language>
    <item>
      <title>How To Structure a Perfect Technical Tutorial?</title>
      <dc:creator>Dunith Danushka</dc:creator>
      <pubDate>Thu, 04 Jan 2024 13:25:13 +0000</pubDate>
      <link>https://dev.to/dunithd/how-to-structure-a-perfect-technical-tutorial-21h9</link>
      <guid>https://dev.to/dunithd/how-to-structure-a-perfect-technical-tutorial-21h9</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RbeUGpt1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3b35z2u0hl4wepsgrh29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RbeUGpt1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3b35z2u0hl4wepsgrh29.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A technical tutorial is a detailed instructional guide that teaches individuals how to perform a specific task or learn a particular skill in a specialized field. These tutorials provide step-by-step instructions, explanations, and examples to help users understand and implement the concepts being taught.&lt;/p&gt;

&lt;p&gt;Creating technical tutorials is a regular part of your job if you are an educator, content creator, subject matter expert, or a DevRel person like me. Maintaining a consistent structure across tutorials offers several benefits to creators as well as readers.&lt;/p&gt;

&lt;p&gt;In this post, I will dissect the structure of a typical tutorial that society would potentially reward as "good." I will lay out the key sections of a tutorial, their goals, and what information should go into them. Whether you are a beginner or an experienced creator, use this post as a guiding light to structure your next tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes a good tutorial?
&lt;/h2&gt;

&lt;p&gt;A good tutorial often goes beyond a typical instruction list and touches upon several factors.&lt;/p&gt;

&lt;p&gt;In my opinion, a tutorial qualifies as "good" if it meets the following criteria.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Clarity of Explanation&lt;/strong&gt;: Clear explanations are crucial for user comprehension. A good tutorial should present concepts in a straightforward and understandable manner, avoiding unnecessary complexity. Users should be able to follow the content without confusion.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Structural Organization&lt;/strong&gt;: A logical and well-organized structure is vital for user navigation and comprehension. Headings, subheadings, and a clear flow of information contribute to a seamless learning experience. Users should be able to follow the progression of the tutorial easily.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Completeness&lt;/strong&gt;: The tutorial should cover all necessary aspects of the topic without leaving critical information gaps. It should strike a balance between providing enough depth to facilitate understanding and avoiding unnecessary details that might overwhelm users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Engagement&lt;/strong&gt;: Engaging content keeps users interested and motivated. A good tutorial employs a variety of content types, such as text, images, videos, and interactive elements. The writing style should be engaging, making the learning experience enjoyable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Usability&lt;/strong&gt;: Usability refers to how easy it is for users to navigate and follow the tutorial. Clear instructions, a user-friendly layout, and well-defined steps contribute to usability. Users should be able to access and implement the information without encountering unnecessary obstacles.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Beginner creators might take a while to master all these criteria at once. But they can certainly start at some point and work iteratively to make tutorials better over time.&lt;/p&gt;

&lt;p&gt;I can help you by providing a starting point - a template that helps you structure your tutorials better. This template divides a tutorial into several sections and guides you to put the right information in the right places.&lt;/p&gt;

&lt;p&gt;The key sections of a tutorial include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Introduction&lt;/strong&gt; - Essential summary of the tutorial&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overview&lt;/strong&gt; - The problem you are trying to solve with the tutorial&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prerequisites&lt;/strong&gt; - What the reader must have before getting started.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution&lt;/strong&gt; - Step-by-step instructions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation&lt;/strong&gt; - How should the reader verify the tutorial outcome?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tearing down&lt;/strong&gt; - Cleaning up resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conclusion&lt;/strong&gt; - Takeaways&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let me dive deep into each section next.&lt;/p&gt;

&lt;h2&gt;
  
  
  The seven sections of a good tutorial
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;This section introduces the tutorial and briefly explains what to expect in the content. After reading the introduction, the reader can decide whether to continue or quit reading, saving him a few minutes in his life.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;hook&lt;/strong&gt; and the &lt;strong&gt;summary&lt;/strong&gt; are two elements you can include in the introduction to persuade the reader to continue reading the rest of the content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hook&lt;/strong&gt;: A few powerful sentences explaining why the reader should read the tutorial. In most cases, this can be the problem you are trying to solve, summarized in 2–3 sentences. The closer the hook gets to the reader's problem, the more readers continue reading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary of the article (TLDR)&lt;/strong&gt;: What can the reader expect in the tutorial? What's the outcome, and what are the takeaways?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8SpkdAIF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w5brysag0ewenm1bvr9p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8SpkdAIF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w5brysag0ewenm1bvr9p.png" alt="Image description" width="776" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure to keep the introduction short and sweet to lure in more readers to the content. Ideally, you should limit it to 3–4 short paragraphs.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Overview
&lt;/h3&gt;

&lt;p&gt;This is where you should describe two things in particular.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem statement&lt;/strong&gt;: What problem are you trying to solve with the tutorial? What benefits does it provide? Spend a paragraph or two mentioning these points.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution architecture&lt;/strong&gt;: How are you trying to solve the problems? What's the high-level solution architecture would look like? Mention the technology components you will use in the tutorial and highlight the data flow, connectivity, and user interactions among them.&lt;/p&gt;

&lt;p&gt;I often find it easy to pick a fictitious use case and build your narrative around that. For example, to build a data pipeline from a database to a data lake, you can think of an online store use case.&lt;/p&gt;

&lt;p&gt;Include visuals as much as possible in this section to provide more clarity to the reader. I highly recommend including a high-level solutions architecture diagram capturing the tutorial scenario. That way, the reader can quickly capture what you are trying to do, even if you miss some instructions.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;You should mention what tools, technologies, and components the reader must have beforehand to complete the tutorial.&lt;/p&gt;

&lt;p&gt;For example, the reader must have installed something like a Git CLI, a database, or a build tool in the local workstation or should create a SaaS account before proceeding with the tutorial. Use a bulleted list to mention them.&lt;/p&gt;

&lt;p&gt;Furthermore, if certain tools are specific to a platform, highlight them as well. For example, some Linux build tools don't work in Windows, causing the reader to install an alternative, like WSL, before starting the tutorial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finished code repo&lt;/strong&gt;: Not all readers are made equal. Some readers are impatient; they want to see how the completed solution would look like, so they copy code from it :) You can help these readers by pushing the completed solution code into a public Git repo and sharing its link here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aHLyuOC1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/29z6msv9m7criz1knlhu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aHLyuOC1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/29z6msv9m7criz1knlhu.png" alt="Image description" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution
&lt;/h3&gt;

&lt;p&gt;Now that we have set the stage so far, we've come to the meat of the tutorial. This is where you provide step-by-step, Detailed guidance on each stage of the process or task.&lt;/p&gt;

&lt;p&gt;Be sure to provide clear instructions in the order of their dependency. Add screenshots and diagrams as needed - the more, the merrier.&lt;/p&gt;

&lt;p&gt;After each instruction, provide the reader the means to verify whether they have executed that properly. For example, if the instruction is about creating a table, the reader can verify the operation by running an SQL SELECT statement against the table.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Validation
&lt;/h3&gt;

&lt;p&gt;Once the reader finishes following instructions, you should provide a way of verifying the end-to-end solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Method of integration testing&lt;/strong&gt;: Provide a sample data set to verify the connectivity, data flow, and expected output of the solution the reader has built. You can include a screenshot or a visual portraying the final solution if the instructions were followed correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Known issues and challenges&lt;/strong&gt;: Sometimes, certain steps can't be completed due to technical challenges or limitations. Therefore, state any known issues and inform the reader about possible workarounds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tearing down
&lt;/h3&gt;

&lt;p&gt;This is an optional step where you instruct the reader to destroy the environment and clean things up to save costs. For example, you can ask the user to turn off an EC2 instance after following the tutorial to reduce the cloud bill.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;At last, we come to the end of the tutorial. This is where we wrap things up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collecting the thoughts&lt;/strong&gt;: Reiterate what you have accomplished in the tutorial. Spend the first paragraph of the conclusion wrapping things up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Taking it to the next level&lt;/strong&gt;: The tutorial might have shown a thin slice of a solution that addresses a simple problem. Suggest to the reader how to extend the solution to implement a real-world solution and take it to production. You don't have to be detailed here, but highlight a few potential steps necessary as a guideline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Call to action&lt;/strong&gt;: Dedicate the last paragraph to insert your CTA, driving satisfied readers to your marketing funnel. After putting so much time and effort into creating the tutorial, you may lure the reader into the next step of your lead funnel ;)&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Technical tutorials can be packaged and presented in various formats, including written articles, video tutorials, interactive online courses, or a combination of these mediums. You can use this post as a template to structure your tutorials, regardless of the format they are presented.&lt;/p&gt;

&lt;p&gt;As a rule of thumb, try to cut your content at the 2000 word mark because most readers, including me, have a short attention span, and it isn't easy to navigate and follow a long document. Use a tutorial series if the content goes beyond 2000 words.&lt;/p&gt;

&lt;p&gt;I hope you learned something new today. If you are interested in reading posts about DevRel, writing, and productivity, subscribe to my newsletter &lt;a href="https://www.linkedin.com/newsletters/7148283663649046528/"&gt;The Tributary&lt;/a&gt;&lt;/p&gt;

</description>
      <category>writing</category>
    </item>
    <item>
      <title>How To Set a Static IP Address in macOS Using Command Line</title>
      <dc:creator>Dunith Danushka</dc:creator>
      <pubDate>Sat, 25 Sep 2021 07:02:05 +0000</pubDate>
      <link>https://dev.to/dunithd/how-to-set-a-static-ip-address-in-macos-using-command-line-4j1b</link>
      <guid>https://dev.to/dunithd/how-to-set-a-static-ip-address-in-macos-using-command-line-4j1b</guid>
      <description>&lt;p&gt;Imagine you have an Apple device like a Macmini, MacBook, or an iMac in a remote location. You only have access to it via SSH and no Desktop GUI interaction is allowed.&lt;/p&gt;

&lt;p&gt;What if you wanted to change the IP address of the device? For example, how do you assign a static IP address instead of DHCP?&lt;/p&gt;

&lt;p&gt;Command line is your option in that case.&lt;/p&gt;

&lt;p&gt;Let's see how to get it done...&lt;/p&gt;

&lt;h2&gt;
  
  
  Make sure you are in the sudoers list
&lt;/h2&gt;

&lt;p&gt;To make changes like network interface configurations, you need to be a user with super user privileges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow the below commands
&lt;/h2&gt;

&lt;p&gt;First, get a list of all your network services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;networksetup &lt;span class="nt"&gt;-listallnetworkservices&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That command will result in an output like below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;networksetup &lt;span class="nt"&gt;-listallnetworkservices&lt;/span&gt;
An asterisk &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; denotes that a network service is disabled.
Apple USB Ethernet Adapter
USB 10/100/1000 LAN
Wi-Fi
Bluetooth PAN
Thunderbolt Bridge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use the desired service name in the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;networksetup &lt;span class="nt"&gt;-setmanual&lt;/span&gt; SERVICE IP SUBNET ROUTER
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, to change the IP address of my Wi-Fi interface, I would use a command like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;networksetup &lt;span class="nt"&gt;-setmanual&lt;/span&gt; Wi-Fi 192.168.1.2 255.255.255.0 192.168.1.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example, &lt;code&gt;192.168.1.2&lt;/code&gt; is my new IP address. &lt;code&gt;192.168.1.1&lt;/code&gt; is the address of the default gateway, which is the router most of the time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting it back to DHCP
&lt;/h3&gt;

&lt;p&gt;To set it back to DHCP, use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;networksetup &lt;span class="nt"&gt;-setdhcp&lt;/span&gt; SERVICE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Keep DHCP with a manual IP
&lt;/h3&gt;

&lt;p&gt;To keep DHCP services but only use a manually designated IP address, then use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;networksetup &lt;span class="nt"&gt;-setmanualwithdhcprouter&lt;/span&gt; SERVICE IP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Confirm settings are permanent
&lt;/h2&gt;

&lt;p&gt;Restart the device &lt;code&gt;sudo shutdown -r now&lt;/code&gt; and see if the new address persists.&lt;/p&gt;

&lt;p&gt;If everything goes well, it should persist.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Redis Sorted Sets Explained</title>
      <dc:creator>Dunith Danushka</dc:creator>
      <pubDate>Tue, 16 Mar 2021 17:14:09 +0000</pubDate>
      <link>https://dev.to/dunithd/redis-sorted-sets-explained-3olb</link>
      <guid>https://dev.to/dunithd/redis-sorted-sets-explained-3olb</guid>
      <description>&lt;p&gt;Before diving in, let's try to understand Sorted Sets by deconstructing the name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzmr6kxercyo2mnxhuxq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzmr6kxercyo2mnxhuxq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As per the above, Sorted Set is a Set data structure that doesn't allow duplicate members. At the same time, its members are ordered in ascending order. By combining both, we can define a Sorted Set as an ordered collection of unique members.&lt;/p&gt;

&lt;p&gt;Sorted Set is similar to the Set data structure in Redis. Members can be a list of non-repeating strings. The only difference is that each member is associated with a score, a floating-point number that provides a sorting order for the Sorted Set. Members are always sorted from the smallest to the greatest score.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fitness leaderboard
&lt;/h2&gt;

&lt;p&gt;The best way to understand a Sorted Set is to picture it as a table with score and member columns. The following is an imaginary fitness leaderboard where the member column represents the players while the score represents the number of steps they had walked so far.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm33ir35tmvrqdqcdbt66.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm33ir35tmvrqdqcdbt66.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The leaderboard has been sorted in ascending order, based on the scores. Scores can be duplicated, but the members can not.&lt;/p&gt;

&lt;p&gt;If two members have the same score, Redis sorts them based on members' lexicographical order. For example, &lt;strong&gt;Jennifer&lt;/strong&gt; and &lt;strong&gt;Peter&lt;/strong&gt; have the same score of 690. But Redis makes sure to put Jennifer before Peter because J comes before P.&lt;/p&gt;

&lt;p&gt;You can also notice the &lt;strong&gt;Rank&lt;/strong&gt; on the right side. Rank is an implicit attribute assigned to each member, based on the magnitude of its score. It's a zero-based index. The member with the smallest score will be assigned the rank of 0.&lt;/p&gt;

&lt;p&gt;Rank is useful when you are retrieving members from the sorted set.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits and use cases
&lt;/h2&gt;

&lt;p&gt;Redis guarantees that a Sorted Set is always sorted. There is no additional sorting required for the client who retrieves the stored members back. That saves CPU cycles in the client and also reduces the code complexity.&lt;/p&gt;

&lt;p&gt;Sorted Sets allow you to add, remove, or update elements in a speedy way (in a time proportional to the logarithm of the number of elements). Since elements are taken in order and not ordered afterward, you can also get ranges by score or rank (position) quickly. Accessing the middle of a sorted set is also very fast, so you can use Sorted Sets as a smart list of non-repeating elements where you can quickly access everything you need.&lt;/p&gt;

&lt;p&gt;That makes Sorted Sets an excellent choice of implementing real-time, low-latency leaderboards, priority queues, and secondary indexes.&lt;/p&gt;

&lt;p&gt;So far, we have covered the basics of Sorted Sets. Let's look at some interesting operations we can perform on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operations
&lt;/h2&gt;

&lt;p&gt;We can perform operations on Sorted Sets to add, remove, increment, and retrieve members. Usually, these operations start with the letter 'Z.'&lt;/p&gt;

&lt;p&gt;Let's use the fitness leaderboard example we discussed above to demonstrate these operations. Operations are performed using the Redis CLI. Note that I'm not going to cover all available Sorted Set operations here. I would instead glance through the operations that matter when building a leaderboard-like application. For a full list of operations, you can always refer to Sorted Sets &lt;a href="https://redis.io/topics/data-types" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add new players and their steps to the game
&lt;/h3&gt;

&lt;p&gt;When a new player joins, he/she should be added to the leaderboard with the number of steps.&lt;br&gt;
The operation &lt;code&gt;ZADD&lt;/code&gt; adds one more element to the Sorted Set. Below is the format of the command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ZADD &amp;lt;key&amp;gt; &amp;lt;score&amp;gt; &amp;lt;member&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;key&lt;/code&gt; is the name of the Sorted Set. The following command adds &lt;strong&gt;Lester&lt;/strong&gt; with 790 steps to the &lt;code&gt;players&lt;/code&gt;. In return, it gives you the number of elements added to the set, which is 1 in this case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; ZADD players 790 Lester
(integer) 1
127.0.0.1:6379&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can add any number of elements along with their scores as follows. Note that the elements are stored according to their score, not based on the order they've added.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; ZADD players 980 Mary 850 Alice
(integer) 2
127.0.0.1:6379&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After adding new players, the structure of players would look like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvacplg9ojy2cegblb2z5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvacplg9ojy2cegblb2z5.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Increment/decrement player steps
&lt;/h3&gt;

&lt;p&gt;When players perform their daily walks, their step count needs to be updated on the leaderboard.&lt;/p&gt;

&lt;p&gt;You can use the &lt;code&gt;ZINCRBY&lt;/code&gt; command to increment the score of a given member by any arbitrary value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ZINCRBY &amp;lt;key&amp;gt; &amp;lt;increment&amp;gt; &amp;lt;member&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following command increments &lt;strong&gt;Tom's&lt;/strong&gt; steps by 10.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; ZINCRBY players 10 Tom
"570"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use a negative value to decrement a score. The following command decrements &lt;strong&gt;Kendra's&lt;/strong&gt; steps by 5.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; ZINCRBY players -5 Kendra
"395"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Retrieve top 10 players by score
&lt;/h3&gt;

&lt;p&gt;Now we need to retrieve only the top 10 players to display them on a leaderboard.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ZREVRANGE&lt;/code&gt; command makes that possible by returning only a specified set of members based on their reverse order score.&lt;br&gt;
The command takes the following format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ZREVRANGE &amp;lt;key&amp;gt; &amp;lt;start&amp;gt; &amp;lt;stop&amp;gt; [WITHSCORES]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ZREVRANGE&lt;/code&gt; returns members in reversed-rank order, with scores ordered from high to low. You have to specify starting and ending rank index positions with &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;stop&lt;/code&gt; parameters.&lt;/p&gt;

&lt;p&gt;Let's get the top 10 players by the steps they had walked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; ZREVRANGE players 0 9
1) "Mary"
2) "Alice"
3) "Lester"
4) "Frank"
5) "Peter"
6) "Jennifer"
7) "Barbara"
8) "Tom"
9) "Kendra"
10) "Dave"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is one additional parameter: &lt;code&gt;WITHSCORES&lt;/code&gt;, which returns the score for each member.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; ZREVRANGE players 0 9 WITHSCORES
1) "Mary"
2) "980"
3) "Alice"
4) "850"
5) "Lester"
6) "790"
7) "Frank"
8) "740"
9) "Peter"
10) "690"
11) "Jennifer"
12) "690"
13) "Barbara"
14) "650"
15) "Tom"
16) "570"
17) "Kendra"
18) "395"
19) "Dave"
20) "340"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ZRANGE&lt;/code&gt; command does the opposite to this. It returns members in rank order, with scores ordered from &lt;strong&gt;low to high.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The following command returns the first ten players who had walked the least number of steps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; ZRANGE players 0 9
1) "Dave"
2) "Kendra"
3) "Tom"
4) "Barbara"
5) "Jennifer"
6) "Peter"
7) "Frank"
8) "Lester"
9) "Alice"
10) "Mary"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Retrieve the rank and the score of an individual player
&lt;/h3&gt;

&lt;p&gt;Now we need to find the score and the rank of a given player. Sometimes, that can be displayed on the player's profile.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ZRANK&lt;/code&gt; and &lt;code&gt;ZREVRANK&lt;/code&gt; commands return the rank of a member, which is a 0 based index position.&lt;/p&gt;

&lt;p&gt;The format of the command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ZRANK|ZREVRANK] &amp;lt;key&amp;gt; &amp;lt;member&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following command returns 0 for &lt;strong&gt;Dave&lt;/strong&gt;, who's currently at the bottom of the leaderboard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; ZRANK players Dave
(integer) 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, &lt;code&gt;ZREVRANK&lt;/code&gt; for &lt;strong&gt;Mary&lt;/strong&gt; should return 0. Because it returns the rank in the reverse order, with scores sorted from low to high.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; ZREVRANK players Mary
(integer) 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ZSCORE&lt;/code&gt; provides the current score associated with the member. The format of the command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ZSCORE &amp;lt;key&amp;gt; &amp;lt;member&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns &lt;strong&gt;Peter's score&lt;/strong&gt;, which is 690.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; ZSCORE players Peter
"690"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Get the player count based on the score
&lt;/h2&gt;

&lt;p&gt;How can we find the number of players who had walked between a given range of steps? For example, how many players had walked more than 500 but less than 700 steps?&lt;/p&gt;

&lt;p&gt;The command &lt;code&gt;ZCOUNT&lt;/code&gt; will count the members between the min and the max score. That is inclusive. The format of the command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ZCOUNT &amp;lt;key&amp;gt; &amp;lt;min&amp;gt; &amp;lt;max&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following command returns the number of players who had walked between 500 and 700 steps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; ZCOUNT players 500 700
(integer) 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Remove players from the game
&lt;/h3&gt;

&lt;p&gt;When players leave the game, we need to remove them from the leaderboard.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ZREM&lt;/code&gt; command removes one or more members from a Sorted Set. The command format is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ZREM &amp;lt;key&amp;gt; &amp;lt;member&amp;gt; [&amp;lt;member&amp;gt; …]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can remove &lt;strong&gt;Barbara&lt;/strong&gt; from the leaderboard as she decided to leave the game because she was tired.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; ZREM players Barbara
(integer) 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, after all these operations, our leaderboard looks like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjh7zy9cwqp8ll8jc1lgg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjh7zy9cwqp8ll8jc1lgg.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Due to its natural ordering and efficiency in accessing stored elements, Sorted Sets are getting increasingly popular for building applications like real-time leaderboards. They typically store more than a million elements in memory and provide low-latency access to real-time dashboards.&lt;/p&gt;

&lt;p&gt;Implementing the same functionality with a traditional database would always be challenging. Even if you manage to do that, feeding that to a dashboard will be a recipe for disaster.&lt;/p&gt;

&lt;p&gt;Thus, an in-memory data structure such as Redis shines well here.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://redis.io/topics/data-types" rel="noopener noreferrer"&gt;https://redis.io/topics/data-types&lt;/a&gt;&lt;/p&gt;

</description>
      <category>redis</category>
      <category>sortedset</category>
      <category>leaderboard</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Production-grade Ballerina Microservices on Azure AKS - Part 2: Continuous Integration With Azure DevOps</title>
      <dc:creator>Dunith Danushka</dc:creator>
      <pubDate>Mon, 17 Aug 2020 15:05:17 +0000</pubDate>
      <link>https://dev.to/dunithd/production-grade-ballerina-microservices-on-azure-aks-part-2-continuous-integration-with-azure-devops-8o4</link>
      <guid>https://dev.to/dunithd/production-grade-ballerina-microservices-on-azure-aks-part-2-continuous-integration-with-azure-devops-8o4</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In our previous tutorial, we built a simple Ballerina microservice that returns a JSON array of Todo list items. There, we did everything manually from building the project, generating a Docker image, and pushing it to Azure Container Registry and deploying it into Azure Kubernetes Service.&lt;/p&gt;

&lt;p&gt;Although that provided you with a simple starter project, you need a production-grade continuous integration (CI) and continuous deployment (CD) pipelines to make the shipping of developer changes to produce more reliable, repeatable, and frequent.&lt;/p&gt;

&lt;p&gt;In the scope of this tutorial, we are going to build a continuous integration pipeline with Azure Pipelines to automate the build and verification process for the &lt;strong&gt;TodoService&lt;/strong&gt; we've implemented in the previous tutorial. During the tutorial, you will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a project and a pipeline in Azure Pipelines.&lt;/li&gt;
&lt;li&gt;Connect a GitHub repo to the pipeline&lt;/li&gt;
&lt;li&gt;Create a private container registry in Azure Container Registry (ACR) to store the TodoService Docker image&lt;/li&gt;
&lt;li&gt;Create an Azure AD service principal to authenticate the pipeline against ACR&lt;/li&gt;
&lt;li&gt;Create a Docker registry service connection to connect the pipeline to ACR&lt;/li&gt;
&lt;li&gt;Code the pipeline to build the TodoService project, generate a Docker image and  push it to ACR&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you have missed the first part of the series, you can find it &lt;a href="https://www.dunithwrites.com/post/production-grade-ballerina-microservices-on-azure-aks-part-1-the-basics/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A GitHub account, where you can create a repository. If you don't have one, you can &lt;a href="https://github.com/"&gt;create one for free&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An Azure DevOps organization. If you don't have one, you can &lt;a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started/pipelines-sign-up?view=azure-devops"&gt;create one for free&lt;/a&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An Azure account. If you don't have one, you can &lt;a href="https://azure.microsoft.com/free/"&gt;create one for free&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Installation of Docker and &lt;a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli"&gt;Azure CLI&lt;/a&gt; in your local workstation.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Azure Pipelines basics - pipelines, stages, agents, jobs, and tasks
&lt;/h2&gt;

&lt;p&gt;Azure Pipelines is a cloud service that you can use to automatically build and test your code project and make it available to other users. It works with just about any language or project type.&lt;/p&gt;

&lt;p&gt;Azure Pipelines combines continuous integration (CI) and continuous delivery (CD) to constantly and consistently test and build your code and ship it to any target.&lt;/p&gt;

&lt;p&gt;In order to complete the tutorial, you need a fair knowledge about the key concepts of Azure Pipelines. Especially with build stages, build agents, and various pipeline tasks that'll be used throughout the tutorial. &lt;/p&gt;

&lt;p&gt;I suggest you visit the official &lt;a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started/key-pipelines-concepts?view=azure-devops"&gt;Azure pipelines documentation&lt;/a&gt; that covers the basic concepts before we dive deeper. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QYF9oUIa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.dunithwrites.com/image/content/key-concepts-overview.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QYF9oUIa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.dunithwrites.com/image/content/key-concepts-overview.svg" alt="key-concepts-overview" width="529" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Get the code
&lt;/h2&gt;

&lt;p&gt;Fork the following repository that contains our initial TodoService implementation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://github.com/dunithd/ballerina-on-aks/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Sign in to Azure Pipelines and create a project
&lt;/h2&gt;

&lt;p&gt;Sign in to &lt;a href="https://azure.microsoft.com/services/devops/pipelines"&gt;Azure Pipelines&lt;/a&gt;. After you sign in, your browser goes to &lt;code&gt;https://dev.azure.com/my-organization-name&lt;/code&gt; and displays your Azure DevOps dashboard.&lt;/p&gt;

&lt;p&gt;Within your selected organization, create a &lt;em&gt;project&lt;/em&gt;. If you don't have any projects in your organization, you see a &lt;strong&gt;Create a project to get started&lt;/strong&gt; screen. Otherwise, select the &lt;strong&gt;Create Project&lt;/strong&gt; button in the upper-right corner of the dashboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Connect and select repository
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to your project.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Pipelines&lt;/strong&gt;, and then select &lt;strong&gt;New Pipeline&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Walkthrough the steps of the wizard by first selecting &lt;strong&gt;GitHub&lt;/strong&gt; as the location of your source code.&lt;/li&gt;
&lt;li&gt;You might be redirected to GitHub to sign in. If so, enter your GitHub credentials.&lt;/li&gt;
&lt;li&gt;When the list of repositories appears, select the repository you just forked above.&lt;/li&gt;
&lt;li&gt;You might be redirected to GitHub to install the Azure Pipelines app. If so, select &lt;strong&gt;Approve and install&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Starter pipeline&lt;/strong&gt;. In the Review tab, replace the contents of azure-pipelines.yml with the following snippet -
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;

&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;self&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;vmImageName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ubuntu-latest'&lt;/span&gt;

&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build stage&lt;/span&gt;
    &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
      &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
      &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;vmImage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(vmImageName)&lt;/span&gt;
      &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;wget https://dist.ballerina.io/downloads/1.2.7/ballerina-1.2.7.zip&lt;/span&gt;
          &lt;span class="s"&gt;unzip -d /opt ballerina-1.2.7.zip&lt;/span&gt;
          &lt;span class="s"&gt;/opt/ballerina-1.2.7/bin/ballerina build todo_service.bal&lt;/span&gt;
        &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Download&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;install&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Ballerina&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;SDK'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Select &lt;strong&gt;Save and run&lt;/strong&gt;, after which you're prompted for a commit message as Azure Pipelines adds the &lt;em&gt;azure-pipelines.yml&lt;/em&gt; file to your repository. After editing the message, select &lt;strong&gt;Save and run&lt;/strong&gt; again to see the pipeline in action.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The above pipeline definition starts with a &lt;code&gt;trigge&lt;/code&gt; expression that tells the pipeline to run when something has been committed to the &lt;code&gt;master&lt;/code&gt; branch of your GitHub repository fork. There's only one stage defined here with one job and one step. The &lt;code&gt;script&lt;/code&gt; step executes the given bash commands to fetch and unzip the Ballerina distribution into the build agent which has the latest version of Ubuntu as specified in the variable &lt;code&gt;vmImageName&lt;/code&gt;. The last bash command builds the local TodoService project with the &lt;code&gt;ballerina&lt;/code&gt; command. That'll produce a &lt;strong&gt;Dockerfile&lt;/strong&gt; and corresponding Kubernetes artifacts. &lt;/p&gt;

&lt;p&gt;For the sake of simplicity, I'm directly downloading the Ballerina distribution into the build agent and running the build from there. But Azure pipeline provides an option to execute a job within a container image of your choice, enabling you to use a standard Ballerina Docker image for the build job.&lt;/p&gt;

&lt;p&gt;Once the build job completes, you should see an output similar to this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6uHzBr3w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.dunithwrites.com/image/content/first-pipeline-run.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6uHzBr3w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.dunithwrites.com/image/content/first-pipeline-run.png" alt="first-pipeline-run" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have a &lt;em&gt;Dockerfile&lt;/em&gt; for the project which needs to be built with Docker to produce a container image and subsequently push it to an Azure Container Registry(ACR) instance. Let's do that with the Docker task in a later section.&lt;/p&gt;

&lt;p&gt;Before that, we need to configure the Azure environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Setup the Azure environment
&lt;/h2&gt;

&lt;p&gt;In this section, you'll configure your Azure environment to create a resource group and an ACR instance using the Azure CLI tool.  If you have already performed these steps in the &lt;strong&gt;Part 1&lt;/strong&gt;, you can skip this section.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Install Azure CLI tool
&lt;/h3&gt;

&lt;p&gt;I’ll be executing almost all the commands related to Azure configurations with Azure CLI. If you haven’t installed it already in your local machine, see &lt;a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest"&gt;this &lt;/a&gt; guide to get it done. Note that you need to have Azure CLI version 2.0.53 or later to complete this tutorial. You can verify the version by running &lt;code&gt;az — version&lt;/code&gt; command.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Create a resource group
&lt;/h3&gt;

&lt;p&gt;Once installed locally, the Azure CLI tool can be accessed with &lt;code&gt;az&lt;/code&gt; command. Type the following command in a terminal to log in with your Azure account credentials.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you are successfully logged in to your Azure account, let’s create a resource group.&lt;/p&gt;

&lt;p&gt;An Azure resource group is a logical group in which Azure resources are deployed and managed. When you create a resource group, you are asked to specify a location. This location is where resource group metadata is stored.&lt;/p&gt;

&lt;p&gt;The following example creates a resource group named &lt;em&gt;myResourceGroup&lt;/em&gt; in the &lt;em&gt;eastus&lt;/em&gt; location.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az group create &lt;span class="nt"&gt;--name&lt;/span&gt; myResourceGroup &lt;span class="nt"&gt;--location&lt;/span&gt; eastus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll get an output like below when your resource group created successfully.&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"/subscriptions/&amp;lt;guid&amp;gt;/resourceGroups/myResourceGroup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"eastus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"managedBy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"myResourceGroup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"provisioningState"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Succeeded"&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;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;null&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;
  
  
  3.3 Create an Azure Container Registry(ACR) instance
&lt;/h3&gt;

&lt;p&gt;Azure Container Registry (ACR) is a private registry for container images. A private container registry lets you securely build and deploy your applications and custom code. Let's create an ACR instance so that once we build the TodoService Docker image, it can be pushed there.&lt;/p&gt;

&lt;p&gt;The following command creates a container registry named &lt;code&gt;dunithd&lt;/code&gt; and attaches it to the resource group that I have created previously. Note that the name &lt;code&gt;dunithd&lt;/code&gt; is just a name given by me. You may choose your own.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az acr create &lt;span class="nt"&gt;--resource-group&lt;/span&gt; myResourceGroup &lt;span class="nt"&gt;--name&lt;/span&gt; dunithd &lt;span class="nt"&gt;--sku&lt;/span&gt; Basic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The registry name must be unique within Azure and contain 5–50 alphanumeric characters. The &lt;em&gt;Basic&lt;/em&gt; SKU is a cost-optimized entry point for development purposes that provides a balance of storage and throughput.&lt;/p&gt;

&lt;p&gt;Once created, you can refer to your container registry as  &lt;code&gt;&amp;lt;your_name&amp;gt;.azurecr.io&lt;/code&gt;. In my case, it is &lt;code&gt;dunithd.azurecr.io&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I chose Azure Container Registry (ACR) for this tutorial since it is well integrated with the Azure ecosystem. But Azure pipelines support a wide variety of container registries including Docker Hub, Google Container Registry (GCR), or any Docker-based registry.&lt;/p&gt;

&lt;h2&gt;
  
  
  4: Create a Service Principal to authenticate with ACR
&lt;/h2&gt;

&lt;p&gt;In the previous tutorial, you manually built the container image and pushed it to ACR with Azure CLI. But when building a fully automated build pipeline, that has to be done unattended. Hence, the pipeline we are building here should be given necessary access permission to log in to ACR and push the built image. &lt;/p&gt;

&lt;p&gt;There are several ways to authenticate with an Azure container registry, each of which is applicable to one or more registry usage scenarios. One option is to log in with &lt;code&gt;az acr login&lt;/code&gt; where it provides an interactive shell for developers and testers to push and pull images. Also, another recommended option is to use an &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals"&gt;Azure AD service principal&lt;/a&gt; to perform unattended push/pull operations from CI/CD pipelines. For more information on all available options, please refer to &lt;a href="https://docs.microsoft.com/en-us/azure/container-registry/container-registry-authentication"&gt;this&lt;/a&gt; link.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Why we need a service principal?
&lt;/h3&gt;

&lt;p&gt;Azure AD &lt;em&gt;service principals&lt;/em&gt; provide access to Azure resources within your subscription. You can think of a service principal as a user identity for a service, where "service" is any application, service, or platform that needs to access the resources. You can configure a service principal with access rights scoped only to those resources you specify. Then, configure your application or service to use the service principal's credentials to access those resources. A good example would be creating a service principal for continuous integration systems like Azure DevOps or Jenkins to push/pull images from the container registry. That way, the pipelines are granted minimal and necessary privileges, but nothing more.&lt;/p&gt;

&lt;p&gt;In the context of this tutorial, you'll use a service principal to provide container image &lt;code&gt;docker push&lt;/code&gt; and &lt;code&gt;pull&lt;/code&gt; access to your container registry. &lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Create a service principal
&lt;/h3&gt;

&lt;p&gt;To create a service principal with access to your container registry, run the following script in the Azure CLI. The script is formatted for the Bash shell.&lt;/p&gt;

&lt;p&gt;Before running the script, update the &lt;code&gt;ACR_NAME&lt;/code&gt; variable with the name of your container registry. The &lt;code&gt;SERVICE_PRINCIPAL_NAME&lt;/code&gt; value must be unique within your Azure Active Directory tenant. If you receive an "&lt;code&gt;'http://acr-service-principal' already exists.&lt;/code&gt;" error, specify a different name for the service principal.&lt;/p&gt;

&lt;p&gt;Notice that I've provided &lt;code&gt;acrpush&lt;/code&gt; as the &lt;code&gt;--role&lt;/code&gt; as it grants me both push and pull access to the registry. For more information on RBAC, please refer to &lt;a href="https://github.com/Azure/acr/blob/master/docs/roles-and-permissions.md"&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After you run the script, take note of the service principal's &lt;strong&gt;ID&lt;/strong&gt; and &lt;strong&gt;password&lt;/strong&gt;. These credentials will be used by the Docker service connection in the next section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Modify for your environment.&lt;/span&gt;
&lt;span class="c"&gt;# ACR_NAME: The name of your Azure Container Registry&lt;/span&gt;
&lt;span class="c"&gt;# SERVICE_PRINCIPAL_NAME: Must be unique within your AD tenant&lt;/span&gt;
&lt;span class="nv"&gt;ACR_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dunithd
&lt;span class="nv"&gt;SERVICE_PRINCIPAL_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;acr-service-principal

&lt;span class="c"&gt;# Obtain the full registry ID for subsequent command args&lt;/span&gt;
&lt;span class="nv"&gt;ACR_REGISTRY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;az acr show &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$ACR_NAME&lt;/span&gt; &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; tsv&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Create the service principal with rights scoped to the registry.&lt;/span&gt;
&lt;span class="c"&gt;# Default permissions are for docker pull access. Modify the '--role'&lt;/span&gt;
&lt;span class="c"&gt;# argument value as desired:&lt;/span&gt;
&lt;span class="c"&gt;# acrpull:     pull only&lt;/span&gt;
&lt;span class="c"&gt;# acrpush:     push and pull&lt;/span&gt;
&lt;span class="c"&gt;# owner:       push, pull, and assign roles&lt;/span&gt;
&lt;span class="nv"&gt;SP_PASSWD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;az ad sp create-for-rbac &lt;span class="nt"&gt;--name&lt;/span&gt; http://&lt;span class="nv"&gt;$SERVICE_PRINCIPAL_NAME&lt;/span&gt; &lt;span class="nt"&gt;--scopes&lt;/span&gt; &lt;span class="nv"&gt;$ACR_REGISTRY_ID&lt;/span&gt; &lt;span class="nt"&gt;--role&lt;/span&gt; acrpush &lt;span class="nt"&gt;--query&lt;/span&gt; password &lt;span class="nt"&gt;--output&lt;/span&gt; tsv&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;SP_APP_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;az ad sp show &lt;span class="nt"&gt;--id&lt;/span&gt; http://&lt;span class="nv"&gt;$SERVICE_PRINCIPAL_NAME&lt;/span&gt; &lt;span class="nt"&gt;--query&lt;/span&gt; appId &lt;span class="nt"&gt;--output&lt;/span&gt; tsv&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Output the service principal's credentials; use these in your services and&lt;/span&gt;
&lt;span class="c"&gt;# applications to authenticate to the container registry.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Service principal ID: &lt;/span&gt;&lt;span class="nv"&gt;$SP_APP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Service principal password: &lt;/span&gt;&lt;span class="nv"&gt;$SP_PASSWD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5: Create a Docker Registry service connection to ACR
&lt;/h2&gt;

&lt;p&gt;In Azure DevOps, a Service Connection defines a connection to an external system. For example, you may need to connect to your Microsoft Azure subscription, to a different build server or file server, to an online continuous integration environment, or to services you install on remote computers.&lt;/p&gt;

&lt;p&gt;A service connection eliminates the need for hard coding external system credentials in your pipeline YAML itself. Instead, Azure DevOps allows you to configure connection details in the portal UI and refer to that from the pipeline at a later stage by using its name.&lt;/p&gt;

&lt;p&gt;Let's create a service connection to the ACR we created earlier. Then use service principal credentials as the username and password.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In Azure DevOps, open the &lt;strong&gt;Service connections&lt;/strong&gt; page from the &lt;a href="https://docs.microsoft.com/en-us/azure/devops/project/navigation/go-to-service-page?view=azure-devops#open-project-settings"&gt;project settings page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;+ New service connection&lt;/strong&gt; and select the type &lt;strong&gt;Docker Registry&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Fill in the parameters for the service connection. Use the service principal ID (&lt;code&gt;$SP_APP_ID&lt;/code&gt;) as the &lt;strong&gt;Docker ID&lt;/strong&gt; and service principal password (&lt;code&gt;SP_PASSWD&lt;/code&gt;) as the &lt;strong&gt;Docker Password&lt;/strong&gt;. I gave &lt;em&gt;acr-service-connection&lt;/em&gt; as the connection name.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;OK&lt;/strong&gt; to create the connection. Use the following as a reference.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2IfAj9y---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.dunithwrites.com/image/content/create-service-connection.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2IfAj9y---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.dunithwrites.com/image/content/create-service-connection.png" width="800" height="1324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll be using this service connection in the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  6: Build and push the project with Docker task
&lt;/h2&gt;

&lt;p&gt;This is the final section in the tutorial where we build the checked out TodoService inside the build agent to produce a container image and push it to ACR with the credentials configured in the previous sections. The build stage uses the &lt;em&gt;Docker task&lt;/em&gt; to build and push the image to the container registry.&lt;/p&gt;

&lt;p&gt;Replace the content of your pipeline YAML with the following.&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;trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;

&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;self&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;dockerRegistryServiceConnection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;acr-service-connection'&lt;/span&gt;
  &lt;span class="na"&gt;imageRepository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;todo-service'&lt;/span&gt;
  &lt;span class="na"&gt;dockerfilePath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/Dockerfile'&lt;/span&gt;
  &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;latest'&lt;/span&gt;
  &lt;span class="na"&gt;vmImageName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ubuntu-latest'&lt;/span&gt;

&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build stage&lt;/span&gt;
    &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
      &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
      &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;vmImage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(vmImageName)&lt;/span&gt;
      &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wget https://dist.ballerina.io/downloads/1.2.7/ballerina-1.2.7.zip&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unzip -d /opt ballerina-1.2.7.zip&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/opt/ballerina-1.2.7/bin/ballerina build todo_service.bal&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mv docker/Dockerfile .&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Docker@2&lt;/span&gt;
        &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and push the image to Azure container registry&lt;/span&gt;
        &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;buildAndPush&lt;/span&gt;
          &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(imageRepository)&lt;/span&gt;
          &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(dockerfilePath)&lt;/span&gt;
          &lt;span class="na"&gt;containerRegistry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(dockerRegistryServiceConnection)&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;$(tag)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select &lt;strong&gt;Save and run&lt;/strong&gt; to commit the new changes into your repository. That will trigger a new build for the pipeline. As your pipeline runs, select the build job to watch your pipeline in action.&lt;/p&gt;

&lt;p&gt;If everything goes well, you should see something similar to this in the pipeline build results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oD1PqSxw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.dunithwrites.com/image/content/final-build.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oD1PqSxw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.dunithwrites.com/image/content/final-build.png" alt="final-build" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Verify the pipeline execution
&lt;/h2&gt;

&lt;p&gt;Congratulations! You've just completed building a build pipeline for the TodoService project. Now let's verify whether it works as we expected.&lt;/p&gt;

&lt;p&gt;To verify, clone the forked GitHub repo into your local workstation and make any change to the todo_service.bal file. Then push the changes back to your fork to trigger a build in the Azure pipeline. That build will checkout the latest source code from the master branch of your fork, build it, and push the built container to your ACR instance.&lt;/p&gt;

&lt;p&gt;Login to your ACR instance with the following command to see the newly built container. Make sure to replace the name accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az acr login &lt;span class="nt"&gt;--name&lt;/span&gt; dunithd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To list the images, type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az acr repository list &lt;span class="nt"&gt;--name&lt;/span&gt; dunithd &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;span class="nt"&gt;------------&lt;/span&gt; 
todo-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you got the above result, you are done!&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Clean up resources
&lt;/h2&gt;

&lt;p&gt;Once you are done with the tutorial, you may want to delete the resources in your Azure account. Use the following command for that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az group delete --name myResourceGroup --yes --no-wait
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we built a continuous integration pipeline with Azure DevOps to automate the build process for a Ballerina microservice. &lt;/p&gt;

&lt;p&gt;Surely, this is more than enough to carry out production-grade build tasks. But There can be a couple of tweaks that can be done to the pipeline such as adding tasks for unit testing and vulnerability scanning etc. &lt;/p&gt;

&lt;p&gt;We will not stop it from here. There's a Continuous Deployment part that has to be considered as well. That is, the build pipeline should be altered to deploy the container image to an Azure Kubernetes Service (AKS) environment automatically, run tests there, and promote till the production environment. &lt;/p&gt;

&lt;p&gt;Let's explore that in the next post...&lt;/p&gt;

</description>
      <category>azuredevops</category>
      <category>ballerina</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Production-grade Microservices on Azure AKS with Ballerina — Part 1: The Basics</title>
      <dc:creator>Dunith Danushka</dc:creator>
      <pubDate>Sat, 16 May 2020 03:07:01 +0000</pubDate>
      <link>https://dev.to/dunithd/production-grade-microservices-on-azure-aks-with-ballerina-part-1-the-basics-38pa</link>
      <guid>https://dev.to/dunithd/production-grade-microservices-on-azure-aks-with-ballerina-part-1-the-basics-38pa</guid>
      <description>&lt;p&gt;In this multipart tutorial series, you will learn how to write a basic Microservice with Ballerina programming language, deploy it to Azure Kubernetes Service (AKS) and make it production-ready with features available in the Azure platform.&lt;/p&gt;

&lt;p&gt;The first installment of this series focuses on getting the simplest things done with the bare minimum effort. At the end of this post, you'll learn the following:&lt;/p&gt;

&lt;h2&gt;
  
  
  Objectives
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Write a simple Ballerina microservice that returns a JSON array of Todo list items.&lt;/li&gt;
&lt;li&gt;Generate a Docker image with Docker annotations built into Ballerina.&lt;/li&gt;
&lt;li&gt;Push the generated image to Azure Container Registry (ACR).&lt;/li&gt;
&lt;li&gt;Create a Kubernetes cluster in Azure Kubernetes Service (AKS) and deploy the above container image into that.&lt;/li&gt;
&lt;li&gt;Test the microservice with Postman.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As we dive deep into the series, I'll show you how to make this microservice production-ready to have features like authentication, auto-scaling, load balancing, monitoring, and integrating with a CI/CD pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you begin
&lt;/h2&gt;

&lt;p&gt;This tutorial assumes a basic understanding of Ballerina programming language. But we are not going to explore all the bells and whistles that come with Ballerina. I'll limit the scope to the implementation of a basic REST service. For a primer on Ballerina basics, see &lt;a href="https://ballerina.io/learn/"&gt;https://ballerina.io/learn/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To complete this tutorial, you need to install Ballerina on your local machine. Use &lt;a href="https://ballerina.io/downloads/"&gt;https://ballerina.io/downloads/&lt;/a&gt; to get it done.&lt;/p&gt;

&lt;p&gt;In addition to that, you need a fair amount of knowledge on Microservices, Docker, and Kubernetes along with a local Docker development environment running Linux containers. Docker provides packages that configure Docker on a &lt;a href="https://docs.docker.com/docker-for-mac/"&gt;Mac&lt;/a&gt;, &lt;a href="https://docs.docker.com/docker-for-windows/"&gt;Windows&lt;/a&gt;, or &lt;a href="https://docs.docker.com/engine/installation/#supported-platforms"&gt;Linux&lt;/a&gt; system.&lt;/p&gt;

&lt;p&gt;Finally, you'll need an account on Azure. It gives you $200 credit to explore Azure for 30 days. If you don't have an account already, I suggest you register for a &lt;a href="https://azure.microsoft.com/en-us/free/"&gt;one&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Ballerina
&lt;/h2&gt;

&lt;p&gt;Ever since the Microservices concept conceived, Java-based frameworks like Spring Boot had been the developer's first choice for writing Microservices. But here I'm going to take a little detour from that and introduce you to Ballerina, an open-source programming language purpose-built for optimizing microservices development.&lt;/p&gt;

&lt;p&gt;The major reason I fell in love with Ballerina is it comes with all batteries included for Microservices development ' you don't need to borrow anything from the outside.&lt;/p&gt;

&lt;p&gt;As a developer, my justification for Ballerina is you'll get more things done with less effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: Run Todo Service locally
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 Code walkthrough
&lt;/h3&gt;

&lt;p&gt;In this section, let's create a Ballerina service called &lt;strong&gt;TodoService&lt;/strong&gt; and run it locally.&lt;/p&gt;

&lt;p&gt;Open up a text editor of your choice (&lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt; in my case) and create a file called &lt;strong&gt;todo_service.bal&lt;/strong&gt; file inside a directory of your choice. Then add the following code to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ballerina&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ballerina&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="nl"&gt;http:&lt;/span&gt;&lt;span class="nc"&gt;Listener&lt;/span&gt; &lt;span class="n"&gt;todosListener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9090&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;@http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;ServiceConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;basePath:&lt;/span&gt; &lt;span class="s"&gt;"/todos"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="nc"&gt;TodoService&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;todosListener&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;ResourceConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;methods:&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
        &lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; 
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;http:&lt;/span&gt;&lt;span class="nc"&gt;Caller&lt;/span&gt; &lt;span class="n"&gt;caller&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;http:&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//return some mock Todo objects as a JSON array&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="n"&gt;todos&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="s"&gt;"id"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Create the Todo Service"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"OPEN"&lt;/span&gt;
            &lt;span class="o"&gt;},&lt;/span&gt;
            &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="s"&gt;"id"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Create an AKS Cluster"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"OPEN"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;];&lt;/span&gt;
        &lt;span class="c1"&gt;//respond to the caller&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;responseResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;caller&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;respond&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;responseResult&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;log:&lt;/span&gt;&lt;span class="n"&gt;printError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error responding back to client."&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;responseResult&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code, &lt;strong&gt;TodoService&lt;/strong&gt; represents an HTTP service that is bound to &lt;code&gt;/todos&lt;/code&gt; context. It listens for incoming HTTP connections with &lt;code&gt;TodosEP&lt;/code&gt; endpoint which is bound to port 9090. It has got only one resource method &lt;code&gt;getAll()&lt;/code&gt; that returns an in-memory array of Todo objects formatted as a JSON array.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 Run TodoService
&lt;/h3&gt;

&lt;p&gt;Now we have the basic Ballerina Microservice code in place. Let's try to run it locally just to make sure that our code works.&lt;/p&gt;

&lt;p&gt;Open up a terminal and navigate to the place where you save the &lt;strong&gt;todo_service.bal&lt;/strong&gt; file. Then execute below command in the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ballerina run todo_service.bal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have installed Ballerina on your local machine successfully, the ballerina command will be added to your *&lt;em&gt;PATH *&lt;/em&gt; variable automatically. When you run a Ballerina service, it will spring up a lightweight web server under the covers and bind it to a port specified with the listener configuration. In our case, the port is 9090.&lt;/p&gt;

&lt;p&gt;You will see an output similar to below when everything is properly in place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B_dDH2VW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2282/1%2A8wjHv3v8SmlK3ovyR1LHzA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B_dDH2VW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2282/1%2A8wjHv3v8SmlK3ovyR1LHzA.png" alt="" width="800" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A GET request to &lt;a href="http://localhost:9090/todos"&gt;http://localhost:9090/todos&lt;/a&gt; will give you the below response in Postman.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BwWLnyVz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/5090/1%2A7fgECzW5m13erTgG5yaPkg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BwWLnyVz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/5090/1%2A7fgECzW5m13erTgG5yaPkg.png" alt="Things are working fine locally" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: Set up the Azure environment
&lt;/h2&gt;

&lt;p&gt;In this section, you'll configure your Azure environment using Azure CLI to deploy above the Todo service.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Install Azure CLI and kubectl tool
&lt;/h3&gt;

&lt;p&gt;I'll be executing almost all the commands related to Azure configurations with Azure CLI. If you haven't installed it already in your local machine, see &lt;a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest"&gt;this &lt;/a&gt;guide to get it done. Note that you need to have Azure CLI version 2.0.53 or later to complete this tutorial. You can verify the version by running &lt;code&gt;az --version&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;In order to point to the Kubernetes cluster in AKS and manage it from the local machine, you are going to need the kubectl command-line tool. If you haven't installed it already, see &lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/"&gt;this &lt;/a&gt;guide to get it done.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2. Create a resource group
&lt;/h3&gt;

&lt;p&gt;Once installed locally, the Azure CLI tool can be accessed with az command. Type the following command in a terminal to log in with your Azure account credentials.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you are successfully logged in to your Azure account, let's create a resource group.&lt;/p&gt;

&lt;p&gt;An Azure resource group is a logical group in which Azure resources are deployed and managed. When you create a resource group, you are asked to specify a location. This location is where resource group metadata is stored.&lt;/p&gt;

&lt;p&gt;The following example creates a resource group named &lt;em&gt;myResourceGroup&lt;/em&gt; in the &lt;em&gt;eastus&lt;/em&gt; location.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az group create --name myResourceGroup --location eastus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get an output like below when your resource group created successfully.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
   "id":"/subscriptions/&amp;lt;guid&amp;gt;/resourceGroups/myResourceGroup",
   "location":"eastus",
   "managedBy":null,
   "name":"myResourceGroup",
   "properties":{
      "provisioningState":"Succeeded"
   },
   "tags":null
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  2.3 Create an Azure Container Registry
&lt;/h3&gt;

&lt;p&gt;Azure Container Registry (ACR) is a private registry for container images. A private container registry lets you securely build and deploy your applications and custom code.&lt;/p&gt;

&lt;p&gt;Let's create a container registry now so that it'll be useful in the later parts of this tutorial. The following example creates a container registry named &lt;code&gt;dunithd&lt;/code&gt; and attaches it to the resource group that I have created previously.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az acr create --resource-group myResourceGroup --name dunithd --sku Basic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The registry name must be unique within Azure and contain 50 alphanumeric characters. The &lt;em&gt;Basic&lt;/em&gt; SKU is a cost-optimized entry point for development purposes that provides a balance of storage and throughput.&lt;/p&gt;

&lt;p&gt;Once created, you can refer to your container registry as &lt;code&gt;dunithd.azurecr.io&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: Generate Kubernetes artifacts with annotations
&lt;/h2&gt;

&lt;p&gt;Now we have a basic Ballerina service that works and we have configured our Azure environment accordingly. Then let's see how to generate Kubernetes artifacts for the Todo service.&lt;/p&gt;

&lt;p&gt;Ballerina offers a convenient way of generating corresponding Kubernetes artifacts automatically with annotations built into the language. The Kubernetes builder extension offers native support for running Ballerina programs on Kubernetes with the use of annotations that you can include as part of your service code. Also, it will take care of the creation of the Docker images, so you don't need to explicitly create Docker images prior to deployment on Kubernetes.&lt;/p&gt;

&lt;p&gt;For more information, see &lt;a href="https://ballerina.io/learn/how-to-deploy-ballerina-programs-in-cloud/"&gt;How to Deploy Ballerina Programs in the Cloud&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To add annotations, open the &lt;strong&gt;todo_service.bal&lt;/strong&gt; file and overwrite its content with the below code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ballerina&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ballerina&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ballerina&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;kubernetes&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@kubernetes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;Service&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"todos"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;serviceType:&lt;/span&gt; &lt;span class="s"&gt;"LoadBalancer"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="nl"&gt;http:&lt;/span&gt;&lt;span class="nc"&gt;Listener&lt;/span&gt; &lt;span class="n"&gt;todosListener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9090&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;@kubernetes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;Deployment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"todos"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"dunithd.azurecr.io/todo-service:v1"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nd"&gt;@http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;ServiceConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;basePath:&lt;/span&gt; &lt;span class="s"&gt;"/todos"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="nc"&gt;TodoService&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;todosListener&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;ResourceConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;methods:&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
        &lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; 
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAllTodos&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;http:&lt;/span&gt;&lt;span class="nc"&gt;Caller&lt;/span&gt; &lt;span class="n"&gt;caller&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;http:&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//return some mock Todo objects as a JSON array&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="n"&gt;todos&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="s"&gt;"id"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Create the Todo Service"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"OPEN"&lt;/span&gt;
            &lt;span class="o"&gt;},&lt;/span&gt;
            &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="s"&gt;"id"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Create an AKS Cluster"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"OPEN"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;];&lt;/span&gt;
        &lt;span class="c1"&gt;//respond to the caller&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;responseResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;caller&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;respond&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;responseResult&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;log:&lt;/span&gt;&lt;span class="n"&gt;printError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error responding back to client."&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;responseResult&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;@kubernetes:Deployment&lt;/code&gt; is used to specify the Docker image name, which will be created as part of building this service. Notice that I have put the private Docker registry name created in **Part 2 **in the image name as below.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;image : dunithd.azurecr.io/todo-service:v1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;@kubernetes:Service {}&lt;/code&gt; annotation will create a Kubernetes service that will expose the Ballerina service running on a Pod. For simplicity, I have put &lt;code&gt;LoadBalancer&lt;/code&gt; as the service type. Then Azure will automatically provision a load balancer resource for me with an external IP address. I will show you how to configure NGINX as an Ingress controller in a later tutorial.&lt;/p&gt;

&lt;p&gt;Now let's use the below command to build the &lt;strong&gt;&lt;em&gt;Todo_service.bal&lt;/em&gt;&lt;/strong&gt; file. This will also create the corresponding Docker image and the Kubernetes artifacts using the Kubernetes annotations that you have configured above.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Jj9cT_Lk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2120/1%2AK6z5SRGVLezhBkD8BaUaMA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Jj9cT_Lk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2120/1%2AK6z5SRGVLezhBkD8BaUaMA.png" alt="" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use the &lt;code&gt;docker images&lt;/code&gt; command to verify that the Docker image specified in the &lt;code&gt;@kubernetes:Deployment&lt;/code&gt; was created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5jO4U91l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2276/1%2AUhgFkwbqnEH51ngsW_8lGg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5jO4U91l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2276/1%2AUhgFkwbqnEH51ngsW_8lGg.png" alt="" width="800" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Kubernetes artifacts related to your service will be generated in addition to the .jar file. Notice the &lt;strong&gt;&lt;em&gt;docker&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;kubernetes&lt;/em&gt;&lt;/strong&gt; directories created in your working directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yA4v0uHv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A1ebcorzXRYSKHfQG906Y5g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yA4v0uHv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A1ebcorzXRYSKHfQG906Y5g.png" alt="Docker and Kubernetes artifacts have been generated for you" width="607" height="745"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 4: Push Docker image to Azure container registry
&lt;/h2&gt;

&lt;p&gt;Now we have Todo service as a Docker image in our local repository. This needs to be pushed to the Azure container registry (ACR) so that our Kubernetes cluster can pull it from there.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Login to container registry
&lt;/h3&gt;

&lt;p&gt;In a previous step, we created a container registry in Azure. In order to use it, you must first login. In the Azure CLI, type the below command with the name you have given as the registry name before (dunithd in my case).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az acr login --name dunithd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command returns a &lt;code&gt;Login Succeeded&lt;/code&gt; message once completed.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Push the image to ACR
&lt;/h3&gt;

&lt;p&gt;Use the docker push command to push to the local image to your ACR as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker push dunithd.azurecr.io/todo-service:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command may take a few minutes to complete.&lt;/p&gt;

&lt;p&gt;The following command returns a list of images that have been pushed to your ACR instance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;gt; az acr repository list --name dunithd --output table

    ------------ 
    todo-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following command returns the tags for the todo-service image we just pushed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;gt; az acr repository show-tags --name dunithd --repository todo-service --output table 

    -------- 
    v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Part 5: Create Kubernetes cluster and deploy artifacts
&lt;/h2&gt;

&lt;p&gt;So far, we have tested the todo-service locally, generated a Docker image, and Kubernetes artifacts out of it and pushed the local image to the Azure container registry. What's left is to create a Kubernetes cluster in AKS and deploy the generated artifacts into that.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 Create a cluster of two nodes
&lt;/h3&gt;

&lt;p&gt;The following command creates a Kubernetes cluster of two nodes named &lt;em&gt;myAKSCluster&lt;/em&gt; in the resource group named &lt;em&gt;myResourceGroup&lt;/em&gt; which we have created in a previous step.&lt;/p&gt;

&lt;p&gt;Here, the AKS cluster needs to access Azure Container Registry (ACR) instance to pull the &lt;code&gt;todo-service:v1&lt;/code&gt; image you pushed earlier. For that, Azure automatically creates an Azure Active Directory service principal and grants the &lt;a href="https://docs.microsoft.com/en-us/azure/aks/cluster-container-registry-integration"&gt;right to pull images&lt;/a&gt; from the ACR instance. If needed, there's an option to use a &lt;a href="https://docs.microsoft.com/en-us/azure/aks/use-managed-identity"&gt;managed identity&lt;/a&gt; instead of a service principal for easier management.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DUfeINUq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2298/1%2Awy10R6Tysntm-e9rNbwBhw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DUfeINUq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2298/1%2Awy10R6Tysntm-e9rNbwBhw.png" alt="" width="800" height="141"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a few minutes, the deployment completes and returns JSON-formatted information about the AKS deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 Configure kubectl to connect to your cluster
&lt;/h3&gt;

&lt;p&gt;The following command configures the kubectl tool to connect to your AKS cluster. It fetches credentials for the AKS cluster named &lt;em&gt;myAKSCluster&lt;/em&gt; in the &lt;em&gt;myResourceGroup&lt;/em&gt; and creates an entry in your *~/.kube *directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az aks get-credentials --resource-group myResourceGroup --name myAKSCluster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the connectivity to your cluster by running kubectl get nodes command as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LWNdCHJR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2278/1%2AYqaTnGr1I3kphaw9CHwMPg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LWNdCHJR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2278/1%2AYqaTnGr1I3kphaw9CHwMPg.png" alt="" width="800" height="105"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5.3 Deploy generated Kubernetes artifacts
&lt;/h2&gt;

&lt;p&gt;Now we are at the final moment of our tutorial. The only thing left is to deploy the Kubernetes artifacts generated in a previous step.&lt;/p&gt;

&lt;p&gt;From your working directory where &lt;strong&gt;&lt;em&gt;todo_service.bal&lt;/em&gt;&lt;/strong&gt; resides, execute the below command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f ./kubernetes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything goes fine, it should produce an output like below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n6UmYvij--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2280/1%2Az3SdCq3OawUFhoMXkRfhzg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n6UmYvij--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2280/1%2Az3SdCq3OawUFhoMXkRfhzg.png" alt="" width="800" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Behind the scenes, kubectl has created a deployment and a service for our Todo service. You can verify the service as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xkMRPht5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2276/1%2A5jIK1263OVNk54eaZXaSpA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xkMRPht5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2276/1%2A5jIK1263OVNk54eaZXaSpA.png" alt="" width="800" height="103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice the external IP address assigned our &lt;strong&gt;todos&lt;/strong&gt; service. That is because I have set the service type to &lt;code&gt;LoadBalancer&lt;/code&gt; when configuring the service annotation.&lt;/p&gt;

&lt;p&gt;Now our service is accessible from outside the cluster. Here's the output I got when I called the todo service with the external IP using Postman.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UZxdMSYu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/5120/1%2A8nbEeniyoSpqM-NPr4p5sA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UZxdMSYu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/5120/1%2A8nbEeniyoSpqM-NPr4p5sA.png" alt="Finally we went live on AKS!!!" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See, how easy it is to spin up a Kubernetes cluster and deploy your Microservice into that!&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleaning up
&lt;/h2&gt;

&lt;p&gt;As this tutorial is the last part of the series, you may want to delete the AKS cluster. As the Kubernetes nodes run on Azure virtual machines (VMs), they continue to incur charges even if you don't use the cluster. The following command removes the resource group, container service, and all related resources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az group delete --name myResourceGroup --yes --no-wait
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;Well, I guess the tutorial was a bit long. But it covers almost every step that is essential to write a Microservice with Ballerina and getting that deployed into AKS. For an absolute beginner on Ballerina and AKS, this tutorial would be an ideal place to start.&lt;/p&gt;

&lt;p&gt;We merely scratched the surface here. When I said ÔÇ£production-gradeÔÇØ in the title, there's a lot more to come in this series.&lt;/p&gt;

&lt;p&gt;Hence, expect the following posts as follow up items to this article in the future.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using Azure DevOps to build a CI/CD process for our Todo service and perform rolling updates&lt;/li&gt;
&lt;li&gt;Configure NGINX as an ingress controller and use TLS with &lt;a href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Secure Todo service with Azure AD and JWT authentication&lt;/li&gt;
&lt;li&gt;Access databases from Todo service&lt;/li&gt;
&lt;li&gt;Configure Service-to-Service communication with Istio (Todo service will be calling another service via Istio)&lt;/li&gt;
&lt;li&gt;Configure Azure Monitoring and logging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If there's any additional topic you want me to discuss in this series, put them as a comment or &lt;a href="https://twitter.com/dunithd"&gt;tweet &lt;/a&gt;me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/aks/"&gt;Azure Kubernetes Service official documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://ballerina.io/"&gt;Ballerina website&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ballerina</category>
      <category>azure</category>
      <category>aks</category>
      <category>kubernetes</category>
    </item>
  </channel>
</rss>
