|
| 1 | +# High throughput tunnels |
| 2 | + |
| 3 | +Use this pattern when a customer has several long-lived TCP services and you |
| 4 | +want a single inlets uplink client process to carry them. |
| 5 | + |
| 6 | +The same approach also works well for RDP, VNC, SSH, Postgres, Redis, and other |
| 7 | +long-lived TCP connections. We will use RTSP for this tutorial. |
| 8 | + |
| 9 | +With [demux](/uplink/troubleshooting/#demux-mode) enabled, inlets-pro opens a |
| 10 | +separate websocket for each incoming TCP connection. |
| 11 | + |
| 12 | +This tutorial runs three [MediaMTX](https://github.com/bluenviron/mediamtx) |
| 13 | +instances on a private host and exposes their |
| 14 | +[RTSP](https://en.wikipedia.org/wiki/Real-Time_Streaming_Protocol) streams |
| 15 | +through one Uplink tunnel. |
| 16 | + |
| 17 | +```text |
| 18 | ++--------------------------------------+ +--------------------------------------+ |
| 19 | +| Private host | | Uplink cluster | |
| 20 | +| | | | |
| 21 | +| MediaMTX #1 rtsp://127.0.0.1:8551 | | Service: mediamtx.streams.svc | |
| 22 | +| MediaMTX #2 rtsp://127.0.0.1:8552 | | Ports: 8551, 8552, 8553 | |
| 23 | +| MediaMTX #3 rtsp://127.0.0.1:8553 | | | |
| 24 | +| | | Cluster clients or port-forward | |
| 25 | +| \ | / | | | |
| 26 | +| \ | / | | rtsp://mediamtx.streams:8551/stream | |
| 27 | +| inlets-pro client --demux |===>| rtsp://mediamtx.streams:8552/stream | |
| 28 | +| | | rtsp://mediamtx.streams:8553/stream | |
| 29 | ++--------------------------------------+ +--------------------------------------+ |
| 30 | +``` |
| 31 | + |
| 32 | +For testing, we will use a sample `demo.mp4` video file instead of a real |
| 33 | +camera feed. |
| 34 | + |
| 35 | +## Prerequisites |
| 36 | + |
| 37 | +- Inlets Uplink installed in your cluster |
| 38 | +- `inlets-pro` 0.11.11 or newer on the private host |
| 39 | +- `mediamtx` and `ffmpeg` on the private host |
| 40 | +- The tunnel plugin for the `inlets-pro` CLI |
| 41 | + |
| 42 | +Install the tunnel plugin: |
| 43 | + |
| 44 | +```bash |
| 45 | +inlets-pro plugin get tunnel |
| 46 | +``` |
| 47 | + |
| 48 | +You can install the host tools with |
| 49 | +[arkade](https://github.com/alexellis/arkade), or via your package manager: |
| 50 | + |
| 51 | +```bash |
| 52 | +arkade get inlets-pro |
| 53 | +arkade get mediamtx |
| 54 | +``` |
| 55 | + |
| 56 | +Set the values used below: |
| 57 | + |
| 58 | +```bash |
| 59 | +export NS="streams" |
| 60 | +export TUNNEL="mediamtx" |
| 61 | +export DOMAIN="uplink.example.com" |
| 62 | +``` |
| 63 | + |
| 64 | +## Create the tunnel |
| 65 | + |
| 66 | +Create one Tunnel resource with three TCP ports. |
| 67 | + |
| 68 | +Demux is enabled on the tunnel server with `uplink_demux=1`. |
| 69 | + |
| 70 | +```yaml |
| 71 | +apiVersion: uplink.inlets.dev/v1alpha1 |
| 72 | +kind: Tunnel |
| 73 | +metadata: |
| 74 | + name: mediamtx |
| 75 | + namespace: streams |
| 76 | +spec: |
| 77 | + licenseRef: |
| 78 | + name: inlets-uplink-license |
| 79 | + namespace: streams |
| 80 | + env: |
| 81 | + uplink_demux: "1" |
| 82 | + tcpPorts: |
| 83 | + - 8551 |
| 84 | + - 8552 |
| 85 | + - 8553 |
| 86 | +``` |
| 87 | +
|
| 88 | +Apply it: |
| 89 | +
|
| 90 | +```bash |
| 91 | +kubectl apply -f mediamtx-tunnel.yaml |
| 92 | +kubectl get -n $NS tunnel/$TUNNEL |
| 93 | +``` |
| 94 | + |
| 95 | +Wait for the tunnel server to start: |
| 96 | + |
| 97 | +```bash |
| 98 | +kubectl rollout status -n $NS deploy/$TUNNEL |
| 99 | +``` |
| 100 | + |
| 101 | +## Run MediaMTX |
| 102 | + |
| 103 | +On the private host, create a config directory: |
| 104 | + |
| 105 | +```bash |
| 106 | +mkdir -p ~/mediamtx-rtsp |
| 107 | +``` |
| 108 | + |
| 109 | +Create three MediaMTX config files. Each instance listens on a different RTSP |
| 110 | +port and has every other protocol disabled. |
| 111 | + |
| 112 | +```bash |
| 113 | +for i in 1 2 3; do |
| 114 | + port=$((8550+i)) |
| 115 | + |
| 116 | + cat > ~/mediamtx-rtsp/rtsp-$i.yml <<EOF |
| 117 | +logLevel: info |
| 118 | +rtsp: yes |
| 119 | +rtspTransports: [tcp] |
| 120 | +rtspAddress: :$port |
| 121 | +rtmp: no |
| 122 | +hls: no |
| 123 | +webrtc: no |
| 124 | +srt: no |
| 125 | +paths: |
| 126 | + stream: |
| 127 | + runOnInit: ffmpeg -hide_banner -loglevel warning -re -stream_loop -1 -i ./demo.mp4 -c:v libx264 -preset ultrafast -tune zerolatency -g 30 -keyint_min 30 -c:a aac -rtsp_transport tcp -f rtsp rtsp://127.0.0.1:$port/stream |
| 128 | + runOnInitRestart: yes |
| 129 | +EOF |
| 130 | +done |
| 131 | +``` |
| 132 | + |
| 133 | +Replace `./demo.mp4` with your own video file. |
| 134 | + |
| 135 | +Start the three servers: |
| 136 | + |
| 137 | +```bash |
| 138 | +cd ~/mediamtx-rtsp |
| 139 | + |
| 140 | +mediamtx rtsp-1.yml & |
| 141 | +mediamtx rtsp-2.yml & |
| 142 | +mediamtx rtsp-3.yml & |
| 143 | +``` |
| 144 | + |
| 145 | +Check that each stream is available locally: |
| 146 | + |
| 147 | +```bash |
| 148 | +for port in 8551 8552 8553; do |
| 149 | + ffprobe -v error \ |
| 150 | + -rtsp_transport tcp \ |
| 151 | + -select_streams v:0 \ |
| 152 | + -show_entries stream=codec_name \ |
| 153 | + -of default=nw=1:nk=1 \ |
| 154 | + rtsp://127.0.0.1:$port/stream |
| 155 | +done |
| 156 | +``` |
| 157 | + |
| 158 | +You should see `h264` three times. |
| 159 | + |
| 160 | +## Connect one client |
| 161 | + |
| 162 | +Get the tunnel token: |
| 163 | + |
| 164 | +```bash |
| 165 | +inlets-pro tunnel token $TUNNEL \ |
| 166 | + --namespace $NS > token.txt |
| 167 | +``` |
| 168 | + |
| 169 | +Run one inlets-pro client with three TCP upstreams: |
| 170 | + |
| 171 | +```bash |
| 172 | +inlets-pro uplink client \ |
| 173 | + --url wss://$DOMAIN/$NS/$TUNNEL \ |
| 174 | + --token-file ./token.txt \ |
| 175 | + --upstream 8551=127.0.0.1:8551 \ |
| 176 | + --upstream 8552=127.0.0.1:8552 \ |
| 177 | + --upstream 8553=127.0.0.1:8553 \ |
| 178 | + --demux |
| 179 | +``` |
| 180 | + |
| 181 | +The left side of each `--upstream` is the tunnel port in Kubernetes. The right |
| 182 | +side is the private host address. |
| 183 | + |
| 184 | +## Test from the cluster |
| 185 | + |
| 186 | +Create a temporary probe pod: |
| 187 | + |
| 188 | +```bash |
| 189 | +kubectl run -n $NS rtsp-probe \ |
| 190 | + --image=alpine:latest \ |
| 191 | + --restart=Never \ |
| 192 | + --command -- sleep 3600 |
| 193 | + |
| 194 | +kubectl exec -n $NS rtsp-probe -- \ |
| 195 | + apk add --no-cache ffmpeg |
| 196 | +``` |
| 197 | + |
| 198 | +Probe all three streams through the same tunnel: |
| 199 | + |
| 200 | +```bash |
| 201 | +for port in 8551 8552 8553; do |
| 202 | + kubectl exec -n $NS rtsp-probe -- \ |
| 203 | + ffprobe -v error \ |
| 204 | + -rtsp_transport tcp \ |
| 205 | + -select_streams v:0 \ |
| 206 | + -show_entries stream=codec_name \ |
| 207 | + -of default=nw=1:nk=1 \ |
| 208 | + rtsp://$TUNNEL.$NS.svc.cluster.local:$port/stream |
| 209 | +done |
| 210 | +``` |
| 211 | + |
| 212 | +You should see `h264` for each port. |
| 213 | + |
| 214 | +## View with port-forward |
| 215 | + |
| 216 | +The Service is cluster-local. To view the streams from your laptop, forward the |
| 217 | +three service ports: |
| 218 | + |
| 219 | +```bash |
| 220 | +kubectl port-forward -n $NS svc/$TUNNEL \ |
| 221 | + 8551:8551 \ |
| 222 | + 8552:8552 \ |
| 223 | + 8553:8553 |
| 224 | +``` |
| 225 | + |
| 226 | +Then open any of the streams with VLC, ffplay, or ffprobe: |
| 227 | + |
| 228 | +```bash |
| 229 | +ffplay -rtsp_transport tcp rtsp://127.0.0.1:8551/stream |
| 230 | +ffplay -rtsp_transport tcp rtsp://127.0.0.1:8552/stream |
| 231 | +ffplay -rtsp_transport tcp rtsp://127.0.0.1:8553/stream |
| 232 | +``` |
| 233 | + |
| 234 | +If a local port is already in use, change only the left-hand side: |
| 235 | + |
| 236 | +```bash |
| 237 | +kubectl port-forward -n $NS svc/$TUNNEL 18551:8551 |
| 238 | +ffplay -rtsp_transport tcp rtsp://127.0.0.1:18551/stream |
| 239 | +``` |
| 240 | + |
| 241 | +## Notes |
| 242 | + |
| 243 | +- Use `rtspTransports: [tcp]` when running several MediaMTX instances on one |
| 244 | + host. Otherwise each instance also tries to bind the default RTP and RTCP UDP |
| 245 | + ports. |
| 246 | +- Demux is useful for RTSP because every viewer gets its own websocket through |
| 247 | + the tunnel. |
| 248 | +- The tunnel remains cluster-local unless you expose it with a Service, |
| 249 | + Ingress, or LoadBalancer. |
0 commit comments