STUNner Tutorial
Open a tunnel via STUNner
This tutorial shows how to tunnel an external connection via STUNner to a UDP service deployed into Kubernetes. The tutorial can also be used to quickly check a STUNner installation.
In this tutorial you will learn how to:
* configure a UDP service in Kubernetes,
* configure STUNner to expose the service to clients,
* use turncat to connect to the UDP service via STUNner,
* benchmark your cloud-setup with iperfv2.
Installation
Prerequisites
The tutorial assumes a fresh STUNner installation; see the STUNner installation and configuration
guide. Create a namespace called stunner if there is none. You must have
iperfv2 installed locally to run this tutorial.
Setup
In this tutorial we perform a quick Kubernetes/STUNner benchmark: we fire up an iperf server inside
the cluster and perform a speed test from the local console. We will use the
turncat client utility to tunnel test traffic to the iperf server via STUNner
acting as a STUN/TURN gateway.
You can easily implement a makeshift VPN with STUNner using a similar setup.
Server configuration
Set up an iperf server in the default Kubernetes namespace and wrap it in a Kubernetes
service called iperf-server.
cd stunner
kubectl apply -f docs/examples/simple-tunnel/iperf-server.yaml
This will start an Deployment that runs the iperf server and wraps it in a Kubernetes service
called iperf-server of type ClusterIP. Check this service and make sure that it is not exposed to
the outside world (i.e., EXTERNAL-IP is set to <none> by Kubernetes); this makes sure that the
only way to reach this service from the local iperf speed-test client is through STUNner.
kubectl get service iperf-server -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
iperf-server ClusterIP 10.120.5.36 <none> 5001/UDP,5001/TCP 19s app=iperf-server
STUNner configuration
Expose the service via the STUNner. The pre-compiled manifest below will create the required
GatewayClass and GateayConfig resources, fire up a Gateway listener at UDP:3478 and another one on
TCP:3478, and route client connections received on the gateways to the iperf-server
service.
kubectl apply -f docs/examples/simple-tunnel/iperf-stunner.yaml
For convenience, below is a dump of the Gateway and UDPRoute resources the manifests create. Note
that the UDPRoute specifies the iperf-server service as the backendRef, which makes sure that
STUNner will forward the client connections received in any of the Gateways to the iperf server.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: udp-gateway
namespace: stunner
spec:
gatewayClassName: stunner-gatewayclass
listeners:
- name: udp-listener
port: 3478
protocol: TURN-UDP
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: tcp-gateway
namespace: stunner
spec:
gatewayClassName: stunner-gatewayclass
listeners:
- name: tcp-listener
port: 3478
protocol: TURN-TCP
---
apiVersion: stunner.l7mp.io/v1
kind: UDPRoute
metadata:
name: iperf-server
namespace: stunner
spec:
parentRefs:
- name: udp-gateway
- name: tcp-gateway
rules:
- backendRefs:
- name: iperf-server
namespace: default
Check your configuration
Check whether you have all the necessary STUNner resources installed namespace.
kubectl get gatewayconfigs,gateways,udproutes.stunner.l7mp.io -n stunner
NAME REALM DATAPLANE AGE
gatewayconfig.stunner.l7mp.io/stunner-gatewayconfig stunner.l7mp.io default 139m
NAME CLASS ADDRESS PROGRAMMED AGE
gateway.gateway.networking.k8s.io/tcp-gateway stunner-gatewayclass 35.187.97.94 True 139m
gateway.gateway.networking.k8s.io/udp-gateway stunner-gatewayclass 35.205.10.190 True 139m
NAME AGE
udproute.stunner.l7mp.io/iperf-server 139m
You can also use the handy stunnerctl CLI tool to dump the running STUNner configuration for the
UDP gateway.
stunnerctl -n stunner config udp-gateway
Gateway: stunner/udp-gateway (loglevel: "all:INFO")
Authentication type: static, username/password: user-1/pass-1
Listeners:
- Name: stunner/udp-gateway/udp-listener
Protocol: TURN-UDP
Public address:port: 34.118.88.91:3478
Routes: [stunner/iperf-server]
Endpoints: [10.76.1.4, 10.80.4.47]
Likewise, the below will dump the config for the TCP gateway.
stunnerctl -n stunner config tcp-gateway
Gateway: stunner/tcp-gateway (loglevel: "all:INFO")
Authentication type: static, username/password: user-1/pass-1
Listeners:
- Name: stunner/tcp-gateway/tcp-listener
Protocol: TURN-TCP
Public address:port: 34.116.180.89:3478
Routes: [stunner/iperf-server]
Endpoints: [10.76.1.4, 10.80.4.47]
NOTE: It usually takes 30-60 seconds for Kubernetes to assign an external IP address to STUNner
gateways. As long as the external address is in <PENDING> status, STUNner exposes the Gateway on
a NodePort. Once Kubernetes finishes the exposition of the Gateway service, STUNner will pick up
the new address/port and update the config accordingly.
If in doubt, you can always query Kubernetes for the service statuses.
kubectl get -n stunner services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
stunner ClusterIP 10.0.9.70 <none> 3478/UDP 15m
tcp-gateway LoadBalancer 10.0.3.91 35.187.97.94 3478:31781/TCP 143m
udp-gateway LoadBalancer 10.0.14.218 35.205.10.190 3478:31048/UDP 143m
Run the benchmark
We will need to learn the ClusterIP assigned by Kubernetes to the iperf-server service: this will
be the peer address to which turncat will ask STUNner to relay the iperf test traffic.
export IPERF_ADDR=$(kubectl get svc iperf-server -o jsonpath="{.spec.clusterIP}")
Next, set up turncat to listen on UDP:127.0.0.1:5000 and tunnel connections from this listener
via the STUNner STUN/TURN listener udp-listener to the iperf server. Luckily, turncat is clever
enough to parse the running STUNner configuration from Kubernetes and set the
STUN/TURN server public address/port and the authentication credentials accordingly.
./turncat --log=all:INFO udp://127.0.0.1:5000 k8s://stunner/udp-gateway:udp-listener \
udp://$IPERF_ADDR:5001
The most important part here is the TURN meta-URI: k8s://stunner/udp-gateway:udp-listener
instructs turncat to look for the Gateway called udp-gateway in the stunner namespace and
create a connection to the TURN listener called udp-listener of the Gateway.
Fire up an iperf client from another terminal that will connect to STUNner via turncat and start
the benchmark.
iperf -c localhost -p 5000 -u -i 1 -l 100 -b 800000 -t 10
If successful, the iperf server logs should contain the benchmark results.
kubectl logs $(kubectl get pods -l app=iperf-server -o jsonpath='{.items[0].metadata.name}')
------------------------------------------------------------
Server listening on UDP port 5001 with pid 1
Read buffer size: 1.44 KByte (Dist bin width= 183 Byte)
UDP buffer size: 208 KByte (default)
------------------------------------------------------------
[ 1] local 10.116.2.30%eth0 port 5001 connected with 10.116.1.21 port 56439 (peer 2.1.7)
[ ID] Interval Transfer Bandwidth Jitter Lost/Total Latency avg/min/max/stdev PPS inP NetPwr
...
[ 1] 0.0000-9.9204 sec 977 KBytes 807 Kbits/sec 1.426 ms 0/10003 (0%) 14.256/10.791/97.428/ 4.993 ms 1008 pps 1.40 KByte 7.07
The results show that we have managed to send 1000 packets/sec through STUNner to the iperf server without packet loss, at an average one-way latency of 14.2 ms and 1.426 ms jitter. Not bad from a Kubernetes cluster running in some remote datacenter!
Repeating the test, this time with a STUN/TURN over TCP, casts a somewhat different picture. Notice
the new meta-URI: k8s://stunner/tcp-gateway:tcp-listener to select the TURN server exposed on
TCP forturncat.
./turncat --log=all:INFO udp://127.0.0.1:5000 k8s://stunner/tcp-gateway:tcp-listener \
udp://$IPERF_ADDR:5001
Run the benchmark again at 10kpps and watch the logs.
iperf -c localhost -p 5000 -u -l 100 -b 8000000 -o /dev/null -t 10 && \
kubectl logs $(kubectl get pods -l app=iperf-server -o jsonpath='{.items[0].metadata.name}') | tail -n 1
[ 3] 0.0000-9.9365 sec 9.41 MBytes 7.94 Mbits/sec 0.085 ms 1361/100003 (1.4%) 148.261/21.098/454.266/73.704 ms 9927 pps 144 KByte 6.70
It seems that average latency has jumped to 148 ms, with a max latency of close to 460 ms! That's why you should avoid TCP at all cost in real-time communications.
Cleaning up
Stop turncat and wipe all Kubernetes configuration.
kubectl delete -f docs/examples/simple-tunnel/iperf-server.yaml
kubectl delete -f docs/examples/simple-tunnel/iperf-stunner.yaml