{"componentChunkName":"component---src-pages-markdown-remark-fields-slug-js","path":"/engineering/rest-api-kubernetes/","result":{"data":{"markdownRemark":{"id":"7f4aaef1-b2c3-5939-b72b-07b24a928765","excerpt":"This blog will help you get started on deploying your REST API in Kubernetes. First, we'll set up a local Kubernetes cluster, then create a simple API to deploy…","html":"<p>This blog will help you get started on deploying your REST API in Kubernetes. First, we'll set up a local Kubernetes cluster, then create a <a href=\"https://www.loginradius.com/blog/engineering/what-is-an-api/\">simple API</a> to deploy.</p>\n<p>There are already a lot of <a href=\"https://www.quora.com/What-are-the-best-resources-to-learn-Kubernetes\">free resources available</a> explaining basic Kubernetes concepts, so go check those out first if you haven't already. This blog is intended for beginners but assumes you already have a <a href=\"https://www.loginradius.com/blog/engineering/understanding-kubernetes/\">basic understanding of Kubernetes</a> and Docker concepts.</p>\n<h2 id=\"1-set-up-local-kubernetes\" style=\"position:relative;\"><a href=\"#1-set-up-local-kubernetes\" aria-label=\"1 set up local kubernetes permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>1. Set Up Local Kubernetes</h2>\n<p>There's a couple options for running Kubernetes locally, with the most popular ones including <a href=\"https://github.com/kubernetes/minikube\">minikube</a>, <a href=\"https://github.com/k3s-io/k3s\">k3s</a>, <a href=\"https://github.com/kubernetes-sigs/kind\">kind</a>, <a href=\"https://github.com/ubuntu/microk8s\">microk8s</a>. In this guide, any of these will work, but we will be using k3s because of the lightweight installation.</p>\n<p>Install <a href=\"https://github.com/rancher/k3d\">k3d</a>, which is a utility for running k3s. k3s will be running in Docker, so make sure you have that installed as well. We used k3d v4.0 in this blog.</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"0\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">curl -s https://raw.githubusercontent.com/rancher/k3d/main/install.sh | bash</span></code></pre>\n<p>Set up a cluster named test:</p>\n<ul>\n<li>The port flag is for mapping port 80 from our machine to port 80 on the k3s load balancer. This is needed later when we use ingress.</li>\n</ul>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"1\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">k3d cluster create test -p &quot;80:80@loadbalancer&quot;</span></code></pre>\n<p>Optionally, check that your kubeconfig got updated and the current context is correct:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"2\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">kubectl config view</span>\n<span class=\"grvsc-line\">kubectl config current-context</span></code></pre>\n<p>Optionally, confirm that k3s is running in Docker. There should be two containers up, one for k3s and the other for load balancing:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"3\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">docker ps</span></code></pre>\n<p>Make sure that all the pods are running. If they are stuck in pending status, it may be that there is not enough disk space on your machine. You can get more information by using the describe command:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"4\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">kubectl get pods -A</span>\n<span class=\"grvsc-line\">kubectl describe pods -A</span></code></pre>\n<p>There's a lot of kubectl commands you can try, so I recommend checking out the list of resources and being aware of their short names:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"5\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">kubectl api-resources</span></code></pre>\n<h2 id=\"2-create-a-simple-api\" style=\"position:relative;\"><a href=\"#2-create-a-simple-api\" aria-label=\"2 create a simple api permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>2. Create a Simple API</h2>\n<p>We will create a simple API using Express.js.</p>\n<p>Set up the project:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"6\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">mkdir my-backend-api && cd my-backend-api</span>\n<span class=\"grvsc-line\">touch server.js</span>\n<span class=\"grvsc-line\">npm init</span>\n<span class=\"grvsc-line\">npm i express --save</span></code></pre>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"7\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">// server.js</span>\n<span class=\"grvsc-line\">const express = require(&quot;express&quot;);</span>\n<span class=\"grvsc-line\">const app = express();</span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\">app.get(&quot;/user/:id&quot;, (req, res) =&gt; {</span>\n<span class=\"grvsc-line\">  const id = req.params.id;</span>\n<span class=\"grvsc-line\">  res.json({</span>\n<span class=\"grvsc-line\">    id,</span>\n<span class=\"grvsc-line\">    name: `John Doe #${id}`</span>\n<span class=\"grvsc-line\">  });</span>\n<span class=\"grvsc-line\">});</span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\">app.listen(80, () =&gt; {</span>\n<span class=\"grvsc-line\">  console.log(&quot;Server running on port 80&quot;);</span>\n<span class=\"grvsc-line\">});</span></code></pre>\n<p>Optionally, you can try running it if you have Node.js installed and test the endpoint /user/{id} with curl:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"8\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">node server.js</span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\">// request:</span>\n<span class=\"grvsc-line\">curl http://localhost:80/user/123</span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\">// response: {&quot;id&quot;:&quot;123&quot;,&quot;name&quot;:&quot;John Doe #123&quot;}</span></code></pre>\n<p>Next, add a Dockerfile and .dockerignore:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"9\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">// Dockerfile</span>\n<span class=\"grvsc-line\">FROM node:12</span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\">WORKDIR /usr/src/app</span>\n<span class=\"grvsc-line\">COPY package*.json ./</span>\n<span class=\"grvsc-line\">RUN npm i</span>\n<span class=\"grvsc-line\">COPY . .</span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\">EXPOSE 80</span>\n<span class=\"grvsc-line\">CMD [&quot;node&quot;, &quot;server.js&quot;]</span></code></pre>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"10\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">// .dockerignore</span>\n<span class=\"grvsc-line\">node_modules</span></code></pre>\n<p>Then, build the image and push it to the Docker Hub registry:</p>\n<ul>\n<li>If you want to skip this step, you can use the existing image <a href=\"https://hub.docker.com/r/andyy5/my-backend-api\">here</a>.</li>\n</ul>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"11\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">docker build -t &lt;YOUR_DOCKER_ID&gt;/my-backend-api .</span>\n<span class=\"grvsc-line\">docker push &lt;YOUR_DOCKER_ID&gt;/my-backend-api</span></code></pre>\n<h2 id=\"3-deploy\" style=\"position:relative;\"><a href=\"#3-deploy\" aria-label=\"3 deploy permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3. Deploy</h2>\n<p>Now, we deploy the image to our local Kubernetes cluster. We use the default namespace.</p>\n<p>Create a deployment:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"12\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">kubectl create deploy my-backend-api --image=andyy5/my-backend-api</span></code></pre>\n<ul>\n<li>Alternatively, create a deployment with a YAML file:</li>\n</ul>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"13\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">kubectl create -f deployment.yaml</span></code></pre>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"14\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">// deployment.yaml</span>\n<span class=\"grvsc-line\">apiVersion: apps/v1</span>\n<span class=\"grvsc-line\">kind: Deployment</span>\n<span class=\"grvsc-line\">metadata:</span>\n<span class=\"grvsc-line\">  name: my-backend-api</span>\n<span class=\"grvsc-line\">  labels:</span>\n<span class=\"grvsc-line\">    app: my-backend-api</span>\n<span class=\"grvsc-line\">spec:</span>\n<span class=\"grvsc-line\">  replicas: 1</span>\n<span class=\"grvsc-line\">  selector:</span>\n<span class=\"grvsc-line\">    matchLabels:</span>\n<span class=\"grvsc-line\">      app: my-backend-api</span>\n<span class=\"grvsc-line\">  template:</span>\n<span class=\"grvsc-line\">    metadata:</span>\n<span class=\"grvsc-line\">      labels:</span>\n<span class=\"grvsc-line\">        app: my-backend-api</span>\n<span class=\"grvsc-line\">    spec:</span>\n<span class=\"grvsc-line\">      containers:</span>\n<span class=\"grvsc-line\">      - name: my-backend-api</span>\n<span class=\"grvsc-line\">        image: andyy5/my-backend-api</span></code></pre>\n<p>Create a service:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"15\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">kubectl expose deploy my-backend-api --type=ClusterIP --port=80</span></code></pre>\n<ul>\n<li>Alternatively, create a service with a YAML file:</li>\n</ul>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"16\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">kubectl create -f service.yaml</span></code></pre>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"17\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">// service.yaml</span>\n<span class=\"grvsc-line\">apiVersion: v1</span>\n<span class=\"grvsc-line\">kind: Service</span>\n<span class=\"grvsc-line\">metadata:</span>\n<span class=\"grvsc-line\">  name: my-backend-api</span>\n<span class=\"grvsc-line\">  labels:</span>\n<span class=\"grvsc-line\">    app: my-backend-api</span>\n<span class=\"grvsc-line\">spec:</span>\n<span class=\"grvsc-line\">  type: ClusterIP</span>\n<span class=\"grvsc-line\">  ports:</span>\n<span class=\"grvsc-line\">  - port: 80</span>\n<span class=\"grvsc-line\">    protocol: TCP</span>\n<span class=\"grvsc-line\">    targetPort: 80</span>\n<span class=\"grvsc-line\">  selector:</span>\n<span class=\"grvsc-line\">    app: my-backend-api</span></code></pre>\n<p>Check that everything was created and the pod is running:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"18\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">kubectl get deploy -A</span>\n<span class=\"grvsc-line\">kubectl get svc -A</span>\n<span class=\"grvsc-line\">kubectl get pods -A</span></code></pre>\n<p>Once the pod is running, the API is accessible within the cluster only. One quick way to verify the deployment from our localhost is by doing port forwarding:</p>\n<ul>\n<li>Replace the pod name below with the one in your cluster</li>\n</ul>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"19\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">kubectl port-forward my-backend-api-84bb9d79fc-m9ddn 3000:80</span></code></pre>\n<ul>\n<li>Now, you can send a curl request from your machine</li>\n</ul>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"20\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">curl http://localhost:3000/user/123</span></code></pre>\n<p>To correctly manage external access to the services in a cluster, we need to use ingress. Close the port-forwarding and let's expose our API by creating an ingress resource.</p>\n<ul>\n<li>An ingress controller is also required, but k3d by default deploys the cluster with a Traefik ingress controller (listening on port 80).</li>\n<li>Recall that when we created our cluster, we set a port flag with the value \"80:80@loadbalancer\". If you missed this part, go back and create your cluster again.</li>\n</ul>\n<p>Create an Ingress resource with the following YAML file:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"21\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">kubectl create -f ingress.yaml</span>\n<span class=\"grvsc-line\">kubectl get ing -A</span></code></pre>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"22\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">// ingress.yaml</span>\n<span class=\"grvsc-line\">apiVersion: networking.k8s.io/v1</span>\n<span class=\"grvsc-line\">kind: Ingress</span>\n<span class=\"grvsc-line\">metadata:</span>\n<span class=\"grvsc-line\">  name: my-backend-api</span>\n<span class=\"grvsc-line\">  annotations:</span>\n<span class=\"grvsc-line\">    ingress.kubernetes.io/ssl-redirect: &quot;false&quot;</span>\n<span class=\"grvsc-line\">spec:</span>\n<span class=\"grvsc-line\">  rules:</span>\n<span class=\"grvsc-line\">  - http:</span>\n<span class=\"grvsc-line\">      paths:</span>\n<span class=\"grvsc-line\">      - path: /user/</span>\n<span class=\"grvsc-line\">        pathType: Prefix</span>\n<span class=\"grvsc-line\">        backend:</span>\n<span class=\"grvsc-line\">          service:</span>\n<span class=\"grvsc-line\">            name: my-backend-api</span>\n<span class=\"grvsc-line\">            port:</span>\n<span class=\"grvsc-line\">              number: 80</span></code></pre>\n<ul>\n<li>Now try it out!</li>\n</ul>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"\" data-index=\"23\"><code class=\"grvsc-code\"><span class=\"grvsc-line\">curl http://localhost:80/user/123</span></code></pre>\n<p>If you want to learn more on how to deploy using a managed Kubernetes service in the cloud, such as Google Kubernetes Engine, then check out the excellent guides on the <a href=\"https://kubernetes.io/docs/tutorials/stateless-application/expose-external-ip-address/\">official Kubernetes docs</a>.</p>\n<style class=\"grvsc-styles\">\n  .grvsc-container {\n    overflow: auto;\n    -webkit-overflow-scrolling: touch;\n    padding-top: 1rem;\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\n    padding-bottom: 1rem;\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\n    border-radius: 8px;\n    border-radius: var(--grvsc-border-radius, 8px);\n    font-feature-settings: normal;\n  }\n  \n  .grvsc-code {\n    display: inline-block;\n    min-width: 100%;\n  }\n  \n  .grvsc-line {\n    display: inline-block;\n    box-sizing: border-box;\n    width: 100%;\n    padding-left: 1.5rem;\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\n    padding-right: 1.5rem;\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\n  }\n  \n  .grvsc-line-highlighted {\n    background-color: var(--grvsc-line-highlighted-background-color, transparent);\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, transparent);\n  }\n  \n  .dark-default-dark {\n    background-color: #1E1E1E;\n    color: #D4D4D4;\n  }\n</style>","headings":[{"value":"1. Set Up Local Kubernetes","depth":2},{"value":"2. Create a Simple API","depth":2},{"value":"3. Deploy","depth":2}],"fields":{"slug":"/engineering/rest-api-kubernetes/"},"frontmatter":{"metatitle":null,"metadescription":null,"description":"Beginner guide on how to create and deploy a REST API in local Kubernetes.","title":"How to Deploy a REST API in Kubernetes","canonical":null,"date":"February 03, 2021","updated_date":null,"tags":["Kubernetes"],"coverImage":{"childImageSharp":{"fluid":{"aspectRatio":1.492537313432836,"src":"/static/3e013df11f99b320029a3e4aadafc0ea/2ad7f/cover.webp","srcSet":"/static/3e013df11f99b320029a3e4aadafc0ea/1c9b5/cover.webp 200w,\n/static/3e013df11f99b320029a3e4aadafc0ea/f1752/cover.webp 400w,\n/static/3e013df11f99b320029a3e4aadafc0ea/2ad7f/cover.webp 800w,\n/static/3e013df11f99b320029a3e4aadafc0ea/e7405/cover.webp 1200w","sizes":"(max-width: 800px) 100vw, 800px"}}},"author":{"id":"Andy Yeung","github":null,"bio":"Software Developer at LoginRadius with an interest in big data and basketball..","avatar":null}}}},"pageContext":{"id":"7f4aaef1-b2c3-5939-b72b-07b24a928765","fields__slug":"/engineering/rest-api-kubernetes/","__params":{"fields__slug":"engineering"}}},"staticQueryHashes":["1171199041","1384082988","1711371485","1753898100","2100481360","229320306","23180105","528864852"]}