salt jinja2 grain pillar module

salt通过Jinja2模板以及grain和pillar扩展主机状态

  1. 简介

    1
    我们学习了简单状态文件的编写,实际情况我们会遇到更复杂的情况,比如:对不同操作系统安装软件,根据主机CPU梳理,内存动态生成软件配置文件等,这一切都需要Jinja2以及grain和pillar的辅助.
  2. Jinja2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    Jinja2是一个强大的python模板引擎,可以使用代码动态生成创建文件的内容

    //Jinja2 变量
    Jinja2包含变量和表达式: 变量用"{{}}"包围,表达式用"{%%}"包围
    * 字符串类型: {% set var = 'good' %} {{var}}
    * 列表类型: {% list = [1,2,3] %} {{list[0]}}
    * 字典类型: {% dict = {'a':1,'b':2} %} {{dict['a']}}

    例如:
    [root@saltserver ~]# vim /srv/salt/var.sls
    {% set var = 'hello world' %}
    test_var:
    cmd.run:
    - name: echo "var is {{var}}"
    [root@saltserver ~]# salt '*' state.sls var
    192.168.13.187:
    ----------
    ID: test_var
    Function: cmd.run
    Name: echo "var is hello world"
    Result: True
    Comment: Command "echo "var is hello world"" run
    Started: 11:07:33.190097
    Duration: 19.801 ms
    Changes:
    ----------
    pid:
    29983
    retcode:
    0
    stderr:
    stdout:
    var is hello world

    Summary for 192.168.13.187
    ------------
    Succeeded: 1 (changed=1)
    Failed: 0
    ------------
    Total states run: 1
    Total run time: 19.801 ms

    //Jinja2 流程控制
    * for
    例一:
    {% for user in users %}
    {{user}}
    {% endfor %}
    例二:
    {% for key,value in my_dict.iteritems() %}
    {{key}}
    {{value}}
    {% endfor %}

    注意: 模板中循环不能有break和continue.但你可以在迭代中过滤序列来跳过项目
    {% for user in users if not user.hidden %}
    {{user.username}}
    {% endfor %}

    * if
    例一:
    {% if users %}
    {% for user in users %}
    {{user.username}}
    {% endfor %}
    {% endif %}

    例二:
    {% if kenny.sick %}
    kenny is sick.
    {% elif kenny.dead %}
    You killed Kennny! You bastard!!!
    {% else %}
    Kenny looks okay --- so far
    {% endif %}
  3. grain 和 pillar

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    grain和pillar本质上都是key,value型数据库.
    grain存储在minion上的数据,minion启动后就进行grains计算.grain是一种静态数据,包括操作系统类型,版本,CPU数量,内存大小等.这些数据不经常变,即时有所变化重启minion也会重新计算生成
    pillar数据存储在master上,指定的minion只能看到自己的pillar数据,其他的minion看不到任何pillar数据,这一点与状态文件正好相反.所有通过认证的minion都可以获取状态文件,但是每个minion却都有自己的一套pillar数据,而且每台minion的pillar都进行了加密,所以很适用于敏感数据


    //grain相关命令
    * 列出minion上的grains项
    [root@saltserver ~]# salt '*' grains.ls
    * 查看minion上具体某个grain项
    [root@saltserver ~]# salt '*' grains.item os
    * 列出所有grain详细信息
    [root@saltserver ~]# salt '*' grains.items
    * 设置grain数据
    [root@saltserver ~]# salt '192.168.13.187' grains.setval my_grain bar
    * 设置grain多个值
    [root@saltserver ~]# salt '192.168.13.187' grains.setvals "{'k1':'v1','k2':'v2'}"
    * 设置列表
    [root@saltserver ~]# salt '192.168.13.187' grains.setval my_dict '[1,2,3]'
    * 查看item
    [root@saltserver ~]# salt '192.168.13.187' grains.item my_grain
    [root@saltserver ~]# salt '192.168.13.187' grains.item k1 k2
    [root@saltserver ~]# salt '192.168.13.187' grains.item my_dict
    * 在saltminion上查看
    [root@saltminion ~]# cat /etc/salt/grains
    k1: v1
    k2: v2
    my_dict:
    - 1
    - 2
    - 3
    my_grain: bar

    * grains_module方式设置
    方式一: 在salt-master端设置,下发到minion
    创建模块目录:
    [root@saltserver ~]# mkdir -p /srv/salt/_grains
    编写模块:
    [root@saltserver ~]# vim /srv/salt/_grains/my_grain.py
    import time
    def now():
    grains = {}
    grains['now'] = time.time()
    return grains
    同步模块到minion:
    [root@saltserver ~]# salt '*' saltutil.sync_all
    192.168.13.187:
    ----------
    beacons:
    clouds:
    engines:
    grains:
    - grains.my_grain
    log_handlers:
    modules:
    output:
    proxymodules:
    renderers:
    returners:
    sdb:
    states:
    utils:
    重新加载一次模块:
    [root@saltserver ~]# salt '*' sys.reload_modules
    192.168.13.187:
    True
    查看设置的grains:
    [root@saltserver ~]# salt '*' grains.item now
    192.168.13.187:
    ----------
    now:
    1502076872.27
    删除自定义granins:
    [root@saltserver ~]# salt '*' grains.delval my_grain
    192.168.13.187:
    None
    方式二: 在minion端设置
    在minion上修改grain.conf配置文件:
    [root@saltminion ~]# vim /etc/salt/minion.d/grain.conf
    grains:
    new_grain: bar
    new_grain_dict:
    - one
    - two
    - three
    重启salt-minion服务:
    [root@saltminion ~]# pkill salt-minion
    [root@saltminion ~]# salt-minion -c /etc/salt -d
    在master端进行验证:
    [root@saltserver ~]# salt '*' grains.item new_grain_dict
    192.168.13.187:
    ----------
    new_grain_dict:
    - one
    - two
    - three


    //pillar相关命令
    * 列出minion上所有pillar详细信息
    [root@saltserver ~]# salt '*' pillar.items
    * 查询minion上某一个具体grain值
    [root@saltserver ~]# salt '*' pillar.item foo

    * 设置pillar数据
    建立pillar目录:
    [root@saltserver ~]# mkdir -p /srv/pillar
    创建pillar文件(sls文件):
    [root@saltserver ~]# vim /srv/pillar/minion_one_key.sls
    private_key: minion_one_key
    建立入口文件:
    [root@saltserver ~]# vim /srv/pillar/top.sls
    base:
    '192.168.13.187':
    - minion_one_key
    刷新pillar数据:
    [root@saltserver ~]# salt '*' saltutil.refresh_pillar
    192.168.13.187:
    True
    查看下发的pillar数据:
    [root@saltserver ~]# salt '*' pillar.items
    192.168.13.187:
    ----------
    private_key:
    minion_one_key
  4. 通过Jinja2配合grain和pillar扩展SLS配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    //扩展apache.sls配置文件
    * apache不同操作系统安装
    [root@saltserver ~]# cat /srv/salt/apache.sls
    install_apache:
    pkg.installed:
    {% if grains['os_family'] == 'Debian' %}
    - name: apache2
    {% elif grains['os_family'] == 'RedHat' %}
    - name: httpd
    {% endif %}

    * vim多系统安装
    [root@saltserver ~]# vim /srv/salt/vim.sls
    vim:
    pkg:
    - installed
    {% if grains['os_family'] == 'RedHat' %}
    - name: vim-enhanced
    {% elif grains['os'] == 'Debian' %}
    - name: vim-nox
    {% endif %}

    {% if grains['os'] == 'Arch' %}
    /etc/vimrc:
    file:
    - managed
    - source: salt://vim/vimrc
    - user: root
    - group: root
    - mode: 644
    - template: jinja
    - makedirs: True
    - require:
    - pkg: vim
    {% endif %}

    * epel多系统安装
    [root@saltserver ~]# vim /srv/salt/epel.sls
    epel:
    cmd:
    - run
    {% if grains['osrelease'].startswitch('5') %}
    - name: rpm -Uvh http://xx/5/xxx.rpm
    {% elif grains['osrelease'].startswitch('6') %}
    - name: rpm -Uvh http://xx/6/xxx.rpm
    {% endif %}
    - unless: test -e /etc/yum.repos.d/epel.repo

    * iptables设置
    [root@saltserver ~]# vim /srv/salt/iptables.sls
    iptables:
    pkg:
    - installed
    service:
    - running
    - watch:
    - pkg: iptables
    - file: iptables
    file:
    - managed
    - source: salt://iptables/iptables
    {% if grains['os'] == 'CentOS' or grains['os'] == 'Fedora' %}
    - name: /etc/sysconfig/iptables
    {% elif grains['os'] == 'Arch' %}
    - name: /etc/conf.d/iptables
    {% endif %}
  5. 通过Jinja2配合grain和pillar 动态下发配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    file模块的一个状态函数managed,这个模块可以从master下发配置文件到匹配的minion上,这种下发方式使所有minion得到同样的配置文件.但现实情况是不同的minion有不同的CPU核心数量,有不同大小内存值.很多软件的配置文件需要根据主机配置的不同进行相应的调整.Jinja2配合grain和pillar可以很好的解决此类问题

    * 一个简单的模板文件下发
    编写模板文件:
    [root@saltserver ~]# vim /srv/salt/templates.sls
    template_test:
    file.managed:
    - source: salt://test.j2
    - name: /tmp/test.conf
    - user: root
    - group: root
    - mode: 644
    - template: jinja
    [root@saltserver ~]# vim /srv/salt/test.j2
    cpu_num = {{ grains['num_cpus'] }}
    mem_total = {{ grains['mem_total'] }}
    hostname = {{ grains['host'] }}
    user = {{ pillar['private_key'] }}
    测试模板文件下发:
    [root@saltserver ~]# salt '*' state.sls templates
    192.168.13.187:
    ----------
    ID: template_test
    Function: file.managed
    Name: /tmp/test.conf
    Result: True
    Comment: File /tmp/test.conf updated
    Started: 15:04:28.737899
    Duration: 44.619 ms
    Changes:
    ----------
    diff:
    New file
    mode:
    0644

    Summary for 192.168.13.187
    ------------
    Succeeded: 1 (changed=1)
    Failed: 0
    ------------
    Total states run: 1
    Total run time: 44.619 ms
    在minion上查看:
    [root@saltminion ~]# cat /tmp/test.conf
    cpu_num = 4
    mem_total = 3832
    hostname = saltminion
    user = minion_one_key

    综上,我们看到配置文件内容的变量已经替换成了对应的值,在这个基础上加上Jinja2的逻辑控制功能:
    [root@saltserver ~]# vim /srv/salt/test.j2
    {% if grains['num_cpus'] >= 8 %}
    cpu_num = {{ grains['num_cpus'] }}
    {% endif %}

    {% if grains['mem_total'] <= 512 %}
    mem_total <= 512
    {% elif grains['mem_total'] >= 1024 %}
    mem_total >= 1024
    {% endif %}

    hostname = {{ grains['host'] }}

    # 下发配置文件
    [root@saltserver ~]# salt '*' state.sls templates
    192.168.13.187:
    ----------
    ID: template_test
    Function: file.managed
    Name: /tmp/test.conf
    Result: True
    Comment: File /tmp/test.conf updated
    Started: 15:10:37.611367
    Duration: 48.785 ms
    Changes:
    ----------
    diff:
    ---
    +++
    @@ -1,4 +1,7 @@
    -cpu_num = 4
    -mem_total = 3832
    +
    +
    +
    +mem_total >= 1024
    +
    +
    hostname = saltminion
    -user = minion_one_key

    Summary for 192.168.13.187
    ------------
    Succeeded: 1 (changed=1)
    Failed: 0
    ------------
    Total states run: 1
    Total run time: 48.785 ms

    在minion上查看:
    [root@saltminion ~]# cat /tmp/test.conf
    mem_total >= 1024
    hostname = saltminion