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.