Use KPNG to Write Specialized kube-proxiers
Author: Lars Ekman (Ericsson)
The post will show you how to create a specialized service kube-proxy
style network proxier using Kubernetes Proxy NG
If the The The common use for a specialized proxier is assumed to be handling
external traffic for some use-case not supported by K8s. In that
case This starts the You can test this yourself outside your cluster. Please see the example
below. Now we start a backend that simply prints the updates from the
controller. A real backend would use some mechanism to load-balance traffic from
the external IPs to the endpoints. The (yes, that is the entire program) A real backend would of course be much more complex, but this
illustrates how You can have several backends connected to a The complete example can be found
As an example we implement an "all-ip" backend. It direct all traffic
for the externalIPs to a local endpoint, regardless of ports and upper
layer protocols. There is a
To direct all traffic from an external address to a local POD only
one iptables rule is
needed,
for instance; As you can see the addresses are in the call to the backend and all it
have to do is: A script doing that may look like: Assuming the JSON output above is stored in As this is an example we make it really simple for ourselves by using
a minor variation of the Where While service.kubernetes.io/service-proxy-name label in the Service
manifest. It is a tedious process to get new features into the Define a service that uses a specialized proxier
apiVersion: v1
kind: Service
metadata:
name: kpng-example
labels:
service.kubernetes.io/service-proxy-name: kpng-example
spec:
clusterIP: None
ipFamilyPolicy: RequireDualStack
externalIPs:
- 10.0.0.55
- 1000::55
selector:
app: kpng-alpine
ports:
- port: 6000
service.kubernetes.io/service-proxy-name
label is defined the
kube-proxy
will ignore the service. A custom controller can watch
services with the label set to it's own name, "kpng-example" in
this example, and setup specialized load-balancing.service.kubernetes.io/service-proxy-name
label is not
new,
but so far is has been quite hard to write a specialized proxier.ClusterIP
is not needed, so we use a "headless" service in this
example.Specialized proxier using kpng
kpng kube --service-proxy-name=kpng-example to-api
kpng
controller and tell it to watch only services
with the "kpng-example" service proxy name. The "to-api" parameter
will open a gRPC server for backends.$ kubectl apply -f kpng-example.yaml
$ kpng-json | jq # (this is the backend)
{
"Service": {
"Namespace": "default",
"Name": "kpng-example",
"Type": "ClusterIP",
"IPs": {
"ClusterIPs": {},
"ExternalIPs": {
"V4": [
"10.0.0.55"
],
"V6": [
"1000::55"
]
},
"Headless": true
},
"Ports": [
{
"Protocol": 1,
"Port": 6000,
"TargetPort": 6000
}
]
},
"Endpoints": [
{
"IPs": {
"V6": [
"1100::202"
]
},
"Local": true
},
{
"IPs": {
"V4": [
"11.0.2.2"
]
},
"Local": true
},
{
"IPs": {
"V4": [
"11.0.1.2"
]
}
},
{
"IPs": {
"V6": [
"1100::102"
]
}
}
]
}
Writing a backend
kpng-json
backend looks like this:package main
import (
"os"
"encoding/json"
"sigs.k8s.io/kpng/client"
)
func main() {
client.Run(jsonPrint)
}
func jsonPrint(items []*client.ServiceEndpoints) {
enc := json.NewEncoder(os.Stdout)
for _, item := range items {
_ = enc.Encode(item)
}
}
kpng
let you focus on load-balancing.kpng
controller, so
during development or debug it can be useful to let something like the
kpng-json
backend run in parallel with your real backend.Example
ip6tables -t nat -A PREROUTING -d 1000::55/128 -j DNAT --to-destination 1100::202
Local: true
ExternalIPs
xip=$(cat /tmp/out | jq -r .Service.IPs.ExternalIPs.V6[0])
podip=$(cat /tmp/out | jq -r '.Endpoints[]|select(.Local == true)|select(.IPs.V6 != null)|.IPs.V6[0]')
ip6tables -t nat -A PREROUTING -d $xip/128 -j DNAT --to-destination $podip
/tmp/out
(
kpng-json
backend above. Instead of just
printing, a program is called and the JSON output is passed as stdin
to that program. The backend can be tested stand-alone:CALLOUT=jq kpng-callout
jq
can be replaced with your own program or script. A script
may look like the example above. For more info and the complete
example please see
Summary
kube-proxy
and
it is not unlikely that they will be rejected, so to write a
specialized proxier may be the only option.