How I'm using KIND for local development

·

3 min read

How I'm using KIND for local development

Cover Photo by Maximilian Weisbecker on Unsplash

We will need


Docker

brew install docker

KIND ( Kubernetes in Docker )

brew install kind

dnsmasq for configuring a local Domain:

brew install dnsmasq

[Optional] Terratest for automatically testing our dummy app

brew install go

Configuring local domain using dnsmasq


Source : mjpitz blog post

First things first, we add desired local domain mapping and DNS servers to dnsmasq config

# /usr/local/etc/dnsmasq.conf
address=/YOUR.DOMAIN/127.0.0.1
server=8.8.8.8
server=8.8.4.4

After the changes are made - restart dnsmasq service to apply them

sudo brew services restart dnsmasq

Configure a DNS resolver to point to localhost address

sudo mkdir /etc/resolver/

cat <<EOF | sudo tee /etc/resolver/YOUR.DOMAIN
domain YOUR.DOMAIN
search YOUR.DOMAIN
nameserver 127.0.0.1
EOF

Restart mDNSResponder process

sudo killall -HUP mDNSResponder

add localhost IP address ( 127.0.0.1 ) to your DNS network settings

image.png

Creating and Configuring KIND


kind create cluster --config ./kind-config.yml

here’s a basic kind cluster configuration, you can find more advanced configuration options in KIND documentation, it can do almost anything you can imagine

# kind-config.yml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: artpav-dev-local
nodes:
  - role: control-plane
    image: kindest/node:v1.20.2@sha256:15d3b5c4f521a84896ed1ead1b14e4774d02202d5c65ab68f30eeaf310a3b1a7
    kubeadmConfigPatches:
      - |
        kind: InitConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            node-labels: "ingress-ready=true"
    extraPortMappings:
      - containerPort: 80
        hostPort: 80
        listenAddress: 127.0.0.1
      - containerPort: 443
        hostPort: 443
        listenAddress: 127.0.0.1

Deploying NGINX Ingress controller

Source : KIND Docs

deploy Nginx ingress with kind specific patches

kubectl apply \
    -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/kind/deploy.yaml

wait for the ingress controller pod to become available

kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=90s

Testing


We need to deploy an application to check that the local domain works as expected

# test-app.yml
---
kind: Pod
apiVersion: v1
metadata:
  name: foo-app
  labels:
    app: foo
spec:
  containers:
  - name: foo-app
    image: hashicorp/http-echo:0.2.3
    args:
    - "-text=foo"
---
kind: Pod
apiVersion: v1
metadata:
  name: bar-app
  labels:
    app: bar
spec:
  containers:
  - name: bar-app
    image: hashicorp/http-echo:0.2.3
    args:
    - "-text=bar"
---
kind: Service
apiVersion: v1
metadata:
  name: foo-service
spec:
  selector:
    app: foo
  ports:
  # Default port used by the image
  - port: 5678
---
kind: Service
apiVersion: v1
metadata:
  name: bar-service
spec:
  selector:
    app: bar
  ports:
  # Default port used by the image
  - port: 5678
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: foo.YOUR.DOMAIN
spec:
  rules:
  - host: foo.YOUR.DOMAIN
    http:
      paths:
      - path: /
        backend:
          serviceName: foo-service
          servicePort: 5678
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: bar.YOUR.DOMAIN
spec:
  rules:
  - host: bar.YOUR.DOMAIN
    http:
      paths:
      - path: /
        backend:
          serviceName: bar-service
          servicePort: 5678

The result of the above config file is

  • web app that returns the text that we provide with
  • ClusterIP service
  • ingresses pointing to our local domain.

Manual

deploy the application described in test-app.yml config snipped from above

kubectl apply -f test-app.yml

check that everything was deployed sucesfully and wait for everything to become ready

kubectl get all,ingress
`

check whether the domain endpoints are available

curl foo.YOUR.DOMAIN
curl bar.YOUR.DOMAIN

using Terratest

Terratest is the my personal framework of choise when it comes to infrastructure and the initial application tests when we need to ensure that an application returns what we expect before running a broader automation test suite.

// app_test.go
package test

import (
    "testing"
    "time"

    http_helper "github.com/gruntwork-io/terratest/modules/http-helper"
    "github.com/gruntwork-io/terratest/modules/k8s"
)

func TestLocalIngress(t *testing.T) {
    t.Parallel()

    kubeResourcePath := "./test-app.yml"

    options := k8s.NewKubectlOptions("", "", "foo-bar-app")

    defer k8s.KubectlDelete(t, options, kubeResourcePath)

    k8s.KubectlApply(t, options, kubeResourcePath)

    k8s.WaitUntilIngressAvailable(t, options, "foo.artpav.here", 10, 10*time.Second)
    k8s.WaitUntilIngressAvailable(t, options, "bar.artpav.here", 10, 10*time.Second)

    http_helper.HttpGetWithRetry(t, "http://foo.artpav.here", nil, 200, "foo", 30, 10*time.Second)
    http_helper.HttpGetWithRetry(t, "http://bar.artpav.here", nil, 200, "bar", 30, 10*time.Second)
}

Terratest code from app_test.go above will deploy the application from test-app.yml into the foo-bar-app namespace, wait for ingresses to become available, and throw a few GET calls at the endpoints, expecting to receive a body of foo and bar respectively

go test ./app_test.go -v -timeout 30m -json | jq .Output

Hope this helps you as much as it helps me :)