<?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: Olufisayo Bamidele</title>
    <description>The latest articles on DEV Community by Olufisayo Bamidele (@ngfizzy).</description>
    <link>https://dev.to/ngfizzy</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%2F83066%2Fe44cbed0-0a30-474c-9f97-85083ab38811.jpg</url>
      <title>DEV Community: Olufisayo Bamidele</title>
      <link>https://dev.to/ngfizzy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ngfizzy"/>
    <language>en</language>
    <item>
      <title>Notify Yourself After Completing a Long-Running Bash Process</title>
      <dc:creator>Olufisayo Bamidele</dc:creator>
      <pubDate>Sat, 09 Dec 2023 01:16:03 +0000</pubDate>
      <link>https://dev.to/ngfizzy/notify-yourself-after-completing-a-long-running-bash-process-5f42</link>
      <guid>https://dev.to/ngfizzy/notify-yourself-after-completing-a-long-running-bash-process-5f42</guid>
      <description>&lt;p&gt;Recently, I worked on projects that take a long time to build. I often stared at the screen, waiting for these to complete, which often resulted in me sleeping off at my desk. Later, I improvised sleeping while listening to some podcasts, but I usually lost valuable time since I couldn't tell when the long-running tasks were done. If only I could get notified if a bash process is done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Say hello to the "say" command
&lt;/h2&gt;

&lt;p&gt;The say command takes a string of text and reads it out loud. The example below says "brew upgrade done" when you're done upgrading your brew packages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew upgrade&lt;span class="p"&gt;;&lt;/span&gt; say, &lt;span class="s2"&gt;"brew upgrade done."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perhaps you often sleep off while listening to one of those monotonic dialogues between Lex Fridman and Elon; you could repeat the alert until you wake up with a little sprinkle of bash.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew upgrade&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..1000&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; say &lt;span class="s2"&gt;"brew upgrade done"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That would say "brew upgrade done" 1000 times unless you manually stop it(with ctrl+c).&lt;/p&gt;

&lt;p&gt;If you don't wake up after 1000 iterations, you should close your laptop for the weekend sleep. Just sleep 😆. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ The say command comes preinstalled with Macos (I think), but its equivalent should be available on your Linux distro. For example, on my Ubuntu desktop installation, I have &lt;code&gt;spd-say.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alright, that's it. Have a great weekend, you all.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>programming</category>
      <category>tip</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Future of Software Engineering: Is Maintainability Still Important?</title>
      <dc:creator>Olufisayo Bamidele</dc:creator>
      <pubDate>Tue, 28 Nov 2023 03:11:22 +0000</pubDate>
      <link>https://dev.to/ngfizzy/future-of-software-engineering-is-maintainability-still-important-2b0</link>
      <guid>https://dev.to/ngfizzy/future-of-software-engineering-is-maintainability-still-important-2b0</guid>
      <description>&lt;p&gt;I recently stumbled upon a YouTube talk by an AI salesman that got me thinking about the future of Software Engineering as a Profession. The speaker said something along the line of "maintainable code for humans". I mean, I have always known that but this time, it got me thinking – all those acronyms we toss around like confetti (DRY, WET, YAGNI, SOLID) are essentially for us. When it comes down to CPUs, all they see is bits and bytes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Today's programming language, tomorrow's joke.
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;At the end, today's bleeding edge is tomorrow's legacy;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, what if the programming language of tomorrow are not lines of code but Natural Language&lt;/p&gt;

&lt;p&gt;Note that not long ago, any serious software had to be written in Assembly. Fast forward to today,  compilers had become better at generating Assembly code than most programmers. I mean, I can't remember the last time I had to inspect the byte code generated by the V8 engine due to a pesky bug.&lt;/p&gt;

&lt;p&gt;So, I ask again, if AI generated code gets better and it starts working 99.9% of the time, of what use is maintainability?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>softwareengineering</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Kubernetes Services; Expose your app to the Internet</title>
      <dc:creator>Olufisayo Bamidele</dc:creator>
      <pubDate>Thu, 23 Nov 2023 22:29:25 +0000</pubDate>
      <link>https://dev.to/ngfizzy/kubernetes-services-expose-your-app-to-the-internet-o13</link>
      <guid>https://dev.to/ngfizzy/kubernetes-services-expose-your-app-to-the-internet-o13</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/ngfizzy/docker-and-kubernetes-from-localhost-to-production-kubernetes-container-orchestrators-the-what-why-and-how-42gg"&gt;last articles&lt;/a&gt; in this series, we talked about the main pillars of Kubernetes: Cluster, control pane, nodes, workloads, pods,  containers, and services. We dockerized a simple nodejs echo server and ran it on our machine inside a single-node Kubernetes cluster. In this article, we run that same workload in a Kubernetes cluster in the Cloud, but before that, let's talk about a topic I graced on quickly in the last article: Services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetes Services.
&lt;/h2&gt;

&lt;p&gt;While writing the &lt;a href="https://dev.to/ngfizzy/docker-and-kubernetes-from-localhost-to-production-kubernetes-container-orchestrators-the-what-why-and-how-42gg"&gt;last articles&lt;/a&gt;, I realized that the word "service" is overloaded in tech; It could mean a web backend application, a daemon running in an OS, a cloud offering, or even a module in a codebase. A "Service" is also a thing in Kubernetes that differs from all the meanings above. For clarity, we would be very specific when discussing "Services" in Kubernetes. We would call them k8s-Services&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a K8S-Service?
&lt;/h3&gt;

&lt;p&gt;A k8s-service is an abstraction that controls communication with a target group of Pods within a cluster and the exposure of those pods outside a Cluster. To be more specific, a k8s-service abstracts the networking of related pods.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Do We Need K8s-Services
&lt;/h3&gt;

&lt;p&gt;In the last part of this series, we mentioned that a Pod gets assigned a unique IP address that we can use to communicate with the Pod inside the Cluster. Why can't we talk to pods directly using their IP addresses? Yes, we can, but we shouldn't. Pods, by nature, are short-lived. They come in and out of existence depending on many factors. Factors like&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Pod's health:&lt;/strong&gt; If it maxes out its allocated memory and CPU, it will stop receiving traffic. Once Kubernetes detects this, it kills that Pod and replaces it with a new one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Initial Configuration Of The Deployment That Started The Pod:&lt;/strong&gt; Pods are usually started as deployment members. A deployment is a Kubernetes configuration that describes the desired state of a pod, including when we would like to have a replica. In a deployment, it is possible to specify the number of pods you want when a specific event occurs. For example, one can add a configuration like this: "If the total % of CPU usage goes up to 60%, create a new replica of this Pod to handle extra requests". In the configuration described above, replicas of a pod are expected to come in and out of existence depending on how intensively each Pod is being used.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each new Pod replica gets a unique IP address, so using Pod's IP addresses is unreliable as a new replica receives a new IP Address. See the image below.&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%2Fvud03ba8t17n53tjcis2.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%2Fvud03ba8t17n53tjcis2.png" alt="Problem with using PodIP for comminication"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;K8s-services solve the problem described above. To ensure that we can communicate with a group of Pods without manually keeping track of the Pods' IP addresses, we must create a k8s-service that sits between a request and the destination Pods. Like Pods, k8s-services get assigned unique IP addresses on creation, but unlike Pods, k8s-services  are not ephemera because nothing capable of crashing is running inside them. They are there until deleted. They are just a data structure translated to a network routing configuration on the operating system.&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%2Fznefpmdkeg13sjgapxyh.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%2Fznefpmdkeg13sjgapxyh.png" alt="illustration of k8s-service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the setup in the image above, clients of Pod replicas do not need to remember the IP address of each Pod. That responsibility is given to k8s-service. Clients only need to remember the k8s-service's IP address.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Geek Bit ℹ️: The image above shows that k8s-services keep track of destination pod IP addresses in a table. While most of the diagram oversimplifies a Kubernetes cluster, this part is literal. A component of Kubernetes called Kubeproxy is responsible for translating the specification of your k8s-service to a network configuration. The configuration is usually implemented on your OS as a NAT iptable or as ipvs. Most cloud services providers like AWS and Google run kubeproxy in NAT IPTable mode. If you're running Kubernetes on Linux, you can view the translation of your k8s-service configuration as NAT table by running &lt;code&gt;sudo iptables -t nat -L KUBE-SERVICES -n  | column -t&lt;/code&gt;. As a Kubernetes user. Of course, you are usually not concerned about this implementation detail unless you're an administrator. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Types Of k8s-Service
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ClusterIP K8S-Service
&lt;/h3&gt;

&lt;p&gt;Whenever you create a k8s-service without specifying the type, the  ClusterIP k8s-service is the type that Kubernetes would create. When you create a ClusterIP k8s-service, Kubernetes assigns a private static IP address to the k8s-service, which routes the request to matching pods. &lt;em&gt;ClusterIPs only allow communication within the Cluster&lt;/em&gt;; Machines outside the Cluster cannot communicate with Pods through ClusterIP k8s service by default unless you allow this through something called an &lt;code&gt;ingress.&lt;/code&gt; We will cover ingresses in another part of this series. See the illustration below.&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%2Fpq3p4n35jj34ozlq8817.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%2Fpq3p4n35jj34ozlq8817.png" alt="ClusterIP Illustration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Defining a ClusterIP in Kubernetes
&lt;/h4&gt;

&lt;p&gt;This example edits the k8s-service example in the last article.&lt;br&gt;
&lt;em&gt;file_name: node-echo-service.yaml&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-service&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo&lt;/span&gt; &lt;span class="c1"&gt;# the label of the group of pods that this service maps to&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;80&lt;/span&gt; 
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5001&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;selector: app: node-echo&lt;/code&gt; instructs Kubernetes to put this &lt;strong&gt;ClusterIP k8s-service&lt;/strong&gt; in front of any pod with the label &lt;code&gt;app=node-echo&lt;/code&gt;. &lt;code&gt;port:80&lt;/code&gt; is the port the service binds to. &lt;code&gt;targetPort:50001&lt;/code&gt; is the port that our container is listening on; that is where the k8s-service will forward traffic to&lt;/p&gt;

&lt;p&gt;To create the service, run &lt;code&gt;kubectl apply -f node-echo-service. yaml&lt;/code&gt;. If the configuration does not contain a syntax error, you should get an output that says &lt;code&gt;service/node-echo-service created&lt;/code&gt; on your terminal.&lt;/p&gt;

&lt;p&gt;To confirm the creation of our service, type&lt;/p&gt;

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

kubectl get services


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

&lt;/div&gt;

&lt;p&gt;You should see the following output.&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%2Fop283t2hzi00q9jkcnlj.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%2Fop283t2hzi00q9jkcnlj.png" alt="kubectl get services nodeip"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Geek Bit ℹ️: Pay attention attention, especially to the external IP column. Notice that the value is none. Also, notice that the ClusterIP column is a class A private IP address. Networking 101: Private IPs are only used in LAN. The Kubernetes Cluster's network is the LAN in this scenario. This ClusterIP k8s-service cannot receive internet traffic by default. The other two kinds of k8s services are built on top of ClusterIPs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  NodePort K8S-Service
&lt;/h3&gt;

&lt;p&gt;NodePort k8s-Services builds on top of ClusterIP. In addition to getting a static private IP address, the NodePort  k8s-service receives traffic from outside the Kubernetes cluster by opening up a Port in every Node. The traffic from these open ports can hit any node, and the service can forward requests to the available matching Pods. When configuring a NodePort k8s-service, we must provide three ports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;targetPort&lt;/strong&gt;: The destination port of the matching pods. Usually, the port that your container is running on&lt;br&gt;
port: The port that the k8s service binds to&lt;br&gt;
&lt;strong&gt;nodePort&lt;/strong&gt;: The port on each Node that accepts public traffic&lt;/p&gt;

&lt;p&gt;See the the illustration below.&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%2Fta6nplp8c3kyk7sv8gdb.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%2Fta6nplp8c3kyk7sv8gdb.png" alt="NodePort k8s-service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 I like to describe NodePort K8s-Service as a k8s-service whose public IP address is the address of every Node in the Kubernetes Cluster.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Although NodePort k8s-services allow clients from outside the Cluster to communicate with our Pods,  they are not production-ready for the following reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;NodePort k8s-services only allow traffic from ports 30000 to 32767. Those are non-standard ports in a production environment. Browsers and HTTP clients look at port 80 by default and port  443 for HTTPS. Any other port would require users to be specific. Imagine having to remember the port of every website that you visit&lt;/li&gt;
&lt;li&gt;NodePort k8s-service receives internet traffic through all the Nodes available in the Cluster. This is problematic in production because clients must keep track of all those IP addresses. At the very least, you need a static, permanent, public IP address associated with the K8S-service for your workload to be production-ready. You can achieve this through the creation of an Ingress(don't think about this for now) or using the next k8s-service type called LoadBalancer&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Defining a NodePort K8s-service
&lt;/h4&gt;

&lt;p&gt;To define a NodePort k8s-service, we need to add two new properties to the configuration in the ClusterIP section.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Under the spec property, add the property "type" whose value is NodePort, i.e., &lt;code&gt;type: NodePort&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Under the port object, add the property "nodePort", whose value is any port you choose, i.e., &lt;code&gt;nodePort: 30000&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;file_name: node-echo-service.yaml&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-service&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodePort&lt;/span&gt; &lt;span class="c1"&gt;# telling k8s that we are talking about NodePort&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo&lt;/span&gt; &lt;span class="c1"&gt;# the label of the group of pods that this service maps to&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;80&lt;/span&gt; 
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5001&lt;/span&gt;
       &lt;span class="na"&gt;nodePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30000&lt;/span&gt; &lt;span class="c1"&gt;# The port port that receives traffic from the internet&lt;/span&gt;


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: If you skip the &lt;code&gt;nodePort&lt;/code&gt; property, Kubernetes will automatically choose your value.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Submit the new configuration to Kubernetes by running &lt;code&gt;kubectl apply-f node-echo-service.yaml&lt;/code&gt;. If your configuration contains no syntax error, you should get an output that says &lt;code&gt;service/node-echo-service configured.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To see the result, run &lt;code&gt;kubectl get services node-echo-service -o wide&lt;/code&gt;. Your result should look similar to the screenshot below.&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%2Fjd3yfe5rf3qc75bsl0ti.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%2Fjd3yfe5rf3qc75bsl0ti.png" alt="Kubectl get services node-echo-service -o wide"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pay attention to the type and the port column. The type column now says "NodePort," the ports column maps the k8s-service's port 80 to port 30000 on our machine.&lt;/p&gt;

&lt;p&gt;We can now communicate with our workload by running &lt;code&gt;curl -d "amazing" 127.0.0.1:30000&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GeekBit ℹ️: NodePort is not useless in production; it's just not unsuitable for most web applications. Assuming I run a compute-intensive workload(say, image processing) in Kubernetes, I have dedicated an entire Cluster to this workload. I want to balance incoming tasks across Nodes so that every Node in the cluster always has the same number of tasks running inside them. I'd go for a NodePort k8s-service and set the &lt;code&gt;externalTrafficPolicy&lt;/code&gt; to &lt;code&gt;Local&lt;/code&gt;, ensuring that traffic to a Node only fulfills a request inside that Node. Finally, I'd put a network load balancer in front of the k8s-service. Of course, don't worry about it if you don't understand everything. Keep following this series, and it'll eventually make sense.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  LoadBalancer K8s-service
&lt;/h3&gt;

&lt;p&gt;With the LoadBalancer K8s-Service type, Kubernetes assigns it a static public IP address. This is what we want in production for web servers 🤗. The IP address is then announced across the underlying network infrastructure.&lt;br&gt;
See the illustration below.&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%2Fogte0drsqtbyh3bdszwr.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%2Fogte0drsqtbyh3bdszwr.png" alt="LoadBalancer Service Type"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Note that Kubernetes doesn't come by default with a network Loadbalancer, so one would usually have to install a plugin such as &lt;code&gt;metallb&lt;/code&gt; for load balancing &lt;em&gt;but you don't need to worry about this since your cloud provider would have made this available to your Cluster by default&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are two other types of Kubernetes services which I am intentionally skipping in this part. As we dive deeper into Kubernetes networking in the future, I will talk about these in more detail.&lt;/p&gt;

&lt;h4&gt;
  
  
  Defining a LoadBalancer K8s-service
&lt;/h4&gt;

&lt;p&gt;To define a k8s-service of type LoadBalancer,  take the yaml config file from the NodePort section and &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Change the &lt;code&gt;type&lt;/code&gt; from NodePort to &lt;code&gt;LoadBalancer,&lt;/code&gt; i.e. &lt;code&gt;type: LoadBalancer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;remove the &lt;code&gt;nodePort&lt;/code&gt; property. The resulting yaml should look like so&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;file_name: node-echo-service.yaml&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-service&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo&lt;/span&gt; &lt;span class="c1"&gt;# the label of the group of pods that this service maps to&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;80&lt;/span&gt; 
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5001&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Apply the configuration by running &lt;code&gt;kubectl apply -f node-echo-service.yaml&lt;/code&gt;. You should get the following output; &lt;code&gt;service/node-echo-service configured&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;kubectl get services node-echo-service -o wide&lt;/code&gt;, you should get an output similar to the screenshot below.&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%2Fxwq179qofeu52f8vi2oz.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%2Fxwq179qofeu52f8vi2oz.png" alt="load balancer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Observe the external ip column. Now it says &lt;code&gt;localhost&lt;/code&gt;; This is because I don't have a load balancer installed in the Cluster, but you would get a public IP address in the Cloud.&lt;br&gt;
If we run &lt;code&gt;curl -d "load balancers are amazing" localhost&lt;/code&gt; without specifying any port, we should get those exact words echoed back to us. &lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying our Kubernetes Workload to the Cloud
&lt;/h2&gt;

&lt;p&gt;From the &lt;a href="https://dev.to/ngfizzy/containers-the-what-why-and-how-391n"&gt;first part of this series&lt;/a&gt; to this particular article, we have learned the very basics things we need to nail down to deploy stuff workloads to a Kubernetes Cluster in the Cloud. Now it's time to do the do. Let's take our workload to the Cloud.&lt;/p&gt;

&lt;p&gt;I chose Google Cloud for this demo because I've had more experience with Kubernetes on GCP.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Set up the projects we want to deploy
&lt;/h3&gt;

&lt;p&gt;We will be using the &lt;a href="https://github.com/ngfizzy/blog-demos/tree/main/docker-and-k8s-from-localhost-to-prod/k8s-node-echo" rel="noopener noreferrer"&gt;project we used&lt;/a&gt; in &lt;a href="https://dev.to/ngfizzy/docker-and-kubernetes-from-localhost-to-production-kubernetes-container-orchestrators-the-what-why-and-how-42gg"&gt;the previous part&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Clone the repository and duplicate the folder "k8s-node-echo&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rename the duplicate folder with a name of your choice. I'm calling mine "k8s-node-echo-with-loadbalancer".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;cd&lt;/code&gt; into the &lt;code&gt;k8s-node-echo-with-loadbalancer&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Build The Project As a Docker Image
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;a href="https://hub.docker.com" rel="noopener noreferrer"&gt;Docker Hub account&lt;/a&gt; - Docker Hub, like GitHub for Docker Images. This is where we would push our docker image. Note: DockerHub is not the only place we can push our images, just as GitHub is not the only place to push our code. Docker hub is just one of the popular destinations for your open-source docker images. &lt;em&gt;Take note of your username while signing up. It would be useful later on&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Log in to your docker hub account on your docker desktop. Click on the Login icon on the docker hub UI, as shown in the screenshot below. &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%2Femi7cshj6nnr84nbd3zx.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%2Femi7cshj6nnr84nbd3zx.png" alt="Docker desktop login"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Back in your terminal, in our project folder, run the following command &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

 build &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &amp;lt;yourdockerhubusername&amp;gt;/node-echo:v1 &lt;span class="nt"&gt;-t&lt;/span&gt; &amp;lt;yourdockerhubusername&amp;gt;/node-echo:latest


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

&lt;/div&gt;

&lt;p&gt;For example, for me, that would be&lt;/p&gt;

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

docker build &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile  &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; ngfizzy/node-echo:v1 &lt;span class="nt"&gt;-t&lt;/span&gt; ngfizzy/node-echo:latest


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 If you're working on an M1 and above Macbook, remember to add the &lt;code&gt;--platform linux/amd&lt;/code&gt; flag, i.e &lt;code&gt;docker build --platform linux/amd -f Dockerfile. -t ngfizzy/node-echo:v1 -t&lt;/code&gt;. This is because arm architecture(which m1 chips are based on) is not the default chip most cloud service providers use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The -t flag specifies the name of your image. The image name contains three parts.&lt;br&gt;
&lt;strong&gt;ngfizzy&lt;/strong&gt;: your docker hub username&lt;br&gt;
&lt;strong&gt;node&lt;/strong&gt;-echo: our application name&lt;br&gt;
&lt;strong&gt;v1&lt;/strong&gt;: the version of our application&lt;/p&gt;

&lt;p&gt;The second -t option only aliases v1 as the latest version.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;docker images | grep "REPOSITORY\|ngfizzy"&lt;/code&gt; should show you more information about the image you just built, like the screenshot below&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%2Fipr4xf8nfazbhf2hufvd.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%2Fipr4xf8nfazbhf2hufvd.png" alt="Docker images"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Push the image to Docker Hub
&lt;/h3&gt;

&lt;p&gt;Run &lt;/p&gt;

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

docker push ngfizzy/node-echo:v1


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

&lt;/div&gt;

&lt;p&gt;If everything works out, your output should look similar to mine in the screenshot below.&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%2Fon9q5r5toshk8ql7j1re.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%2Fon9q5r5toshk8ql7j1re.png" alt="Docker push"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run the same command for the &lt;code&gt;ngfizzy/node-echo:latest&lt;/code&gt;. If you visit your docker hub account, you should be able to see those images there now. Here's &lt;a href="https://hub.docker.com/repository/docker/ngfizzy/node-echo/general" rel="noopener noreferrer"&gt;mine&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Update your  &lt;code&gt;node-echo-deployment.yaml&lt;/code&gt; file1.
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Clear the excessive comments I used for explaining the file in the last part of this article&lt;/li&gt;
&lt;li&gt;Update the &lt;code&gt;image&lt;/code&gt; property &lt;code&gt;ngfizzy/node-echo:latest&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Change the &lt;code&gt;imagePullPolicy&lt;/code&gt; property's value to &lt;code&gt;Always&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The resulting configuration should look like this.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-deployment&lt;/span&gt; 
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt; 
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ngfizzy/node-echo:latest&lt;/span&gt;
          &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&lt;/span&gt;
          &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt; 
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;256Mi&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-port&lt;/span&gt; 
              &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5001&lt;/span&gt;
          &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-port&lt;/span&gt; 
          &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-port&lt;/span&gt;
          &lt;span class="na"&gt;startupProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# configuration for endpoints&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; 
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-port&lt;/span&gt;    


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

&lt;/div&gt;

&lt;p&gt;Only lines 16 - 18 changed in the configuration above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Update the node-echo-service.yaml file
&lt;/h3&gt;

&lt;p&gt;Replace the content of node-echo-service.yaml with the &lt;code&gt;LoadBalancer&lt;/code&gt; configuration in the load balancer section of this article. Here's the configuration to save you from scrolling&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-service&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo&lt;/span&gt; &lt;span class="c1"&gt;# the label of the group of pods that this service maps to&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;80&lt;/span&gt; 
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5001&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Step 7: Create a GKE Cluster On GCP
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a GCP account if you don't already have one&lt;/li&gt;
&lt;li&gt;Create a GCP Project if you don't have one previously&lt;/li&gt;
&lt;li&gt;On the home page, click on the &lt;code&gt;Create GKE Cluster&lt;/code&gt; button as shown in the image below
&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%2F7o3npsjdc7t2yo7upim6.png" alt="GCP Home"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ If you have previously enabled if you have not previously enabled Cloud Compute and GKE API, you'd be prompted to do so by following the prompts. When you're done, return to the home page and click the &lt;code&gt;Create GKE Cluster&lt;/code&gt; button again. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You'd be presented with the following page settings page after clicking.&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%2F59prxyu4f402rrxgfkt1.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%2F59prxyu4f402rrxgfkt1.png" alt="GKE Autopilot config"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For demo purposes, we would accept all the default settings and click the submit button at the bottom of the screen.&lt;/p&gt;

&lt;p&gt;That should redirect you to this, as seen in the screenshot below. &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%2Fmltcz4214xvzyh7cwsbv.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%2Fmltcz4214xvzyh7cwsbv.png" alt="Cluster creating"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It takes a couple of minutes for the Cluster to be created.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 8: Install GCloud CLI if you've not done that already
&lt;/h3&gt;

&lt;p&gt;Follow the instructions here &lt;a href="https://cloud.google.com/sdk/docs/install" rel="noopener noreferrer"&gt;https://cloud.google.com/sdk/docs/install&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 9: Log in to Google Cloud on your CLI and your gcloud project
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;gcloud auth login&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gcloud config set project &amp;lt;your-project-id&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Step 9: Connect to your GKE Cluster on your local machine
&lt;/h3&gt;

&lt;p&gt;On the GKE Cluster page, click on the connect button. Follow the numbers in the screenshot below for Navigation.&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%2Fg69lqu9h96lm699prwhy.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%2Fg69lqu9h96lm699prwhy.png" alt="Connect to ke"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the pop-up box after clicking connect, then click the copy icon to copy the connection command to your clipboard. Go back to your CLI and paste the command. You should get the following output.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Fetching cluster endpoint and auth data.
kubeconfig entry generated for &amp;lt;your cluster name&amp;gt;.


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

&lt;/div&gt;

&lt;p&gt;Confirm that you are now connected by running &lt;code&gt;kubectl config get-contexts&lt;/code&gt;, you should see at least one entry in the table. The name of one of them should start with &lt;code&gt;gke_*&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy To GKE Cluster
&lt;/h3&gt;

&lt;p&gt;Now that we are connected to the GKE cluster&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Apply our deployment by running &lt;code&gt;kubectl apply -f node-echo-deployment.yaml&lt;/code&gt;. You might get a warning saying, &lt;code&gt;Warning: autopilot-default-resources-mutator:Autopilot updated Deployment...&lt;/code&gt; Don't worry about this&lt;/li&gt;
&lt;li&gt;Apply your k8s-service config by running &lt;code&gt;kubectl apply -f node-echo-service.yaml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Confirm the deployment by running &lt;code&gt;kubectl get all&lt;/code&gt;. You should see an output similar to the screenshot below.&lt;/li&gt;
&lt;/ol&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%2Fb3gnb6xwjxpp1jphy8up.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%2Fb3gnb6xwjxpp1jphy8up.png" alt="Kubectl get all"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Test your deployment
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;kubectl get services node-echo-service&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copy the IP address under the &lt;code&gt;External IP&lt;/code&gt; column and send a post request to it like the one below
```bash
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;curl -d "hello world" 34.123.423.124&lt;/p&gt;

&lt;h1&gt;
  
  
  The server would echo "Hello world" back to you
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
## Summary
In this article, we took a more detailed look at Kubernetes services; we then used our knowledge to deploy a simple server to the Internet. We are just scratching the surface of Kubernetes. In this series, I aim to gradually reveal containers, and Kubernetes features until we can paint a complete picture of how everything works from end to end.

In the next article, we will take a full circle look deeper at containers, and explore how they do what they do.




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

&lt;/div&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>containers</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>Infer Function Return Type In Typescript</title>
      <dc:creator>Olufisayo Bamidele</dc:creator>
      <pubDate>Fri, 10 Nov 2023 12:34:14 +0000</pubDate>
      <link>https://dev.to/ngfizzy/infer-function-return-type-in-typescript-4mko</link>
      <guid>https://dev.to/ngfizzy/infer-function-return-type-in-typescript-4mko</guid>
      <description>&lt;h2&gt;
  
  
  Scenario
&lt;/h2&gt;

&lt;p&gt;You are using a function called &lt;code&gt;doFunkyStuff&lt;/code&gt; from a library called &lt;code&gt;funky-lib&lt;/code&gt;. Do &lt;code&gt;doFunkyStuff&lt;/code&gt; returns an &lt;code&gt;interface FunkyStuff&lt;/code&gt;, but &lt;code&gt;funky-lib&lt;/code&gt; does not export &lt;code&gt;FunkyStuff&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lib code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// node_modules/funcky-lib/index.ts&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;FunkyStuff&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;funkinessLevel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;doFunkyStuff&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;FunkyStuff&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doing funky stuff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;did some funky stuff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;funkinessLevel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Our Code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;doFunkyStuff&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;funky-stuff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// eslint yells no-explicit-any&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;repeatFunkyStuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fStuff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;repeating funky stuff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fStuff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;fStuff&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;repeatFunkyStuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doFunkyStuff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on your eslint-config, eslint would either yell "no-implicit-any" or "no-explicit-any"&lt;/p&gt;

&lt;h2&gt;
  
  
  Bad Solution
&lt;/h2&gt;

&lt;p&gt;Redefine &lt;code&gt;FunkyStuff&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;repeatFunkyStuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fStuff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;funkinessLevel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The solution above is bad because it becomes unreadable if &lt;code&gt;FunkyStuff&lt;/code&gt; has more than a handful of properties.&lt;/p&gt;

&lt;p&gt;Also, we must update our code each time something changes in  &lt;code&gt;FunkyStuff&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good solution
&lt;/h2&gt;

&lt;p&gt;Use the &lt;code&gt;ReturnType&lt;/code&gt; utility type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// our code in index.ts&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AppFunkyStuff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;doFunkyStuff&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;repeatFunkyStuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fStuff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AppFunkyStuff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;You can combine &lt;code&gt;ReturnType&amp;lt;T&amp;gt;&lt;/code&gt; with &lt;code&gt;Awaited&amp;lt;T&amp;gt;&lt;/code&gt; util type for functions that return promises.&lt;/p&gt;

&lt;p&gt;Assuming &lt;code&gt;doFunkyStuff&lt;/code&gt; returns Promise, then &lt;code&gt;AppFunkyStuff&lt;/code&gt; would be defined like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AppFunkyStuff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Awaited&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;doFunkyStuff&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ Awaited util became only available in typescript 4.5. Achieving the same effect can be a little tricky. Previous versions&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;This tip is just the tip of the iceberg regarding what typescript offers.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>programming</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Docker and Kubernetes From Localhost To Production: Kubernetes - Container Orchestrators, The What, Why and How</title>
      <dc:creator>Olufisayo Bamidele</dc:creator>
      <pubDate>Wed, 08 Nov 2023 14:08:23 +0000</pubDate>
      <link>https://dev.to/ngfizzy/docker-and-kubernetes-from-localhost-to-production-kubernetes-container-orchestrators-the-what-why-and-how-42gg</link>
      <guid>https://dev.to/ngfizzy/docker-and-kubernetes-from-localhost-to-production-kubernetes-container-orchestrators-the-what-why-and-how-42gg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;💡 This is a very long article. It is so because Kubernetes pulls many computing concepts along before one can do anything with it. I broke those concepts down as much as possible in this article, but because of the length of this article, I'd encourage the readers to read it three times.&lt;br&gt;
The first read should be a quick scan to get a general idea of what the article is about. Come back to it at some later point in time for a second read, this time, a slower, more careful read. Then, come back again at a later point in time for a more engaged form of learning. This time, you would take notes, Google unclear terms, ask questions in the comment section, and try out the examples.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is Kubernetes
&lt;/h2&gt;

&lt;p&gt;Kubernetes is a container orchestrator. Just what exactly does that mean?&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/ngfizzy/containers-the-what-why-and-how-391n"&gt;previous article&lt;/a&gt;, we learned about containers and their role in the deployment/management of web applications.&lt;/p&gt;

&lt;p&gt;In modern software engineering, we often find more than one web service communicating with each other over a network to serve a single purpose. For example, an e-commerce website would have a recommendation-service that recommends products to users based on their preferences, a cart-service that takes care of items in the user's cart, a payment-gateway-service that integrates multiple payment services, etc. Services that work in coordination as described above are called &lt;em&gt;Distributed Systems&lt;/em&gt;, but you might be more familiar with the buzzword &lt;strong&gt;Microservices&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Microservices&lt;/strong&gt; are distributed systems that contain &lt;em&gt;highly specialized&lt;/em&gt; web services. They are also usually deployed in containers due to their lightweight and scalable nature. Learn why containers are lightweight in the &lt;a href="https://dev.to/ngfizzy/containers-the-what-why-and-how-391n"&gt;previous article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When deploying a group of services,  a container orchestrator becomes critical. An orchestrator ensures the deployed services adhere to the configuration specified during the deployment. A developer(or a platform engineer) would give a configuration file that describes the desired state of the distributed system.  The configuration usually looks something like this in human language.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Answer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What are you trying to configure?&lt;/td&gt;
&lt;td&gt;A deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How Can I Find The Executable For the App&lt;/td&gt;
&lt;td&gt;Look for a docker image called &lt;code&gt;auth-service:latest&lt;/code&gt; on this machine and run it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How many CPUs do you want to assign to this app&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How much memory do you want to assign to the app&lt;/td&gt;
&lt;td&gt;1Gi&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The platform engineer /developer would then submit that configuration to the container orchestrator. Once received, the orchestrator would constantly look at the configuration and compare it to the current state of your app(s). If there is any variation between the comparisons, the orchestrator will automatically adjust the state of your services to match the developer's desired state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's learn those concepts  by associating them with concepts we already know
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/ngfizzy/containers-the-what-why-and-how-391n"&gt;previous article&lt;/a&gt; in this series, I drew a parallel comparison between a program and a container. As a reminder, a program that is running is called a &lt;em&gt;process&lt;/em&gt;, and an image that is running is a container. Let's continue with that analogy.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Processes&lt;/th&gt;
&lt;th&gt;Kubernetes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;At any moment, there are hundreds of processes running in your system. To get a snapshot of all the processes loaded to your computer memory right now, run &lt;code&gt;ps -A&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;At any point in a distributed system, there is more than one container running in your deployment.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Most modern laptop CPUs have between 2 to 8 cores, which means they can only execute instructions from 2 to 8 processes at once. To cater to all the processes in memory, a component of the Operating System kernel called the Scheduler ensures that all processes take turns executing a bit of their instructions in fractions of a second. The schedulers(usually more than one) decide which process gets the CPU's attention at any time.&lt;/td&gt;
&lt;td&gt;A cloud platform typically has millions of machines that they configure to act like one or multiple supercomputers. Orchestrators work like OS schedulers in the cloud, assigning your services to the suitable machine while making sure they are using the right amount of memory, CPU, and disk space.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Essential Kubernetes Components/Terminologies
&lt;/h2&gt;

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

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

&lt;p&gt;A workload is an application(usually a web service) running in Kubernetes. From this point onward, we refer to applications running inside Kubernetes as a workload.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pod
&lt;/h3&gt;

&lt;p&gt;Techies love to wrap things, and Kubernetes is no exception. Kubernetes developers, at some point, decided that containers were not enough; they needed to wrap them within something else called Pods. Whenever you deploy a workload on Kubernetes, Kubernetes puts that workload(i.e., the container your application is running in) inside a pod. So, in Kubernetes, the smallest unit of a deployment is a pod, not a container. &lt;/p&gt;

&lt;h4&gt;
  
  
  Let's associate pods with what we already know
&lt;/h4&gt;

&lt;p&gt;An application running on an operating system is called a process. When you run that program in a Docker environment, it becomes a container; When you run your container directly in Kubernetes, it becomes a Pod.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️It is possible to run multiple containers inside a single pod just as it is possible to run more than one application inside a single container. However, for 99% of use cases,  you should only have one application running in a container and one container running in a pod. We will discuss the exceptions as we advance in this series.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Like many techies, you're probably asking why. Isn't container isolation enough? Are pods not redundant? No, Pods are not redundant. I'll answer this entirely in the Whys section of this article.&lt;/p&gt;

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

&lt;p&gt;Node is Kubernetes' lingo for a physical or virtual machine. Pods run in nodes(Obviously). Before now, I said workload runs in Kubernetes, but the right way to phrase that is "workloads run in nodes." From now on, when talking about computers/machines/virtual machines in the context of Kubernetes, we will refer to them as nodes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cluster
&lt;/h3&gt;

&lt;p&gt;A cluster is a group of Nodes available for your workload(s). A cluster contains one master node and one or more worker nodes. The master node in Kubernetes lingo is called the  Control Pane, while the worker nodes are simply called nodes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Control Pane&lt;/strong&gt; is the node responsible for assigning workloads to other Nodes. It is the component of Kubernetes that fits more into the OS scheduler analogy. But wait! Does this not suggest that Kubernetes requires at least two machines to work correctly? How are people now able to run them on their laptops? In production, yes, Kubernetes requires at least one Control Pane Node and one Worker Node.  That said, for experimental purposes and local development, it is possible to set up a single-node Kubernetes cluster where a single node serves as both the Control Pane and the Worker.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Now, let's learn that concept from bottom to top.&lt;br&gt;
A cluster contains nodes -&amp;gt; a node runs one or more pods -&amp;gt;  a pod runs containers(most time, one container)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Whys
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Are Containers not Enough Isolation? Why Do We Need Pods?
&lt;/h3&gt;

&lt;p&gt;As redundant as they may look, they serve a purpose. A pod controls how a container can be discovered within a node. One problem it solves is the problem of clashing port bindings on a node. If you bind an internal port of a container to a port on a host machine and you try to bind another container port to that same port, you'll get an error. Pods solve this problem because each pod in a cluster is assigned unique IP addresses at creation time. Pods can communicate using those IP addresses through their node's port.&lt;/p&gt;

&lt;h3&gt;
  
  
  But why do I need Kubernetes?
&lt;/h3&gt;

&lt;p&gt;That is a very valid question. After all, there are simpler alternatives like Google Cloud Run, Heroku, AWS Lambda, and many more. I'll answer with a quote from  Eskil Steenberg; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In the beginning, you always want results; In the end,  all you've always wanted is control.".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When building a product, you want to get to the market quickly and beat your competition. Beyond that stage, things can get very expensive with all these serverless tools. Even Amazon had to &lt;a href="https://www.infoq.com/news/2023/05/prime-ec2-ecs-saves-costs/"&gt;move one of its applications off serverless recently&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Beyond scheduling your pods, Kubernetes abstracts underlying infrastructure, exposing them to you as just resources (resources such as memory, disk, and CPUs, giving you an illusion that all your web services are running on a humongous machine and you can assign resources to each application by simply specifying the amount of memory, CPU and disk space it needs; This is the kind of control required by scaleups and enterprises.&lt;/p&gt;

&lt;h3&gt;
  
  
  But I Only Have A Portfolio Website. Why should I use Kubernetes?
&lt;/h3&gt;

&lt;p&gt;Please don't unless you enjoy pain 🙊. &lt;/p&gt;

&lt;h3&gt;
  
  
  Why is it so complicated?
&lt;/h3&gt;

&lt;p&gt;Well, it is, and it's not. If you asked a regular developer, it probably is. If you ask a system admin who's had to provision virtual machines manually in the past, supervise port mappings, sit and watch system logs to figure out when something is wrong with one of the numerous applications running on his servers, and manually restart applications on VMS when they crash, to me, Kubernetes is not that complicated.&lt;/p&gt;

&lt;p&gt;Don't get me wrong, I'm not insinuating that Kubernetes is easy, but it's not rocket science; it's learnable. Programming was complex, but you learned it; Kubernetes is no different.&lt;/p&gt;

&lt;h2&gt;
  
  
  The How
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How to Set up Kubernetes On Your Computer System
&lt;/h3&gt;

&lt;p&gt;I'm currently on a Macbook; I'll speak quickly about setup on macOS. If you need step-by-step instructions about how to set it up on Windows and Linux, drop a comment, and I'm sure either I or other members of the community would help out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Download and install Docker Desktop for Mac. Follow the instructions &lt;a href="https://docs.docker.com/desktop/install/mac-install/"&gt;here&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Step 2:&lt;/strong&gt; Once you start the docker desktop, go to settings. Under settings, click on the Kubernetes tab. In this tab, you'll see a check box with a label that says "Enable Kubernetes". Click on the check box and then the apply and restart button.&lt;br&gt;
&lt;strong&gt;Step 3:&lt;/strong&gt; Confirm that you have Kubernetes by running with the command, &lt;code&gt;kubectl cluster-info&lt;/code&gt;. You should see an output that looks like the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yq6j_Caj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a591vbf3qwvit5aw40qc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yq6j_Caj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a591vbf3qwvit5aw40qc.png" alt="cluster-info output" width="800" height="80"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Let's get a simple application to work with
&lt;/h3&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/ngfizzy/containers-the-what-why-and-how-391n"&gt;previous article&lt;/a&gt;, we ran &lt;a href="https://github.com/ngfizzy/blog-demos/tree/main/docker-and-k8s-from-localhost-to-prod/node-echo"&gt;a simple echo&lt;/a&gt; server written in nodejs inside a container. We would continue from where that post stopped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Clone the repository, cd into the &lt;code&gt;docker-and-k8s-from-localhost-to-prod&lt;/code&gt; directory. Duplicate the folder called &lt;code&gt;node-echo&lt;/code&gt; and rename the duplicate to &lt;code&gt;k8s-node-echo&lt;/code&gt; and &lt;code&gt;cd&lt;/code&gt; into it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Build the application as a docker image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt;  node-echo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run the image in a Kubernetes node
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Step 1: Create a Kubernetes &lt;em&gt;Deployment&lt;/em&gt; Config
&lt;/h4&gt;

&lt;p&gt;Kubernetes accepts configuration in YAML or JSON format. I'll choose YAML because it's the most popular choice in the community. Besides, it allows me to add comments to explain my configuration, so it is a favorable choice for tutorials like this.&lt;/p&gt;

&lt;p&gt;Create a file called node-echo-deployment.yaml and add the following config to it. It's preferrable if you type it out yourself. I have added an explanation to each line of the config&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="c1"&gt;# The kubernetes api version; because kubernetes' api is constantly revised&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="c1"&gt;# The Kubernetes object that we are defining with this configuration. In this case, it's a Deployment&lt;/span&gt;
&lt;span class="c1"&gt;# A deployment is a Kubernetes _Object_ that stores desired state of pods&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# We are just giving a human-readable name to this deployment here&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-deployment&lt;/span&gt; 
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# We are telling Kubernetes that we only want one instance of our workload to run simultaneously. If this instance crashes for any reason, Kubernetes will create a new instance of our &lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt; 
  &lt;span class="c1"&gt;# The next 7 lines assign metadata to our node to easily query them in the cluster. More on these later in the series&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo&lt;/span&gt;
    &lt;span class="c1"&gt;# From here downward, we are describing how to run our web app to Kubernetes &lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# We are telling Kubernetes to find the docker image "node-echo:latest" and&lt;/span&gt;
      &lt;span class="c1"&gt;# run it as a container. The container's name should also be called node-echo&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo:latest&lt;/span&gt;
          &lt;span class="c1"&gt;# Next, we are telling Kubernetes to look for node-echo:latest image on our machine first. if&lt;/span&gt;
          &lt;span class="c1"&gt;# If it isn't present on our local machine, then Kubernetes will go look for it on the&lt;/span&gt;
          &lt;span class="c1"&gt;# internet&lt;/span&gt;
          &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt; 
          &lt;span class="c1"&gt;# The next 4 lines define the limit of the resources that Kubernetes should make available in our app &lt;/span&gt;
          &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt; 
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;256Mi&lt;/span&gt;
          &lt;span class="c1"&gt;# We are telling Kubernetes the ports that our workload will be using&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# We are giving the port 5001 a name. Similar to declaring a variable&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-port&lt;/span&gt; 
              &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5001&lt;/span&gt;
          &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-port&lt;/span&gt; 
              &lt;span class="c1"&gt;# In the 4 lines above, told Kubernetes to check localhost:5001/ in the pod node-echo&lt;/span&gt;
              &lt;span class="c1"&gt;# the application is still running properly. If the endpoints return any status code &amp;gt; 299&lt;/span&gt;
              &lt;span class="c1"&gt;# kubernetes restarts the application&lt;/span&gt;
          &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-port&lt;/span&gt;
              &lt;span class="c1"&gt;# The 4 lines above defines the endpoint that Kubernetes would check to determine that&lt;/span&gt;
              &lt;span class="c1"&gt;# the pod is ready to receive trafic &lt;/span&gt;
          &lt;span class="na"&gt;startupProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# configuration for endpoints&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; 
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-port&lt;/span&gt;
              &lt;span class="c1"&gt;# The 4 lines above are uses by Kubernetes to determine if our application has started &lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Comparing the table in the opening section of this article might clarify this even further.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: Submit That Config To Kubernetes
&lt;/h4&gt;

&lt;p&gt;Run &lt;code&gt;kubectl apply -f node-echo-deployment.yaml&lt;/code&gt; where the &lt;code&gt;-f&lt;/code&gt; flag specifies the file containing your config. If the file has no syntax error, you should get a message that says "deployment.apps/node-echo-deployment created"&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3: Confirm The Created Deployment
&lt;/h4&gt;

&lt;p&gt;To check that your deployments are running, run &lt;code&gt;kubectl get deployments&lt;/code&gt;. The output should look similar to the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qHXKVsKY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hvo2ory2onvzxjxrqqrx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qHXKVsKY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hvo2ory2onvzxjxrqqrx.png" alt="Kubectl get deployments output" width="583" height="69"&gt;&lt;/a&gt;&lt;br&gt;
You can also see your running pods by running the following command: &lt;code&gt;kubectl get pods&lt;/code&gt;. Your output should also look like the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YKQQafll--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k51r55558eb8l8k9usqx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YKQQafll--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k51r55558eb8l8k9usqx.png" alt="Kubectl get pods output" width="706" height="76"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 4: Test our running workload
&lt;/h3&gt;

&lt;p&gt;We know that our application is running on the localhost:5001, so if we run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'hello'&lt;/span&gt; localhost:5001/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our server should respond with "Hello", right? No. This is because a Kubernetes cluster's network is Isolated from the outside world by default. Going inside the pod, we can hit that endpoint. What a Bummer! I know, right 🙂? We can't tell users to always enter a pod before they can use our service.&lt;/p&gt;

&lt;p&gt;We can fix this by creating another Kubernetes object called a Service. Oh no. 🤦‍♂️ Not another concept, right? Hold on a minute. We are almost done. This is the last concept you have to learn in this article.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 5: Create a Kubernetes Service
&lt;/h4&gt;

&lt;p&gt;A Kubernetes service defines networking between pods and how pod networks are exposed to the internet. There is more to it, but for now, in development, we need the following configuration.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;node-echo-service-deployment.yaml&lt;/code&gt; file and add the following config.&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="c1"&gt;# specifying which version of Kubernetes API we are using&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="c1"&gt;# Telling Kubernetes that we are configuring a service. &lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="c1"&gt;# assigning some metadata to the services&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# We are assigning a name to the service here. We are calling it&lt;/span&gt;
  &lt;span class="c1"&gt;# node-echo-service&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo-service&lt;/span&gt;
&lt;span class="c1"&gt;# From here below, we are specifying the configuration of the Service&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# There are different types of services.&lt;/span&gt;
  &lt;span class="c1"&gt;# For demo purposes, we stick to NodePort. Node+Port or Machine+Port.&lt;/span&gt;
  &lt;span class="c1"&gt;# With this config, we are telling Kubernetes that we would like to bind the Pods port to a Port on the Node running our Pod&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodePort&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# We are specifying the app that we are configuring this service for&lt;/span&gt;
    &lt;span class="c1"&gt;# If you go back to the deployment.yaml file, you'd remember that we defined&lt;/span&gt;
    &lt;span class="c1"&gt;# this app metadata over there. It has proved to be useful when defining other&lt;/span&gt;
    &lt;span class="c1"&gt;# Objects for our deployment&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node-echo&lt;/span&gt;
  &lt;span class="c1"&gt;# Define port mapping&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# The port that this service would listen on&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;80&lt;/span&gt; 
      &lt;span class="c1"&gt;# The Pod's port to which the Kubernetes service would forward requests.&lt;/span&gt;
      &lt;span class="c1"&gt;# This is usually the port that your workload binds to&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5001&lt;/span&gt;
      &lt;span class="c1"&gt;# The port on your Node that the service binds to&lt;/span&gt;
      &lt;span class="na"&gt;nodePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30001&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 6: Submit the Kubernetes service config
&lt;/h4&gt;

&lt;p&gt;Then run &lt;code&gt;kubectl apply -f node-echo-service.yaml&lt;/code&gt;. If your configuration is fine, you should get the following message: &lt;code&gt;service/node-echo-service configured&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now we can communicate with our service on &lt;code&gt;localhost:30001/&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"Hurray!! Now I can talk to my service in Kubernetes"&lt;/span&gt; localhost:30001/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see all the code base and the configuration files in one, place, check out &lt;a href="https://github.com/ngfizzy/blog-demos/tree/main/docker-and-k8s-from-localhost-to-prod/k8s-node-echo"&gt;this repository&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;In this article, we learned the main pillars of Kubernetes: Cluster -&amp;gt; Node -&amp;gt; Services -&amp;gt;  Deployment -&amp;gt; Pods -&amp;gt; Containers.  We discussed the relevance of Kubernetes and why it can benefit a developer to learn it.&lt;/p&gt;

&lt;p&gt;It is OK if everything stays the same. Kubernetes is best learned by trying stuff in it, running into issues, and fixing them.&lt;/p&gt;

&lt;p&gt;In the next part of this series, we will go in-depth on services and deploy our &lt;code&gt;node-echo&lt;/code&gt; to a cloud provider.&lt;/p&gt;

&lt;p&gt;If you learned something, leave a &lt;br&gt;
reaction, and do not hesitate to correct me if I have mixed up certain explanations.&lt;/p&gt;

&lt;p&gt;See you on the next one.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>containers</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Docker and Kubernetes From Localhost To Production: Containers; The what, why, and How</title>
      <dc:creator>Olufisayo Bamidele</dc:creator>
      <pubDate>Wed, 01 Nov 2023 06:06:26 +0000</pubDate>
      <link>https://dev.to/ngfizzy/containers-the-what-why-and-how-391n</link>
      <guid>https://dev.to/ngfizzy/containers-the-what-why-and-how-391n</guid>
      <description>&lt;p&gt;&lt;em&gt;Warnings:&lt;/em&gt; &lt;br&gt;
&lt;em&gt;1. Some concepts in this article are explained like it's meant for 5-year-olds. If you're already an advanced user, this blog post is not for you.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;2. If you want to see some code, scroll to the How subheading of this blog post. If everything there seems foreign to you or you don't get the point, you can come back to the very beginning.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For many people, Docker and Kubernetes are the things they learn halfway and drop, never to touch again after finding alternatives. Some, after learning it, never get to use it on their jobs because they have DevOps/platform teams, but once in a while, they pop up again. They're required to learn/relearn it. Usually, they have to relearn it from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do Docker and Kubernetes Never Stick For Many People?
&lt;/h2&gt;

&lt;p&gt;Why is Docker and Kubernetes(especially Kubernetes) tricky for many to be proficient in? Why is it so easy to forget everything once you stop using them?&lt;/p&gt;

&lt;p&gt;As humans, we learn best through association. It is easy to remember new things when you can associate that learning with a simpler but related concept. Your entire knowledge base is a tree, and new learnings need to be attached to existing branches of that tree. The reason why people forget Docker and Kubernetes(k8s) quickly is that they encapsulate the significant pillars of computer systems, i.e., Operating systems, programs(running in userland), and computer networking. My aim in this series is not to graze over these fundamental topics but to go in-depth as necessary. Understanding those basic concepts would increase your chances of not having to relearn everything from scratch each time you return to them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Whats
&lt;/h2&gt;

&lt;p&gt;What is&lt;/p&gt;

&lt;h3&gt;
  
  
  A Container
&lt;/h3&gt;

&lt;p&gt;A container is a basic unit of deployment. That's vague, so let's rephrase it. A container is your program coupled with a virtualized operating system alongside every other dependency needed to run it. &lt;em&gt;The virtualized operating system part of that definition is essential.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  A Container Image:
&lt;/h3&gt;

&lt;p&gt;A container image is "an executable", runnable by a  container Engine.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Container Engine/Container Runtime:
&lt;/h3&gt;

&lt;p&gt;A container engine is a program that knows how to build and execute a container image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker
&lt;/h3&gt;

&lt;p&gt;Docker is a container technology company. The  Container engine they created is called the &lt;strong&gt;Docker Engine&lt;/strong&gt;; a container image built with docker-engine is called &lt;strong&gt;Docker Image&lt;/strong&gt;. An executed Docker image is called a &lt;strong&gt;Docker Container&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  A DockerFile
&lt;/h3&gt;

&lt;p&gt;A docker file contains information on what a docker image should have.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's  Relearn All The Concepts Above By Associating It With What We Already Know From Bottom Back To The Top
&lt;/h3&gt;

&lt;p&gt;I assume anyone reading this article has written a program at some point, so let's compare the process of creating a program to creating a container.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To write a C program, you must first write some instructions in a .c file.

&lt;ul&gt;
&lt;li&gt;A docker file is the source code that contains instructions about what a docker image should have, which is usually a description of the OS your program is built to run on, the dependencies of your program, your program itself, and an instruction about how to start it.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;To transform your C source file into something your machine can understand, you must build the .c file using a c compiler.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To transform your DockerFile to a docker image, you must "compile" it with the "docker image builder." The builder comes with the docker engine, and you have access to it through &lt;strong&gt;docker-cli&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;To run the C executable, you can either open a command line interface and call the executable directly or, if you have access to a GUI, you will double-click on the executable.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To run the docker image, you run it through docker-cli.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Why
&lt;/h2&gt;

&lt;h3&gt;
  
  
  I already know how to run a program on a computer; why do I also need to run it in a container?
&lt;/h3&gt;

&lt;p&gt;Yes, you could rent a physical machine and run your web app on it. Well, these days, cloud companies will only give you that deal if you're Facebook or Netflix. If you're a regular Joe like me who wants to run a portfolio website, you won't get a contract to run it on a physical server. &lt;/p&gt;

&lt;h3&gt;
  
  
  Yes, I know this already, but these businesses can still run multiple programs on a single machine.
&lt;/h3&gt;

&lt;p&gt;Yes, they can. There is already a business model for this. It's called Shared Hosting. In this hosting model, the provider runs multiple customer programs on a single machine. This model is more cost-effective for both you(the customer) and the service provider but not suitable for fast-growing startups. One downside to this approach is that websites with heavy traffic might use the shared server resources (CPU and memory), leaving your website with little to no resources. &lt;/p&gt;

&lt;p&gt;Another issue that arises in shared hosting is security. A customer running a malicious program on the same server as yours can also infect your website. Of course, these businesses take measures against worse-case scenarios like this, but it requires the expertise of specialized system admins. &lt;/p&gt;

&lt;p&gt;Programs running in containers, on the other hand, by default don't have this problem because a container is a completely isolated environment, and resource limits can be easily assigned to the container as part of their startup instructions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Talking about Isolation and resource allocation, isn't that achievable Through Virtual Machines? Why Do I Still Need Containers?
&lt;/h3&gt;

&lt;p&gt;Yes, there is also already a business model for that. Today, you can rent a virtual machine on any cloud platform. They give these things different marketing names, like EC2 on AWS or Cloud Compute Engine on Google  Cloud. Irrespective of what cloud platforms call it, you get a virtual machine running some open-source or proprietary distribution of the Linux operating system.&lt;/p&gt;

&lt;h3&gt;
  
  
  So, if virtual machines solve the problem with shared hosting, why do you still need containers?
&lt;/h3&gt;

&lt;p&gt;Well, businesses are always trying to do two things: make more money and spend less money. Virtual machines virtualize the hardware, so for every virtual machine running on a physical machine, at the very minimum, a virtual CPU, memory, hard drive, and a virtual ROM containing a virtual bootloader would be created. The virtual machine also boots up a full-fledged operating system just to run a Hello World program. Also, for every client hosting software on a physical machine, a new virtual machine would have to be provisioned for them.&lt;/p&gt;

&lt;p&gt;All of these leave a huge resource footprint on the host machine. So, in 2006, google added more features to an existing Linux OS kernel feature called namespaces.&lt;br&gt;
Additionally, they implemented something called cgroups. Those two features enabled system administrators to run programs in isolation without hardware virtualization. That work serves as the foundation for container technologies. I would dive deeper into cgroups and namespaces later in this series.&lt;/p&gt;

&lt;p&gt;So, with containers, cloud service providers don't need to provision a virtual machine for each customer. With the help of container technologies taking advantage of namespaces and cgroups, you can run your programs in isolated environments with a much lower resource footprint. Additionally, since containers are just programs with limited access to system resources, they have a faster startup time than virtual machines. This kind of efficiency translates to substantial cost savings on hardware for cloud service providers at the same time, allows them to provide cheaper offerings to you, the customer.&lt;/p&gt;

&lt;p&gt;Follow the money, my friends; it will lead you to the answers to most modern tech questions. 🙊&lt;/p&gt;

&lt;h4&gt;
  
  
  Do Container Technologies Solve the: "But it works on my machine problem"
&lt;/h4&gt;

&lt;p&gt;From my experience, yes, &lt;strong&gt;it could&lt;/strong&gt;, but it takes some proficiency to containerize your application such that it works the same way everywhere outside your machine. In short, dockerizing your application does not automatically solve that problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  The How
&lt;/h3&gt;

&lt;p&gt;Talk is cheap; show me some code.&lt;br&gt;
&lt;em&gt;Prerequisite: Make sure you have docker desktop installed.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Let's create a simple echo server in nodejs
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;started server at localhost:5001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test that our echo server works, run &lt;code&gt;node index.js&lt;/code&gt; and then in another terminal, run &lt;code&gt;curl -d 'hello world" loalhost:5001&lt;/code&gt;. The server should echo back "hello world" to you.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: Write the requirements of your docker image in a Dockerfile
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;  1 FROM node:20.9.0-alpine-3.18
  2
  3 WORKDIR /web
  4 COPY ./index.js ./index.js
  5
  6 ENTRYPOINT ["node," "./index.js"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starting from line one&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We instructed Docker to pull nodejs 20.9 from the docker hub. If "docker hub" sounds foreign,  consider it as GitHub for docker images. That nodejs image is the base(or foundation) to build our application image. A Dockerfile always starts with the &lt;code&gt;FROM&lt;/code&gt; instruction.&lt;/li&gt;
&lt;li&gt;Next, we define the default working directory of the container. The working directory is the default directory when your container starts. This is similar to the &lt;code&gt;$HOME&lt;/code&gt; directory in the Linux operating system or the &lt;code&gt;~&lt;/code&gt; directory on Mac.&lt;/li&gt;
&lt;li&gt;Next, we copy our source code into a docker image.
And finally, on line six, we tell Docker the command to run when our application container starts up.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 3: Build the image using the DockerFile we just created; run the following command
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; node-echo&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-f&lt;/code&gt; argument specifies where the Dockefile is located, in this case the current directory.&lt;br&gt;
The &lt;code&gt;.&lt;/code&gt; specifies the execution context of the build process, i.e., the folder where the source code(or executable) of your application lives. The command above assumes you're running the "docker build" in the same folder as your source code.&lt;br&gt;
The &lt;code&gt;-t&lt;/code&gt; argument assigns a name to the resulting image.&lt;br&gt;
To see information about the resulting image, run &lt;code&gt;docker images&lt;/code&gt;&lt;br&gt;
The output should be similar to what is in the screenshot below&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%2Fhgbgkwl6rrsvzzwv4cz4.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%2Fhgbgkwl6rrsvzzwv4cz4.png" alt="docker images"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 4: Create a running container with the image
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 5001:5001 node-echo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Breaking that command down,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;-t&lt;/code&gt; flag allocates a pseudo-tty to the docker container. Assigning a pseudo-tty is a "Linuxy" way of saying, "We allow our program, in this case our container, to accept input from the keyboard.&lt;/li&gt;
&lt;li&gt;The -i flag instructs docker that we want to keep the container's &lt;em&gt;stdin&lt;/em&gt; open. For beginners, stdin is where your keyboard input goes to.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;-p 5001:5001&lt;/code&gt; publishes port 5001 on the container to port 5001 to the host's operating system. That's also a fancy way of saying that we want users to be able to send HTTP requests to port 5001 in the container via port 5001 on the host operating system. This part is essential because, by default, everything running a container, including the network configurations of a container, is isolated from that of the host machine. To expose anything inside the container to the host machine, we need to let the docker engine know we intend to expose it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node-echo&lt;/code&gt; is the docker image we are executing
After running that command, our echo server should start up quickly. Sending a request to our server like so, &lt;code&gt;curl -d "hello" localhost:5001&lt;/code&gt; should send the word hello back to us.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Edit 1/10/2023
&lt;/h4&gt;

&lt;p&gt;You can play with the full sourcecode &lt;a href="https://github.com/ngfizzy/blog-demos/tree/main/docker-and-k8s-from-localhost-to-prod/node-echo" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetes
&lt;/h2&gt;

&lt;p&gt;Kubernetes is a container orchestration tool for automating the deployment and management of containers. Usually, you don't deploy only one container in the modern web; you deploy tens to hundreds of them, and they have to work in a coordinated manner. This is the job of Kubernetes. I know those words do not hold significant meanings if you're a beginner, but this blog post is too long, so I'll pick up from here in the next part of this series.&lt;/p&gt;

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

&lt;p&gt;In this first part of the series, we learned what a container is, why we need a container, the problems container technologies solve, and how you can create a simple nodejs server container using docker. We also introduced Kubernetes. We have just scratched the surface of this subject, so stay tuned for more parts.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>kubernetes</category>
      <category>containers</category>
    </item>
    <item>
      <title>You Don't Need Axios</title>
      <dc:creator>Olufisayo Bamidele</dc:creator>
      <pubDate>Tue, 24 Oct 2023 01:38:15 +0000</pubDate>
      <link>https://dev.to/ngfizzy/you-dont-need-axios-34j9</link>
      <guid>https://dev.to/ngfizzy/you-dont-need-axios-34j9</guid>
      <description>&lt;p&gt;You don't need Axios, well, for most use cases. The web is maturing, and the days of Axios are ending.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we needed Axios
&lt;/h2&gt;

&lt;p&gt;For newcomers, not long ago, we needed to use &lt;code&gt;XMLHttpRequest&lt;/code&gt; to fetch data from servers. To fetch data from &lt;a href="https://jsonplaceholder.typicode.com/todos/"&gt;https://jsonplaceholder.typicode.com/todos/&lt;/a&gt;, we would have needed the following lines of code at the very minimum.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;XMLHttpRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseText&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com/todos/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We needed an elegant abstraction over XMLHttpRequest, and &lt;code&gt;axios&lt;/code&gt; filled that space perfectly, especially with the introduction of Promise in ES6.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;axios&lt;/code&gt;, we can achieve the same thing with just one line of code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com/todos/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But time has passed, the web has matured, and now, we have &lt;code&gt;fetch()&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Use fetch() Instead
&lt;/h2&gt;

&lt;p&gt;With fetch, you get most of the benefits of Axios with zero external dependencies in your application. I am a proponent of minimal dependencies. If the platform I'm on provides a functionality, and it's relatively easy to implement, I'll explore it first. This is even more important with JavaScript runtimes, as every abstraction comes at a cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  GET Request Using Fetch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com/todos/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  POST Request Using Fetch
&lt;/h3&gt;

&lt;p&gt;The fetch API takes in a second parameter, which allows you to specify other options, including your HTTP method, headers, and body. The example below shows how to make a post request using fetch API. This example also applies to other HTTP methods that mutate the server;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt;  &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com/todos/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;buy milk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Uploading Files
&lt;/h3&gt;

&lt;p&gt;Assuming you have the following HTML file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="na"&gt;Docktype&lt;/span&gt; &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-eqiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatibly"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"ie=edge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;File Upload&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"fileform"&lt;/span&gt; &lt;span class="na"&gt;enctype=&lt;/span&gt;&lt;span class="s"&gt;"multipart/form-data"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"upload"&lt;/span&gt;  &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"upload file"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;submit&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./index.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then your fetch call would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fileform&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:5001/upload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;result&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pro Tip
&lt;/h3&gt;

&lt;p&gt;Always wrap your fetch function in with another function; This gives you leeway for abstracting HTTP call patterns emerging as your application grows. See the code snippet below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalFetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// intercept request&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;originalFetch&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// intercept response e.g&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="c1"&gt;//Do whatever you want to handle the error&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Whataboutisms
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What about Request and Response Interceptors
&lt;/h3&gt;

&lt;p&gt;We've talked about that briefly in the previous subheading&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;interceptRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// perform some magic&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;interceptResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// perform some magic&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalFetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modifiedArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;interceptRequest&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;orginalFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modifiedArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;interceptResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Edit 24 Oct 2023 More On Intercepting Requests
&lt;/h4&gt;

&lt;p&gt;Due to questions from colleagues, I realized I needed to address this a little more. &lt;/p&gt;

&lt;p&gt;To intercept means to stop something from proceeding. Interceptors in HTTP libraries(you could call them middleware), stop requests and modify the request configuration using your provided function. For response, interceptors take raw server responses and modify them based on your logic before returning them to the library consumers.&lt;/p&gt;

&lt;p&gt;These can be implemented as simple functions that get called before and after your call to &lt;code&gt;fetch&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Axios and other JS libraries don't do magic with your interceptors. They only call the functions you give to them and call them for you&lt;/p&gt;

&lt;h3&gt;
  
  
  What About Timeouts
&lt;/h3&gt;

&lt;p&gt;Timeouts should primarily be the business of your backend. It would help if you also had a backend that integrates the third-party platforms, but should you choose to incorporate an unreliable backend with your frontend, you can still implement timeouts with fetch API so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// timeout request after 5 seconds&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com/todos/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AbortSignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What About Javascript In The Backend, Aka Nodejs/Deno/Bun
&lt;/h3&gt;

&lt;p&gt;The three significant runtimes now implement fetch, and if you must install any HTTP client for your javascript backend, install &lt;code&gt;undici.&lt;/code&gt; Undici is an HTTP client built from the ground for nodejs, and the nodejs organization maintains it. Undici also implements fetch API&lt;/p&gt;

&lt;h3&gt;
  
  
  What about a progress update for large file uploads
&lt;/h3&gt;

&lt;p&gt;Use XMLHttpRequest ;). Unfortunately, fetch() has yet to include this feature.&lt;/p&gt;

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

&lt;p&gt;Regarding dependencies, I follow the &lt;a href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it"&gt;YAGNI principle in extreme programming&lt;/a&gt;. There is no need to bloat your application with thousands of dependencies. The web is maturing, and like JQuery, axios' time is gradually ending. With current developments, Axios and other popular HTTP client libraries are no longer required to have good developer experience.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>node</category>
    </item>
    <item>
      <title>SQL JOINS; Identifying The Left And Right Table</title>
      <dc:creator>Olufisayo Bamidele</dc:creator>
      <pubDate>Sun, 22 Oct 2023 20:10:30 +0000</pubDate>
      <link>https://dev.to/ngfizzy/sql-joins-identifying-the-left-and-right-table-4nkb</link>
      <guid>https://dev.to/ngfizzy/sql-joins-identifying-the-left-and-right-table-4nkb</guid>
      <description>&lt;p&gt;If the blog post's title caught your attention, then you're probably like me;  you remember the big things and always need to remind yourself of the small stuff. You spell length as "lenght"  all the time, then go back to correct yourself; you need someone to remind you of the correct way to end a formal letter: yours faithfully, yours sincerely, yours violently, or yours sarcastically, and when joining tables in SQL, you argue with yourself about which table you're joining is the left or right table.&lt;/p&gt;

&lt;h2&gt;
  
  
  So Which One Is The Left Table?
&lt;/h2&gt;

&lt;p&gt;I'll tell you now, for the one hundred and sixty-fifth times, &lt;strong&gt;the table that follows your &lt;code&gt;FROM&lt;/code&gt; keyword is always the left table&lt;/strong&gt;. The right table is the one that comes immediately after the &lt;code&gt;JOIN&lt;/code&gt; keywords. You just need to remember one of those.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Practice
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt;  &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt;   &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, which table is the left table? The left table in the query above is the users' table. Why? Because it comes immediately after the "FROM" keyword. That makes the &lt;code&gt;posts&lt;/code&gt; table the right table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's flip it around.
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;RIGHT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, which table is the right table? The right table is still the posts table. To identify the right table, first identify the left table. The left table is the table that comes immediately after the&lt;code&gt;FROM&lt;/code&gt; keyword, which is the users table. This automatically makes the posts table the right table.&lt;/p&gt;

&lt;p&gt;Now we get it but let's bookmark this blog post because I know we'll forget again 😉.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Frontend Vs Backend: An Objective Look</title>
      <dc:creator>Olufisayo Bamidele</dc:creator>
      <pubDate>Fri, 20 Oct 2023 23:29:52 +0000</pubDate>
      <link>https://dev.to/ngfizzy/frontend-vs-backend-an-objective-look-14f0</link>
      <guid>https://dev.to/ngfizzy/frontend-vs-backend-an-objective-look-14f0</guid>
      <description>&lt;p&gt;&lt;em&gt;Image credit: &lt;a href="https://media.geeksforgeeks.org/wp-content/uploads/20190712131938/front-end-vs-back-end.png"&gt;geeks for geeks&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I wrote a short post about how backend development is more critical than frontend development and shared it among a few friends and colleagues yesterday; that's just me being silly.&lt;/p&gt;

&lt;p&gt;Today, I have decided to write a more abjective blog post about the debate. Without further ado, here's what I think.&lt;/p&gt;

&lt;h2&gt;
  
  
  There are no Frontends Or Backends.
&lt;/h2&gt;

&lt;p&gt;Yes, there are no frontends or backends. We only have software that runs on machines; any software can be a client or server at anytime. The end user is the only one who doesn't get to serve as a server. What do I mean?&lt;/p&gt;

&lt;p&gt;Let's start at the very top of the stack&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The UI:&lt;/em&gt; There are many kinds of UI, but in this instance, I'm talking about the web UI. Yes, it is a client to a web backend (say a RESTful API), but simultaneously a server for the end user.  But a RESTFUL API is also a client to a database server. &lt;/p&gt;

&lt;p&gt;It might surprise you that databases also have frontends and backends. Well, databases are as complex as operating systems, but to keep it as simple as possible, I'd say most databases contain two main parts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Query Engine:&lt;/strong&gt; This is the front end of your database. It takes your query, plans execution,  and optimizes it when possible. The query engine then passes your question to the next component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Storage Engine:&lt;/strong&gt; This is your database's backend. It takes the prepared query from the query engine and executes it(oversimplification). This layer handles data storage and retriever from disk, what gets stored in memory, scheduled database cleanups, etc. Of course, it achieves this by talking to the operating system, so we can accurately say that a database's storage engine is a client of the operating system. But wait a minute; the operating system also has a front and back end, namely, the userland and kernel, which are the front and back end of the operating system.&lt;/p&gt;

&lt;p&gt;Well, by now, you get the gist. No matter what kind of software you choose to write, your software is serving and being served simultaneously.&lt;/p&gt;

&lt;h2&gt;
  
  
  But which one is more complex?
&lt;/h2&gt;

&lt;p&gt;I can tell you this: creating a database, a compiler, an operating system, and anything that directly talks to the hardware is always difficult. Every other thing is easy, but at scale, nothing is.&lt;/p&gt;

&lt;p&gt;You could sort an array containing five items, but let's make that five billion items, and now you'd have to deal with all sorts of demons.&lt;/p&gt;

&lt;p&gt;We are all software developers; some prefer to work on the top of the stack, while others prefer to work very close to the bottom. Whatever area you like to stay, ensure you're competent and can build software that works at various scales.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>backend</category>
      <category>fullstack</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Tip: Never Forget a console.log In Your Patch Again</title>
      <dc:creator>Olufisayo Bamidele</dc:creator>
      <pubDate>Thu, 19 Oct 2023 15:37:55 +0000</pubDate>
      <link>https://dev.to/ngfizzy/tip-never-forget-a-consolelog-in-your-patch-again-4c3b</link>
      <guid>https://dev.to/ngfizzy/tip-never-forget-a-consolelog-in-your-patch-again-4c3b</guid>
      <description>&lt;p&gt;&lt;strong&gt;TLDR;&lt;/strong&gt; stage your changes &lt;code&gt;git add . -p&lt;/code&gt; to slow down your personal code review process.&lt;/p&gt;

&lt;p&gt;Yes, we've all been there. You've spent the last 5 hours looking for a bug in your codebase and finally came up with a fix. You implemented the patch and removed all your log statements or thought you did. You called for a review only to get rejected because you left some unnecessary logs in your patch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Have you tried running  &lt;code&gt;git add&lt;/code&gt; with the &lt;code&gt;-p&lt;/code&gt; flag?
&lt;/h2&gt;

&lt;p&gt;With &lt;code&gt;git add &amp;lt;path/to/the/changed/files&amp;gt; -p&lt;/code&gt;, git brings back your changes a chunk at a time or, in git lingo, one hunk at a time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Geek bit:&lt;/strong&gt; See &lt;a href="https://www.gnu.org/software/diffutils/manual/html_node/Hunks.html"&gt;here&lt;/a&gt; for more info about what a hunk is. &lt;/p&gt;

&lt;p&gt;As git gradually reveals the changes you made, it presents the following options:&lt;br&gt;
y - stage this hunk&lt;br&gt;
n - do not stage this hunk&lt;br&gt;
q - quit; do not stage this hunk or any of the remaining ones&lt;br&gt;
a - stage this hunk and all later hunks in the file&lt;br&gt;
d - do not stage this hunk or any of the later hunks in the file&lt;br&gt;
e - manually edit the current hunk&lt;br&gt;
? - print help&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Side Note:&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;For the &lt;code&gt;e&lt;/code&gt; option, git presents Vim as your default editor. Again, you meet Vim 😈 . To change git's default editor from Vim to vscode, run  &lt;code&gt;git config --global core.editor = "code"&lt;/code&gt; assuming you have already added vscode to your path variable. I'll advise against this, though; take this opportunity to learn basic Vim motions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once git has presented you with all the hunks, you'd be left with only the ones you accepted. The rejected hunks would be left unstaged. Staging your file in git like this allows you to slow down and do a personal code review, allowing you to catch those annoying leftover comments, logs, and debug statements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;-p&lt;/code&gt; flag only works for files already known by git. If you've never staged a file before, git will never present you with hunks from new files because there are no hunks. A hunk is nothing but a diff between the previous version of a file and the newest version, revealed one chunk at a time. There is nothing to diff for a new file, so I like to stage new files with the &lt;code&gt;-N&lt;/code&gt; flag, i.e., &lt;code&gt;git add . -N&lt;/code&gt;. The "N" flag does not stage the new file but instructs git that you intend to do so later. Now git knows about the file and will present the entire file to you when you're staging your changes with the &lt;code&gt;-p&lt;/code&gt; flag.&lt;/p&gt;

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

&lt;p&gt;I couldn't think of a way to conclude this article, so I inserted a dry joke here.👇🏽&lt;/p&gt;

&lt;p&gt;If git is a tool, and this article presents tips about git,  does that make it a tooltip❓🤔&lt;/p&gt;

&lt;p&gt;Okay, I'll give you the time to cringe now. See you on the next one.&lt;/p&gt;

</description>
      <category>tools</category>
      <category>git</category>
      <category>productivity</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>POSTGRESQL PSEUDOCOLUMNS: CTID</title>
      <dc:creator>Olufisayo Bamidele</dc:creator>
      <pubDate>Tue, 17 Oct 2023 07:24:07 +0000</pubDate>
      <link>https://dev.to/ngfizzy/postgresql-pseudocolumns-ctid-108a</link>
      <guid>https://dev.to/ngfizzy/postgresql-pseudocolumns-ctid-108a</guid>
      <description>&lt;p&gt;Postgres CTID is an internal table column identifying how Postgres stores table data physically on the disc. It comes as two comma-separated numbers, e.g. (0, 1), 0 here serving as the page number and 1 is the row's location on the page. &lt;/p&gt;

&lt;p&gt;Let's establish some knowledge about the CTID column.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up a Postgres Database
&lt;/h2&gt;

&lt;p&gt;Let's spin up a Postgres container on our local machine.&lt;/p&gt;

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

docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; pg-internals-ctid &lt;span class="nt"&gt;-p&lt;/span&gt; 5438:5432 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;verysecret &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;verysecretuser &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ctidplayground  postgres


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

&lt;/div&gt;

&lt;p&gt;The command above spins up a docker container called &lt;code&gt;pg-internal-ctid&lt;/code&gt;, and publishes the container's internal port &lt;code&gt;5432&lt;/code&gt; to port &lt;code&gt;5438&lt;/code&gt; on your local machine with the default password, "verysecret",  a user named "verysecretuser" and a default db called "ctidplayground".&lt;/p&gt;

&lt;p&gt;If that command works, you should get some outputs on your command line, and the last line of that output should look like this:&lt;/p&gt;

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

2023-10-15 01:32:35.278 UTC [1] LOG:  database system is ready to accept connections.


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;:&lt;em&gt;If all of the above is foreign to you, drop a comment if you'd like me to start a series on docker.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Connect To Our Database
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;em&gt;You can skip the node project setups and run the queries directly in the docker container created above.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I assume you already know how to set up a typescript/nodejs project, so let's install a Postgres client for our database.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install --save postgres&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;src/index.ts&lt;/code&gt;, let's connect to our Postgres instance and create a "users" table.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;postgres&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// connect to the postgres instance&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;postgres&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createUsersTable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="s2"&gt;`
CREATE TABLE IF NOT EXISTS users (
    name varchar(100) NOT NULL,
    country varchar(100) NOT NULL,
    age integer NOT NULL
    )
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;the creation results&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createUsersTable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In the snippet above, the call to postgres() looks for the following environment variables for connection details:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PGHOST,  PGPORT, PGDATABASE, PGUSER, PGPASSWORD&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The snippet, then, creates a "users" table with columns &lt;code&gt;name, country, and age&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next, let's run our code to see if this works.
&lt;/h3&gt;

&lt;p&gt;In your &lt;code&gt;package.json&lt;/code&gt; file, add the following script.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"start:dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"PGHOST=localhost PGPORT=5438, PGDATABASE=ctidplayground PGUSER=verysecretuser PGPASSWORD=verysecret nodemon src/index.ts"&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;Now run the just-added script in your terminal&lt;/p&gt;

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

npm run start:dev


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

&lt;/div&gt;

&lt;p&gt;You should see the following logged in your console.&lt;/p&gt;

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

the creation results Result(0) []


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

&lt;/div&gt;

&lt;p&gt;Execute the following commands to confirm that the table was created.&lt;/p&gt;

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

docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt;  pg-internals-ctid psql &lt;span class="nt"&gt;-d&lt;/span&gt; ctidplayground &lt;span class="nt"&gt;-U&lt;/span&gt; verysecretuser


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

&lt;/div&gt;

&lt;p&gt;You should get the following prompt if everything works well.&lt;/p&gt;

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

&lt;span class="nv"&gt;ctidplayground&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;#&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now instruct Postgres to describe the user table with the command &lt;code&gt;\d users&lt;/code&gt;. Your output should be similar to the one in the screenshot below&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%2F3wb8oqari9cmclprp2sx.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%2F3wb8oqari9cmclprp2sx.png" alt="Describe users table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If something went wrong in our code, the output would be "Did not find any relation named users."&lt;/p&gt;

&lt;h3&gt;
  
  
  Now, let's insert some records into our user's table.
&lt;/h3&gt;

&lt;p&gt;Inserts three users into the database and calls the function in the main.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;insertUsers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="s2"&gt;`
        INSERT INTO users (name, country, age)
        VALUES(
            UNNEST(ARRAY['John Doe', 'Tom Williman', 'Billy Wilson']),
            UNNEST(ARRAY['Nigeria', 'Canada', 'USA']),
            UNNEST(ARRAY[32, 16, 16])
        )
        RETURNING *

    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;insersion results&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;insertUsers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;If that works,  the inserted users should be logged on your console like so:&lt;/p&gt;

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

insertion results Result(3) [
  { name: 'John Doe', country: 'Nigeria', age: 32 },
  { name: 'Tom Williman', country: 'Canada', age: 16 },
  { name: 'Billy Wilson', country: 'USA', age: 16 }


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Confirming The Characteristics Of CTID the Column
&lt;/h2&gt;

&lt;p&gt;Now, let's establish some characteristics of the CTID column.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Inserted columns have Sequential CTIDs, and the latest modified records always have higher CTID values.
&lt;/h3&gt;

&lt;p&gt;If you've already closed your Postgres shell, reopen it by running the previous docker exec command &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt;  pg-internals-ctid psql &lt;span class="nt"&gt;-d&lt;/span&gt; ctidplayground &lt;span class="nt"&gt;-U&lt;/span&gt; verysecretuser


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

&lt;/div&gt;

&lt;p&gt;Next, list the inserted record with their ctid by running the following query.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctid&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Your result should look like the one in the screenshot below, confirming the above facts.&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%2Fmrqw42z8z2pqnyw6f12f.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%2Fmrqw42z8z2pqnyw6f12f.png" alt="List users citds"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. CTIDs cannot be a unique identifier
&lt;/h3&gt;

&lt;p&gt;You cannot use a ctid as a unique identifier because an update operation or a full vacuum command after deletion can reassign new CTIDs existing rows. Let's see this in action. &lt;/p&gt;

&lt;p&gt;Run the following query against the user database.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'John Doe'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now select all records from the user's table again;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctid&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&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%2F2sjdv0yb5wjcslloljj5.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%2F2sjdv0yb5wjcslloljj5.png" alt="CTIDs after update"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;John Doe now has a CTID of (0, 4)  instead of the original (0, 1) ctid. The first record on the table is now that of Tom Williman with a CTID of (0,2).&lt;/p&gt;

&lt;p&gt;What happened to ctid (0,1)? Just by this observation, it can be established that Postgres doesn't mutate existing tuples. An update inserts a new record into the database and assigns the pointer to the old record to the new one. That leaves old records as garbage, which are automatically skipped during scans.&lt;/p&gt;

&lt;p&gt;To reclaim this space taken by the garbage, run the &lt;code&gt;VACUUM FULL;&lt;/code&gt; command in your pg shell. Making a &lt;code&gt;select *, ctid&lt;/code&gt; again. This should make our record completely sequential again; the ctid now starts from (0, 1).&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%2Flfqxv6ze8kqjianolv8q.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%2Flfqxv6ze8kqjianolv8q.png" alt="CTID reset after full vacuum"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To summarise the preceding paragraphs, the last modified record always has the highest ctid on a table, and ctids should never be used as identifiers. However, if there is a table without a updated_at column, or for some reason (e.g., during a bulk concurrent insert), multiple columns end up having the same &lt;code&gt;updated_at&lt;/code&gt; timestamp, the ctid column can be used to decide which record gets updated last.&lt;/p&gt;

&lt;h2&gt;
  
  
  USE CASE FOR CTID: Identifying the last/first inserted record in duplicates and Deduplication.
&lt;/h2&gt;

&lt;p&gt;Since we've observed that the last modified record always holds the highest ctid value, ctid is a great candidate for picking the latest record between two duplicates. At some point in our career, we would have to insert records from CSV files prone to human errors into a database table; Human errors like multiple entries of the same record. Whenever you have a guarantee that a database table is an append-only table (i.e., the records are immutable), selecting the record with the max ctid value would give you the latest value of that record.&lt;/p&gt;

&lt;p&gt;Let's modify our &lt;code&gt;insertUsers&lt;/code&gt; function to &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;insertUsers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// clear users table&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TRUNCATE users;`;

    // insert data into the users table;
    const result = await sql`
        INSERT INTO users (name, country, age)
        VALUES(
            UNNEST(ARRAY[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;John&lt;/span&gt; &lt;span class="nx"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;Tom&lt;/span&gt; &lt;span class="nx"&gt;Williman&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;Billy&lt;/span&gt; &lt;span class="nx"&gt;Wilson&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;John&lt;/span&gt; &lt;span class="nx"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;Tom&lt;/span&gt; &lt;span class="nx"&gt;Williman&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;Billy&lt;/span&gt; &lt;span class="nx"&gt;Wilson&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;]),
            UNNEST(ARRAY[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;Nigeria&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;Canada&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;USA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;Nigeria&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;Canada&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;USA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;]),
            UNNEST(ARRAY[32, 16, 16, 25, 21, 22])
        )
        RETURNING *

    `;
    console.log(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;insersion&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, result);
}
```
The snippet would insert six records into the database. Each unique record has one duplicate. Our table should now look like that in the screenshot below.


![Insert duplicate rows](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kgdo50tjbxcgoja598z8.png)


To select the latest duplicate record, use the query below

```sql
SELECT users.*, users.ctid  FROM  users INNER JOIN (SELECT max(ctid) AS max_ctid, name FROM users GROUP BY name) AS latest_duplicate
ON users.ctid = latest_duplicate.max_ctid;
```
The query above has a subquery that groups the records in the user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt; &lt;span class="nx"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;assuming&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;supposed&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;be&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="nx"&gt;identifier&lt;/span&gt; &lt;span class="nx"&gt;here&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt; &lt;span class="nx"&gt;From&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;we&lt;/span&gt; &lt;span class="nx"&gt;select&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;maximum&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="nx"&gt;ctid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;We&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="nx"&gt;subquery&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;original&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; table to select all users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nx"&gt;Your&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;look&lt;/span&gt; &lt;span class="nx"&gt;like&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;screenshot&lt;/span&gt; &lt;span class="nx"&gt;below&lt;/span&gt;

&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Deduplicating&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;ctid&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//dev-to-uploads.s3.amazonaws.com/uploads/articles/lss7qfzprtm4sy5r1d2v.png)&lt;/span&gt;
&lt;span class="nx"&gt;We&lt;/span&gt; &lt;span class="nx"&gt;ended&lt;/span&gt; &lt;span class="nx"&gt;up&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;last&lt;/span&gt; &lt;span class="nx"&gt;inserted&lt;/span&gt; &lt;span class="nx"&gt;duplicate&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="err"&gt;##&lt;/span&gt; &lt;span class="nx"&gt;What&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;You&lt;/span&gt; &lt;span class="nx"&gt;can&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t Guarantee That Your Database Table is an Append-Only Table

Solving the above problem in your database strictly depends on problem-solving skills. I would solve this problem by creating an intermediate table that guarantees immutability(I would probably use a Postgresql materialized view for this). I would always perform the Deduplication on the intermediate table and merge the Deduplication result with the final table. The algorithm would be something like this.

• insert records from the users CSV file into the `temp_users` table
• deduplicate by selecting max ctid record from the DB
• merge the deduplicated record with the existing users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;
    &lt;span class="err"&gt;•&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt; &lt;span class="nx"&gt;already&lt;/span&gt; &lt;span class="nx"&gt;exists&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; table, update the record with the latest record, `temp_users`
    • else insert a new user record.

## Why Can&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="nx"&gt;I&lt;/span&gt; &lt;span class="nx"&gt;DO&lt;/span&gt; &lt;span class="nx"&gt;This&lt;/span&gt; &lt;span class="nx"&gt;In&lt;/span&gt; &lt;span class="nx"&gt;Memory&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;

&lt;span class="nx"&gt;Sometimes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;you&lt;/span&gt; &lt;span class="nx"&gt;just&lt;/span&gt; &lt;span class="nx"&gt;can&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t. Let&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="nx"&gt;assume&lt;/span&gt; &lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;re to insert and dedupe a file with 10 million rows, and all you have is a virtual machine with just 1 gig of memory. Well, you could try doing that in memory which would end up eating up all the resources on your server. Or you could actually do the deduplication in the database, which was built to handle that kind of task.

Conclusion

Beyond just coding, having a deep understanding of how your tool works internally is always beneficial. If you&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;re&lt;/span&gt; &lt;span class="nx"&gt;interested&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;topics&lt;/span&gt; &lt;span class="nx"&gt;like&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;follow&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;more&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nx"&gt;If&lt;/span&gt; &lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;re a beginner, this article introduces some new database concepts. Feel free to request a deep dive on any of them; as usual, I appreciate all forms of feedback.


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

&lt;/div&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>sql</category>
      <category>programming</category>
    </item>
    <item>
      <title>Tool: Preview Markdown Document on Your Terminal</title>
      <dc:creator>Olufisayo Bamidele</dc:creator>
      <pubDate>Fri, 13 Oct 2023 14:53:16 +0000</pubDate>
      <link>https://dev.to/ngfizzy/tool-preview-markdown-document-on-your-terminal-2n8d</link>
      <guid>https://dev.to/ngfizzy/tool-preview-markdown-document-on-your-terminal-2n8d</guid>
      <description>&lt;p&gt;If you've recently been initiated into the Vim users cult and, like me,  you also don't like leaving your terminal that much anymore, you're probably being forced to do so whenever you want to do stuff like previewing your markdown documents. Sweat no more; &lt;code&gt;glow&lt;/code&gt; got you covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;On Mac, &lt;code&gt;brew install glow&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;Usage: &lt;code&gt;glow MyWonderfulDoc.md&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternative: markdown + lynx
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Markdown&lt;/strong&gt;: This is a command line tool for converting your MD files to HTML&lt;br&gt;
&lt;strong&gt;Lynx&lt;/strong&gt;: A terminal-based web browser for rendering HTML pages.&lt;/p&gt;
&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;lynx markdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;

&lt;p&gt;Convert your MD file to HTML with markdown and pipe the output to Lynx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markdown MyWonderfulDoc.md | lynx &lt;span class="nt"&gt;-stdin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>tooling</category>
      <category>vim</category>
      <category>productivity</category>
      <category>markdown</category>
    </item>
  </channel>
</rss>
