<?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: shubhamrai1993</title>
    <description>The latest articles on DEV Community by shubhamrai1993 (@shubhamrai1993).</description>
    <link>https://dev.to/shubhamrai1993</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%2F74146%2F8574f739-9845-4ac4-b98c-df05b5694e60.png</url>
      <title>DEV Community: shubhamrai1993</title>
      <link>https://dev.to/shubhamrai1993</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shubhamrai1993"/>
    <language>en</language>
    <item>
      <title>Kubernetes for data scientists - Hosting predictions</title>
      <dc:creator>shubhamrai1993</dc:creator>
      <pubDate>Sat, 08 Oct 2022 02:48:56 +0000</pubDate>
      <link>https://dev.to/shubhamrai1993/kubernetes-for-data-scientists-hosting-predictions-2ljj</link>
      <guid>https://dev.to/shubhamrai1993/kubernetes-for-data-scientists-hosting-predictions-2ljj</guid>
      <description>&lt;p&gt;In the last issue we went through the workflow of a data scientist and where exactly kubernetes can prove to be a useful base on which to build a platform for it.&lt;/p&gt;

&lt;p&gt;In this issue let’s go through a simple example to gain hands on experience for the same.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Docker - &lt;a href="https://docs.docker.com/get-docker/"&gt;https://docs.docker.com/get-docker/&lt;/a&gt; - To launch the cluster nodes as containers&lt;/li&gt;
&lt;li&gt;kubectl - &lt;a href="https://kubernetes.io/docs/tasks/tools/#kubectl"&gt;https://kubernetes.io/docs/tasks/tools/#kubectl&lt;/a&gt; - A cli tool to interact with a running kubernetes cluster.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting up a playground
&lt;/h2&gt;

&lt;p&gt;Before starting we need a playground to perform the demo. We will set up a kubernetes cluster on the local machine to do this. Although a cluster should contain multiple nodes for fault tolerance and high availability, we will mimic that behaviour using an awesome tool &lt;a href="https://kind.sigs.k8s.io/"&gt;kind&lt;/a&gt; (kubernetes-in-docker).&lt;/p&gt;

&lt;p&gt;At the end of this section we will have multiple containers running with each container acting as a separate cluster node.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;Follow the directions provided &lt;a href="https://kind.sigs.k8s.io/docs/user/quick-start/#installation"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Test the installation by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kind &lt;span class="nt"&gt;--version&lt;/span&gt;
kind version 0.14.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Launching the cluster
&lt;/h3&gt;

&lt;p&gt;Now we start a local cluster using &lt;code&gt;kind&lt;/code&gt;. We will create one control plane and two worker nodes. It is possible to have multiple of both.&lt;/p&gt;

&lt;p&gt;Kubernetes can have multiple control plane and worker nodes. All the centralised cluster management components live on the control plane nodes while user workload run on worker nodes. Read more &lt;a href="https://kubernetes.io/docs/concepts/overview/components/"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;First, create a &lt;code&gt;kind&lt;/code&gt; configuration in a file called &lt;code&gt;kind-config.yaml&lt;/code&gt;. You can find it &lt;a href="https://github.com/shubham-rai-tf/iris-classifier-kubernetes/blob/main/kind-config.yaml"&gt;here&lt;/a&gt;. This will define the structure of our cluster -&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Here, we have defined three nodes with one in the role of control plane and other two as worker nodes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Launch a cluster using this config. This might take some time. Make sure docker daemon is up on your system before executing this -&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kind create cluster &lt;span class="nt"&gt;--config&lt;/span&gt; kind-config.yaml
...
Thanks &lt;span class="k"&gt;for &lt;/span&gt;using kind! 😊
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Lets run &lt;code&gt;kubectl&lt;/code&gt; to make sure our cluster is up -&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:63122
CoreDNS is running at https://127.0.0.1:63122/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
...
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This tells us that the cluster is indeed up. We can also see the individual containers acting as nodes by executing &lt;code&gt;docker ps&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pmCvqc-U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/huzc01z7zpvxc1k8jt40.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pmCvqc-U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/huzc01z7zpvxc1k8jt40.png" alt="" width="880" height="685"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With our cluster up, let’s look at a broad architecture of what we are about to provision.&lt;/p&gt;

&lt;p&gt;Broadly speaking, we will host multiple replicas of our application inside the cluster and try to access them from outside with requests being load balanced across the different instances.&lt;/p&gt;

&lt;p&gt;To achieve this, there are a few kubernetes specific terminology that we need to be aware of - &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Pod&lt;/code&gt; - Pods are the smallest deployable units of computing that you can create and manage in Kubernetes. In our case, one instance of the application will be running inside one independent pod. These are ephemeral resources and control plane can move them across nodes if needed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Deployment&lt;/code&gt; - A deployment is useful where we want to have more than one replicas for an application. Kubernetes tries to always maintain the number of replicas to be equal to what is provided in a deployment. We will create three identical replicas for our application.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Service&lt;/code&gt; - A service is useful to load balance across a set of pods running on the cluster. Since pods are essentially ephemeral and can be replaced at any time, service provides a stable interface to access the pods running behind it. We will use a service to test our application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These three resources will enable us to host a scalable endpoint for serving our application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing the image
&lt;/h2&gt;

&lt;p&gt;With the cluster up, we can now deploy an application and test it out. We will create an application using the popular iris classifier dataset.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clone the repo
&lt;/h3&gt;

&lt;p&gt;The repo is available at &lt;a href="https://github.com/shubham-rai-tf/iris-classifier-kubernetes"&gt;https://github.com/shubham-rai-tf/iris-classifier-kubernetes&lt;/a&gt;. It already contains the code for building and serving predictions at &lt;code&gt;/iris/classify_iris&lt;/code&gt; endpoint using &lt;code&gt;fastapi&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the docker image
&lt;/h3&gt;

&lt;p&gt;We need to package this code in a docker image to prepare it for &lt;code&gt;kubernetes&lt;/code&gt;. A &lt;code&gt;Dockerfile&lt;/code&gt; is provided in the repo for doing that - &lt;a href="https://github.com/shubham-rai-tf/iris-classifier-kubernetes/blob/main/Dockerfile"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;Dockerfile&lt;/code&gt; specifies the image we will need to create a container hosting the prediction endpoints in a &lt;code&gt;uvicorn&lt;/code&gt; server on port 5000. More details about the syntax is available &lt;a href="https://docs.docker.com/engine/reference/builder/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Execute this command to build a local image -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; iris-classifier:poc
...
&lt;span class="nv"&gt;$ &lt;/span&gt;docker image &lt;span class="nb"&gt;ls
&lt;/span&gt;REPOSITORY       TAG  IMAGE ID      CREATED         SIZE
iris-classifier  poc  549913d5b1f9  12 seconds ago  737MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that the image has been successfully created with name &lt;code&gt;iris-classifier&lt;/code&gt; and tag &lt;code&gt;poc&lt;/code&gt;. We will now load this image into the cluster to use it inside the cluster&lt;/p&gt;

&lt;h3&gt;
  
  
  Loading the image
&lt;/h3&gt;

&lt;p&gt;This step is only needed because we  don’t have an image registry to pull the newly built image from. In production, the image should be hosted in a private registry like Dockerhub or AWS ECR and then pulled into the cluster directly&lt;/p&gt;

&lt;p&gt;Execute this command to load the locally built image to the cluster -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kind load docker-image iris-classifier:poc
Image: &lt;span class="s2"&gt;"iris-classifier:poc"&lt;/span&gt; with ID &lt;span class="s2"&gt;"sha256:549913d5b1f9456a4beedc73e04c3c0ad70da8691a8745a6b56a4f483c4f0862"&lt;/span&gt; not yet present on node &lt;span class="s2"&gt;"kind-worker2"&lt;/span&gt;, loading...
Image: &lt;span class="s2"&gt;"iris-classifier:poc"&lt;/span&gt; with ID &lt;span class="s2"&gt;"sha256:549913d5b1f9456a4beedc73e04c3c0ad70da8691a8745a6b56a4f483c4f0862"&lt;/span&gt; not yet present on node &lt;span class="s2"&gt;"kind-control-plane"&lt;/span&gt;, loading...
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify that the images have been loaded by listing images inside any of the three containers -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; kind-worker crictl images
IMAGE                                TAG    IMAGE ID         SIZE
docker.io/library/iris-classifier    poc    549913d5b1f94    753MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploying to kubernetes
&lt;/h2&gt;

&lt;p&gt;Kubernetes is essentially a declarative system. That means, we describe the contours of what we want to do and control plane components constantly drive the system towards reaching that state.&lt;/p&gt;

&lt;p&gt;To implement the architecture we discussed earlier, we will describe our intent in the form of a &lt;code&gt;yaml&lt;/code&gt; file which acts as a record of intent. In kubernetes parlance these are called &lt;code&gt;manifests&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All kubernetes manifests have the following fields - &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;apiVersion&lt;/code&gt; - Multiple resources are grouped together in same api versions. This provides a standardised way of deprecating or promoting a resource across kubernetes versions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kind&lt;/code&gt; - Identifies the exact object type that is to be created&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;metadata&lt;/code&gt; - Contains fields that act as metadata for the created object. The &lt;code&gt;apiVersion&lt;/code&gt;, &lt;code&gt;kind&lt;/code&gt; and &lt;code&gt;metadata.name&lt;/code&gt; fields together identify a unique resource inside a &lt;code&gt;namespace&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;spec&lt;/code&gt; - This field contains the specification for the object to be created. Every kind defines its own structure for this field with its own implementation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will use the manifests present in the repo in files within the &lt;code&gt;manifests&lt;/code&gt; directory &lt;a href="https://github.com/shubham-rai-tf/iris-classifier-kubernetes/tree/main/manifests"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It defines two kubernetes resources, &lt;code&gt;Deployment&lt;/code&gt; and &lt;code&gt;Service&lt;/code&gt; in &lt;code&gt;deployment.yaml&lt;/code&gt; and &lt;code&gt;service.yaml&lt;/code&gt; respectively. Let’s go through both the sections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&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;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# number of replicas&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&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;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="c1"&gt;# name of the image&lt;/span&gt;
      &lt;span class="pi"&gt;-&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;iris-classifier:poc&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;iris-classifier&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The deployment manifest in &lt;code&gt;deployment.yaml&lt;/code&gt; majorly defines the pod spec we want to deploy in terms of the image name and the number of replicas. Once we apply this, kubernetes will constantly take steps to maintain the number of replicas to what we specify here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service
&lt;/h3&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;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Type of the service&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;ClusterIP&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;# Port where the service will be accessible&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="m"&gt;8080&lt;/span&gt;
        &lt;span class="c1"&gt;# Port on the container where the traffic is to be forwarded&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;5000&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&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;iris-classifier&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service manifest in &lt;code&gt;service.yaml&lt;/code&gt; defines how to load balance across the replicas created by the deployment. Here we have defined how the port on service is to be mapped to port on the containers. Since our application runs at port 5000, the &lt;code&gt;targetPort&lt;/code&gt; is set to 5000. The service is exposed on port 8080. TCP traffic sent to 8080 will be load balanced across port 5000 on the containers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Applying the manifests
&lt;/h3&gt;

&lt;p&gt;Run the following command to apply the manifests to kubernetes -&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="s"&gt;$ kubectl apply -f manifests/&lt;/span&gt;
&lt;span class="s"&gt;deployment.apps/iris-classifier created&lt;/span&gt;
&lt;span class="s"&gt;service/iris-classifier created&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both resources are successfully created on the cluster. We can verify running the following commands -&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="s"&gt;$ kubectl get service iris-classifier&lt;/span&gt;
&lt;span class="s"&gt;NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE&lt;/span&gt;
&lt;span class="s"&gt;iris-classifier   ClusterIP   10.96.107.238   &amp;lt;none&amp;gt;        8080/TCP   37m&lt;/span&gt;
&lt;span class="s"&gt;$ kubectl get deployment iris-classifier&lt;/span&gt;
&lt;span class="s"&gt;NAME              READY   UP-TO-DATE   AVAILABLE   AGE&lt;/span&gt;
&lt;span class="s"&gt;iris-classifier   3/3     3            3           38m&lt;/span&gt;
&lt;span class="s"&gt;$ kubectl get pods&lt;/span&gt;
&lt;span class="s"&gt;NAME                               READY   STATUS    RESTARTS   AGE&lt;/span&gt;
&lt;span class="s"&gt;iris-classifier-5d97498ff9-77wqw   1/1     Running   0          39m&lt;/span&gt;
&lt;span class="s"&gt;iris-classifier-5d97498ff9-8twjm   1/1     Running   0          39m&lt;/span&gt;
&lt;span class="s"&gt;iris-classifier-5d97498ff9-znrz8   1/1     Running   0          39m&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, the service is exposed at port 8080 and three pods have been created as we specified.&lt;/p&gt;

&lt;p&gt;Modify &lt;code&gt;deployment.yaml&lt;/code&gt; to have 2 replicas instead of 3 and re-apply. Kubernetes will delete one of the replicas to match the spec.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making the prediction
&lt;/h3&gt;

&lt;p&gt;Now that the resources have been created in the cluster, we can verify our deployment by calling the model using the service endpoint. Since we are using a local setup, we will have to &lt;code&gt;port-forward&lt;/code&gt; the service to a port on the local machine.&lt;/p&gt;

&lt;p&gt;In a cloud provider setup, this service will be bound to an external load balancer which can be accessed from the internet if needed.&lt;/p&gt;

&lt;p&gt;Run the following command to perform port forwarding for the service -&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="s"&gt;$ kubectl port-forward services/iris-classifier &lt;/span&gt;&lt;span class="m"&gt;8080&lt;/span&gt;
&lt;span class="s"&gt;Forwarding from 127.0.0.1:8080 -&amp;gt; &lt;/span&gt;&lt;span class="m"&gt;5000&lt;/span&gt;
&lt;span class="s"&gt;Forwarding from [::1]:8080 -&amp;gt; &lt;/span&gt;&lt;span class="m"&gt;5000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can verify by calling the &lt;code&gt;/healthcheck&lt;/code&gt; endpoint on the model -&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="s"&gt;$ curl 'http://localhost:8080/healthcheck'&lt;/span&gt;
&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Iris&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;classifier&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ready!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To perform a test prediction, we will send a sample input to get a prediction -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="s1"&gt;'http://localhost:8080/iris/classify_iris'&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"sepal_length": 2, "sepal_width": 4, "petal_length": 2, "petal_width": 4}'&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"class"&lt;/span&gt;:&lt;span class="s2"&gt;"setosa"&lt;/span&gt;,&lt;span class="s2"&gt;"probability"&lt;/span&gt;:0.99&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get a prediction of class &lt;code&gt;setosa&lt;/code&gt; with a 99% probability. By running multiple of these predictions, we can verify that the requests are indeed being routed to different pods in a round robin fashion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;p&gt;Let’s remove all the kubernetes resources we had installed first -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl delete &lt;span class="nt"&gt;-f&lt;/span&gt; manifests/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will cleanup all the kubernetes resources we had created in the previous sections. Now we can take down the cluster as well -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kind delete cluster
Deleting cluster &lt;span class="s2"&gt;"kind"&lt;/span&gt; ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;In this issue we went through how to host a model as a callable service in kubernetes. Although this was a toy example where we built a docker image locally and ran it on a cluster running on the same machine, a typical production setup works on similar principles. A lot can be achieved with just these two resources.&lt;/p&gt;

&lt;p&gt;In the subsequent issues we will explore other more advanced features like multi tenancy and access control which become essential as we move towards day 2 operations.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>mlops</category>
      <category>devops</category>
    </item>
    <item>
      <title>Container networking: A tour</title>
      <dc:creator>shubhamrai1993</dc:creator>
      <pubDate>Sun, 24 May 2020 14:21:35 +0000</pubDate>
      <link>https://dev.to/shubhamrai1993/container-networking-a-tour-2ao0</link>
      <guid>https://dev.to/shubhamrai1993/container-networking-a-tour-2ao0</guid>
      <description>&lt;p&gt;Recently I was trying to move an application from one machine to another since the free trial for the earlier cloud provider was getting expired 😅. I took this opportunity to containerize this application and all it's upstream dependencies and learn something new along the way. I soon realised that containers, especially when it comes to networking, don't quite work the same way as that of a process. There are nuances to how you want to go about setting up your application deployments. There are multiple options to choose from which makes it all the more worse for a newbie. Let's go through those one at a time.&lt;/p&gt;

&lt;p&gt;We are not considering the docker swarm specific options here. That was not my use case and since the arrival of Kubernetes you have a better alternative.&lt;/p&gt;

&lt;p&gt;Docker provides several networking drivers for you to start your container with. But first let's find out where to look at all the networks that the docker has created for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker network &lt;span class="nb"&gt;ls
&lt;/span&gt;NETWORK ID          NAME                DRIVER              SCOPE
4071b98202b2        bridge              bridge              &lt;span class="nb"&gt;local
&lt;/span&gt;badd725b14a0        host                host                &lt;span class="nb"&gt;local
&lt;/span&gt;97a49ff283fd        none                null                &lt;span class="nb"&gt;local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can see that there are three types of networks docker daemon has already created for you. Each of these have their own names and network id. We will go into the various drivers and how to use them next.&lt;/p&gt;

&lt;h1&gt;
  
  
  Docker network drivers
&lt;/h1&gt;

&lt;p&gt;Docker has six networking options you can start your container with &lt;/p&gt;

&lt;h2&gt;
  
  
  1. Bridge networks
&lt;/h2&gt;

&lt;p&gt;Bridge is a link layer device that forwards traffic between two network segments. It provides an interface through which multiple disparate network segments can interact with each other and behave as a single cohesive unit. Docker uses the same idea to create a software bridge network that provides isolation between the docker host (the machine where the docker daemon is running) and the network that all the containers are running on. A bridge is created as soon as the docker daemon starts and all the containers launched on this host connect to this bridge by default. You can get the details for a network using it's name or id -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker network inspect bridge
&lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"Name"&lt;/span&gt;: &lt;span class="s2"&gt;"bridge"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Id"&lt;/span&gt;: &lt;span class="s2"&gt;"15f169f892264417a374f774cd48a332831e64d89f9e1182e958b47183d675c2"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Scope"&lt;/span&gt;: &lt;span class="s2"&gt;"local"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Driver"&lt;/span&gt;: &lt;span class="s2"&gt;"bridge"&lt;/span&gt;,
        &lt;span class="s2"&gt;"EnableIPv6"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
        &lt;span class="s2"&gt;"IPAM"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Driver"&lt;/span&gt;: &lt;span class="s2"&gt;"default"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Options"&lt;/span&gt;: null,
            &lt;span class="s2"&gt;"Config"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"Subnet"&lt;/span&gt;: &lt;span class="s2"&gt;"172.17.0.0/16"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"Gateway"&lt;/span&gt;: &lt;span class="s2"&gt;"172.17.0.1"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="s2"&gt;"Internal"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
        &lt;span class="s2"&gt;"Attachable"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
        &lt;span class="s2"&gt;"Ingress"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
        &lt;span class="s2"&gt;"ConfigFrom"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Network"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="s2"&gt;"ConfigOnly"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
        &lt;span class="s2"&gt;"Containers"&lt;/span&gt;: &lt;span class="o"&gt;{}&lt;/span&gt;,
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The thing to note here is IP Address Management (IPAM) config. All the containers attached to this network get an IP addresse from this subnet.&lt;/p&gt;

&lt;p&gt;Docker also provides you an option to create your own bridge network if required. These are called User Defined Bridge networks. Let's look at how to create one quickly -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker network create custom-network
34c9707787c96e777b3a2e0a2fac6284dd59da9753e3609c4007686b2e91fbbd

&lt;span class="nv"&gt;$ &lt;/span&gt;docker network &lt;span class="nb"&gt;ls
&lt;/span&gt;NETWORK ID          NAME                DRIVER              SCOPE
4071b98202b2        bridge              bridge              &lt;span class="nb"&gt;local
&lt;/span&gt;34c9707787c9        custom-network      bridge              &lt;span class="nb"&gt;local
&lt;/span&gt;badd725b14a0        host                host                &lt;span class="nb"&gt;local
&lt;/span&gt;97a49ff283fd        none                null                &lt;span class="nb"&gt;local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can see that a new bridge network named custom network has been defined for you. By default it uses the bridge driver so we don't need to specify that here. It has several advantages over the default network. User defined networks allow you to address another container on the same network using it's name along with the IP assigned to it. You also get better isolation for your containers since all other containers listen on the default bridge network. You can also configure your custom network to cater to your requirements which might have unintended effects on the default network.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Host networks
&lt;/h2&gt;

&lt;p&gt;Docker allows you to leverage the network of the host machine i.e. the machine where docker daemon is running to launch containers directly. This is similar to launching an application on a host bound to a particular port. The container is assigned no IP address of it's own. You can address the application running inside the container directly using &lt;code&gt;localhost&lt;/code&gt;. It is very useful when you want to just run an application similar to a local deployment. One significant limitation to this approach is that you can only use it from Linux.&lt;/p&gt;

&lt;p&gt;To use this option, just specify it while launching your container -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--network&lt;/span&gt; host &lt;span class="nt"&gt;-d&lt;/span&gt; nginx:latest
27b01648936d03ccb214dd59f0175a6fada94fb80da32fa7b485ee91c37f2ffc

&lt;span class="nv"&gt;$ &lt;/span&gt;curl localhost:80
&amp;lt;&lt;span class="o"&gt;!&lt;/span&gt;DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;&lt;span class="nb"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&amp;lt;title&amp;gt;Welcome to nginx!&amp;lt;/title&amp;gt;
&lt;span class="nt"&gt;-----------------------------------------&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  3. None
&lt;/h2&gt;

&lt;p&gt;As the name suggests, this is the docker network that does not exist. It is used to provide absolute network isolation to containers by disabling its networking. No incoming or outgoing connections are allowed to and from such containers respectively. To use this option simply supply this with &lt;code&gt;--network&lt;/code&gt; option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--network&lt;/span&gt; none &lt;span class="nt"&gt;-d&lt;/span&gt; nginx:latest
27b01648936d03ccb214dd59f0175a6fada94fb80da32fa7b485ee91c37f2ffc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can &lt;code&gt;inspect&lt;/code&gt; the container and verify that no IP address has been assigned to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Macvlan
&lt;/h2&gt;

&lt;p&gt;This option enables you to launch containers with MAC addresses assigned to them. Your container behaves as an independent device once launched on this network and can be connected to by legacy applications that require connection to a physical device. The option might be useful in some cases but docker recommends avoiding this method if possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Overlay
&lt;/h2&gt;

&lt;p&gt;This is a multi node network that enables you to create a distributed network over a set of hosts. It allows containers launched on separate hosts to communicate with each other. This option is mostly relevant to Docker Swarm so we won't go into detail here. But we will look into how Kubernetes handles a similar use case at the end.&lt;/p&gt;

&lt;p&gt;Docker also provides option to integrate with a specific networking driver provided by an external party through a plugin based interface.&lt;/p&gt;

&lt;h1&gt;
  
  
  Networking from a container's perspective
&lt;/h1&gt;

&lt;p&gt;Now let's take a slight detour and look at how networking looks from a container's perspective. This will be helpful when we take a look at the Kubernetes networking later. &lt;/p&gt;

&lt;p&gt;A container is basically a collection of Linux isolation technologies that enables it to behave as an independent entity of it's own. We are concerned with the networking namespace here. &lt;/p&gt;

&lt;p&gt;Every container is launched with a networking namespace of it's own by default. It means a separate IP address, routing table, network interface and other networking resources. The containers are initialized with a virtual networking interface connected to the underlying network. The container does not care about the specific network type it is connected to at the moment and can be shifted between networks while running. While initializing, a container is assigned it's own IP by the docker daemon. Docker daemon satisfies the need for a DHCP in this case. It has it's own port space independent from the host machine. Docker provides you a way to create a mapping between the two port spaces -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 nginx:latest
c5897567ef62f35c47757e5a511d60aefab2b6cf13eabeb8f4543a5ec29c35ab

&lt;span class="nv"&gt;$ &lt;/span&gt;curl localhost:8080
&amp;lt;&lt;span class="o"&gt;!&lt;/span&gt;DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;&lt;span class="nb"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&amp;lt;title&amp;gt;Welcome to nginx!&amp;lt;/title&amp;gt;
&lt;span class="nt"&gt;-----------------------------------------&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here the port &lt;code&gt;80&lt;/code&gt; inside the container has been mapped to &lt;code&gt;8080&lt;/code&gt; outside. This option won't work if you choose the &lt;code&gt;host&lt;/code&gt; networking driver since the host and the container already share their port spaces.&lt;/p&gt;

&lt;p&gt;You can also ask a container to share the networking namespace of an already running container while running it. This basically integrates the two containers at the networking level while being isolated in other aspects. Let's look at how to achieve this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; nginx-1 nginx:latest
27b01648936d03ccb214dd59f0175a6fada94fb80da32fa7b485ee91c37f2ffc

&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt; container:nginx-1 ubuntu
/# apt update&lt;span class="p"&gt;;&lt;/span&gt; apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; curl
/# curl localhost:80
&amp;lt;&lt;span class="o"&gt;!&lt;/span&gt;DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;&lt;span class="nb"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&amp;lt;title&amp;gt;Welcome to nginx!&amp;lt;/title&amp;gt;
&lt;span class="nt"&gt;-----------------------------------------&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We launched an &lt;code&gt;nginx&lt;/code&gt; container named &lt;code&gt;nginx-1&lt;/code&gt; followed by an &lt;code&gt;ubuntu&lt;/code&gt; container that shares its network. You can see that once inside the &lt;code&gt;ubuntu&lt;/code&gt; container we can hit the &lt;code&gt;nginx-1&lt;/code&gt; server on &lt;code&gt;localhost&lt;/code&gt; directly without addressing it through IP or container name. Both the containers share IP addresses and port spaces. Another container trying to use the same port already engaged by &lt;code&gt;nginx-1&lt;/code&gt; container will not be able to come up because of port conflict.&lt;/p&gt;

&lt;p&gt;You can assign a specific IP address to a docker container at the time of running it. The DNS settings are inherited directly from the daemon and can be overridden piecemeal if required. Docker docs has a really comprehensive &lt;a href="https://docs.docker.com/config/containers/container-networking/"&gt;page&lt;/a&gt; on how to achieve some of these.&lt;/p&gt;

&lt;h1&gt;
  
  
  How does Kubernetes do networking
&lt;/h1&gt;

&lt;p&gt;Pods are the basic building block of a kubernetes cluster. It is a collection of containers that logically belong together. This abstraction can be compared to a set of processes running on a single VM. All the containers in a pod can access each other using localhost. As you might have guessed this is achieved similarly to the last example we saw in the last section but there is a little more subtlety involved.&lt;/p&gt;

&lt;p&gt;All the pods on a single node are connected to a single bridge network that kubernetes names &lt;code&gt;cbr0&lt;/code&gt; or Custom Bridge. Whenever a new pod is created, a container called &lt;code&gt;pause&lt;/code&gt; is started. The only job of this container is to hold on to an IP address on the &lt;code&gt;bridge&lt;/code&gt; network. All other containers coming up in this pod are configured to share the networking namespace of this particular container. And thus all containers in the same pod can access each other using &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;Containers and the technologies built over it have revolutionized the way people build and deploy applications. But the basics of networking that these shiny new technologies leverage is as useful as ever. It was illuminating for me to look into the basics of these technologies and I hope anyone that comes across this finds it helpful as well.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>networking</category>
      <category>devops</category>
      <category>kubernetes</category>
    </item>
  </channel>
</rss>
