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