Cover Photo by Maximilian Weisbecker on Unsplash
We will need
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
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 :)