文章目录
Salt的灵活性带来了许多关于配置文件组织结构的问题。
本文档旨在通过示例和代码阐明这些方面上的使用要点。
您也可以参考在Github上维护的这一篇技术资料:Salt Best Practices
GENERAL RULES - 通用规则
- 应尽可能强调模块化和清晰度。
- 在pillars和states之间建立明确的关系。
- 在有意义的情况下多利用变量但不要过度使用变量。
- 将敏感数据存储在pillar中。
- 不要在pillar top file文件中使用grains匹配任何敏感的pillars数据。
STRUCTURING STATES AND FORMULAS - 按结构化的方式组织states和formulas数据
在构造Salt States和Formulas时,重要的是从目录层次的结构化开始。 正确的目录结构可以通过目视检查状态的名称就清楚地了解用户为每个状态定义的功能。
回顾MySQL Salt公式,在查看可用状态的示例时,可以清楚地看到最终用户的所能获得的好处:
/srv/salt/mysql/files/
/srv/salt/mysql/client.sls
/srv/salt/mysql/map.jinja
/srv/salt/mysql/python.sls
/srv/salt/mysql/server.sls
有这样的目录结构以后,就可以按以下列方式在top file文件中引用这些状态:
base:
'web*':
- mysql.client
- mysql.python
'db*':
- mysql.server
这个明确的定义确保用户可以被正确地告知每个state将要做什么。
下面是另一个来自 vim-formula 的示例:
/srv/salt/vim/files/
/srv/salt/vim/absent.sls
/srv/salt/vim/init.sls
/srv/salt/vim/map.jinja
/srv/salt/vim/nerdtree.sls
/srv/salt/vim/pyflakes.sls
/srv/salt/vim/salt.sls
看我们可以怎么在 top file 文件中引用这些状态:
/srv/salt/top.sls:
base:
'web*':
- vim
- vim.nerdtree
- vim.pyflakes
- vim.salt
'db*':
- vim.absent
使用清晰的顶级目录以及正确命名的状态可以降低整体的复杂性,并使用户既可以一目了然地了解所包含的内容以及它所在的位置。
此外,应尽可能经常使用Formulas。
注意: saltstack-formula GitHub项目中的公式存储库不应直接配置到自动获取新更新(如GitFS或类似工具)的系统。 相反,公式存储库应该在GitHub上创建分支或在本地克隆,以确保不会发生意外的自动更改。
STRUCTURING PILLAR FILES - 按结构化的方式管理pillar文件
Pillar用于存储与minions有关的安全和不安全的数据。 在设计/srv/pillar
目录的结构时,其中包含的pillars应该专注于清晰简洁的数据,让用户可以轻松查看、修改和理解这些数据。
/srv/pillar/
目录主要由top.sls
控制。 应该注意的是,pillar top.sls
文件本身不用作声明变量及其值的位置。 top.sls
用作包含其他pillars文件的方式,并根据环境或grains组织它们的匹配方式。
top.sls
示能如下所示:
/srv/pillar/top.sls:
base:
'*':
- packages
可以将任意数量的匹配器添加到base环境中。 例如,以下是上述Pillar top file文件的一个扩展版本:
/srv/pillar/top.sls:
base:
'*':
- packages
'web*':
- apache
- vim
或者看一个更复杂的例子,在众多环境中使用各种匹配器:
/srv/pillar/top.sls:
base:
'*':
- apache
dev:
'os:Debian':
- match: grain
- vim
test:
'* and not G@os: Debian':
- match: compound
- emacs
通过这些示例可以清楚地看到top file文件如何为用户提供支持,但如果使用不当,可能会导致配置混乱。 这就是为什么理解pillar的top file文件不用于变量定义的一个很重要的原因。
/srv/pillar/
目录中的每个SLS文件应对应于它匹配的状态。
这意味着apache
pillar文件应该包含与Apache相关的数据。 以这种方式构建文件可以再次确保模块化,并在整个Salt环境中创建一致的理解。 用户可以预期在Apache状态中找到的pillar变量将存在于Apache pillar文件内:
/srv/pillar/apache.sls:
apache:
lookup:
name: httpd
config:
tmpl: /etc/httpd/httpd.conf
虽然这个pillar文件很简单,但它显示了一个pillar文件如何与它所关联的状态明确相关。
VARIABLE FLEXIBILITY - 变量的灵活性
Salt允许用户在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,在该pillar中可以覆盖、修改数据并将其应用于多个状态或单个状态内的位置:
/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
这种灵活性为用户提供了一个集中的位置来修改变量,这在环境不断变得庞大和复杂时非常重要。
MODULARITY WITHIN STATES - 按模块化的方式使用STATES
确保状态是模块化的是Salt中需要理解的关键概念之一。 在创建状态时,用户必须考虑可以重复使用状态的次数,以及它依赖于操作的次数。 下面是几个例子,它们将迭代地解释用户如何从一个非常不模块化的状态转变为模块化:
/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
上面的例子可能是编写状态时最糟糕的一种情况。 通过将pkg/service和托管文件直接命名为状态ID,明显缺乏重点。 这将导致在该状态下改变多个需求,以及可能依赖于状态的其他需求。
想象一下,如果在另一个状态下对httpd
包使用了require,那么突然它就是一个自定义包。 现在需要在多个位置进行更改,这会增加复杂性并导致更容易出错的配置。
还存在将配置文件放在init中的问题,因为用户将无法简单地安装该服务并选择使用默认的conf文件。
我们的第二个修订版开始使用-name
来引用,而不是使用直接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
上面的init文件比我们原来的要好一些,但它有几个问题会导致缺乏模块化。 第一个问题是一些条目的静态值的使用,例如服务名称、托管文件的名称和托管文件的来源。 当这些项目被硬编码时,它们变得难以修改并且增加了出现出错的机会。 它还会导致在更改这些项目时需要进行多次编辑(想象一下,如果整个state都有数十次这样的事件发生!)。 还存在关于配置文件数据与服务和包处于相同状态中的问题。
在下一个示例中,将采取步骤开始解决这些问题。 首先添加map.jinja文件(如公式文档中所述),并修改静态值:
/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
apache_conf:
file.managed:
- name: {{ apache.conf }}
- source: {{ salt['pillar.get']('apache:lookup:config:tmpl') }}
- template: jinja
- user: root
- watch_in:
- service: apache
对此状态的更改现在允许我们轻松识别变量的位置,并确保它们灵活且易于修改。 虽然这朝着正确的方向迈出了一步,但尚未完成。 假设用户不想使用提供的conf文件,甚至是他们自己的配置文件,而是使用默认的apache conf。 使用当前状态设置,这是不可能的。 为了达到这种模块化水平,这个状态需要再分为两种状态。
/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
这个新结构现在允许用户选择是否只安装默认的Apache,或者如果他们愿意,可以覆盖默认包、服务、配置文件位置或配置文件本身。 除此之外,多个文件之间的数据已被打破,允许用户识别他们并根据需要更改相关数据的位置。
STORING SECURE DATA - 关于保存敏感数据
敏感数据是指您不希望与访问服务器的任何人共享的那些信息。 这可能包括密码、密钥或其他信息等数据。
由于连接的每个服务器都可以访问状态中的所有数据,因此将敏感数据存储在pillar中非常重要。 这将确保只有那些需要此敏感数据的服务器才能访问它。 在此示例中,用户可以从不安全配置转到只能由相应主机访问的配置:
/srv/salt/mysql/testerdb.sls:
testdb:
mysql_database.present:
- name: testerdb
/srv/salt/mysql/user.sls:
include:
- mysql.testerdb
testdb_user:
mysql_user.present:
- name: frank
- password: "test3rdb"
- host: localhost
- require:
- sls: mysql.testerdb
许多用户会查看此状态,并看到密码是以纯文本形式存在的,这是非常有问题的。它导致几个可能无法立即看到的问题。
大多数用户都清楚这些问题中的第一个,即密码在此状态下可见。这意味着任何minion都会拥有此数据的副本,因此密码是一个主要的安全问题,因为minions可能不会像Master服务器那样做严格的访问控制。
可以遇到的另一个问题是Master服务器上的用户访问。如果每个人都可以访问状态(或其存储库),那么他们就可以查看此密码。只让少数用户可访问您的密码数据对于安全性至关重要。
还有便携性问题。以这种方式配置状态时,会导致需要进行多项更改。这在上面的章节中进行了讨论。如果状态不便携,可能会导致更多工作!
修复此问题相对简单,只需将内容移动到关联的pillar中:
/srv/pillar/mysql.sls:
mysql:
lookup:
name: testerdb
password: test3rdb
user: frank
host: localhost
/srv/salt/mysql/testerdb.sls:
testdb:
mysql_database.present:
- name: {{ salt['pillar.get']('mysql:lookup:name') }}
/srv/salt/mysql/user.sls:
include:
- mysql.testerdb
testdb_user:
mysql_user.present:
- name: {{ salt['pillar.get']('mysql:lookup:user') }}
- password: {{ salt['pillar.get']('mysql:lookup:password') }}
- host: {{ salt['pillar.get']('mysql:lookup:host') }}
- require:
- sls: mysql.testerdb
既然已将数据库详细信息移动到关联的pillar文件,则只有通过pillar定向匹配的计算机才能访问这些详细信息。 还可以防止对无权查看这些详细信息的用户的访问,同时确保他们仍然能够编写利用此信息的状态。