Skip to content

Commit 77f45e3

Browse files
authored
Use service annotations to choose IPVS scheduling method (#207)
Fixes #6
1 parent 6d43268 commit 77f45e3

File tree

2 files changed

+66
-10
lines changed

2 files changed

+66
-10
lines changed

Documentation/README.md

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,19 +216,45 @@ kubectl annotate service my-service "kube-router.io/service.hairpin="
216216
### Direct server return
217217

218218
You can enable DSR(Direct Server Return) functionality per service. When enabled service endpoint
219-
will directly respond to the client. When DSR is enabled Kube-router will uses LVS's tunneling mode to achive this.
219+
will directly respond to the client by passign the service proxy. When DSR is enabled Kube-router
220+
will uses LVS's tunneling mode to achieve this.
220221

221-
To enable DSR you need to annotate service with `kube-router.io/service.dsr=tunnel` annotation.
222+
To enable DSR you need to annotate service with `kube-router.io/service.dsr=tunnel` annotation. For e.g.
222223

223-
In the current implementation althouh annotation is enabled, DSR will be applicable only to the external IP's.
224+
```
225+
kubectl annotate service my-service "kube-router.io/service.dsr=tunnel"
226+
```
227+
228+
**In the current implementation when annotation is applied on the service, DSR will be applicable only to the external IP's.**
229+
230+
**Also when DSR is used, current implementation does not support port remapping. So you need to use same port and target port for the service**
224231

225232
You will need to enable `hostIPC: true` and `hostPID: true` in kube-router daemonset manifest.
226233
Also host path `/var/run/docker.sock` must be made a volumemount to kube-router.
227234

228235
Above changes are required for kube-router to enter pod namespeace and create ipip tunnel in the pod and to
229236
assign the external IP to the VIP.
230237

231-
For an e.g manifest please look at [manifest](../daemonset/kubeadm-kuberouter-all-features-dsr.yaml) with DSR requirments enabled.
238+
For an e.g manifest please look at [manifest](../daemonset/kubeadm-kuberouter-all-features-dsr.yaml) with DSR requirements enabled.
239+
240+
### Load balancing Scheduling Algorithms
241+
242+
Kube-router uses LVS for service proxy. LVS support rich set of [scheduling alogirthms](http://kb.linuxvirtualserver.org/wiki/IPVS#Job_Scheduling_Algorithms). You can annotate
243+
the service to choose one of the scheduling alogirthms. When a service is not annotated `round-robin` scheduler is selected by default
244+
245+
```
246+
For least connection scheduling use:
247+
kubectl annotate service my-service "kube-router.io/service.scheduler=lc"
248+
249+
For round-robin scheduling use:
250+
kubectl annotate service my-service "kube-router.io/service.scheduler=rr"
251+
252+
For source hashing scheduling use:
253+
kubectl annotate service my-service "kube-router.io/service.scheduler=sh"
254+
255+
For destination hashing scheduling use:
256+
kubectl annotate service my-service "kube-router.io/service.scheduler=dh"
257+
```
232258

233259
## BGP configuration
234260

app/controllers/network_services_controller.go

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ type serviceInfo struct {
9797
nodePort int
9898
sessionAffinity bool
9999
directServerReturn bool
100+
scheduler string
100101
directServerReturnMethod string
101102
hairpin bool
102103
externalIPs []string
@@ -272,7 +273,7 @@ func (nsc *NetworkServicesController) syncIpvsServices(serviceInfoMap serviceInf
272273
}
273274

274275
// create IPVS service for the service to be exposed through the cluster ip
275-
ipvsClusterVipSvc, err := ipvsAddService(svc.clusterIP, protocol, uint16(svc.port), svc.sessionAffinity)
276+
ipvsClusterVipSvc, err := ipvsAddService(svc.clusterIP, protocol, uint16(svc.port), svc.sessionAffinity, svc.scheduler)
276277
if err != nil {
277278
glog.Errorf("Failed to create ipvs service for cluster ip: %s", err.Error())
278279
continue
@@ -288,7 +289,7 @@ func (nsc *NetworkServicesController) syncIpvsServices(serviceInfoMap serviceInf
288289
if vip = nsc.nodeIP; nsc.nodeportBindOnAllIp {
289290
vip = net.ParseIP("127.0.0.1")
290291
}
291-
ipvsNodeportSvc, err = ipvsAddService(vip, protocol, uint16(svc.nodePort), svc.sessionAffinity)
292+
ipvsNodeportSvc, err = ipvsAddService(vip, protocol, uint16(svc.nodePort), svc.sessionAffinity, svc.scheduler)
292293
if err != nil {
293294
glog.Errorf("Failed to create ipvs service for node port due to: %s", err.Error())
294295
continue
@@ -310,7 +311,7 @@ func (nsc *NetworkServicesController) syncIpvsServices(serviceInfoMap serviceInf
310311
// without a VIP http://www.austintek.com/LVS/LVS-HOWTO/HOWTO/LVS-HOWTO.routing_to_VIP-less_director.html
311312
// to avoid martian packets
312313
for _, externalIP := range svc.externalIPs {
313-
ipvsExternalIPSvc, err := ipvsAddFWMarkService(net.ParseIP(externalIP), protocol, uint16(svc.port), svc.sessionAffinity)
314+
ipvsExternalIPSvc, err := ipvsAddFWMarkService(net.ParseIP(externalIP), protocol, uint16(svc.port), svc.sessionAffinity, svc.scheduler)
314315
if err != nil {
315316
glog.Errorf("Failed to create ipvs service for External IP: %s due to: %s", externalIP, err.Error())
316317
continue
@@ -612,6 +613,19 @@ func buildServicesInfo() serviceInfoMap {
612613
svcInfo.directServerReturn = true
613614
svcInfo.directServerReturnMethod = dsrMethod
614615
}
616+
svcInfo.scheduler = ipvs.RoundRobin
617+
schedulingMethod, ok := svc.ObjectMeta.Annotations["kube-router.io/service.scheduler"]
618+
if ok {
619+
if schedulingMethod == ipvs.RoundRobin {
620+
svcInfo.scheduler = ipvs.RoundRobin
621+
} else if schedulingMethod == ipvs.LeastConnection {
622+
svcInfo.scheduler = ipvs.LeastConnection
623+
} else if schedulingMethod == ipvs.DestinationHashing {
624+
svcInfo.scheduler = ipvs.DestinationHashing
625+
} else if schedulingMethod == ipvs.SourceHashing {
626+
svcInfo.scheduler = ipvs.SourceHashing
627+
}
628+
}
615629
copy(svcInfo.externalIPs, svc.Spec.ExternalIPs)
616630
svcInfo.sessionAffinity = (svc.Spec.SessionAffinity == "ClientIP")
617631
_, svcInfo.hairpin = svc.ObjectMeta.Annotations["kube-router.io/service.hairpin"]
@@ -941,7 +955,7 @@ func ipvsSetPersistence(svc *ipvs.Service, p bool) {
941955
}
942956
}
943957

944-
func ipvsAddService(vip net.IP, protocol, port uint16, persistent bool) (*ipvs.Service, error) {
958+
func ipvsAddService(vip net.IP, protocol, port uint16, persistent bool, scheduler string) (*ipvs.Service, error) {
945959
svcs, err := h.GetServices()
946960
if err != nil {
947961
return nil, err
@@ -959,6 +973,14 @@ func ipvsAddService(vip net.IP, protocol, port uint16, persistent bool) (*ipvs.S
959973
glog.Infof("Updated persistence/session-affinity for service: %s", ipvsServiceString(svc))
960974
}
961975

976+
if scheduler != svc.SchedName {
977+
svc.SchedName = scheduler
978+
err = h.UpdateService(svc)
979+
if err != nil {
980+
return nil, errors.New("Failed to update the scheduler for the service due to " + err.Error())
981+
}
982+
glog.Infof("Updated schedule for the service: %s", ipvsServiceString(svc))
983+
}
962984
// TODO: Make this debug output when we get log levels
963985
// glog.Fatal("ipvs service %s:%s:%s already exists so returning", vip.String(),
964986
// protocol, strconv.Itoa(int(port)))
@@ -972,7 +994,7 @@ func ipvsAddService(vip net.IP, protocol, port uint16, persistent bool) (*ipvs.S
972994
AddressFamily: syscall.AF_INET,
973995
Protocol: protocol,
974996
Port: port,
975-
SchedName: ipvs.RoundRobin,
997+
SchedName: scheduler,
976998
}
977999

9781000
ipvsSetPersistence(&svc, persistent)
@@ -996,7 +1018,7 @@ func generateFwmark(ip, protocol, port string) uint32 {
9961018
}
9971019

9981020
// ipvsAddFWMarkService: creates a IPVS service using FWMARK
999-
func ipvsAddFWMarkService(vip net.IP, protocol, port uint16, persistent bool) (*ipvs.Service, error) {
1021+
func ipvsAddFWMarkService(vip net.IP, protocol, port uint16, persistent bool, scheduler string) (*ipvs.Service, error) {
10001022

10011023
var protocolStr string
10021024
if protocol == syscall.IPPROTO_TCP {
@@ -1027,6 +1049,14 @@ func ipvsAddFWMarkService(vip net.IP, protocol, port uint16, persistent bool) (*
10271049
glog.Infof("Updated persistence/session-affinity for service: %s", ipvsServiceString(svc))
10281050
}
10291051

1052+
if scheduler != svc.SchedName {
1053+
svc.SchedName = scheduler
1054+
err = h.UpdateService(svc)
1055+
if err != nil {
1056+
return nil, errors.New("Failed to update the scheduler for the service due to " + err.Error())
1057+
}
1058+
glog.Infof("Updated schedule for the service: %s", ipvsServiceString(svc))
1059+
}
10301060
// TODO: Make this debug output when we get log levels
10311061
// glog.Fatal("ipvs service %s:%s:%s already exists so returning", vip.String(),
10321062
// protocol, strconv.Itoa(int(port)))

0 commit comments

Comments
 (0)