Create the deploymentn with 100 backends and one Service
kubectl apply -f backends.yaml
Run a pod to test the Service using the httptest program, that allows to specify different parameters of the http connections
./httptest -h
Usage: httpget [options] [url]
-duration int
Duration in seconds (default 5)
-keepalives
http keepalives (default true)
-qps int
queries per second (default 3)
-requests int
Number of requests (default 5)
-workers int
Number of workers (default 2)
The output is easy to parse with the common cli tools like sort, uniq, awk, ...
kubectl run -it test --privileged --image registry.k8s.io/e2e-test-images/agnhost:2.39 --command -- ash
$ wget https://storage.googleapis.com/aojea-misc/httptest && chmod +x httptest
$ ./httptest --keepalives=false -requests 200 -workers 10 -duration 200 -qps 10 http://10.128.0.12/hostname > test.log
$ ./httptest --keepalives=true -requests 200 -workers 10 -duration 200 -qps 10 http://10.128.0.12/hostname > test_keepalives.log
Using http keepalives the connection is reused and the traffic is directed to the same backend because the client reuses the TCP connection
cut -d\ -f 5 test_keepalives.log | sort | uniq -c | sort -r
40 backends-7cbf886b86-jg27f
40 backends-7cbf886b86-fsgk8
20 backends-7cbf886b86-wgrc7
20 backends-7cbf886b86-mbc74
20 backends-7cbf886b86-dhd2m
20 backends-7cbf886b86-ch8rj
20 backends-7cbf886b86-b5l72
20 backends-7cbf886b86-7rvds
without http keepalives the traffic is sent to different backends
cut -d\ -f 5 test.log | sort | uniq -c | sort -r
6 backends-7cbf886b86-zv99t
6 backends-7cbf886b86-wpwz7
6 backends-7cbf886b86-pvs5s
5 backends-7cbf886b86-sgcg8
5 backends-7cbf886b86-9mvd4
4 backends-7cbf886b86-x9rxc
4 backends-7cbf886b86-v6498
4 backends-7cbf886b86-jqtjh
4 backends-7cbf886b86-d7mvr
4 backends-7cbf886b86-7sgrg
4 backends-7cbf886b86-5b2tc
4 backends-7cbf886b86-4pwwg
3 backends-7cbf886b86-w4s9z
3 backends-7cbf886b86-tn5x9
3 backends-7cbf886b86-tfsmz
3 backends-7cbf886b86-t4knn
3 backends-7cbf886b86-ssj9t
3 backends-7cbf886b86-r778l
3 backends-7cbf886b86-qmlmz
3 backends-7cbf886b86-qlfvv
3 backends-7cbf886b86-pw9jd
3 backends-7cbf886b86-pn7ts
3 backends-7cbf886b86-lfj9v
3 backends-7cbf886b86-hdvfd
3 backends-7cbf886b86-g6nc8
3 backends-7cbf886b86-c7pp2
3 backends-7cbf886b86-c7n5c
3 backends-7cbf886b86-7gxd2
3 backends-7cbf886b86-5qh9d
3 backends-7cbf886b86-4tklz
3 backends-7cbf886b86-27s6f
2 backends-7cbf886b86-z9jhd
2 backends-7cbf886b86-xf6vw
2 backends-7cbf886b86-xbmrm
2 backends-7cbf886b86-wgrc7
2 backends-7cbf886b86-vvbgw
2 backends-7cbf886b86-rr7dc
2 backends-7cbf886b86-r8bz5
2 backends-7cbf886b86-mbc74
2 backends-7cbf886b86-lwctj
2 backends-7cbf886b86-lvxm4
2 backends-7cbf886b86-knz85
2 backends-7cbf886b86-jg27f
2 backends-7cbf886b86-hrsqx
2 backends-7cbf886b86-h8zvz
2 backends-7cbf886b86-gjr8m
2 backends-7cbf886b86-fsgk8
2 backends-7cbf886b86-fpq4k
2 backends-7cbf886b86-fm2b8
2 backends-7cbf886b86-dm566
2 backends-7cbf886b86-b9cxf
2 backends-7cbf886b86-b72dv
2 backends-7cbf886b86-b6dng
2 backends-7cbf886b86-8lqmn
2 backends-7cbf886b86-7rvds
2 backends-7cbf886b86-7cz9g
2 backends-7cbf886b86-2wg6b
2 backends-7cbf886b86-2nxs7
2 backends-7cbf886b86-2nmhr
2 backends-7cbf886b86-294wq
1 backends-7cbf886b86-z9sdz
1 backends-7cbf886b86-z4rjx
1 backends-7cbf886b86-xlwjs
1 backends-7cbf886b86-xhkn9
1 backends-7cbf886b86-x8dms
1 backends-7cbf886b86-x295h
1 backends-7cbf886b86-vxxzg
1 backends-7cbf886b86-vhq9j
1 backends-7cbf886b86-tm4wt
1 backends-7cbf886b86-swpx4
1 backends-7cbf886b86-qvrvp
1 backends-7cbf886b86-phz87
1 backends-7cbf886b86-phcp2
1 backends-7cbf886b86-ndd28
1 backends-7cbf886b86-mztxk
1 backends-7cbf886b86-mr42c
1 backends-7cbf886b86-l9lrv
1 backends-7cbf886b86-kc4df
1 backends-7cbf886b86-dkmmh
1 backends-7cbf886b86-dhd2m
1 backends-7cbf886b86-ch8rj
1 backends-7cbf886b86-c45ct
1 backends-7cbf886b86-b5l72
1 backends-7cbf886b86-9gttp
1 backends-7cbf886b86-94wqh
1 backends-7cbf886b86-8bqm6
1 backends-7cbf886b86-8bc55
1 backends-7cbf886b86-5lhgc
1 backends-7cbf886b86-2fgdj
the distribution does not seem even, but let's see if there is statistically difference running much more requests
/ # ./httptest --keepalives=false -requests 50000 -workers 30 -duration 20000 -qps 1000 http://10.128.0.12/hostname > test2.log
/ # cut -d\ -f 5 test2.log | sort | uniq -c | sort -r
620 backends-7cbf886b86-tfsmz
591 backends-7cbf886b86-7cz9g
590 backends-7cbf886b86-5lhgc
588 backends-7cbf886b86-knz85
580 backends-7cbf886b86-mlrnb
577 backends-7cbf886b86-xbmrm
575 backends-7cbf886b86-phz87
571 backends-7cbf886b86-jqtjh
570 backends-7cbf886b86-vvbgw
568 backends-7cbf886b86-27s6f
567 backends-7cbf886b86-pn7ts
567 backends-7cbf886b86-hdvfd
566 backends-7cbf886b86-txbn6
565 backends-7cbf886b86-rsdm6
564 backends-7cbf886b86-2nxs7
562 backends-7cbf886b86-dhd2m
559 backends-7cbf886b86-w4s9z
553 backends-7cbf886b86-vhq9j
548 backends-7cbf886b86-9mvd4
546 backends-7cbf886b86-7rvds
545 backends-7cbf886b86-hrsqx
542 backends-7cbf886b86-c7pp2
540 backends-7cbf886b86-2wg6b
538 backends-7cbf886b86-fpq4k
536 backends-7cbf886b86-t4knn
534 backends-7cbf886b86-2fgdj
531 backends-7cbf886b86-ppz2g
529 backends-7cbf886b86-jzck4
525 backends-7cbf886b86-v6498
520 backends-7cbf886b86-tn5x9
517 backends-7cbf886b86-xf6vw
517 backends-7cbf886b86-x8dms
517 backends-7cbf886b86-ssj9t
517 backends-7cbf886b86-8bqm6
515 backends-7cbf886b86-d7mvr
514 backends-7cbf886b86-phcp2
509 backends-7cbf886b86-wgrc7
509 backends-7cbf886b86-r778l
507 backends-7cbf886b86-swpx4
506 backends-7cbf886b86-kc4df
506 backends-7cbf886b86-g6nc8
503 backends-7cbf886b86-z4rjx
503 backends-7cbf886b86-ndd28
503 backends-7cbf886b86-mztxk
501 backends-7cbf886b86-b9cxf
500 backends-7cbf886b86-x9rxc
500 backends-7cbf886b86-wpwz7
498 backends-7cbf886b86-bprgg
497 backends-7cbf886b86-jjvjz
497 backends-7cbf886b86-gjr8m
497 backends-7cbf886b86-c7n5c
493 backends-7cbf886b86-lfj9v
492 backends-7cbf886b86-qvrvp
492 backends-7cbf886b86-h8zvz
492 backends-7cbf886b86-7sgrg
492 backends-7cbf886b86-4pwwg
490 backends-7cbf886b86-l9lrv
490 backends-7cbf886b86-5b2tc
489 backends-7cbf886b86-zv99t
488 backends-7cbf886b86-x295h
487 backends-7cbf886b86-c45ct
486 backends-7cbf886b86-z9jhd
486 backends-7cbf886b86-rr7dc
486 backends-7cbf886b86-dkmmh
483 backends-7cbf886b86-5qh9d
482 backends-7cbf886b86-pw9jd
481 backends-7cbf886b86-94wqh
481 backends-7cbf886b86-8bc55
479 backends-7cbf886b86-mbc74
478 backends-7cbf886b86-vxxzg
473 backends-7cbf886b86-qlfvv
470 backends-7cbf886b86-z9sdz
470 backends-7cbf886b86-qmlmz
470 backends-7cbf886b86-b72dv
470 backends-7cbf886b86-9z5nr
470 backends-7cbf886b86-2nmhr
470 backends-7cbf886b86-294wq
469 backends-7cbf886b86-b6dng
467 backends-7cbf886b86-mr42c
462 backends-7cbf886b86-b5l72
460 backends-7cbf886b86-xlwjs
460 backends-7cbf886b86-xhkn9
458 backends-7cbf886b86-tqs7b
457 backends-7cbf886b86-r8bz5
456 backends-7cbf886b86-pvs5s
455 backends-7cbf886b86-4tklz
448 backends-7cbf886b86-9gttp
446 backends-7cbf886b86-ch8rj
445 backends-7cbf886b86-jg27f
443 backends-7cbf886b86-2sb69
441 backends-7cbf886b86-fsgk8
440 backends-7cbf886b86-8lqmn
436 backends-7cbf886b86-fcjd9
430 backends-7cbf886b86-dm566
425 backends-7cbf886b86-sgcg8
408 backends-7cbf886b86-lvxm4
394 backends-7cbf886b86-fm2b8
390 backends-7cbf886b86-tm4wt
390 backends-7cbf886b86-7gxd2
380 backends-7cbf886b86-lwctj
the number of conntrack entries is checking the PodIP is similra to the number of requests and we didn't exhaused the ephemeral port range on this environment, so it is not likely that we are reusing something
cilium bpf ct list global -d | grep 10.48.0.115 | wc
level=info msg="Initializing Google metrics" subsys=metrics
39308 1021869 11781540
It seems cilium uses this code to select the backend
* Backend slot 0 is always reserved for the service frontend. */
#if LB_SELECTION == LB_SELECTION_RANDOM
static __always_inline __u32
lb4_select_backend_id(struct __ctx_buff *ctx,
struct lb4_key *key,
const struct ipv4_ct_tuple *tuple __maybe_unused,
const struct lb4_service *svc)
{
__u16 slot = (get_prandom_u32() % svc->count) + 1;
struct lb4_service *be = lb4_lookup_backend_slot(ctx, key, slot);
return be ? be->backend_id : 0;
}
kube-proxy shows similar behavior
./httptest --keepalives=false -requests 50000 -workers 30 -duration 20000 -qps 1000 http://10.76.6.103/hostname > test2.log
cut -d\ -f 5 test2.log | sort | uniq -c | sort -r
552 backends-99fc4ffd8-tnx77
545 backends-99fc4ffd8-m8k58
543 backends-99fc4ffd8-lpxmf
543 backends-99fc4ffd8-bpfxv
540 backends-99fc4ffd8-bzxgq
538 backends-99fc4ffd8-jc8vt
533 backends-99fc4ffd8-tcgg6
532 backends-99fc4ffd8-7bfll
530 backends-99fc4ffd8-mtwfs
530 backends-99fc4ffd8-dsx68
528 backends-99fc4ffd8-477kx
526 backends-99fc4ffd8-gd226
526 backends-99fc4ffd8-flpbv
525 backends-99fc4ffd8-2gfws
524 backends-99fc4ffd8-h2crh
523 backends-99fc4ffd8-rhpvx
522 backends-99fc4ffd8-9mg44
521 backends-99fc4ffd8-x2kng
521 backends-99fc4ffd8-p8rsf
521 backends-99fc4ffd8-dtz5w
520 backends-99fc4ffd8-7bbhn
519 backends-99fc4ffd8-27j9l
518 backends-99fc4ffd8-p8h8c
518 backends-99fc4ffd8-mkqbs
518 backends-99fc4ffd8-7n4qq
517 backends-99fc4ffd8-9hq8c
516 backends-99fc4ffd8-s627l
516 backends-99fc4ffd8-98qhk
516 backends-99fc4ffd8-7vl86
514 backends-99fc4ffd8-vbj5t
513 backends-99fc4ffd8-tmsjd
513 backends-99fc4ffd8-84rb7
512 backends-99fc4ffd8-zkb6j
512 backends-99fc4ffd8-xvztf
512 backends-99fc4ffd8-7gvfs
511 backends-99fc4ffd8-cf7s8
510 backends-99fc4ffd8-xsgvm
509 backends-99fc4ffd8-fq2s2
508 backends-99fc4ffd8-sx5zd
508 backends-99fc4ffd8-pchnx
507 backends-99fc4ffd8-hpvmd
506 backends-99fc4ffd8-kdzg4
505 backends-99fc4ffd8-b5wfg
504 backends-99fc4ffd8-vs2sb
504 backends-99fc4ffd8-brzg6
504 backends-99fc4ffd8-6fx6r
502 backends-99fc4ffd8-8mmdd
502 backends-99fc4ffd8-648f4
500 backends-99fc4ffd8-p4v85
500 backends-99fc4ffd8-8rt52
499 backends-99fc4ffd8-txhtx
496 backends-99fc4ffd8-hcmcw
495 backends-99fc4ffd8-t7rp8
494 backends-99fc4ffd8-vldmv
494 backends-99fc4ffd8-s7fld
494 backends-99fc4ffd8-rkbfh
494 backends-99fc4ffd8-q9hqq
494 backends-99fc4ffd8-l5r7p
494 backends-99fc4ffd8-7sqzh
494 backends-99fc4ffd8-6nznr
493 backends-99fc4ffd8-w8jfk
493 backends-99fc4ffd8-vmmbc
492 backends-99fc4ffd8-zvj46
492 backends-99fc4ffd8-pmlz4
491 backends-99fc4ffd8-kjc7k
491 backends-99fc4ffd8-7kp2l
489 backends-99fc4ffd8-r72lh
489 backends-99fc4ffd8-hhdjb
489 backends-99fc4ffd8-gdfxv
489 backends-99fc4ffd8-4nwkr
488 backends-99fc4ffd8-48p8c
487 backends-99fc4ffd8-rhdt5
487 backends-99fc4ffd8-n2xsx
487 backends-99fc4ffd8-5hrcf
486 backends-99fc4ffd8-2dzsn
485 backends-99fc4ffd8-k8ncv
484 backends-99fc4ffd8-s6s6m
484 backends-99fc4ffd8-dnhm4
484 backends-99fc4ffd8-dfdl2
481 backends-99fc4ffd8-xwdb7
481 backends-99fc4ffd8-k9hsd
480 backends-99fc4ffd8-vsm7x
480 backends-99fc4ffd8-t42cw
480 backends-99fc4ffd8-cvtrn
480 backends-99fc4ffd8-9fzpx
476 backends-99fc4ffd8-rbz7c
476 backends-99fc4ffd8-n7wnz
475 backends-99fc4ffd8-wbgc9
475 backends-99fc4ffd8-dpxp8
473 backends-99fc4ffd8-x5zr5
473 backends-99fc4ffd8-25zpz
472 backends-99fc4ffd8-mqcwn
468 backends-99fc4ffd8-nvk6d
464 backends-99fc4ffd8-zk66c
463 backends-99fc4ffd8-fwdzl
459 backends-99fc4ffd8-8x569
457 backends-99fc4ffd8-h2r8f
451 backends-99fc4ffd8-7fd7g
448 backends-99fc4ffd8-fzqjp
423 backends-99fc4ffd8-x7zh9
kube-proxy uses the statistics mode that use the same function get_random_u32
static bool
statistic_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_statistic_info *info = par->matchinfo;
bool ret = info->flags & XT_STATISTIC_INVERT;
int nval, oval;
switch (info->mode) {
case XT_STATISTIC_MODE_RANDOM:
if ((get_random_u32() & 0x7FFFFFFF) < info->u.random.probability)
ret = !ret;
break;
case XT_STATISTIC_MODE_NTH:
do {
oval = atomic_read(&info->master->count);
nval = (oval == info->u.nth.every) ? 0 : oval + 1;
} while (atomic_cmpxchg(&info->master->count, oval, nval) != oval);
if (nval == 0)
ret = !ret;
break;
}
return ret;
}
However, kube-proxy iptables uses the random function differently
@thockin share a nice repro to show that this is working as intended https://go.dev/play/p/2Q-q4EVNBfm