saltstack状态管理详细介绍

状态是对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:

    -

    -

    -

    -

    - 

: 每个状态的 字符串必须独一无二。 可以包含字母,数字,空格和下划线

. : 模块.函数

: 函数的参数。首个参数通常是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 实战  一书

你可能感兴趣的:(devops)