blob: 503c2ca721e256d1e1b51c6e691b05b1852d06cf [file] [log] [blame]
OlgaGusarenko0a99f8a2018-07-30 18:07:08 +03001=====
2Usage
3=====
Filip Pytloun48d38302015-10-06 16:28:31 +02004
Ildar Svetlov77636142017-09-28 16:42:16 +04005The Reliable, High Performance TCP/HTTP Load Balancer.
Filip Pytloun48d38302015-10-06 16:28:31 +02006
Jiri Konecny371c6de2016-03-02 11:32:46 +01007Sample pillars
8==============
9
10Simple admin listener
11
12.. code-block:: yaml
13
14 haproxy:
15 proxy:
16 enabled: True
17 listen:
18 admin_page:
19 type: admin
20 binds:
21 - address: 0.0.0.0
22 port: 8801
23 user: fsdfdsfds
24 password: dsfdsf
25
Jiri Konecny371c6de2016-03-02 11:32:46 +010026Simple stats listener
27
28.. code-block:: yaml
29
30 haproxy:
31 proxy:
32 enabled: True
33 listen:
34 admin_page:
35 type: stats
36 binds:
37 - address: 0.0.0.0
38 port: 8801
39
40
41
42Sample pillar with admin
Filip Pytloun48d38302015-10-06 16:28:31 +020043
44.. code-block:: yaml
45
46 haproxy:
47 proxy:
48 enabled: True
Filip Pytloun98f7a4f2016-09-19 14:07:24 +020049 maxconn: 1024
50 timeout:
Martin Polreich3c7dd062019-10-14 13:13:41 +020051 connect: 5000ms
52 client: 50000ms
53 server: 50000ms
Brian McQueen306a5ca2017-12-09 17:30:32 -080054 listen:
55 https-in:
56 binds:
57 - address: 0.0.0.0
58 port: 443
59 servers:
60 - name: server1
61 host: 10.0.0.1
62 port: 8443
63 - name: server2
64 host: 10.0.0.2
65 port: 8443
66 params: 'maxconn 256'
Filip Pytloun48d38302015-10-06 16:28:31 +020067
Martin Polreich3c7dd062019-10-14 13:13:41 +020068.. note::
69
70 Timeout values are assumed to be defined in 'ms' if no other unit is specifically defined.
71
Jiri Konecny371c6de2016-03-02 11:32:46 +010072
Filip Pytloun48d38302015-10-06 16:28:31 +020073Sample pillar with custom logging
Filip Pytloun48d38302015-10-06 16:28:31 +020074
75.. code-block:: yaml
76
77 haproxy:
78 proxy:
79 enabled: True
Filip Pytloun98f7a4f2016-09-19 14:07:24 +020080 maxconn: 1024
81 timeout:
Martin Polreich3c7dd062019-10-14 13:13:41 +020082 connect: 5000ms
83 client: 50000ms
84 server: 50000ms
Brian McQueen306a5ca2017-12-09 17:30:32 -080085 listen:
86 https-in:
87 binds:
88 address: 0.0.0.0
89 port: 443
90 servers:
91 - name: server1
92 host: 10.0.0.1
93 port: 8443
94 - name: server2
95 host: 10.0.0.2
96 port: 8443
97 params: 'maxconn 256'
Filip Pytloun48d38302015-10-06 16:28:31 +020098
Martin Polreich3c7dd062019-10-14 13:13:41 +020099
100.. note::
101
102 Timeout values are assumed to be defined in 'ms' if no other unit is specifically defined.
103
Filip Pytloun48d38302015-10-06 16:28:31 +0200104.. code-block:: yaml
105
106 haproxy:
107 proxy:
108 enabled: true
Brian McQueen306a5ca2017-12-09 17:30:32 -0800109 listen:
110 mysql:
111 type: mysql
112 binds:
113 - address: 10.0.88.70
114 port: 3306
115 servers:
116 - name: node1
117 host: 10.0.88.13
118 port: 3306
119 params: check inter 15s fastinter 2s downinter 1s rise 5 fall 3
120 - name: node2
121 host: 10.0.88.14
122 port: 3306
123 params: check inter 15s fastinter 2s downinter 1s rise 5 fall 3 backup
124 - name: node3
125 host: 10.0.88.15
126 port: 3306
127 params: check inter 15s fastinter 2s downinter 1s rise 5 fall 3 backup
128 rabbitmq:
129 type: rabbitmq
130 binds:
131 - address: 10.0.88.70
132 port: 5672
133 servers:
134 - name: node1
135 host: 10.0.88.13
136 port: 5673
137 params: check inter 5000 rise 2 fall 3
138 - name: node2
139 host: 10.0.88.14
140 port: 5673
141 params: check inter 5000 rise 2 fall 3 backup
142 - name: node3
143 host: 10.0.88.15
144 port: 5673
145 params: check inter 5000 rise 2 fall 3 backup
146 keystone-1:
147 type: general-service
148 binds:
149 - address: 10.0.106.170
150 port: 5000
151 servers:
152 - name: node1
153 host: 10.0.88.13
154 port: 5000
155 params: check
Filip Pytloun48d38302015-10-06 16:28:31 +0200156
157.. code-block:: yaml
158
159 haproxy:
160 proxy:
161 enabled: true
Brian McQueen306a5ca2017-12-09 17:30:32 -0800162 listen:
163 mysql:
164 type: mysql
165 binds:
166 - address: 10.0.88.70
167 port: 3306
168 servers:
169 - name: node1
170 host: 10.0.88.13
171 port: 3306
172 params: check inter 15s fastinter 2s downinter 1s rise 5 fall 3
173 - name: node2
174 host: 10.0.88.14
175 port: 3306
176 params: check inter 15s fastinter 2s downinter 1s rise 5 fall 3 backup
177 - name: node3
178 host: 10.0.88.15
179 port: 3306
180 params: check inter 15s fastinter 2s downinter 1s rise 5 fall 3 backup
181 rabbitmq:
182 type: rabbitmq
183 binds:
184 - address: 10.0.88.70
185 port: 5672
186 servers:
187 - name: node1
188 host: 10.0.88.13
189 port: 5673
190 params: check inter 5000 rise 2 fall 3
191 - name: node2
192 host: 10.0.88.14
193 port: 5673
194 params: check inter 5000 rise 2 fall 3 backup
195 - name: node3
196 host: 10.0.88.15
197 port: 5673
198 params: check inter 5000 rise 2 fall 3 backup
199 keystone-1:
200 type: general-service
201 binds:
202 - address: 10.0.106.170
203 port: 5000
204 servers:
205 - name: node1
206 host: 10.0.88.13
207 port: 5000
208 params: check
Filip Pytloun48d38302015-10-06 16:28:31 +0200209
Petr Jedinýc1461642018-04-17 17:21:28 +0200210Sample pillar with port range and port offset
211
212This is usefull in listen blocks for definition of multiple servers
213that differs only by port number in port range block. This situation
214can be result of multiple single-thread servers deployed in multi-core
215environment to better utilize the available cores.
216
OlgaGusarenko0a99f8a2018-07-30 18:07:08 +0300217For example, five contrail-api workers occupy ports ``9100-9104``.
Petr Jedinýc1461642018-04-17 17:21:28 +0200218This can be achieved by using ``port_range_length`` in the pillar,
219``port_range_length: 5`` in this case.
220For skipping first worker (``worker_id 0``), because it has other
221responsibilities and to avoid overloading it by http requests
222use the ``port_range_start_offset`` in the pillar,
223``port_range_start_offset: 1`` in this case, it will only use ports
2249101-9104 (skipping 9100).
225
226- ``port_range_length`` parameter is used to calculate port range end
227- ``port_range_start_offset`` will skip first n ports in port range
228
229For backward compatibility, the name of the first server in port range
230has no ``pN`` suffix.
231
232The following sample will result in
233
234.. code-block:: text
235
236 listen contrail_api
237 bind 172.16.10.252:8082
Petr Jedinýc1461642018-04-17 17:21:28 +0200238 balance leastconn
239 server ntw01p1 172.16.10.95:9101 check inter 2000 rise 2 fall 3
240 server ntw01p2 172.16.10.95:9102 check inter 2000 rise 2 fall 3
241 server ntw01p3 172.16.10.95:9103 check inter 2000 rise 2 fall 3
242 server ntw01p4 172.16.10.95:9104 check inter 2000 rise 2 fall 3
243 server ntw02 172.16.10.96:9100 check inter 2000 rise 2 fall 3
244 server ntw02p1 172.16.10.96:9101 check inter 2000 rise 2 fall 3
245 server ntw02p2 172.16.10.96:9102 check inter 2000 rise 2 fall 3
246 server ntw02p3 172.16.10.96:9103 check inter 2000 rise 2 fall 3
247 server ntw02p4 172.16.10.96:9104 check inter 2000 rise 2 fall 3
248 server ntw03 172.16.10.94:9100 check inter 2000 rise 2 fall 3
249 server ntw03p1 172.16.10.94:9101 check inter 2000 rise 2 fall 3
250 server ntw03p2 172.16.10.94:9102 check inter 2000 rise 2 fall 3
251 server ntw03p3 172.16.10.94:9103 check inter 2000 rise 2 fall 3
252 server ntw03p4 172.16.10.94:9104 check inter 2000 rise 2 fall 3
253
254.. code-block:: yaml
255
256 haproxy:
257 proxy:
258 listen:
259 contrail_api:
260 type: contrail-api
261 service_name: contrail
262 balance: leastconn
263 binds:
264 - address: 10.10.10.10
265 port: 8082
266 servers:
267 - name: ntw01
268 host: 10.10.10.11
269 port: 9100
270 port_range_length: 5
271 port_range_start_offset: 1
272 params: check inter 2000 rise 2 fall 3
273 - name: ntw02
274 host: 10.10.10.12
275 port: 9100
276 port_range_length: 5
277 port_range_start_offset: 0
278 params: check inter 2000 rise 2 fall 3
279 - name: ntw03
280 host: 10.10.10.13
281 port: 9100
282 port_range_length: 5
283 params: check inter 2000 rise 2 fall 3
284
285
Filip Pytloune1a6f062016-08-26 15:13:38 +0200286Custom more complex listener (for Artifactory and subdomains for docker
287registries)
288
289.. code-block:: yaml
290
291 haproxy:
292 proxy:
293 listen:
294 artifactory:
295 mode: http
296 options:
297 - forwardfor
298 - forwardfor header X-Real-IP
299 - httpchk
300 - httpclose
301 - httplog
Jakub Pavlikc9f84c42016-12-10 16:16:08 +0100302 sticks:
303 - stick on src
304 - stick-table type ip size 200k expire 2m
Filip Pytloune1a6f062016-08-26 15:13:38 +0200305 acl:
306 is_docker: "path_reg ^/v[12][/.]*"
307 http_request:
308 - action: "set-path /artifactory/api/docker/%[req.hdr(host),lower,field(1,'.')]%[path]"
309 condition: "if is_docker"
310 balance: source
311 binds:
312 - address: ${_param:cluster_vip_address}
313 port: 8082
Filip Pytloun4201b382016-09-09 12:21:18 +0200314 ssl:
315 enabled: true
316 # This PEM file needs to contain key, cert, CA and possibly
317 # intermediate certificates
318 pem_file: /etc/haproxy/ssl/server.pem
Filip Pytloune1a6f062016-08-26 15:13:38 +0200319 servers:
320 - name: ${_param:cluster_node01_name}
321 host: ${_param:cluster_node01_address}
322 port: 8082
323 params: check
324 - name: ${_param:cluster_node02_name}
325 host: ${_param:cluster_node02_address}
326 port: 8082
327 params: backup check
328
Filip Pytlound95069e2017-03-10 16:12:03 +0100329It's also possible to use multiple certificates for one listener (eg. when
330it's bind on multiple interfaces):
331
332.. code-block:: yaml
333
334 haproxy:
335 proxy:
336 listen:
337 dummy_site:
338 mode: http
339 binds:
340 - address: 127.0.0.1
341 port: 8080
342 ssl:
343 enabled: true
344 key: |
345 my super secret key follows
346 cert: |
347 certificate
348 chain: |
349 CA chain (if any)
350 - address: 127.0.1.1
351 port: 8081
352 ssl:
353 enabled: true
354 key: |
355 my super secret key follows
356 cert: |
357 certificate
358 chain: |
359 CA chain (if any)
360
361Definition above will result in creation of ``/etc/haproxy/ssl/dummy_site``
362directory with files ``1-all.pem`` and ``2-all.pem`` (per binds).
363
Dzmitry Stremkouski9510dcf2018-10-25 17:48:20 +0200364Custom listener with http-check options specified
365
366.. code-block:: yaml
367
368 haproxy:
369 proxy:
370 enabled: true
371 forwardfor:
372 enabled: true
373 except: 127.0.0.1
374 header: X-Forwarded-For
375 if-none: false
376 listen:
377 glance_api:
378 binds:
379 - address: 192.168.2.11
380 port: 9292
381 ssl:
382 enabled: true
383 pem_file: /etc/haproxy/ssl/all.pem
384 http_request:
385 - action: set-header X-Forwarded-Proto https
386 mode: http
387 options:
388 - httpchk GET /
389 - httplog
390 - httpclose
391 servers:
392 - host: 127.0.0.1
393 name: ctl01
394 params: check inter 10s fastinter 2s downinter 3s rise 3 fall 3
395 port: 9292
396
Adam Tengler4cf961b2017-01-26 16:05:21 +0000397Custom listener with tcp-check options specified (for Redis cluster with Sentinel)
398
399.. code-block:: yaml
400
401 haproxy:
402 proxy:
403 listen:
404 redis_cluster:
405 service_name: redis
Guillaume Thouvenin3adff8c2017-02-21 14:29:07 +0100406 health-check:
Adam Tengler4cf961b2017-01-26 16:05:21 +0000407 tcp:
408 enabled: True
409 options:
410 - send PING\r\n
411 - expect string +PONG
412 - send info\ replication\r\n
413 - expect string role:master
414 - send QUIT\r\n
415 - expect string +OK
416 binds:
417 - address: ${_param:cluster_address}
418 port: 6379
419 servers:
420 - name: ${_param:cluster_node01_name}
421 host: ${_param:cluster_node01_address}
422 port: 6379
423 params: check inter 1s
424 - name: ${_param:cluster_node02_name}
425 host: ${_param:cluster_node02_address}
426 port: 6379
427 params: check inter 1s
428 - name: ${_param:cluster_node03_name}
429 host: ${_param:cluster_node03_address}
430 port: 6379
431 params: check inter 1s
432
Sergey Otpuschennikov3e831332017-08-01 13:31:03 +0400433Frontend for routing between exists listeners via URL with SSL an redirects.
434You can use one backend for several URLs.
435
436.. code-block:: yaml
437
438 haproxy:
439 proxy:
440 listen:
441 service_proxy:
442 mode: http
443 balance: source
444 format: end
445 binds:
446 - address: ${_param:haproxy_bind_address}
447 port: 80
448 ssl: ${_param:haproxy_frontend_ssl}
449 ssl_port: 443
450 redirects:
451 - code: 301
452 location: domain.com/images
453 conditions:
454 - type: hdr_dom(host)
455 condition: images.domain.com
456 acls:
457 - name: gerrit
458 conditions:
459 - type: hdr_dom(host)
460 condition: gerrit.domain.com
461 - name: jenkins
462 conditions:
463 - type: hdr_dom(host)
464 condition: jenkins.domain.com
465 - name: docker
466 backend: artifactroy
467 conditions:
468 - type: hdr_dom(host)
469 condition: docker.domain.com
470
Ildar Svetlov77636142017-09-28 16:42:16 +0400471Enable customisable ``forwardfor`` option in ``defaults`` section.
472
473.. code-block:: yaml
474
475 haproxy:
476 proxy:
477 enabled: true
Ildar Svetlov77636142017-09-28 16:42:16 +0400478 forwardfor:
479 enabled: true
480 except:
481 header:
482 if-none: false
483
484.. code-block:: yaml
485
486 haproxy:
487 proxy:
488 enabled: true
Ildar Svetlov77636142017-09-28 16:42:16 +0400489 forwardfor:
490 enabled: true
491 except: 127.0.0.1
492 header: X-Real-IP
493 if-none: false
494
stelucz01752fe2018-01-26 12:39:23 +0100495Sample pillar with multiprocess multicore configuration
496
497.. code-block:: yaml
498
499 haproxy:
500 proxy:
501 enabled: True
502 nbproc: 4
503 cpu_map:
504 1: 0
505 2: 1
506 3: 2
507 4: 3
508 stats_bind_process: "1 2"
stelucz01752fe2018-01-26 12:39:23 +0100509 maxconn: 1024
510 timeout:
Martin Polreich3c7dd062019-10-14 13:13:41 +0200511 connect: 5000ms
512 client: 50000ms
513 server: 50000ms
stelucz01752fe2018-01-26 12:39:23 +0100514 listen:
515 https-in:
516 bind_process: "1 2 3 4"
517 binds:
518 - address: 0.0.0.0
519 port: 443
520 servers:
521 - name: server1
522 host: 10.0.0.1
523 port: 8443
524 - name: server2
525 host: 10.0.0.2
526 port: 8443
527 params: 'maxconn 256'
528
Martin Polreich3c7dd062019-10-14 13:13:41 +0200529.. note::
530
531 Timeout values are assumed to be defined in 'ms' if no other unit is specifically defined.
532
Michel Nederlof14da7092018-03-12 22:46:36 +0100533Implement rate limiting, to prevent excessive requests
534This feature only works if using 'format: end'
535
536.. code-block:: yaml
OlgaGusarenko0a99f8a2018-07-30 18:07:08 +0300537
Michel Nederlof14da7092018-03-12 22:46:36 +0100538 haproxy:
539 proxy:
540 ...
541 listen:
542 nova_metadata_api:
543 ...
544 format: end
545 options:
546 - httpchk
547 - httpclose
548 - httplog
549 rate_limit:
550 duration: 900s
551 enabled: true
552 requests: 125
553 track: content
554 servers:
555 ...
556 type: http
557
Oleksii Grudevb53828d2018-11-07 12:14:40 +0200558Implement haproxy configuration without specifying certain type or with type='None'.
559This approach allows to set all major haproxy parameters manually.
560Sample pillar:
561
562.. code-block:: yaml
563
564 haproxy:
565 proxy:
566 listen:
567 manila_api:
568 type: None
569 mode: tcp
570 balance: roundrobin
571 timeout:
Martin Polreich3c7dd062019-10-14 13:13:41 +0200572 check: 10s
573 client: 20s
Oleksii Grudevb53828d2018-11-07 12:14:40 +0200574 http_request:
575 - action: "add-header X-Forwarded-Proto https"
576 condition: "if { ssl_fc }"
577 options: ${_param:haproxy_https_check_options}
578 capture:
579 - cookie ASPSESSION len 32
580 - request header Host len 15
581 compression:
582 - algo gzip
583 - type text/html text/plain
584 declare_capture: request len 50
585 email_alert:
586 - myhostname myserver
587 - from server@localhost
588 - level warning
589 errorfile:
590 file_500:
591 code: 500
592 file: /tmp/error_500.log
593 file_404:
594 code: 400
595 file: /tmp/error_400.log
596 max_keep_alive_queue: 100
597 maxconn: 10000
598 reqadd:
599 - X-Proto:\ SSL if is-ssl
600 reqirep:
601 - ^Host:\ www.mydomain.com Host:\ www
602 modify_headers:
603 - reqallow ^Host:\ www\.
604 - reqdel ^Host:\ .*\.local
605 - reqdeny ^Host:\ .*\.local
606 - reqiallow ^Host:\ www\.
607 - reqidel ^Host:\ .*\.local
608 - reqideny ^Host:\ .*\.local
609 - reqipass ^Host:\ .*\.local
610 - reqpass ^Host:\ .*\.local
611 - reqitarpit ^Host:\ .*\.local
612 - reqtarpit ^Host:\ .*\.local
613 retries: 10
614 stats:
615 - enable
616 - auth admin1:AdMiN123
617 rate_limit_sessions: 1000
618
Martin Polreich3c7dd062019-10-14 13:13:41 +0200619.. note::
620
621 Timeout values are assumed to be defined in 'ms' if no other unit is specifically defined.
622
Oleksandr Bryndzii57aa7b42018-12-11 11:48:23 +0000623Implement rate limiting, to prevent excessive requests
624using 'format: listen'
625
626.. code-block:: yaml
627
628 haproxy:
629 proxy:
630 ...
631 listen:
632 nova_metadata_api:
633 ...
634 rate_limit:
635 duration: 3s
636 enabled: true
637 requests: 60
638 track: connection
639 servers:
640 ...
Oleksandr Bryndziiff290262019-01-18 13:06:35 +0000641Implement rate limiting, to prevent excessive requests
642using 'format: listen' and acls/request/backend stick list
643
644.. code-block:: yaml
645
646 haproxy:
647 proxy:
648 listen:
649 nova_metadata_api:
650 options:
651 - httplog
652 rate_limit:
653 enabled: true
654 type: string
655 len: 36
656 size: 10m
657 duration: 60s
658 acls:
659 101:
660 enabled: true
661 value: acl too_many_requests_3 sc0_gpc0_rate() gt 3
662 102:
663 enabled: true
664 value: acl mark_seen sc0_inc_gpc0 gt 0
665 110:
666 enabled: true
667 value: acl x_instance_id hdr(x-instance-id) -i 4777e8e0-16e8-46ce-a3fe-0a1ad9b3ebdc
668 111:
669 enabled: true
670 value: acl x_instance_id hdr(x-instance-id) -i ca2395dd-f73f-4d43-8fe7-f7078a0920af
671 201:
672 enabled: true
673 value: acl too_many_requests_6 sc0_gpc0_rate() gt 6
674 202:
675 enabled: true
676 value: acl mark_seen sc0_inc_gpc0 gt 0
677 210:
678 enabled: true
679 value: acl x_tenant_id hdr(x-tenant-id) -i 2b76cc56a437404bb8cb6cb20dbb0ea4
680 tcp_request:
681 001:
682 enabled: true
683 value: tcp-request inspect-delay 5s
684 101:
685 enabled: true
686 value: tcp-request content track-sc0 hdr(x-instance-id) if ! too_many_requests_3
687 201:
688 enabled: true
689 value: tcp-request content track-sc0 hdr(x-tenant-id) if ! too_many_requests_6
690 use_backend:
691 101:
692 enabled: true
693 value: use_backend nova_metadata_api-rate_limit if mark_seen too_many_requests_3 x_instance_id
694 201:
695 enabled: true
696 value: use_backend nova_metadata_api-rate_limit if mark_seen too_many_requests_6 x_tenant_id
Oleksandr Bryndzii57aa7b42018-12-11 11:48:23 +0000697
Martin Polreicheef51a12019-04-17 11:31:01 +0200698Pillar demostrating all global variables which are parametrized
699
700 All values may be defined as a single element or a list of elements with the same keyword
701 Keyword is added automatically and should not be included in the value
702
703.. code-block:: yaml
704
705 haproxy:
706 proxy:
707 global:
708 chroot: /var/lib/haproxy
709 daemon: true
710 gid: ''
711 group: haproxy
712 cpu-map:
713 - '0 1'
714 - '1 2'
715 log:
716 - '/dev/log local0'
717 - '/dev/log local1 notice'
718 log-send-hostname: ''
719 nbproc: 5
720 pidfile: '/var/run/haproxy.pid'
721 uid: ''
722 ulimit-n: ''
723 user: 'haproxy'
724 stats:
725 - 'socket /var/run/new.sock mode 660 level admin'
726 - 'timeout 30s'
727 - 'bind-process 1 2'
728 node: ''
729 description: ''
730 maxconn: 25000
731 maxpipes: ''
732 noepoll: ''
733 nokqueue: ''
734 nopoll: ''
735 nosepoll: ''
736 nosplice: ''
737 spread-checks: 4
738 tune_bufsize: 32768
739 tune_chksize: ''
740 tune_maxaccept: ''
741 tune_maxpollevents: ''
742 tune_maxrewrite: 1024
743 tune_rcvbuf_client: ''
744 tune_rcvbuf_server: ''
745 tune_sndbuf_client: ''
746 tune_sndbuf_server: ''
Oleksandr Bryndzii57aa7b42018-12-11 11:48:23 +0000747
Filip Pytloun48d38302015-10-06 16:28:31 +0200748Read more
749=========
750
751* https://github.com/jesusaurus/hpcs-salt-state/tree/master/haproxy
OlgaGusarenko0a99f8a2018-07-30 18:07:08 +0300752* http://www.nineproductions.com/saltstack-ossec-state-using-reactor/
753* https://gist.github.com/tomeduarte/6340205 - example on how to use peer
754 from within a config file (using jinja)
755* http://youtu.be/jJJ8cfDjcTc?t=8m58s - from 9:00 on, a good overview
756 of peer vs mine
Filip Pytloun48d38302015-10-06 16:28:31 +0200757* https://github.com/russki/cluster-agents