<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Calvin Tran</title>
    <description>The latest articles on Forem by Calvin Tran (@calvintran).</description>
    <link>https://forem.com/calvintran</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%2F260454%2F91891c44-cc91-4a04-acad-7999498700b0.jpeg</url>
      <title>Forem: Calvin Tran</title>
      <link>https://forem.com/calvintran</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/calvintran"/>
    <language>en</language>
    <item>
      <title>How do I deploy my machine learning model on local kubernetes</title>
      <dc:creator>Calvin Tran</dc:creator>
      <pubDate>Mon, 04 Nov 2019 04:29:09 +0000</pubDate>
      <link>https://forem.com/calvintran/how-do-i-deploy-my-machine-learning-model-on-local-kubernetes-234o</link>
      <guid>https://forem.com/calvintran/how-do-i-deploy-my-machine-learning-model-on-local-kubernetes-234o</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zptuM6-Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/v6qzybny79kwzwmepnuz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zptuM6-Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/v6qzybny79kwzwmepnuz.png" alt="Kubernetes Kind"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Being a data scientist / data engineer / data wizards …. we don’t want to spend so much our time and efforts to setup a development environment.&lt;/p&gt;

&lt;p&gt;With the help of Kind (Kubernetes in Docker), we can run a single cluster kubernetes in docker and even deploy our model into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why kind ?
&lt;/h2&gt;

&lt;p&gt;Yes, why kind while we have other options: minikube, microk8s, kubeadm,..&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Kind using docker while minikube requires virtualbox which consume more resources&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Kind can run on macbook, ubuntu, linux, window. Basically, if you have docker, you can run kind while microk8s only supports ubuntu.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In Kind, we have the cluster with single command&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Installation kind on my local
&lt;/h2&gt;

&lt;p&gt;Kind has available stable binaries. This is the easiest way to run kind on my local machine with CLI. According to the docs, we have to download the binary and and place in to PATH&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;curl &lt;span class="nt"&gt;-Lo&lt;/span&gt; ./kind https://github.com/kubernetes-sigs/kind/releases/download/v0.5.1/kind-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;-amd64&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ./kind
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mv&lt;/span&gt; ./kind /usr/local/bin/kind
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Set up kubectl
&lt;/h2&gt;

&lt;p&gt;We also need Kubernetes command-line tool, the installation can be found at the documentation (&lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/"&gt;https://kubernetes.io/docs/tasks/tools/install-kubectl/&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Make sure kubectl is running&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;kubectl version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once kind is ready, we run these commands to have kubeconfig in our environment&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;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;”&lt;span class="si"&gt;$(&lt;/span&gt;kind get kubeconfig-path&lt;span class="si"&gt;)&lt;/span&gt;”
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl cluster-info
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Hoooray !! everything is ready. Let’s jump to the deployment our first model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy iris model
&lt;/h2&gt;

&lt;p&gt;I always use iris model as a “Hello World” example for every project. The deployment.yaml manifest have been &lt;a href="https://gist.github.com/canhtran/33d7bf2161fe0452b4a0481f6093160a"&gt;prepared&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;iris-app&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;iris-app&lt;/span&gt;
    &lt;span class="na"&gt;tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
    &lt;span class="na"&gt;version&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;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;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;iris-app&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;2&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;iris-app&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;iris-app&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;canhtran/iris-svm-model-api:latest&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;containerPort&lt;/span&gt;&lt;span class="pi"&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;Apply deployment to the cluster.&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;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://gist.githubusercontent.com/canhtran/33d7bf2161fe0452b4a0481f6093160a/raw/3600ac616b03fb3e9ff3c81ced52e45a790cdc9b/iris-api-depployment.yaml

deployment.apps/iris-app created
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I create a service to expose the deployment.&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;kubectl expose deployment iris-app &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;LoadBalancer &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;iris-svc

service/iris-svc exposed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, we forward the port to our local machine for testing.&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;kubectl port-forward svc/iris-svc 5000:5000

Forwarding from 127.0.0.1:5000 -&amp;gt; 5000
Forwarding from &lt;span class="o"&gt;[&lt;/span&gt;::1]:5000 -&amp;gt; 500
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Test with curl command.&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;curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type:application/json"&lt;/span&gt; http://localhost:5000/iris/predict &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"payload":[6.2, 3.4, 5.4, 2.3]}'&lt;/span&gt;

HTTP/1.1 200 OK
Server: gunicorn/19.5.0
Date: Mon, 02 Sep 2019 02:51:04 GMT
Connection: close
Content-Type: application/json
Content-Length: 13

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"result"&lt;/span&gt;:2&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The service is up and running. Everything work perfectly.&lt;/p&gt;

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

&lt;p&gt;In my opinion, &lt;code&gt;KinD&lt;/code&gt; (Kubernetes in Docker) is the best alternative so far compare with minikube / microk8s. With a few commands, we’re able to have an environment to deploy our models.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>kubernetes</category>
      <category>deployment</category>
    </item>
    <item>
      <title>DataOps part 3: deploy Titanic to kubernetes</title>
      <dc:creator>Calvin Tran</dc:creator>
      <pubDate>Fri, 01 Nov 2019 03:38:09 +0000</pubDate>
      <link>https://forem.com/calvintran/dataops-part-3-deploy-titanic-to-kubernetes-48el</link>
      <guid>https://forem.com/calvintran/dataops-part-3-deploy-titanic-to-kubernetes-48el</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nuYTjuG0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/kc7zjtf72f9wooqrh8do.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nuYTjuG0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/kc7zjtf72f9wooqrh8do.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;Image Source: Blue Star Line&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the last part of dataops series, if you haven’t gone through the first and second ones, here is the link: &lt;a href="https://dev.to/calvintran/kubernetes-on-local-with-vagrant-and-kubeadm-5bed"&gt;part1&lt;/a&gt;, &lt;a href="https://dev.to/calvintran/dataops-part-2-sail-the-titanic-to-api-2p2o"&gt;part2&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisite
&lt;/h2&gt;

&lt;p&gt;We have to make sure that our kubernetes cluster has an ingress installed. If you have configured, here is the way how to do it&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For vagrant kubernetes
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/service-nodeport.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;For microk8s
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml

&lt;span class="nv"&gt;$ &lt;/span&gt;microk8s.enable ingress
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;Before starting with deployment process, let’s go through the process flow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SVBbURjC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2A75OTEtD4D6YeDLU0y8xGfw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SVBbURjC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2A75OTEtD4D6YeDLU0y8xGfw.png" alt="Kubernetes process flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That are the steps for deploy machine learning model on kubernete the easy way.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a deployment (&lt;em&gt;Deployment runs multiple replicas of your application and automatically replaces any instances that fail or become unresponsive — google k8s&lt;/em&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Expose deployment to services (&lt;em&gt;Services for expose your pods into network, there are different type of services like NodePort or LoadBalancer&lt;/em&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create ingress (&lt;em&gt;Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster, on another way, ingress help outside traffic access to our API).&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Create Deployment
&lt;/h3&gt;

&lt;p&gt;Deployment is an object in kubernetes that let you manage a set of identical pods. Without the deployment, you have to create, update, delete the pod manually&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;titanic-app&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;titanic-app&lt;/span&gt;
    &lt;span class="na"&gt;tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
    &lt;span class="na"&gt;version&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;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;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;titanic-app&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;2&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;titanic-app&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;titanic-app&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;canhtran/titanic-app-demo:latest&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;containerPort&lt;/span&gt;&lt;span class="pi"&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;I create two replicas (2 pods) for our api, the default port is flask port 5000.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Apply deployment&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; deployment-titanic.yaml
deployment.apps/titanic-app created
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can check the pods status.&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;kubectl get pod
NAME                           READY   STATUS              RESTARTS   AGE
titanic-app-6f4fdf8668-7nctk   1/1     Running             0          64s
titanic-app-6f4fdf8668-zhghw   0/1     ContainerCreating   0          64s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Expose services
&lt;/h3&gt;

&lt;p&gt;Unlike deployment is used to keep a set of pods running by creating pods from a template, service is used to allow network access to a set of pods.&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;kubectl expose deployment titanic-app &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;LoadBalancer &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5000
service/titanic-app exposed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Also get our service status&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;kubectl get svc
NAME          TYPE           CLUSTER-IP    EXTERNAL-IP   PORT&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;          AGE
titanic-app   LoadBalancer   10.102.5.24   &amp;lt;pending&amp;gt;     5000:32755/TCP   14
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Create ingress
&lt;/h3&gt;

&lt;p&gt;Finally, an ingress so that our service can be accessible from outside.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;extensions/v1beta1&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;Ingress&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;titanic-ingress&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;nginx.ingress.kubernetes.io/rewrite-target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
    &lt;span class="s"&gt;nginx.ingress.kubernetes.io/ssl-redirect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false"&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;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&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;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;titanic-app&lt;/span&gt;
            &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; ingress-titanic.yaml
ingress.extensions/titanic-ingress created
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Let's wrap it up and test our API&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type:application/json"&lt;/span&gt; http://10.98.11.201/predict &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"Pclass":3,"Sex":1,"Age":2,"Fare":7,"Embarked":0,"Title":1,"FamilySize":2,"IsAlone":0,"Age_Class":6, "FarePerPerson":3}'&lt;/span&gt;

HTTP/1.1 200 OK
Server: openresty/1.15.8.1
Date: Mon, 29 Jul 2019 12:00:10 GMT
Content-Type: application/json
Content-Length: 13
Connection: keep-alive

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"result"&lt;/span&gt;:1&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The API have been up and running as expected :)&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>helm</category>
      <category>data</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>DataOps part 2: Sail the Titanic to API</title>
      <dc:creator>Calvin Tran</dc:creator>
      <pubDate>Thu, 31 Oct 2019 03:59:40 +0000</pubDate>
      <link>https://forem.com/calvintran/dataops-part-2-sail-the-titanic-to-api-2p2o</link>
      <guid>https://forem.com/calvintran/dataops-part-2-sail-the-titanic-to-api-2p2o</guid>
      <description>&lt;p&gt;Before starting the deployment part, let’s prepare our model. If you haven’t had the running Kubernetes environment, refer to &lt;a href="https://dev.to/calvintran/kubernetes-on-local-with-vagrant-and-kubeadm-5bed"&gt;Part 1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To be short, in this part, I will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Training the model and persistence model using pickle.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create Restful API to serve model file.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I won’t go details about the EDA or training model in this article however I made a &lt;a href="https://nbviewer.jupyter.org/github/canhtran/data-devops-medium/blob/master/notebook/Titanic_survival.ipynb"&gt;notebook&lt;/a&gt; using Titanic dataset (can be found &lt;a href="https://www.kaggle.com/c/titanic"&gt;here&lt;/a&gt;). I used different machine learning methods to train the model and here is the results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--37FZ3Gok--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/jqn56zm4spi92s6phfeq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--37FZ3Gok--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/jqn56zm4spi92s6phfeq.png" alt="Model Comparison"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The winner belongs to &lt;code&gt;Random Forest &amp;amp; Decision Tree&lt;/code&gt; with accuracy 93.60%. I exported the random forest model to pickle format &lt;code&gt;model.pkl&lt;/code&gt;, we use this file from now onward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pickle&lt;/span&gt;
&lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_forest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'model.pkl'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'wb'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Flask framework
&lt;/h3&gt;

&lt;p&gt;One app.py file to serve the titanic model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pickle&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'model.pkl'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'rb'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/heartbeat'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;heartbeat&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'ok'&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/predict'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;np_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_dims&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;pred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pred&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'Model cannot predict with input'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pred&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let’s break it down&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'model.pkl'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'rb'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    
   &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I load the model to &lt;code&gt;app.model&lt;/code&gt; variable during starting flask server, the model will be stored in memory so that it doesn’t reload every time we send requests.&lt;/p&gt;

&lt;p&gt;I also create a GET endpoint name &lt;code&gt;heartbeat&lt;/code&gt; which returned ‘OK’ (200 response code). This using by kubernetes to check the liveness of api.&lt;/p&gt;

&lt;p&gt;The main part of service is from line 21. It gets the parameters from POST request and convert it to 2D numpy array&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;np_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_dims&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This step is required, machine learning model only accept the 2D numpy array as the input.&lt;/p&gt;

&lt;p&gt;Let’s do a quick test for our API&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run the server&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;python app.py
&lt;span class="k"&gt;*&lt;/span&gt; Serving Flask app &lt;span class="s2"&gt;"app"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;lazy loading&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; Environment: development
&lt;span class="k"&gt;*&lt;/span&gt; Debug mode: on
&lt;span class="k"&gt;*&lt;/span&gt; Running on &lt;span class="o"&gt;[&lt;/span&gt;http://127.0.0.1:5000/]&lt;span class="o"&gt;(&lt;/span&gt;http://127.0.0.1:5000/&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And &lt;code&gt;curl&lt;/code&gt; on another terminal.&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;curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type:application/json"&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;http://localhost:5000/predict]&lt;span class="o"&gt;(&lt;/span&gt;http://localhost:5000/predict&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"Pclass":3,"Sex":1,"Age":2,"Fare":7,"Embarked":0,"Title":1,"FamilySize":2,"IsAlone":0,"Age_Class":6, "FarePerPerson":3}'&lt;/span&gt;

HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 18
Server: Werkzeug/0.14.1 Python/3.6.2
Date: Sun, 14 Jul 2019 16:48:57 GMT

&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"result"&lt;/span&gt;: 1
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That’s awesome, our API has been working as expected. One more final step, dockerize our application and push it to dockerhub&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.6&lt;/span&gt;

&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; maintainer "Calvin Tran &amp;lt;trandcanh@gmail.com&amp;gt;"&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; APP_HOME /app&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$APP_HOME&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; $APP_HOME&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt $APP_HOME&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm &lt;/span&gt;requirements.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/list/&lt;span class="k"&gt;*&lt;/span&gt;


&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.py $APP_HOME&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; model.pkl $APP_HOME&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; gunicorn -w 3 -b 0.0.0.0:5000 --timeout=120 --reload "app:app"&lt;/span&gt;

&lt;span class="k"&gt;VOLUME&lt;/span&gt;&lt;span class="s"&gt; $APP_HOME&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;A few commands for pushing our image to docker hub.&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 build &lt;span class="nt"&gt;-t&lt;/span&gt; titanic-api &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker tag titanic-api canhtran/titanic-app-demo
&lt;span class="nv"&gt;$ &lt;/span&gt;docker push titanic-api canhtran/titanic-app-demo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The image will be available on &lt;a href="https://hub.docker.com"&gt;https://hub.docker.com&lt;/a&gt;. Alternatively, you can use my image.&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 pull canhtran/titanic-app-demo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;All right! Our preparation have been completed and ready to deploy to kubernetes.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>deployment</category>
      <category>kubernetes</category>
      <category>data</category>
    </item>
    <item>
      <title>DataOps: Kubernetes on local with vagrant and kubeadm</title>
      <dc:creator>Calvin Tran</dc:creator>
      <pubDate>Wed, 30 Oct 2019 15:37:10 +0000</pubDate>
      <link>https://forem.com/calvintran/kubernetes-on-local-with-vagrant-and-kubeadm-5bed</link>
      <guid>https://forem.com/calvintran/kubernetes-on-local-with-vagrant-and-kubeadm-5bed</guid>
      <description>&lt;p&gt;This series is a story about a data guy (which is me) with zero knowledge of Kubernetes pick up devops skill and successfully deploy a machine learning model.&lt;/p&gt;




&lt;p&gt;This series is a story about a data guy (which is me) with zero knowledge of Kubernetes pick up devops skill and successfully deploy a machine learning model.&lt;/p&gt;

&lt;p&gt;I have been working with Kubernetes for a few months, the cluster is set up by devops team and me myself is only a user. It’s time for me to take a break, build my own local cluster and understand deeply the whole process.&lt;/p&gt;

&lt;p&gt;If you guys don’t want to setup a k8s cluster from scratch, Microk8s is the best choice, you can get a full kubernetes system running in under 60s (like snap a finger)&lt;/p&gt;




&lt;h2&gt;
  
  
  Kubeadm vs Minikube.
&lt;/h2&gt;

&lt;p&gt;After researching, I found two candidates that good for my local cluster:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Minikube&lt;/code&gt; is the most popular way to deploy Kubernetes locally, it comes with support for a variety of hypervisors, set up is completely automatic within a few minutes. You really doesn’t need to take care anything.&lt;/p&gt;

&lt;p&gt;However, I want to get my hand dirty by doing some installation stuffs as well as understand how to set up Kubernetes cluster. Hence, one of the most frequency criticism that Kubernetes is very hard to install, Kubeadm really makes this easier.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w3kgux1k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pjsobi6w7l84hcjtblgb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w3kgux1k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pjsobi6w7l84hcjtblgb.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;TL;DR: There are 2 the basic steps that I did:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spin up a vagrant machine with Ubuntu 16.04 and at least 2GB of memory.&lt;/li&gt;
&lt;li&gt;Install and run kubeadm on single master cluster. (official guide can be found at &lt;a href="https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/"&gt;https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Vagrant
&lt;/h3&gt;

&lt;p&gt;A simple vagrant with Ubuntu 16.04 and 2Gb of memory is good enough for this series. You can copy the following code, put it to Vagrantfile and run it with vagrant up .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;Vagrant.configure("2") do |config|&lt;/span&gt;
  &lt;span class="s"&gt;config.vm.box = "bento/ubuntu-16.04"&lt;/span&gt;
  &lt;span class="s"&gt;config.vm.box_check_update = &lt;/span&gt;&lt;span class="no"&gt;false&lt;/span&gt;
  &lt;span class="s"&gt;config.vm.network "private_network", type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dhcp"&lt;/span&gt;
  &lt;span class="s"&gt;config.vm.provider "virtualbox" do |vb|&lt;/span&gt;
    &lt;span class="s"&gt;vb.gui = &lt;/span&gt;&lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="s"&gt;vb.memory = "2048"&lt;/span&gt;
    &lt;span class="s"&gt;vb.customize ["modifyvm", :id, "--cableconnected1", "on"]&lt;/span&gt;
  &lt;span class="s"&gt;end&lt;/span&gt;
  &lt;span class="s"&gt;config.vm.provision "docker"&lt;/span&gt;
&lt;span class="s"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Inside the VM, I do a couple of prerequisite steps. Turn off swap and make sure it won’t run again whenever I reboot my machine.&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;swapoff &lt;span class="nt"&gt;-a&lt;/span&gt; 
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'/ swap / s/^\(.*\)$/#\1/g'&lt;/span&gt; /etc/fstab
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Kubeadm
&lt;/h3&gt;

&lt;p&gt;I follow the official guide to install kubernetes on ubuntu.&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;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; apt-transport-https curl
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt;/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;apt-get update
&lt;span class="nv"&gt;$ &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; kubelet kubeadm kubectl
&lt;span class="nv"&gt;$ &lt;/span&gt;apt-mark hold kubelet kubeadm kubectl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The most difficult for me during the installation is to config the cgroup driver used by Kubelet. Luckily, A post from Liz Rice saves me, she did the kubeadm on vagrant with a little hack code, I copy it from her.&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;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'0,/ExecStart=/s//Environment="KUBELET_EXTRA_ARGS=--cgroup-driver=cgroupfs"\n&amp;amp;/'&lt;/span&gt; /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Initializing the master with IP which generated from DHCP, the reason for it is we can connect to our cluster from outside/host machine.&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;kubeadm init &lt;span class="nt"&gt;--apiserver-cert-extra-sans&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;IP Address&amp;gt;  &lt;span class="nt"&gt;--node-name&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To get IP Address, we use ifconfig command, the default network name is eth1&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;ifconfig eth1
eth1      Link encap:Ethernet  HWaddr 08:00:27:1c:68:af
          inet addr:172.28.128.4  Bcast:172.28.128.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fe1c:68af/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:21 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:2800 &lt;span class="o"&gt;(&lt;/span&gt;2.8 KB&lt;span class="o"&gt;)&lt;/span&gt;  TX bytes:2978 &lt;span class="o"&gt;(&lt;/span&gt;2.9 KB&lt;span class="o"&gt;)&lt;/span&gt;
          Interrupt:16 Base address:0xd240
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I make the kubectl works for vagrant user&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;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vagrant &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/vagrant/.kube
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; /etc/kubernetes/admin.conf /home/vagrant/.kube/config
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; vagrant&lt;span class="si"&gt;)&lt;/span&gt;:&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; vagrant&lt;span class="si"&gt;)&lt;/span&gt; /home/vagrant/.kube/config
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Installing a pod network add-on, we can find the list from third-party pod network providers here. I choose weave net because they support multiple architect (amd64, arm, arm64 and ppc641e)&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;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"https://cloud.weave.works/k8s/net?k8s-version=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl version | &lt;span class="nb"&gt;base64&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\n'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, I make the master isolate. By default, the cluster will not schedule pods on the master for security reason and we have only one node for both master / worker.&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;kubectl taint nodes &lt;span class="nt"&gt;--all&lt;/span&gt; node-role.kubernetes.io/master-
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Test our cluster is running with kubectl command.&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;kubectl get nodes
NAME      STATUS   ROLES    AGE     VERSION
vagrant   Ready    master   3h28m   v1.12.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Below is my sh file for install kubeadm. You also can find the Vagrantfile with full installation process at my repo: &lt;a href="https://github.com/canhtran/data-devops-medium/tree/master/vagrant.d"&gt;https://github.com/canhtran/data-devops-medium/tree/master/vagrant.d&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install kubernetes&lt;/span&gt;
apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; apt-transport-https curl
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt;/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;apt-get update
apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

&lt;span class="c"&gt;# kubelet requires swap off&lt;/span&gt;
swapoff &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="nb"&gt;sudo sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'/ swap / s/^\(.*\)$/#\1/g'&lt;/span&gt; /etc/fstab

&lt;span class="c"&gt;# Configure cgroup driver used by kubelet on Master Node&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'0,/ExecStart=/s//Environment="KUBELET_EXTRA_ARGS=--cgroup-driver=cgroupfs"\n&amp;amp;/'&lt;/span&gt; /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

&lt;span class="nv"&gt;IPADDRESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;ifconfig eth1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"inet addr"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;':'&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 2 | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 1&lt;span class="sb"&gt;`&lt;/span&gt;

&lt;span class="c"&gt;# Set up Kubernetes&lt;/span&gt;
kubeadm init &lt;span class="nt"&gt;--apiserver-cert-extra-sans&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$IPADDRESS&lt;/span&gt;  &lt;span class="nt"&gt;--node-name&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Make kubectl work for your non-root user&lt;/span&gt;
&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vagrant &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/vagrant/.kube
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; /etc/kubernetes/admin.conf /home/vagrant/.kube/config
&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; vagrant&lt;span class="si"&gt;)&lt;/span&gt;:&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; vagrant&lt;span class="si"&gt;)&lt;/span&gt; /home/vagrant/.kube/config

&lt;span class="c"&gt;# Installing a pod network add-on&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~vagrant/.kube/config
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"https://cloud.weave.works/k8s/net?k8s-version=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl version | &lt;span class="nb"&gt;base64&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\n'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Master Isolation&lt;/span&gt;
kubectl taint nodes &lt;span class="nt"&gt;--all&lt;/span&gt; node-role.kubernetes.io/master-
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Optional
&lt;/h3&gt;

&lt;p&gt;In order to get a kubectl on other computers (e.g. laptop) talk to the cluster, we need to copy the administrator kubeconfig file from master to our workstation.&lt;/p&gt;

&lt;p&gt;By default, the SSH access in vagrant is not enable for root. We have to copy the admin.conf file to be accessible by some users.&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;vagrant ssh &lt;span class="c"&gt;# Login to vagrant&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="c"&gt;# Get root access&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The from our machine, we can copy it via scp and test with get nodes command. (We have to change the server ip in admin.conf with the vagrant ip which is eth1 ip).&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;scp &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; 2222 vagrant@127.0.0.1:/home/vagrant/admin.conf &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;##### Edit and change the IP address ####&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl &lt;span class="nt"&gt;--kubeconfig&lt;/span&gt; ./admin.conf get nodes
NAME      STATUS   ROLES    AGE     VERSION
vagrant   Ready    master   3h28m   v1.12.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After we have a running cluster, let’s start with helm chart&lt;/p&gt;

&lt;h3&gt;
  
  
  Helm chart
&lt;/h3&gt;

&lt;p&gt;Helm chart is Kubernetes package management.&lt;/p&gt;

&lt;p&gt;Kubernetes can become very complex with a tons of objects that we need to handle: configmaps, services, pods, persistance volumnes. I should mention that the number of release of each service also a big headache for us to manage. These can be solved with Helm, which offers us a simple way to manage, deploy in one simple application with standard template (chart).&lt;/p&gt;

&lt;p&gt;Base on the documentation&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Helm uses a packaging format called charts. A chart is a collection of files that describe a related set of Kubernetes resources. A single chart might be used to deploy something simple, like a memcached pod, or something complex, like a full web app stack with HTTP servers, databases, caches, and so on.&lt;/p&gt;

&lt;p&gt;Charts are created as files laid out in a particular directory tree, then they can be packaged into versioned archives to be deployed&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Helm chart can be installed on a specific namespace. In this series, I will create a new namespace call data&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;kubectl create namespace data
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then I can install helm with simple command&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;helm init &lt;span class="nt"&gt;--tiller-namespace&lt;/span&gt; data
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Helm will create a Tiller pod in Kubernetes cluster under namespace data. To verify the success of our installation.&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;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; data 
NAME                             READY     STATUS    RESTARTS   AGE
tiller-deploy-5b9757b87c-s295g   1/1       Running   0          23s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That’s all, everything is ready for the next chapter.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>data</category>
    </item>
  </channel>
</rss>
