Kubernetes UDP service not running with Google Cloud Load balancer

1/9/2018

I am trying to run a very simple UDP service in kubernetes on Google Cloud but am unable to access the port I am exposing to the internet. Here is the deployment and service file:

Deployment.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: udp-server-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        name: udp-server
    spec:
      containers:
      - name: udp-server
        image: jpoon/udp-server
        imagePullPolicy: Always
        ports:
        - containerPort: 10001
          protocol: UDP

Service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: udp-server-service
  labels:
    app: udp-server
spec:
  type: LoadBalancer
  ports:
  - port: 10001
    protocol: UDP
  selector:
    name: udp-server

This creates the loadbalancer in Google Cloud with the correct port exposed. Like so:

enter image description here

But when i try to access the port it's unaccessible. I have tried a few variations in GCE to expose udp port but none seem to be working.

➜  udp-example telnet 35.192.59.72 10001 
Trying 35.192.59.72...
telnet: connect to address 35.192.59.72: Connection refused
telnet: Unable to connect to remote host
-- Tameem Iftikhar
google-cloud-platform
google-compute-engine
google-kubernetes-engine
kubernetes
udp

1 Answer

2/14/2018

I tried to replicate this issue and I believe it is caused by the kubernetes configuration that is failing but how you are trying test it and possibly a missing firewall rule.

First of all consider that opening a connection to the target host:port using telnet will always fail because the protocol you are using is UDP and not TCP, therefore no connection will be created even if the server is up and running. And remember to create the corresponding firewall rule to be sure that the traffic is allowed.

I went to check the source image of the go server and of the go client, I took their go source code and they are working on localhost, but they are not running the client on a different instance and targeting the loadbalancer.

Therefore to understand if it was kubernetes failing I tried to make use to the very same yaml file with the nginx image and TCP and everything works.

In the very same GitHub page there is the example of the Dockerfile of the server that contains this line:

# Expose your port 
  EXPOSE 61243

Therefore I have ssh into the container (note ash and not bash) in order to understand what is actually going on and on which port the server it is listening and which port is exposed by Docker.

kubectl exec -it udp-server-deployment-8306694-zq412 -- /bin/ash

However keep in mind that:

The EXPOSE instruction does not actually publish the port. It functions as a type of documentation between the person who builds the image and the person who runs the container, about which ports are intended to be published.

Once I was inside the container I have run the client with the following output both targeting local host and the loadbalancer IP:

/go/src/udp # go run client.go
SERVER_ADDRESS=
ServerAddr=:10001
Received  udp-server-deployment-8306694-zq412  from  127.0.0.1:10001

Mening that the server was correctly working.

UPDATE

Connecting with the client from Cloud Shell was not working since the firewall rules do not applies there (my mistake).

To test it is correctly working spin up an instance, install go, run the client and check that it is working.

curl -O https://storage.googleapis.com/golang/go1.7.4.linux-amd64.tar.gz
tar xvf go1.7.4.linux-amd64.tar.gz
sudo chown -R root:root ./go
sudo mv go /usr/local
vi client.go
/usr/local/go/bin/go run client.go

where this is the content of client.go:

package main

import (
    "fmt"
    "net"
    "strconv"
    "time"
)

func checkError(err error) {
    if err != nil {
        fmt.Println("Error: ", err)
    }
}

func main() {
    Server := "[LOAD BALANCER IP]"
    fmt.Println("SERVER_ADDRESS=" + Server)

    serverAddr, err := net.ResolveUDPAddr("udp", Server+":10001")
    fmt.Println("ServerAddr=" + serverAddr.String())
    checkError(err)

    conn, err := net.DialUDP("udp", nil, serverAddr)
    checkError(err)
    defer conn.Close()

    buf := make([]byte, 1024)
    i := 0

    for {
        msg := strconv.Itoa(i)
        fmt.Fprintf(conn, msg)
        i++

        n, addr, err := conn.ReadFromUDP(buf)
        fmt.Println("Received ", string(buf[0:n]), " from ", addr)

        if err != nil {
            fmt.Println("Error: ", err)
        }

        time.Sleep(time.Second * 1)
    }
} 

Note that you have to replace the [LOAD BALANCER IP] placeolder with your value.

-- GalloCedrone
Source: StackOverflow