Dynamic service discovery on top of Kubernetes – part 2 (self-registering etcd GO service)

This is part 2 of the article; an example of self-registering etcd service with key:value expiration if no backed renewal triggered.

You need to have the etcd independent deployment from previous article (part 1) running, find it https://zeding.ro/2018/09/08/dynamic-service-discovery-on-top-of-kubernetes-part-1-etcd-cluster/ or https://www.linkedin.com/pulse/dynamic-service-discovery-top-kubernetes-part-1-etcd-victor-laza/

Written in GO, the service is a simple pong JSON

#server.go
package main
import (
  "net/http"
  "net"
  "github.com/gin-gonic/gin"
  "os"
  "strings"
)

func main() {

	r := gin.Default()

	name, _ := os.Hostname()
	addresses, _ := net.LookupHost(name)

	msg := []string{"pong from", addresses[0]}
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": strings.Join(msg, " ")})
	})

	// Listen and serve on 0.0.0.0:80
	r.Run(":80")
}


#register.go
package main
import (
	"strings"
	"net/http"
	"time"
)

func main() {
        // register
        body := strings.NewReader(`value=${MY_POD_IP}:80&ttl=5`)
        req, err := http.NewRequest("PUT", "http://etcd-client-service:2379/v2/keys/pong-service/backends/${HOSTNAME}", body)
        if err != nil {
        // handle err
        }
        req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

        resp, err := http.DefaultClient.Do(req)
        if err != nil {
        // handle err
        }
        defer resp.Body.Close()

	// refresh loop
	for {
	// refresh ttl
        body := strings.NewReader(`ttl=5&refresh=true&prevExist=true`)
        req, err := http.NewRequest("PUT", "http://etcd-client-service:2379/v2/keys/pong-service/backends/${HOSTNAME}", body)
        if err != nil {
        // handle err
        }
        req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

        resp, err := http.DefaultClient.Do(req)
        if err != nil {
        // handle err
        }
        defer resp.Body.Close()
        time.Sleep(time.Second * 4)
	}
}


The 2 services have to be integrated in a Docker image and ran in parallel (supervisord would be an option).

Already created image can be found here: https://hub.docker.com/r/zeding/alpine-pong/tags/

If you want to run it on Kubernetes also bellow is the YAML template.

#pong.yaml
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: pong
spec:
  replicas: 2
  revisionHistoryLimit: 15
  template:
    metadata:
      labels:
        app: pong
    spec:
      containers:
        - name: pong
          - env:
            - name: MY_POD_IP
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: status.podIP
                  image: zeding/alpine-pong:1.0.0
          ports:
            - containerPort: 80

Create the deployment:

kubectl -n <your-namespace> create -f pong.yaml

And now you have a functional etcd self-registering GO service.

Here is what you should have in your namespace:

  kubectl -n <your-namespace> get pods -o wide

  NAME                    READY     STATUS              RESTARTS   AGE       IP              NODE
  infra-etcd-cluster-0    1/1       Running             0          1d        100.96.13.85    ip-172-20-42-48.eu-central-1.compute.internal
  infra-etcd-cluster-1    1/1       Running             0          1d        100.96.15.148   ip-172-20-44-47.eu-central-1.compute.internal
  infra-etcd-cluster-2    1/1       Running             0          1d        100.96.10.144   ip-172-20-43-86.eu-central-1.compute.internal
  pong-5fd5578995-6f9bk   1/1       Running             0          24s       100.96.7.220    ip-172-20-44-102.eu-central-1.compute.internal
  pong-5fd5578995-rz7h8   0/1       ContainerCreating   0          24s       <none>          ip-172-20-44-212.eu-central-1.compute.internal

Live monitoring your backends from etcd:

watch "etcdctl ls --recursive -p | grep -v '/$' | xargs -n 1 -I% sh -c 'echo -n %:; etcdctl get %;' | cut -d ':' -f2"

example output ** multiple endpoints ** IPs only:

100.96.7.220
100.96.7.218

Now you can create a script to fetch those from etcd and use for dynamic containers communications.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s