Occasionally when developing and deploying services to Kubernetes you may encounter a situation where you need to access an API that your service relies on from your local machine. Depending on your network setup or internal company policies you may realize: “S**t, that API is only accessible from within the Kubernetes cluster”.
This post shows how to deal with such a situation using a Kubernetes port forward and a special Docker container.
Kubernetes already contains a mechanism for port forwarding, that lets us forward a port on our local machine to the port of a running service inside Kubernetes. So assuming we are running some service in Kubernetes that depends on an external API.
kubectl port-forward some-service 8080
We can point a local port to that deployed service, and subsequently are able to reach this service via localhost:
curl http://localhost:8080 [...]
This first step brings us from our local machine into the Kubernetes cluster. For the next step to get us from a running container to the external service, we are going to use socat. Socat does something similar to
kubectl port-forward, it can forward a local TCP port to any host and port it can reach.
For example running:
socat TCP4-LISTEN:8080 TCP4:httpbin.org:80
will forward the local port 8080 to
httpbin.org:80 which then afterwards can be reached via:
So if we run combine these two components and run
socat as a service inside Kubernetes we can reach this external service that would normally only be reachable from Kubernetes.
Luckily with marcnuri/port-forward there already exists a pre-packed docker image, containing socat, where the configuration of the port forward can be controlled by environment variables.
kubectl run --env REMOTE_HOST=httpbin.org --env REMOTE_PORT=80 --env LOCAL_PORT=8080 --port 8080 --image marcnuri/port-forward my-pretty-port-forwarder
deploy this service listening on 8080 and forwarding all traffic that arrives at 8080 to httpbin.org port 80. If we combine this with a Kubernetes port forward:
kubectl port-forward my-pretty-port-forwarder 8080
we then are able to reach this service routed through kubernetes with
Doing this manually is fine for a single port forward, but you may run into trouble managing multiple forwards, because you need to start the service, then start the forward and finally, very important clean up again to not let any port forwarders dangling around. Also with multiple port you may get local port clashes.
I wrote a small wrapper in bash that:
- automatically detects a local available tcp port
- starts a
socatservice forwarding to a specific host and port combination
- automatically chooses an appropriate pod name to avoid name clashed
- starts a local port forward routing all request via the socat service to this host and port
- cleans up the pod if the command exits or is aborted
./k8s-port-forward -h httpbin.org -p 80 ================================================================================ forwarding tcp://localhost:35446 to tcp://httpbin.org:80 ================================================================================ ================================================================================ starting port forwarding pod 'port-forward-pelle-httpbinorg-80' pod/port-forward-pelle-httpbinorg-80 created ================================================================================ ================================================================================ waiting for port forwarding pod 'port-forward-pelle-httpbinorg-80' to be ready pod/port-forward-pelle-httpbinorg-80 condition met ================================================================================ ================================================================================ starting local port forward on port localhost:35446 to pod 'port-forward-pelle-httpbinorg-80:80' Forwarding from 127.0.0.1:35446 -> 80 Forwarding from [::1]:35446 -> 80 ^C ================================================================================ deleting port forwarding pod 'port-forward-pelle-httpbinorg-80 pod "port-forward-pelle-httpbinorg-80" deleted ================================================================================
The command is available via my CTHUL project here (Commons Tools Utilities Helper and Libraries )