fightclub

saltstack中的设计模式(四)

上一篇文章中,我们介绍了一个技巧,在syndic环境中,让syndic和master都能进行事件处理。在此需要声明的事,salt是一个发展中的软件,通常每年都会有一到两次大的版本更新,会带来若干变更。很多geek技巧会因为版本的更新而被替换为更为简洁的方法。本篇文章是基于2018.3,很多已经同我最早从使用的2015版本的有了极大的差异。因此,在使用新的发布版前,务必阅读release note,了解有哪些关键的特性改变。

在本篇文章,我将介绍一种使用import jinja进行sls复用的方法。接下来我们进入今天的案例

为通过salt部署的服务打上标签

我们会使用salt快速部署一些服务,比如mysql, redis, mongodb, rabbitmq等等。
我们希望每次在部署以后,给这个minion添加一个对应的标签,便于搜索时候使用。

salt有pillar和grains两种方式可以实现这个功能

如果使用pillar,我们需要引入一个动态的pillar存储。我们还要再做一些用来进行写入的state模块,比较复杂。
使用grains会更简单的一些,这样,我们可以通过salt -G ‘roles:tag’按照标签进行搜索。
我们可以使用salt内置的grains.append模块对roles这个列表进行标签添加,但是这个函数不具备stateful的能力,每次执行都会把内容重新append一次。

针对这个问题,我们可以有以下几个方案:

  • 修改grains内置模块,并将代码提交给社区
  • 另外写一个state模块,比如mygrains, 实现自己想要的功能
  • 自己在sls中,通过jinja进行简单的逻辑处理

前两种方案都是推荐使用的,但是我们今天要介绍的是第三种方案,这种处理方式会更为轻量级,也更容易一些。

实现方案

/srv/salt/macros/setrole.sls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{% macro setrole() -%}
{% set roles = salt["grains.get"]("roles", []) %}
{% for role in varargs %}
{% if role not in roles %}
{{ role }}_roles:
{% if roles == [] and loop.index == 1 %}
grains.present:
- force: True
- value:
- {{ role }}
- name: roles
{% else %}
grains.append:
- value:
- {{ role }}
- name: roles
{% endif %}
{% endif %}
{% endfor %}
{%- endmacro %}

我们单独写一个jinja macro, 叫做setrole,并放在某个路径下。
我们再通过jinja import的方式,应用这个写好的宏。

比如我们写了一个dnsmasq部署的state /srv/salt/services/dnsmasq/init.sls,
我们可以在适当的地方加入两行jinja代码即可

1
2
{% from "macros/setrole.sls" import setrole %}
{{ setrole("dns", "dnsmasq") }}

这样,dns和dnsmasq都会被附加在grains 的roles里面。
唯一需要注意的是jinja import的路径和salt fileserver保持一致即可。

总结

  • 默认的sls的所使用的渲染器是jinja|yaml,jinja可以使用import进行引用,而yaml可以通过include进行引用。大部分时候,我们都可以使用include就能解决sls引用的问题, 但是直接import jinja的实现会更简单一些
  • 对于每个sls的执行逻辑是先执行jinja渲染,再执行yaml的方式,类似c语言中的编译预处理,而非根据实际代码出现的先后顺序执行。一定要注意这个逻辑上的区别