状态是对minion的一种描述和定义,管理人员可以不关心具体部署任务时如何完成的,只需要描述minion要达到什么状态,底层由salt的状态模块来完成功能
基本入门
我们先做个小案例,使用 salt 的状态模块安装一个 http
1 首先修改 /etc/salt/master,打开file_roots的注释
file_roots是 告诉master,默认sls配置文件在哪里
vim /etc/salt/master
file_roots:
base:
- /srv/salt
2 然后在 /srv下新建一个目录 和 重启 salt-master
mkdir /srv/salt
systemctl restart salt-master
3 切换到 /srv/salt目录 编写sls文件
cd /srv/salt/
vim apache.sls
apache-install:
pkg.installed:
- names:
- httpd
- httpd-devel
apache-service:
service.running:
- name: httpd
- enable: True #设置开启自动启动
- reload: True # 重新加载开启
执行 salt "*" state.sls apache 在所有minion下执行该状态
在任意一台 minion 上执行 lsof -i:80,验证一下httpd是否执行
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
httpd 3311 root 4u IPv6 35197 0t0 TCP *:http (LISTEN)
httpd 3314 apache 4u IPv6 35197 0t0 TCP *:http (LISTEN)
httpd 3315 apache 4u IPv6 35197 0t0 TCP *:http (LISTEN)
httpd 3316 apache 4u IPv6 35197 0t0 TCP *:http (LISTEN)
httpd 3317 apache 4u IPv6 35197 0t0 TCP *:http (LISTEN)
httpd 3318 apache 4u IPv6 35197 0t0 TCP *:http (LISTEN)
高级状态入门
在生产环境中,不同的机器肯定有不同的状态,如果再每台机器一个个执行,显然太慢了,我们可以使用高级状态,在配置文件中,配好哪台机器执行哪些状态
1 要使用高级状态首先,首先配置高级状态的文件,默认为top.sls,存在在/srv/salt/
编辑 top.sls
cd /srv/salt/
vim top.sls
base:
'*.oldboyedu.com':
- apache
2 然后执行 salt '*' state.highstate
这是 匹配 *.oldboyedu.com 的minion 就会执行 apache 这个状态
SLS配置文件的格式
- name:
-
-
-
-
-
常用的状态模块:
file 模块
file.managed 下发文件,确保文件存在
/etc/foo.conf:
file.managed:
- source:
- salt://foo.conf # 来源的文件,相对于 /srv/salt
- user: root
- group: root
- mode: 644
salt "linux-node2.oldboyedu.com" state.sls file_managed
Function: file.managed Result: True Comment: File /etc/foo.conf updated |
file.directory 建立目录
/srv/stuff/substuf:
file.directory:
- user: root
- group: root
- mode: 755
- makedirs: Ture
salt "linux-node2.oldboyedu.com" state.sls file_directory
Function: file.directory Result: True Comment: Directory /srv/stuff/substuf updated |
symlink 建立软链接
/usr/local/foo.conf: # master机器必须存在
file.symlink:
- target: /srv/foo.conf #链接到目标机器上目录
salt "linux-node2.oldboyedu.com" state.sls file_symlink
Function: file.symlink Result: True Comment: Created new symlink /usr/local/foo.conf -> /srv/foo.conf |
file.recurse 下发整个目录
/srv/flask: #目标目录
file.recurse:
- source: salt://flask
- include_empty: True
salt "linux-node2.oldboyedu.com" state.sls file_recurse
Function: file.recurse Result: True Comment: Recursively updated /srv/flask |
pkg模块
pkg.installed 软件安装
指定安装版本:
mypkgs:
pkg.installed:
- pkgs:
- foo
- bar: '>=1.2.3-4'
- baz
指定安装的rpm来源:
mypkgs:
pkg.installed:
- sources:
- foo: salt://rpms/foo.rpm
- bar: http://somesite.org/bar.rpm
- baz: ftp://somesite.org/baz.rpm
- qux: /minion/path/to/qux.rpm
指定安装最新版本的软件:
pkg.latest
mypkgs:
pkg.latest:
- pkgs:
- foo
- bar
- baz
service模块
启动 httpd 服务
httpd:
service:
- running #使服务处于运行状态
- enable: True #设置开机自动启动
- reload: True #watch函数下监控的/etc/httpd/conf/httpd.conf文件发生变化,则会重新加载reload;若reload函数不存在或reload值为False,则会重新启动restart
- watch:
- file: /etc/httpd/conf/httpd.conf
ser 模块
user.present建立用户:
shendu:
user.present:
- fullname: shendu
- shell: /bin/zsh
- home: /home/fred
- uid: 4000
- groups:
- wheel
- games
salt "linux-node2.oldboyedu.com" state.sls user_present
ID: shendu Function: user.present Result: True Comment: User shendu is present and up to date |
使用 requisites 对状态进行排序控制
如果一个主机涉及多个状态,并且状态之间有相互关联,需要在执行顺序上有先后之分,那就必须引入requisites
来进行控制了
sls 文件
install_httpd:
pkg.installed:
- name: httpd
httpd_running:
service.running:
- name: httpd
- enable: True
- reload: True
- require:
- pkg: install_httpd
- watch:
- file: httpd_conf
httpd_conf:
file.managed:
- name: /etc/httpd/conf/httpd.conf
- source: salt://httpd.conf
- user: root
- group: root
- mode: 600
上面的sls 大概流程是,首先必须安装httpd软件,然后要确保httpd启动,最后对比需要下发的httpd.conf和minion 上已有的是否相同,如果不同就下发文件,下发文件后要触发httpd进程的重载以加载新的配置。
reload : Ture #启动重新加载
enable : True # 开机自动启动
- require:
- pkg: install_httpd # 依赖 install_httpd id下的pkg 状态
- watch:
- file: httpd_conf #当 http_conf id下 file 转态下操作的文件被修改才触发
linux-node1.example.com: ---------- ID: install_httpd Function: pkg.installed Name: httpd Result: True Comment: Package httpd is already installed. Started: 05:17:26.075723 Duration: 3918.941 ms Changes: ---------- ID: httpd_conf Function: file.managed Name: /etc/httpd/conf/httpd.conf Result: True Comment: File /etc/httpd/conf/httpd.conf updated Started: 05:17:30.141921 Duration: 251.361 ms Changes: |
使用Jinja2模板编写更为复杂的状态
Jinja2变量
Jinja2模板包含变量和表达式:
变量用{{}}包围,表达式用{{%%}}包围
下面 使用 Jinja 编写一个 sls
vim var.sls
{% set var= 'hello world' %}
test_var:
cmd.run:
- name: echo "var is {{ var }}"
运行 salt "*" state.sls var
输出
ID: test_var Function: cmd.run Name: echo "var is hello world" Result: True Comment: Command "echo "var is hello world"" run 。。。。。 |
Jinja2的变量
字符串类型
{% set var= 'good' %}
{{var}}
列表类型:
{% set list = ['one','two','three'] %}
test_var:
cmd.run:
- name: echo "var is {{ list[1] }}"
字典 类型
{% set dict = {'name':'shendu'} %}
test_var:
cmd.run:
- name: echo "name is {{ dict['name'] }}"
流程控制语句
For
遍历序列中的每一项。例如,要显示一个由users变量提供的用户列表
{% for user in users %}
{{user}}
{% endfor %}
变量字典:
{% for key,value in my_dict.iteritems() %}
{{ key }}
{{ value }}
{% endfor %}
过滤序列表
{% for user in users if not user.hidden %}
{{ user.username }}
{% endfor %}
If
Jinja2中的if语句类似python中的if语句。在最简单的形式中,你可以测试一个变量是否未定义,为空,
或false
{% if users %}
{% for user in users %}
{{ user.username }}
{% endfor %}
{% endif %}
像在python中一样,用elif和else来构建多个分支。
{% if kenny.sick %}
kenny is sick
{% elif kenny.dead %}
You killed Kenny! You bastard!!!
{% else %}
Kenny looks okay --- so far
{% endif %}
Pillar的概念
Grains很强大,但缺点是这些数据相对来说是静态数据。如果有变化的数据该如何处理呢?Pillar可以解决这个问题,Pillar数据存储在master上。指定的minion只能看到自己的pillar数据,其他的minion看不到任何Pillar数据
简单使用 pillar
1 首先修改 /etc/salt/master,打开 pillar_roots注释
pillar_roots:
base:
- /srv/pillar
2 然后定义一个top.sls文件作为入口,用来指定数据对哪个minion有效
base:
'192.168.86.131':
- minion_one_key
'linux-node1.example.com'
- minion_two_key
上面定义了 minion_one_key 对 192.168.86.131 有效, minion_two_key 对 linux-node1.example.com 有效
4 然后执行 salt '*' saltutil.refresh_pillar
使用 salt "*" pillar.items 查看,各个节点的 pillar
用Jinja2配合Grain和Pillar扩展SLS配置文件
例子 在不同操作系统上安装apache
install_apache:
pkg.installed:
{% if grains['os_family']=='Debian' %}
- name: apache2
{% elif grains['os_family']=='RedHat' %}
- name: httpd
{% endif %}
用Jinja2配合Grain和Pillar 动态下发配置文件
在现实情况下不同minion有不同的CPU核心数量,有不同大小的内存值。很多软件的配置需要根据主机配置的不同进行相应的调整,例如Nginx的启动进程数量需要根据CPU核心数进行调整,php-fpm启动数量需要根据内存值进行调整,MYSQL的启动参数也需要根据CPU和内存值进行调整等
下面直接 进入正题
首先在base环境下 编写 状态文件
cat template.sls
template_test:
file.managed:
- source: salt://test.j2
- name: /tmp/test.conf
- user: root
- group: root
- mode: 644
- template: jinja
test.j2
cpu_num = {{ grains['num_cpus'] }}
mem_total = {{ grains['mem_total'] }}
hostname = {{ grains['host'] }}
key = {{ pillar['private_key'] }}
执行 salt "linux-node1.example.com" state.sls template后,查看 linux-node1.example.com 下 /tmp/test.conf文件
cat test.conf
cpu_num = 1 mem_total = 981 hostname = linux-node1 key = minion_two_key |
在上述的例子加上 部分Jinja2的逻辑功能
{% 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'] }}
user = {{ pillar['user'][0] }}
cat test.conf
hostname=linux-node1 user = shendu |
鸣谢
老男孩教育
saltstack 实战 一书