blob: ed820076316bd6a0ad6b109c92ed459e38a88528 [file] [log] [blame]
marcoacdae7e2015-12-02 15:35:37 +01001
2==================
3Kubernetes Formula
4==================
5
Jakub Pavlik495d06f2016-06-17 11:33:05 +02006Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications.
7
8This formula deploys production ready Kubernetes and generate Kubernetes manifests as well.
marcoacdae7e2015-12-02 15:35:37 +01009
10Based on official Kubernetes salt
11https://github.com/kubernetes/kubernetes/tree/master/cluster/saltbase
12
13Extended on Contrail contribution https://github.com/Juniper/kubernetes/blob/opencontrail-integration/docs/getting-started-guides/opencontrail.md
14
15
16Sample pillars
17==============
18
Tomáš Kukrál189da4b2017-01-18 14:30:09 +010019**REQUIRED:** Define image to use for hyperkube, CNIs and calicoctl image
20
21.. code-block:: yaml
22
23 parameters:
24 kubernetes:
25 common:
26 hyperkube:
Tomáš Kukrála636f0e2017-03-21 11:09:55 +010027 image: gcr.io/google_containers/hyperkube:v1.5.2
Tomáš Kukrál189da4b2017-01-18 14:30:09 +010028 pool:
29 network:
30 calicoctl:
31 image: calico/ctl
32 cni:
33 image: calico/cni
34
Tomáš Kukrál25a64d72017-03-23 14:14:07 +010035Enable helm-tiller addon
Tomáš Kukrál1b50f772017-03-23 12:51:32 +010036
37.. code-block:: yaml
38
39 parameters:
40 kubernetes:
41 master:
42 addons:
43 helm:
44 enabled: true
45
Tomáš Kukrál25a64d72017-03-23 14:14:07 +010046Enable netchecker addon
47
48.. code-block:: yaml
49
50 parameters:
51 kubernetes:
52 master:
53 namespace:
54 netchecker:
55 enabled: true
56 addons:
57 netchecker:
58 enabled: true
Tomáš Kukrál1b50f772017-03-23 12:51:32 +010059
Tomáš Kukrálf78baa62017-04-20 16:18:16 +020060Configure service verbosity
61
62.. code-block:: yaml
63
64 parameters:
65 kubernetes:
66 master:
67 verbosity: 2
68 pool:
69 verbosity: 2
70
Tomáš Kukrálaff35262017-04-18 12:37:45 +020071Enable autoscaler for dns addon. Poll period can be skipped.
72
73.. code-block:: yaml
74
75 kubernetes:
76 master:
77 addons:
78 dns:
79 domain: cluster.local
80 enabled: true
81 replicas: 1
82 server: 10.254.0.10
83 autoscaler:
84 enabled: true
85 poll-period-seconds: 60
86
87
Tomáš Kukrál6ef3f892017-02-15 12:02:22 +010088Pass aditional parameters to daemons:
89
90.. code-block:: yaml
91
92 parameters:
93 kubernetes:
94 master:
95 apiserver:
96 daemon_opts:
97 storage-backend: pigeon
98 controller_manager:
99 daemon_opts:
100 log-dir: /dev/nulL
101 pool:
102 kubelet:
103 daemon_opts:
104 max-pods: "6"
105
Tomáš Kukrál189da4b2017-01-18 14:30:09 +0100106
Ales Komarek688a04c2016-07-15 15:12:30 +0200107Containers on pool definitions in pool.service.local
108
Jakub Pavlik7e985322016-07-17 13:16:15 +0200109.. code-block:: yaml
110
111 parameters:
112 kubernetes:
113 pool:
114 service:
115 local:
116 enabled: False
117 service: libvirt
118 cluster: openstack-compute
119 namespace: default
120 role: ${linux:system:name}
121 type: LoadBalancer
122 kind: Deployment
123 apiVersion: extensions/v1beta1
124 replicas: 1
125 host_pid: True
126 nodeSelector:
127 - key: openstack
128 value: ${linux:system:name}
129 hostNetwork: True
130 container:
131 libvirt-compute:
132 privileged: True
133 image: ${_param:docker_repository}/libvirt-compute
134 tag: ${_param:openstack_container_tag}
Ales Komarek688a04c2016-07-15 15:12:30 +0200135
136Master definition
137
marcoacdae7e2015-12-02 15:35:37 +0100138.. code-block:: yaml
139
140 kubernetes:
Jakub Pavlik495d06f2016-06-17 11:33:05 +0200141 master:
142 addons:
143 dns:
144 domain: cluster.local
145 enabled: true
146 replicas: 1
147 server: 10.254.0.10
Jakub Pavlik495d06f2016-06-17 11:33:05 +0200148 admin:
149 password: password
150 username: admin
151 apiserver:
152 address: 10.0.175.100
Swann Croisetff97efc2017-02-23 13:32:33 +0100153 secure_port: 443
154 insecure_address: 127.0.0.1
155 insecure_port: 8080
Jakub Pavlik495d06f2016-06-17 11:33:05 +0200156 ca: kubernetes
157 enabled: true
158 etcd:
159 host: 127.0.0.1
160 members:
161 - host: 10.0.175.100
162 name: node040
163 name: node040
164 token: ca939ec9c2a17b0786f6d411fe019e9b
165 kubelet:
166 allow_privileged: true
167 network:
168 engine: calico
169 hash: fb5e30ebe6154911a66ec3fb5f1195b2
170 private_ip_range: 10.150.0.0/16
171 version: v0.19.0
172 service_addresses: 10.254.0.0/16
173 storage:
174 engine: glusterfs
175 members:
176 - host: 10.0.175.101
177 port: 24007
178 - host: 10.0.175.102
179 port: 24007
180 - host: 10.0.175.103
181 port: 24007
182 port: 24007
183 token:
184 admin: DFvQ8GJ9JD4fKNfuyEddw3rjnFTkUKsv
185 controller_manager: EreGh6AnWf8DxH8cYavB2zS029PUi7vx
186 dns: RAFeVSE4UvsCz4gk3KYReuOI5jsZ1Xt3
187 kube_proxy: DFvQ8GelB7afH3wClC9romaMPhquyyEe
188 kubelet: 7bN5hJ9JD4fKjnFTkUKsvVNfuyEddw3r
189 logging: MJkXKdbgqRmTHSa2ykTaOaMykgO6KcEf
190 monitoring: hnsj0XqABgrSww7Nqo7UVTSZLJUt2XRd
191 scheduler: HY1UUxEPpmjW4a1dDLGIANYQp1nZkLDk
192 version: v1.2.4
193
marcoacdae7e2015-12-02 15:35:37 +0100194
195 kubernetes:
Jakub Pavlik495d06f2016-06-17 11:33:05 +0200196 pool:
197 address: 0.0.0.0
198 allow_privileged: true
199 ca: kubernetes
200 cluster_dns: 10.254.0.10
201 cluster_domain: cluster.local
202 enabled: true
203 kubelet:
204 allow_privileged: true
205 config: /etc/kubernetes/manifests
206 frequency: 5s
207 master:
208 apiserver:
209 members:
210 - host: 10.0.175.100
211 etcd:
212 members:
213 - host: 10.0.175.100
214 host: 10.0.175.100
215 network:
216 engine: calico
217 hash: fb5e30ebe6154911a66ec3fb5f1195b2
218 version: v0.19.0
219 token:
220 kube_proxy: DFvQ8GelB7afH3wClC9romaMPhquyyEe
221 kubelet: 7bN5hJ9JD4fKjnFTkUKsvVNfuyEddw3r
222 version: v1.2.4
marcoacdae7e2015-12-02 15:35:37 +0100223
Jakub Pavlik495d06f2016-06-17 11:33:05 +0200224Kubernetes with OpenContrail network plugin
225------------------------------------------------
marcoacdae7e2015-12-02 15:35:37 +0100226
227On Master:
228
229.. code-block:: yaml
230
231 kubernetes:
232 master:
233 network:
234 engine: opencontrail
235 host: 10.0.170.70
236 port: 8082
237 default_domain: default-domain
238 default_project: default-domain:default-project
239 public_network: default-domain:default-project:Public
240 public_ip_range: 185.22.97.128/26
241 private_ip_range: 10.150.0.0/16
242 service_cluster_ip_range: 10.254.0.0/16
243 network_label: name
244 service_label: uses
245 cluster_service: kube-system/default
246 network_manager:
247 image: pupapaik/opencontrail-kube-network-manager
248 tag: release-1.1-jpa-final-1
249
250On pools:
251
252.. code-block:: yaml
253
254 kubernetes:
255 pool:
256 network:
257 engine: opencontrail
258
Jakub Pavlik1cfc1fe2016-07-25 11:01:52 +0200259Kubernetes control plane running in systemd
260-------------------------------------------
261
262By default kube-apiserver, kube-scheduler, kube-controllermanager, kube-proxy, etcd running in docker containers through manifests. For stable production environment this should be run in systemd.
263
264.. code-block:: yaml
265
266 kubernetes:
267 master:
268 container: false
269
270 kubernetes:
271 pool:
272 container: false
273
marco055ff852016-07-27 15:22:33 +0200274Because k8s services run under kube user without root privileges, there is need to change secure port for apiserver.
275
276.. code-block:: yaml
277
278 kubernetes:
279 master:
280 apiserver:
281 secure_port: 8081
282
marcoacdae7e2015-12-02 15:35:37 +0100283Kubernetes with Flannel
284-----------------------
285
286On Master:
287
288.. code-block:: yaml
289
290 kubernetes:
291 master:
292 network:
293 engine: flannel
Jakub Pavlik7e985322016-07-17 13:16:15 +0200294 # If you don't register master as node:
marcoa05621f2016-07-14 10:35:24 +0200295 etcd:
296 members:
297 - host: 10.0.175.101
298 port: 4001
299 - host: 10.0.175.102
300 port: 4001
301 - host: 10.0.175.103
302 port: 4001
marcoacdae7e2015-12-02 15:35:37 +0100303 common:
304 network:
305 engine: flannel
306
307On pools:
308
309.. code-block:: yaml
310
311 kubernetes:
312 pool:
313 network:
314 engine: flannel
marcoa05621f2016-07-14 10:35:24 +0200315 etcd:
316 members:
317 - host: 10.0.175.101
318 port: 4001
319 - host: 10.0.175.102
320 port: 4001
321 - host: 10.0.175.103
322 port: 4001
marcoacdae7e2015-12-02 15:35:37 +0100323 common:
324 network:
325 engine: flannel
326
327Kubernetes with Calico
328-----------------------
329
330On Master:
331
332.. code-block:: yaml
333
334 kubernetes:
335 master:
336 network:
337 engine: calico
Jakub Pavlik7e985322016-07-17 13:16:15 +0200338 # If you don't register master as node:
marcoa05621f2016-07-14 10:35:24 +0200339 etcd:
340 members:
341 - host: 10.0.175.101
342 port: 4001
343 - host: 10.0.175.102
344 port: 4001
345 - host: 10.0.175.103
346 port: 4001
marcoacdae7e2015-12-02 15:35:37 +0100347
348On pools:
349
350.. code-block:: yaml
351
352 kubernetes:
353 pool:
354 network:
355 engine: calico
marcoa05621f2016-07-14 10:35:24 +0200356 etcd:
357 members:
358 - host: 10.0.175.101
359 port: 4001
360 - host: 10.0.175.102
361 port: 4001
362 - host: 10.0.175.103
363 port: 4001
marcoacdae7e2015-12-02 15:35:37 +0100364
Tomáš Kukrál34c59362017-03-01 14:00:37 +0100365Running with secured etcd:
366
367.. code-block:: yaml
368
369 kubernetes:
370 pool:
371 network:
372 engine: calico
373 etcd:
374 ssl:
375 enabled: true
376 master:
377 network:
378 engine: calico
379 etcd:
380 ssl:
381 enabled: true
382
Tomáš Kukrál7e91a942017-03-23 16:02:52 +0100383Enable Prometheus metrics in Felix
384
385.. code-block:: yaml
386
387 kubernetes:
388 pool:
389 network:
390 prometheus:
391 enabled: true
392 master:
393 network:
394 prometheus:
395 enabled: true
396
Jakub Pavlik7e985322016-07-17 13:16:15 +0200397Post deployment configuration
398
399.. code-block:: bash
Jakub Pavlik232833c2016-07-17 13:21:00 +0200400
Jakub Pavlik7e985322016-07-17 13:16:15 +0200401 # set ETCD
402 export ETCD_AUTHORITY=10.0.111.201:4001
403
404 # Set NAT for pods subnet
405 calicoctl pool add 192.168.0.0/16 --nat-outgoing
406
407 # Status commands
408 calicoctl status
409 calicoctl node show
410
Jakub Pavlik495d06f2016-06-17 11:33:05 +0200411Kubernetes with GlusterFS for storage
412---------------------------------------------
413
414.. code-block:: yaml
415
416 kubernetes:
Tomáš Kukrál4f0dae32017-03-21 19:04:19 +0100417 master:
Jakub Pavlik495d06f2016-06-17 11:33:05 +0200418 ...
419 storage:
420 engine: glusterfs
421 port: 24007
422 members:
423 - host: 10.0.175.101
424 port: 24007
425 - host: 10.0.175.102
426 port: 24007
427 - host: 10.0.175.103
428 port: 24007
429 ...
430
marco45fc1b72016-07-02 16:11:18 +0200431Kubernetes namespaces
432---------------------
433
434Create namespace:
435
436.. code-block:: yaml
437
438 kubernetes:
Tomáš Kukrál4f0dae32017-03-21 19:04:19 +0100439 master:
marco45fc1b72016-07-02 16:11:18 +0200440 ...
441 namespace:
442 kube-system:
443 enabled: True
444 namespace2:
445 enabled: True
446 namespace3:
447 enabled: False
448 ...
449
450Kubernetes labels
451-----------------
452
Marek Celoud901020b2017-01-27 14:51:41 +0100453Label node:
marco45fc1b72016-07-02 16:11:18 +0200454
455.. code-block:: yaml
456
Marek Celoud901020b2017-01-27 14:51:41 +0100457 kubernetes:
458 master:
459 label:
460 label01:
461 value: value01
462 node: node01
463 enabled: true
464 key: key01
marco45fc1b72016-07-02 16:11:18 +0200465 ...
marco45fc1b72016-07-02 16:11:18 +0200466
marcof7efecb2016-07-16 16:13:37 +0200467Pull images from private registries
468-----------------------------------
469
470.. code-block:: yaml
471
472 kubernetes:
Tomáš Kukrál4f0dae32017-03-21 19:04:19 +0100473 master:
marcof7efecb2016-07-16 16:13:37 +0200474 ...
475 registry:
476 secret:
477 registry01:
478 enabled: True
479 key: (get from `cat /root/.docker/config.json | base64`)
480 namespace: default
481 ...
482 control:
483 ...
484 service:
485 service01:
486 ...
487 image_pull_secretes: registry01
488 ...
489
Jakub Pavlik495d06f2016-06-17 11:33:05 +0200490Kubernetes Service Definitions in pillars
491==========================================
492
493Following samples show how to generate kubernetes manifest as well and provide single tool for complete infrastructure management.
494
495Deployment manifest
496---------------------
marcoacdae7e2015-12-02 15:35:37 +0100497
498.. code-block:: yaml
499
500 salt:
501 control:
502 enabled: True
503 hostNetwork: True
504 service:
505 memcached:
506 privileged: True
507 service: memcached
508 role: server
509 type: LoadBalancer
510 replicas: 3
511 kind: Deployment
512 apiVersion: extensions/v1beta1
513 ports:
514 - port: 8774
515 name: nova-api
516 - port: 8775
517 name: nova-metadata
518 volume:
519 volume_name:
520 type: hostPath
521 mount: /certs
522 path: /etc/certs
523 container:
524 memcached:
525 image: memcached
526 tag:2
527 ports:
528 - port: 8774
529 name: nova-api
530 - port: 8775
531 name: nova-metadata
532 variables:
533 - name: HTTP_TLS_CERTIFICATE:
534 value: /certs/domain.crt
535 - name: HTTP_TLS_KEY
536 value: /certs/domain.key
537 volumes:
538 - name: /etc/certs
539 type: hostPath
540 mount: /certs
541 path: /etc/certs
542
marcobe30c8d2016-10-11 19:16:35 +0200543PetSet manifest
544---------------------
545
546.. code-block:: yaml
547
548 service:
549 memcached:
550 apiVersion: apps/v1alpha1
551 kind: PetSet
552 service_name: 'memcached'
553 container:
554 memcached:
555 ...
556
557
Filip Pytloun9a4a40f2016-09-22 16:28:19 +0200558Configmap
559---------
560
561You are able to create configmaps using support layer between formulas.
562It works simple, eg. in nova formula there's file ``meta/config.yml`` which
563defines config files used by that service and roles.
564
565Kubernetes formula is able to generate these files using custom pillar and
566grains structure. This way you are able to run docker images built by any way
567while still re-using your configuration management.
568
569Example pillar:
570
571.. code-block:: bash
572
573 kubernetes:
574 control:
Jakub Pavlika2779722016-11-25 15:35:26 +0100575 config_type: default|kubernetes # Output is yaml k8s or default single files
Filip Pytloun9a4a40f2016-09-22 16:28:19 +0200576 configmap:
577 nova-control:
578 grains:
579 # Alternate grains as OS running in container may differ from
580 # salt minion OS. Needed only if grains matters for config
581 # generation.
582 os_family: Debian
583 pillar:
584 # Generic pillar for nova controller
585 nova:
586 controller:
587 enabled: true
588 versionn: liberty
589 ...
590
591To tell which services supports config generation, you need to ensure pillar
592structure like this to determine support:
593
594.. code-block:: yaml
595
596 nova:
597 _support:
598 config:
599 enabled: true
600
marcod4d3dbd2016-09-27 11:36:40 +0200601initContainers
602--------------
603
604Example pillar:
605
606.. code-block:: bash
607
608 kubernetes:
609 control:
610 service:
611 memcached:
612 init_containers:
613 - name: test-mysql
614 image: busybox
615 command:
616 - sleep
617 - 3600
618 volumes:
619 - name: config
620 mount: /test
621 - name: test-memcached
622 image: busybox
623 command:
624 - sleep
625 - 3600
626 volumes:
627 - name: config
628 mount: /test
629
marcoee859d32016-11-07 11:04:57 +0100630Affinity
631--------
632
633podAffinity
634===========
635
636Example pillar:
637
638.. code-block:: bash
639
640 kubernetes:
641 control:
642 service:
643 memcached:
644 affinity:
645 pod_affinity:
646 name: podAffinity
647 expression:
648 label_selector:
649 name: labelSelector
650 selectors:
651 - key: app
652 value: memcached
653 topology_key: kubernetes.io/hostname
654
655podAntiAffinity
656===============
657
658Example pillar:
659
660.. code-block:: bash
661
662 kubernetes:
663 control:
664 service:
665 memcached:
666 affinity:
667 anti_affinity:
668 name: podAntiAffinity
669 expression:
670 label_selector:
671 name: labelSelector
672 selectors:
673 - key: app
674 value: opencontrail-control
675 topology_key: kubernetes.io/hostname
676
677nodeAffinity
678===============
679
680Example pillar:
681
682.. code-block:: bash
683
684 kubernetes:
685 control:
686 service:
687 memcached:
688 affinity:
689 node_affinity:
690 name: nodeAffinity
691 expression:
692 match_expressions:
693 name: matchExpressions
694 selectors:
695 - key: key
696 operator: In
697 values:
698 - value1
699 - value2
700
marcoacdae7e2015-12-02 15:35:37 +0100701Volumes
702-------
703
704hostPath
Jakub Pavlik495d06f2016-06-17 11:33:05 +0200705==========
marcoacdae7e2015-12-02 15:35:37 +0100706
707.. code-block:: yaml
708
marcob469f882016-09-27 09:56:13 +0200709 service:
marcoacdae7e2015-12-02 15:35:37 +0100710 memcached:
marcob469f882016-09-27 09:56:13 +0200711 container:
712 memcached:
713 volumes:
714 - name: volume1
715 mountPath: /volume
716 readOnly: True
marcoacdae7e2015-12-02 15:35:37 +0100717 ...
marcob469f882016-09-27 09:56:13 +0200718 volume:
719 volume1:
720 name: /etc/certs
721 type: hostPath
722 path: /etc/certs
marcoacdae7e2015-12-02 15:35:37 +0100723
724emptyDir
Ales Komarek688a04c2016-07-15 15:12:30 +0200725========
marcoacdae7e2015-12-02 15:35:37 +0100726
727.. code-block:: yaml
728
marcob469f882016-09-27 09:56:13 +0200729 service:
marcoacdae7e2015-12-02 15:35:37 +0100730 memcached:
marcob469f882016-09-27 09:56:13 +0200731 container:
732 memcached:
733 volumes:
734 - name: volume1
735 mountPath: /volume
736 readOnly: True
marcoacdae7e2015-12-02 15:35:37 +0100737 ...
marcob469f882016-09-27 09:56:13 +0200738 volume:
739 volume1:
740 name: /etc/certs
741 type: emptyDir
742
743configMap
744=========
745
746.. code-block:: yaml
747
748 service:
749 memcached:
750 container:
751 memcached:
752 volumes:
753 - name: volume1
754 mountPath: /volume
755 readOnly: True
756 ...
757 volume:
758 volume1:
759 type: config_map
760 item:
761 configMap1:
762 key: config.conf
763 path: config.conf
764 configMap2:
765 key: policy.json
766 path: policy.json
Jakub Pavlik27ad3a62016-08-05 11:39:45 +0200767
marco0eda4fb2016-10-10 19:08:27 +0200768To mount single configuration file instead of whole directory:
769
770.. code-block:: yaml
771
772 service:
773 memcached:
774 container:
775 memcached:
776 volumes:
777 - name: volume1
778 mountPath: /volume/config.conf
779 sub_path: config.conf
780
marcofcc20d02016-10-10 09:56:12 +0200781Generating Jobs
782===============
783
784Example pillar:
785
786.. code-block:: yaml
787
788 kubernetes:
789 control:
790 job:
791 sleep:
792 job: sleep
793 restart_policy: Never
794 container:
795 sleep:
796 image: busybox
797 tag: latest
798 command:
799 - sleep
800 - "3600"
801
802Volumes and Variables can be used as the same way as during Deployment generation.
803
804Custom params:
805
806.. code-block:: yaml
807
808 kubernetes:
809 control:
810 job:
811 host_network: True
812 host_pid: True
813 container:
814 sleep:
815 privileged: True
816 node_selector:
817 key: node
818 value: one
819 image_pull_secretes: password
820
Jakub Pavlik27ad3a62016-08-05 11:39:45 +0200821Documentation and Bugs
Filip Pytloun06a55402016-08-12 14:53:30 +0200822======================
Jakub Pavlik27ad3a62016-08-05 11:39:45 +0200823
824To learn how to deploy OpenStack Salt, consult the documentation available
825online at:
826
827 https://wiki.openstack.org/wiki/OpenStackSalt
828
829In the unfortunate event that bugs are discovered, they should be reported to
830the appropriate bug tracker. If you obtained the software from a 3rd party
831operating system vendor, it is often wise to use their own bug tracker for
832reporting problems. In all other cases use the master OpenStack bug tracker,
833available at:
834
835 http://bugs.launchpad.net/openstack-salt
836
837Developers wishing to work on the OpenStack Salt project should always base
838their work on the latest formulas code, available from the master GIT
839repository at:
840
841 https://git.openstack.org/cgit/openstack/salt-formula-kubernetes
842
843Developers should also join the discussion on the IRC list, at:
844
845 https://wiki.openstack.org/wiki/Meetings/openstack-salt
Filip Pytloun06a55402016-08-12 14:53:30 +0200846
847Copyright and authors
848=====================
849
850(c) 2016 tcp cloud a.s.
851(c) 2016 OpenStack Foundation
Filip Pytlound06f6272017-02-02 13:02:03 +0100852
853Documentation and Bugs
854======================
855
856To learn how to install and update salt-formulas, consult the documentation
857available online at:
858
859 http://salt-formulas.readthedocs.io/
860
861In the unfortunate event that bugs are discovered, they should be reported to
862the appropriate issue tracker. Use Github issue tracker for specific salt
863formula:
864
865 https://github.com/salt-formulas/salt-formula-kubernetes/issues
866
867For feature requests, bug reports or blueprints affecting entire ecosystem,
868use Launchpad salt-formulas project:
869
870 https://launchpad.net/salt-formulas
871
872You can also join salt-formulas-users team and subscribe to mailing list:
873
874 https://launchpad.net/~salt-formulas-users
875
876Developers wishing to work on the salt-formulas projects should always base
877their work on master branch and submit pull request against specific formula.
878
879 https://github.com/salt-formulas/salt-formula-kubernetes
880
881Any questions or feedback is always welcome so feel free to join our IRC
882channel:
883
884 #salt-formulas @ irc.freenode.net