1. 程式人生 > >saltstack狀態管理詳細介紹

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配置檔案的格式

<ID Declaration>

  <State Module>.<Function>:

    - name: <name>

    - <Function Arg>

    - <Function Arg>

    - <Function Arg>

    - <Requisite Declaration>

    - <Requisite Reference>

<ID Declaration> : 每個狀態的 <ID Declaration> 字串必須獨一無二。<ID Declaration> 可以包含字母,數字,空格和下劃線

<State Module>.<Function> : 模組.函式

<Function Arg> : 函式的引數。首個引數通常是name

<Requisite Declaration> 與 <Requisite Reference> : 狀態之間關係的描述

 

常用的狀態模組:

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 實戰  一書