SaltStack 最佳实践
通用规则
- 应尽可能强调模块化和清晰性
- 在 pillars 和 states 之间建立明确的关系
- 在必要的时候使用变量,但不要过度使用它们
- 将敏感信息保存在 pillars
- 不要在 pillars top 文件中用 grains 匹配任何敏感的 pillars
遵循 fomulas 的方式构造 states
Formulas 是 Salt 官方预先写好的 states,一个 Salt Formula 对应一个 Git 仓库。如果我们要直接使用,在 salt-master 加入相应配置
gitfs_remotes:
- https://github.com/saltstack-formulas/apache-formula
- https://github.com/saltstack-formulas/memcached-formula
这样就可以使用 salt 提供的 formula 安装这两个软件了。
我们看下 MySQL 这个 formula 的目录结构,
/srv/salt/mysql/files/ # 主要是配置文件
/srv/salt/mysql/client.sls # 客户端安装
/srv/salt/mysql/map.jinja # 变量定义
/srv/salt/mysql/python.sls # python 客户端安装
/srv/salt/mysql/server.sls # 服务端安装
/srv/salt/mysql/top.sls
:
base:
'web*':
- mysql.client
- mysql.python
'db*':
- mysql.server
这样就能清晰地看到依赖 mysql 的设备中,web* 安装客户端,db* 安装服务端。
再看另外一个 vim 的例子,
/srv/salt/vim/files/ # 主要是配置文件
/srv/salt/vim/absent.sls # 卸载 vim
/srv/salt/vim/init.sls # 安装 vim 并配置
/srv/salt/vim/map.jinja # 变量定义
/srv/salt/vim/nerdtree.sls # NERD tree 插件配置
/srv/salt/vim/pyflakes.sls # pyflakes 插件配置
/srv/salt/vim/salt.sls # salt 插件配置
/srv/salt/vim/top.sls
:
base:
'web*':
- vim
- vim.nerdtree
- vim.pyflakes
- vim.salt
'db*':
- vim.absent
我们自己写 states 时,多参考这些 formula,有现成的拿来用就行。
构造 pillars 文件
Pillars 用于存储与 minion 相关的数据(特别是需要加密的数据),在构造 pillars 时要尽量做到让使用者容易查看、修改和理解。下面看个例子,
/srv/pillar/top.sls
:
base:
'*':
- apache
dev:
'os:Debian':
- match: grain
- vim
test:
'* and not G@os: Debian':
- match: compound
- emacs
注意,pillars 的 top.sls 不是用于定义变量的,而是定义不同环境下设备和具体 pillar 文件的对应关系。
比如 apache 的变量保存在相应的 apache.sls 中。
/srv/pillar/apache.sls
:
apache:
lookup:
name: httpd
config:
tmpl: /etc/httpd/httpd.conf
定义变量
虽然变量可以直接在 state sls 里定义,当时通常不这么做。
/srv/salt/apache/conf.sls
:
# 不推荐在这里定义变量
{% set name = 'httpd' %}
{% set tmpl = 'salt://apache/files/httpd.conf' %}
include:
- apache
apache_conf:
file.managed:
- name: {{ name }}
- source: {{ tmpl }}
- template: jinja
- user: root
- watch_in:
- service: apache
建议定义在 pillar 里,这样可以被多个 state 共享。
/srv/pillar/apache.sls
:
apache:
lookup:
name: httpd
config:
tmpl: salt://apache/files/httpd.conf
/srv/salt/apache/conf.sls
:
{% from "apache/map.jinja" import apache with context %}
include:
- apache
apache_conf:
file.managed:
- name: {{ salt['pillar.get']('apache:lookup:name') }}
- source: {{ salt['pillar.get']('apache:lookup:config:tmpl') }}
- template: jinja
- user: root
- watch_in:
- service: apache
平台特定的变量写在 map.jinja 文件,跟 state 放在同一目录。
/srv/salt/apache/map.jinja
:
{% set mysql = salt['grains.filter_by']({
'Debian': {
'name': 'apache2',
'config': '/etc/apache2/apache.conf',
},
'RedHat': {
'name': 'httpd',
'config': '/etc/httpd/httpd.conf',
},
'Gentoo': {
'name': 'apache2',
'config': '/etc/httpd/httpd.conf',
},
}, merge=salt['pillar.get']('apache:lookup')) %}
Pillar 数据会消耗更多 salt-master 的资源,如果 minion 众多,可能对 master 造成冲击,建议将不敏感的也不会多处引用的变量,写在 state 内部的 map.jinja 文件。
states 内部模块化
如何确保 state 模块化是 Salt 中需要理解的关键概念之一。创建 state 时,必须考虑是否可以重复使用,以及操作时有什么依赖。下面看个例子,
/srv/salt/apache/init.sls
:
httpd:
pkg:
- installed
service.running:
- enable: True
/etc/httpd/httpd.conf:
file.managed:
- source: salt://apache/files/httpd.conf
- template: jinja
- watch_in:
- service: httpd
这是个很不好的写法,httpd 直接作为 state ID,但是一个软件的包名和服务名很可能是不一样的。重构之后如下,
/srv/salt/apache/init.sls
:
apache:
pkg.installed:
- name: httpd
service.running:
- name: httpd
- enable: True
apache_conf:
file.managed:
- name: /etc/httpd/httpd.conf
- source: salt://apache/files/httpd.conf
- template: jinja
- watch_in:
- service: apache
进步了一些,但还存在一些问题,比如值都是静态写在 state,不能适配多平台安装。软件和配置写在同一个 state 里,不支持只需要安装软件并使用默认配置的场景。再次重构如下,
/srv/salt/apache/map.jinja
:
{% set apache = salt['grains.filter_by']({
'Debian': {
'server': 'apache2',
'service': 'apache2',
'conf': '/etc/apache2/apache.conf',
},
'RedHat': {
'server': 'httpd',
'service': 'httpd',
'conf': '/etc/httpd/httpd.conf',
},
}, merge=salt['pillar.get']('apache:lookup')) %}
/srv/pillar/apache.sls
:
apache:
lookup:
config:
tmpl: salt://apache/files/httpd.conf
/srv/salt/apache/init.sls
:
{% from "apache/map.jinja" import apache with context %}
apache:
pkg.installed:
- name: {{ apache.server }}
service.running:
- name: {{ apache.service }}
- enable: True
/srv/salt/apache/conf.sls
:
{% from "apache/map.jinja" import apache with context %}
include:
- apache
apache_conf:
file.managed:
- name: {{ apache.conf }}
- source: {{ salt['pillar.get']('apache:lookup:config:tmpl') }}
- template: jinja
- user: root
- watch_in:
- service: apache
这次拆分成 4 个文件,使用 pillar 和 map.jinja 定了变量,state 里动态值改成读取变量,软件和配置拆分到各自的 state。