SaltStack學習

學習地址

更新于 3.25 23:16

salt簡介

SaltStack是一個服務器基礎架構(gòu)集中化管理平臺粪小,具備配置管理、遠程執(zhí)行抡句、監(jiān)控等功能探膊,基于Python語言實現(xiàn),結(jié)合輕量級消息隊列(ZeroMQ)與Python第三方模塊(Pyzmq待榔、PyCrypto逞壁、Pyjinjia2、python-msgpack和PyYAML等)構(gòu)建究抓。

通過部署SaltStack猾担,我們可以在成千萬臺服務器上做到批量執(zhí)行命令袭灯,根據(jù)不同業(yè)務進行配置集中化管理刺下、分發(fā)文件、采集服務器數(shù)據(jù)稽荧、操作系統(tǒng)基礎及軟件包管理等橘茉,SaltStack是運維人員提高工作效率工腋、規(guī)范業(yè)務配置與操作的利器。

salt基本原理

SaltStack 采用 C/S模式畅卓,server端就是salt的master擅腰,client端就是minion,minion與master之間通過ZeroMQ消息隊列通信

minion上線后先與master端聯(lián)系翁潘,把自己的pub key發(fā)過去趁冈,這時master端通過salt-key -L命令就會看到minion的key,接受該minion-key后拜马,也就是master與minion已經(jīng)互信

master可以發(fā)送任何指令讓minion執(zhí)行了渗勘,salt有很多可執(zhí)行模塊,比如說cmd模塊俩莽,在安裝minion的時候已經(jīng)自帶了旺坠,它們通常位于你的python庫中,locate salt | grep /usr/ 可以看到salt自帶的所有東西扮超。

這些模塊是python寫成的文件取刃,里面會有好多函數(shù),如cmd.run出刷,當我們執(zhí)行salt '*' cmd.run 'uptime'的時候璧疗,master下發(fā)任務匹配到的minion上去,minion執(zhí)行模塊函數(shù)馁龟,并返回結(jié)果病毡。master監(jiān)聽4505和4506端口,4505對應的是ZMQ的PUB system屁柏,用來發(fā)送消息折晦,4506對應的是REP system是來接受消息的。

具體步驟如下

  1. Salt stack的Master與Minion之間通過ZeroMq進行消息傳遞窟赏,使用了ZeroMq的發(fā)布-訂閱模式瞄摊,連接方式包括tcp,ipc
  2. salt命令裸删,將cmd.run ls命令從salt.client.LocalClient.cmd_cli發(fā)布到master八拱,獲取一個Jodid,根據(jù)jobid獲取命令執(zhí)行結(jié)果涯塔。
  3. master接收到命令后肌稻,將要執(zhí)行的命令發(fā)送給客戶端minion。
  4. minion從消息總線上接收到要處理的命令匕荸,交給minion._handle_aes處理
  5. minion._handle_aes發(fā)起一個本地線程調(diào)用cmdmod執(zhí)行l(wèi)s命令爹谭。線程執(zhí)行完ls后,調(diào)用minion._return_pub方法榛搔,將執(zhí)行結(jié)果通過消息總線返回給master
  6. master接收到客戶端返回的結(jié)果诺凡,調(diào)用master._handle_aes方法东揣,將結(jié)果寫的文件中
  7. salt.client.LocalClient.cmd_cli通過輪詢獲取Job執(zhí)行結(jié)果,將結(jié)果輸出到終端腹泌。

安裝salt

導入salt

7版本
rpm --import https://repo.saltstack.com/yum/redhat/7/x86_64/latest/SALTSTACK-GPG-KEY.pub

6版本
rpm --import https://repo.saltstack.com/yum/redhat/6/x86_64/latest/SALTSTACK-GPG-KEY.pub


#新增文件 /etc/yum.repos.d/saltstack.repo
7 & 6版本

[saltstack-repo]
name=SaltStack repo for RHEL/CentOS $releasever
baseurl=https://repo.saltstack.com/yum/redhat/$releasever/$basearch/latest
enabled=1
gpgcheck=1
gpgkey=https://repo.saltstack.com/yum/redhat/$releasever/$basearch/latest/SALTSTACK-GPG-KEY.pub

安裝 salt-minion, salt-master,或Salt components:

yum install salt-master
yum install salt-minion

yum install salt-ssh
yum install salt-syndic
yum install salt-cloud

配置salt

master

一般使用默認就好 (/etc/salt/master)

#指定master嘶卧,冒號后有一個空格
master: 192.168.2.22
user: root

#-------以下為可選--------------
# salt運行的用戶,影響到salt的執(zhí)行權(quán)限
user: root
#s alt的運行線程凉袱,開的線程越多一般處理的速度越快芥吟,但一般不要超過CPU的個數(shù)
worker_threads: 10
# master的管理端口
publish_port : 4505
# master跟minion的通訊端口,用于文件服務专甩,認證运沦,接受返回結(jié)果等
ret_port : 4506
# 如果這個master運行的salt-syndic連接到了一個更高層級的master,那么這個參數(shù)需要配置成連接到的這個高層級master的監(jiān)聽端口
syndic_master_port : 4506
# 指定pid文件位置
pidfile: /var/run/salt-master.pid
# saltstack 可以控制的文件系統(tǒng)的開始位置
root_dir: /
# 日志文件地址
log_file: /var/log/salt_master.log
# 分組設置
nodegroups:
  group_all: '*'
# salt state執(zhí)行時候的根目錄
file_roots:
  base:
    - /srv/salt/
# 設置pillar 的根目錄
pillar_roots:
  base:
    - /srv/pillar
啟動master
systemctl start salt-master
systemctl enable salt-master

minion

(/etc/salt/minion)

#指定master,冒號后有一個空格
master: 192.168.2.22
id: minion-01
user: root

#-------以下為可選--------------
# minion的識別ID配深,可以是IP携添,域名,或是可以通過DNS解析的字符串
id: 192.168.0.100
# salt運行的用戶權(quán)限
user: root
# master的識別ID篓叶,可以是IP烈掠,域名,或是可以通過DNS解析的字符串
master : 192.168.0.100
# master通訊端口
master_port: 4506
# 備份模式缸托,minion是本地備份左敌,當進行文件管理時的文件備份模式
backup_mode: minion
# 執(zhí)行salt-call時候的輸出方式
output: nested 
# minion等待master接受認證的時間
acceptance_wait_time: 10
# 失敗重連次數(shù),0表示無限次俐镐,非零會不斷嘗試到設置值后停止嘗試
acceptance_wait_time_max: 0
# 重新認證延遲時間矫限,可以避免因為master的key改變導致minion需要重新認證的syn風暴
random_reauth_delay: 60
# 日志文件位置
log_file: /var/logs/salt_minion.log
# 文件路徑基本位置
file_roots:
  base:
    - /etc/salt/minion/file
# pillar基本位置
pillar_roots:
  base:
    - /data/salt/minion/pillar
啟動minion
systemctl start salt-minion
systemctl enable salt-minion

添加key

master 端查看key

[root@master salt]# salt-key 
Accepted Keys:
Denied Keys:
Unaccepted Keys:   #可看到 minion已經(jīng)檢測到,沒有認證key
minion-01
Rejected Keys:

[root@master salt]# salt-key -a minion-01
The following keys are going to be accepted:
Unaccepted Keys:
minion-01
Proceed? [n/Y] y    #Y確認添加
Key for minion minion-01 accepted.  #添加成功
[root@master salt]# salt-key 
Accepted Keys:
minion-01
Denied Keys:
Unaccepted Keys:
Rejected Keys:
[root@master salt]#
salt-key常用參數(shù)
-a 添加指定ID 的key
-A 添加全部
-R 拒絕全部
-d 刪除指定ID的
-D 刪除全部
測試連通性
[root@master salt]# salt 'minion-01' test.ping
minion-01:
    True   #返回結(jié)果表示成功
[root@master salt]# 

簡單服務的安裝
[root/] ]$salt 'minion-01' pkg.install ftp  #解釋
minion-01:
    ----------
    ftp:
        ----------
        new:
            0.17-67.el7
        old:
[root/] ]$

#去minion查看
[root@minion-01 tmp]# rpm -qa ftp
ftp-0.17-67.el7.x86_64

#salt 'minion-01' pkg.install ftp
#1.'*' 代表的是target是指在那些minion上操作
#2. 'pkg' 是一個執(zhí)行模塊,就像'test' 
#3.'install' 是執(zhí)行模塊下面的函數(shù)佩抹,像test下的ping
#4.'ftp' 是函數(shù)的參數(shù)(arg)叼风,有的函數(shù)需要參數(shù),有的不需要比如test.ping就不需要參數(shù)
 ##查看所有執(zhí)行模塊的doc
 salt 'minion' sys.doc
 ##查看test模塊的幫助
 salt 'minion' sys.doc test  
 ##查看test.ping函數(shù)的幫助
 salt 'minion' sys.doc test.ping 

salt常用命令

salt

該命令執(zhí)行salt的執(zhí)行模塊,通常在master端運行.常用命令

salt [option] '<target>' <function> [arguments]

#例如
salt 'minion-01' cmd.run 'ip addr'

salt-run

該命令執(zhí)行runner(salt自帶或者自定義的棍苹,)无宿,通常在master端執(zhí)行,比如經(jīng)常用到的manage

salt-run [options] [runner.func]

#例如
salt-run manage.status   ##查看所有minion狀態(tài)
salt-run manage.down     ##查看所有沒在線minion
salt-run manage.up       ##查看所有在線minion

salt-key

密鑰管理枢里,通常在master端執(zhí)行

salt-key [options]
salt-key -L              ##查看所有minion-key
salt-key -a <key-name>   ##接受某個minion-key
salt-key -d <key-name>   ##刪除某個minion-key
salt-key -A              ##接受所有的minion-key
salt-key -D              ##刪除所有的minion-key

salt-call

該命令通常在minion上執(zhí)行孽鸡,minion自己執(zhí)行可執(zhí)行模塊,不通過master下發(fā)job

salt-call [options] <function> [arguments]
salt-call test.ping           ##自己執(zhí)行test.ping命令
salt-call cmd.run 'ifconfig'  ##自己執(zhí)行cmd.run函數(shù)

salt-cp

分發(fā)文件到minion上,不支持目錄分發(fā).運行在master

salt-cp [options] '<target>' SOURCE DEST
#例如
salt-cp '*' testfile.html /tmp
salt-cp 'test*' index.html /tmp/a.html

salt-master

salt-master [options]
salt-master            ##前臺運行master
salt-master -d         ##后臺運行master
salt-master -l debug   ##前臺debug輸出

salt-minion

salt-minion [options]
salt-minion            ##前臺運行
salt-minion -d         ##后臺運行
salt-minion -l debug   ##前臺debug輸出

普通用戶執(zhí)行salt

兩種方法

1: ACL(修改master)

    client_acl:
    monitor: #uonghu
     - test*: #權(quán)限
    - test.*
    dev:
     - service.*
    sa:
     - .*
#重啟master
     
#給予目錄和文件權(quán)限
chmod +r /etc/salt/master
chmod +x /var/run/salt
chmod +x /var/cache/salt

2 external_auth(修改master)

  pam:
    fred:
      - test.*
#重啟master
     
#給予目錄和文件權(quán)限
chmod +r /etc/salt/master
chmod +x /var/run/salt
chmod +x /var/cache/salt

使用Token不必每次都輸入賬號密碼栏豺,使用external_auth每次都是需要密碼的彬碱,這樣多麻煩,這里引入了Token奥洼,它會保存一串字符到在當前用戶家目錄下.salt_token中巷疼,在有效時間內(nèi)使用external_auth是不需要輸入密碼的,默認時間12hour溉卓,可以通過master配置文件修改

salt -T -a pam '*' test.ping

target

target也就是目標,目的.指定master命令應該對誰執(zhí)行

  • 正則匹配
[root@master /]# salt -E  'mini*' test.ping
minion-02:
    True
minion-01:
    True
  • 列表匹配
[root@master ~]# salt -L minion-01,minion-02 test.ping
minion-02:
    True
minion-01:
    True
  • grains匹配
[root@master ~]# salt -G 'os:CentOs' test.ping
minion-02:
    True
minion-01:
    True
  • 組匹配
#開啟master 的default_include
vim /etc/salt/master.d/nodegroup.conf 
#寫到master中也是這個格式
nodegroups:
 test1: 'L@test1,test2 or test3*'
 test2: 'G@os:CenOS or test2'

salt -N test1 test.ping   #-N指定groupname

在top file中使用nodegroups

'test1':
 - match: nodegroup     ##沒s,匹配的是文件
 - webserver
[root@master ~]# salt -N nodegroups test.ping
minion-02:
    True
minion-01:
    True
#組需要在master中預先定義
  • 復合匹配 salt -C 'G@os:MacOS or L@Minion1'
  • Pillar匹配 salt -I 'key:value' test.ping
  • CIDR匹配 salt -S '192.168.1.0/24' test.ping

在top文件中匹配 grains

'node_type:web':
  - match: grain         #沒有s
  - webserver

top文件中使用jinja

{% set self = grains['node_type'] %}
    - match: grain
- {{ self }}
  • 一次在n個minion上執(zhí)行
-b n
--batch-size n
#例:
salt '*' -b 5 test.ping
#5個5個的ping

多master

2個master并不會共享Minion keys皮迟,一個master刪除了一個key不會影響另一個

不會自動同步File_roots,所以需要手動去維護,如果用git就沒問題了

不會自動同步Pillar_Roots桑寨,所以需要手工去維護伏尼,也可以用git

Master的配置文件也是獨立的

#安裝 salt-master

#原master的密鑰cp一份到新的master
scp /etc/salt/pki/master/master* newmaster:/etc/salt/pki/master/
#啟動新的Master

#修改配置minion的配置
master:
  - master1
  - master2
#重啟minion

#新master接受所有的key
salt-key -L
salt-key -A

YAML

語法風格

  • 空格和TAB

    yaml兩個空格為縮進, TAB不要使用!

  • 冒號: 和減號-

    : 和- 后面要跟上一個空格在寫

  • 數(shù)字解析

    mode: 0644 會解析成為mode: 644 最好使用mode: (0644)

  • 簡寫

    vim:
      pkg.installed #第一個簡寫
      user.present #第二個簡寫.不被支持,因為不支持雙簡寫
    
    #建議規(guī)范書寫
    vim:
      pkg:
        - installed
      user:
        - present
        
    

Jinja

Jinja 基于Python模板引擎開發(fā),saltstack默認使用yaml_jinja渲染器,渲染流程時先jinja在yaml解析.所以在開始解析yaml的時候可以使用jinja

  • 區(qū)分模板文件

在salt中,files和templates都使用file這個state模塊.那么如何區(qū)分模板是什么文件呢.

  - templates: jinja
  
file.managed:
  - name: /tmp/test
  - source: salt://tmp/test
  - template: jinja
  - defaults:
    Server: {{ pillar['.....'] }}
  
  • jinja中使用grains
{{ grains['os'] }}
  • jinja中使用執(zhí)行模塊
{{ salt['network.hw_addr']('eth0') }}
  • jinja中使用Pillar
{{ pillar['apache']['PORT'] }}

Jinja的邏輯關(guān)系

{% if grains['os'] == 'RedHat' %}
apache: httpd
{% elif grains['os'] == 'Debian' %}
apache: apache2
{% endif %}

更多使用自行研究

salt常用模塊和API

查看支持的所有modules

[root/] ]$salt 'minion-01' sys.list_modules
minion-01:
    - acl
...

salt.client調(diào)用API舉例

[root/] ]$cd /usr/lib/python2.7/site-packages/salt/modules/ 模塊path

API調(diào)用示例

[root/] ]$cat test.py 
#!/usr/bin/python
import salt.client
client = salt.client.LocalClient()

res = client.cmd('*','test.ping')
print res
[root/] ]$./test.py 
{'minion-02': True, 'minion-01': True}

##解釋一下
#當我們調(diào)用salt.client.LocalClient的時候,其實就等于我們執(zhí)行了 salt '*' test.ping

API調(diào)用:

client.cmd('*','file.remove',['/tmp/foo'])

salt <target> sys.doc module

可以查看模塊支持那些命令

Archive

實現(xiàn)對系統(tǒng)曾經(jīng)的壓縮包調(diào)用支持gzip,gunzip.rar,tar,unrar,unzip等

#采用gunzip解壓sourcefile.txt.gz包
salt '*' archive.gunzip sourcefile.txt.gz

#采用gzip壓縮sourcefile.txt文件
salt '*' archive.gzip sourcefile.txt

API調(diào)用:

client.cmd('*','archive.gunzip',['sourcefile.txt.gz'])

cmd

實現(xiàn)對遠程命令的調(diào)用執(zhí)行,(默認具備root權(quán)限!謹慎使用)

#獲取所欲被控主機的內(nèi)存使用情況
salt '*' cmd.run 'free -m'

#在wx主機上運行test.py腳本,其中script/test.py存放在file_roots指定的目錄(默認是在/srv/salt,自定義在/etc/salt/master文件中定義)尉尾,
#該命令會做2個動作:首先同步test.py到minion的cache目錄爆阶;起床運行該腳本
salt 'minion-01' cmd.script salt://script/test.py

API調(diào)用:

client.cmd('*','cmd.run',['free -m'])

cp

實現(xiàn)遠程文件目錄的復制,以及下載URL文件等操作

#將被控主機的/etc/hosts文件復制到被控主機本地的salt cache目錄(/var/cache/salt/minion/localfiles/)
salt '*' cp.cache_local_file /etc/hosts

#將主控端file_roots指定位置下的目錄復制到被控主機/minion/目錄下
salt '*' cp.get_dir salt://script/ /minion/

#將主控端file_roots指定位置下的文件復制到被控主機/minion/test.py文件(file為文件名)
salt '*' cp.get_dir salt://script/test.py /minion/test.py

#下載URL內(nèi)容到被控主機指定位置(/tmp/index.html)
salt '*' cp.get_url http://www.slashdot.ort /tmp/index.html

API調(diào)用:

client.cmd('*','cp.get_file',['salt://script/test.py','/minion/test.py'])

cron

實現(xiàn)對minion的crontab控制

#查看指定被控主機、root用戶的crontab操作
salt 'minion-01' cron.raw_cron root

#為指定被控主機沙咏、root用戶添加/usr/local/weekly任務zuoye
salt 'minion-01' cron.set_job root '*' '*' '*' '*' 1 /usr/local/weekly 

#刪除指定被控主機辨图、root用戶crontab的/usr/local/weekly任務zuoye
salt 'minion-01' cron.rm_job root /usr/local/weekly 

API調(diào)用:

client.cmd('wx','cron.set_job',['root','*','*','*','*',1,'/usr/local/weekly'])

file

對minion的文件操作,包括文件讀寫,權(quán)限,查找校驗

#校驗所有被控主機/etc/fstab文件的md5值是否為xxxxxxxxxxxxx,一致則返回True值
salt '*' file.check_hash /etc/fstab md5=xxxxxxxxxxxxxxxxxxxxx

#校驗所有被控主機文件的加密信息,支持md5肢藐、sha1故河、sha224、shs256吆豹、sha384鱼的、sha512加密算法
salt '*' file.get_sum /etc/passwd md5

#修改所有被控主機/etc/passwd文件的屬組、用戶權(quán)限痘煤、等價于chown root:root /etc/passwd
salt '*' file.chown /etc/passwd root root

#復制所有被控主機/path/to/src文件到本地的/path/to/dst文件
salt '*' file.copy /path/to/src /path/to/dst

#檢查所有被控主機/etc目錄是否存在凑阶,存在則返回True,檢查文件是否存在使用file.file_exists方法
salt '*' file.directory_exists /etc

#獲取所有被控主機/etc/passwd的stats信息
salt '*' file.stats /etc/passwd

#獲取所有被控主機/etc/passwd的權(quán)限mode,如755衷快,644
salt '*' file.get_mode /etc/passwd

#修改所有被控主機/etc/passwd的權(quán)限mode為0644
salt '*' file.set_mode /etc/passwd 0644

#在所有被控主機創(chuàng)建/opt/test目錄
salt '*' file.mkdir /opt/test

#將所有被控主機/etc/httpd/httpd.conf文件的LogLevel參數(shù)的warn值修改為info
salt '*' file.sed /etc/httpd/httpd.conf 'LogLevel warn' 'LogLevel info'

#給所有被控主機的/tmp/test/test.conf文件追加內(nèi)容‘maxclient 100’
salt '*' file.append /tmp/test/test.conf 'maxclient 100'

#刪除所有被控主機的/tmp/foo文件
salt '*' file.remove /tmp/foo

network

返回minion的主機信息

#在指定被控主機獲取dig宙橱、ping、traceroute目錄域名信息
salt 'minion-01' network.dig www.qq.com
salt 'minion-01' network.ping www.qq.com
salt 'minion-01' network.traceroute www.qq.com

#獲取指定被控主機的mac地址
salt 'minion-01' network.hwaddr eth0

#檢測指定被控主機是否屬于10.0.0.0/16子網(wǎng)范圍蘸拔,屬于則返回True
salt 'minion-01' network.in_subnet 10.0.0.0/16

#獲取指定被控主機的網(wǎng)卡配置信息
salt 'minion-01' network.interfaces

#獲取指定被控主機的IP地址配置信息
salt 'minion-01' network.ip_addrs

#獲取指定被控主機的子網(wǎng)信息
salt 'minion-01' network.subnets

API調(diào)用:

client.cmd('minion-01','network.ip_addrs')

pkg

minion的程序包管理,如yum, apt-get等

#為所有被控主機安裝PHP環(huán)境师郑,根據(jù)不同系統(tǒng)發(fā)行版調(diào)用不同安裝工具進行部署,如redhat平臺的yum调窍,等價于yum -y install php
salt '*' pkg.install php

#卸載所有被控主機的PHP環(huán)境
salt '*' pkg.remove php

#升級所有被控主機的軟件包
salt '*' pkg.upgrade

API調(diào)用:

client.cmd('*','pkg.remove',['php'])

status

salt '*' status.version

API

import salt.client
client = salt.client.LocalClient()
client.cmd('*','status.uptime')

system

用來日常操作計算機

system.halt        #停止正在運行的系統(tǒng)
system.init 3      #切換到字符界面呕乎,5是圖形界面
system.poweroff
system.reboot
system.shutdown

systemd(service)

  service.available sshd            #查看服務是否可用
  service.disable <service name>    #設置開機啟動的服務
  service.enable <service name>
  service.disabled <service name>   #查看服務是不是開機啟動
  service.enabled <service name>
  service.get_disabled              #返回所有關(guān)閉的服務
  service.get_enabled               #返回所有開啟的服務
  service.get_all                   #返回所有服務
  service.reload <service name>     #重新載入指定的服務
  service.restart <service name>    #重啟服務
  service.start <service name>
  service.stop <service name>
  service.status <service name>
  service.force_reload <service name>  #強制載入指定的服務

使用

[root@mail python]# salt '*' service.available sshdmonitor:    True

api調(diào)用:
>>> client.cmd('*','service.available',['sshd']){'monitor': True}

grains

服務器的一些靜態(tài)信息,強調(diào)的是靜態(tài)陨晶,就是不會變的東西猬仁,比如說os是centos,不會變化先誉,除非重新安裝系統(tǒng)

grains的使用

#查詢所有g(shù)rains信息
[root@master salt]# salt 'minion-01' grains.items 
minion-01:
    ----------
    SSDs:
    biosreleasedate:
        09/21/2015
    biosversion:
        6.00
    cpu_flags:
        - fpu
        - vme
        - de
.....

#查詢grains指定項
[root@master salt]# salt '*' grains.item os
minion-02:
    ----------
    os:
        CentOS
minion-01:
    ----------
    os:
        CentOS
[root@master salt]# 


[root@master salt]# salt -G 'os:CentOS' test.ping
minion-01:
    True

#對系統(tǒng)是CentOS的服務器進行ping測試操作
#os:CentOS ; 就是對應上面grains.items顯示出來的os值是CentOs的對象進行匹配 

 
#對cpu架構(gòu)是x86_64的服務器顯示CPU的個數(shù)
salt -G 'cpuarch:x86_64' grains.item num_cpus
 
#對字典值的對象進行匹配
salt -G 'ip_interfaces:eno16777728:192.168.2.*' test.ping

在SLS中用grains

# 在xxx.sls中使用grains
'os:CentOS':
    - match: grain
    - webserver

自定義grains(兩種方法)

1 . minion端修改 重啟生效

修改配置文件 /etc/salt/minion 或者寫在/etc/salt/grains中

打開 default_include: minion.d/*.conf 或者直接添加此命令

在minion端的/etc/salt/minion.d/ 目錄下新建并編輯.conf后綴文件

grains: #如果是/etc/salt/grains中,不需此行
  roles:
    - webserver
  sex: boy  #名字:值
  age:      #名字:多個值
    - 33
    - 44
 # 重啟生效
[root@master ~]# salt 'minion-01' grains.item age
minion-01:
    ----------
    age:
        - 33
        - 44
[root@master ~]# 

**2 . minion端修改 ** 同步之后生效

base目錄(在/etc/salt/master中配置的file_roots項湿刽,默認在/srv/salt)下生成_grains 目錄,新建文件,用python來寫

編寫文件,需要返回一個字典

 vim test1.py
def hello(): ##函數(shù)名字無所謂,應該是所有函數(shù)都會運行
    agrain = {}
    agrain['hello'] = 'lzl' 
    return agrain   ##返回這個字典


========================

#!/usr/bin/python
# -*- coding:utf-8 -*-
import os
def file():
    grains={}#初始化一個字典褐耳,
    file = os.popen('ulimit -n').read()
    grains['my_file']=file
    return grains


#注意文件賦予權(quán)限
chmod a+x .py
#同步到各個minion中去
salt '*' saltutil.sync_all

#查看
[root/srv/salt/_grains] ]$salt 'minion-01' grains.item hello
minion-01:
    ----------
    hello:
        lzl

pillar

Pillar在salt中是非常重要的組成部分诈闺,利用它可以完成很強大的功能,它可以指定一些信息到指定的minion上铃芦,不像grains一樣是分發(fā)到所有Minion上的雅镊,它保存的數(shù)據(jù)可以是動態(tài)的,Pillar以sls來寫的襟雷,格式是鍵值

適用

1.比較敏感的數(shù)據(jù),比如密碼仁烹,key等

2.特殊數(shù)據(jù)到特定Minion上

3.動態(tài)的內(nèi)容

4.其他數(shù)據(jù)類型

pillar基本使用

查看所有

salt '*' pillar.items

查看某個

salt '*' pillar.item KEY
#可以取到更小粒度的
salt '*' pillar.get <key>:<key> 

編寫pillar

指定pillar_roots

默認是/srv/pillar/(可通過修改master配置文件修改),建立目錄

top.sls

base:           #指定環(huán)境
  '*':          #target
    - test1     #引用test1.sls 或者test1/init.sls
    
#通過分組名匹配耸弄,
base:
  group1:
    - match: nodegroup    #必須要有 - match: nodegroup  
    - webserver  

#通過grain模塊匹配的示例
base:
  'os:CentOS':
    - match: grain   #必須要有- match: grain
    - webserver
    

test1.sls

name: test1
user: lzl

刷新 pillar數(shù)據(jù)

salt '*' saltutil.refresh_pillar

查看結(jié)果

[root/srv/pillar] ]$salt 'minion-01' pillar.items
minion-01:
    ----------
    name:
        test1
    user:
        lzl
[root/srv/pillar] ]$

在state中通過jinja使用pillar

默認state文件位置/src/salt/

user.sls

{% for user, uid in pillar.get('users', {}).items() %}  
 ##pillar.get('users',{})可用pillar['users']代替,前者在沒有得到值的情況下卓缰,賦默認值
{{user}}:
  user.present:
    - uid: {{uid}}
{% endfor %}
 

jinja配合grains 指定pillar數(shù)據(jù)

{% if grains['os_family'] == 'RedHat' %}
apache: httpd
{% elif grains['os'] == 'CentOS' %}
apache: httpd
vim: vim
{% elif grains['os'] == 'Arch' %}
apache: apache
vim: vim
{% endif %}

使用salt state

它的核心是寫sls(SaLt State file)文件,sls文件默認格式是YAML格式计呈,并默認使用jinja模板,jinja是根據(jù)django的模板語言發(fā)展而來的語言征唬,簡單并強大捌显,支持for if 等循環(huán)判斷。salt state主要用來描述系統(tǒng)总寒,軟性扶歪,服務,配置文件的狀態(tài)摄闸,常常被稱為配置管理击罪!

通常state,pillar,top file會用sls文件來編寫贪薪。state文件默認是放在/srv/salt中媳禁,它與你的master配置文件中的file_roots設置有關(guān)

簡單的state文件配置&介紹

#/srv/salt/apahce.sls

apache:           ##state ID,全文件唯一,如果模塊沒跟-name默認用的ID作為-name
 pkg:             ##模塊
   #- name: apache ##函數(shù)參數(shù)画切,可以省略
   - installed    ##函數(shù)
 service:         ##模塊
   - running      ##函數(shù)
  #- name: apache ##函數(shù)參數(shù)竣稽,這個是省略的,也可以寫上
   - require:     ##依賴系統(tǒng)
     - pkg: apache  ##表示依賴id為apache的pkg狀態(tài)
     

#聲明一個叫apache的狀態(tài)id,該id可以隨意霍弹,最好能表示一定意思

#pkg代表的是pkg模塊

#installed是pkg模塊下的一個函數(shù)毫别,描述的是狀態(tài),該函數(shù)表示apache是否部署典格,返回值為True或者False岛宦,為真時,表示狀態(tài)OK耍缴,否則會去滿足該狀態(tài)(下載安裝apache)砾肺,如果滿足不了會提示error,在該模塊上面省略了參數(shù)-name: apache,因為ID為apache,這些參數(shù)是模塊函數(shù)需要的(可以去查看源碼)

#service是指的service模塊
#這個模塊下主要是描述service狀態(tài)的函數(shù),running狀態(tài)函數(shù)表示apache在運行防嗡,省略-name不在表述变汪,-require表示依賴系統(tǒng),依賴系統(tǒng)是state system的重要組成部分蚁趁,在該處描述了apache服務的運行需要依賴apache軟件的部署裙盾,這里就要牽涉到sls文件的執(zhí)行,sls文件在salt中執(zhí)行時無序(如果沒有指定順序,后面會講到order)番官,假如先執(zhí)行了service這個狀態(tài)庐完,它發(fā)現(xiàn)依賴pkg包的安裝,會去先驗證pkg的狀態(tài)有沒有滿足徘熔,如果沒有依賴關(guān)系的話门躯,我們可以想象,如果沒有安裝apache近顷,apache 的service肯定運行會失敗的生音,我們來看看怎么執(zhí)行這個sls文件:
     
salt '*' state.sls apache  

#在命令行里這樣執(zhí)行宁否,.sls不寫窒升,如果在目錄下,將目錄與文件用’.’隔開慕匠,
#如: httpd/apache.sls –> httpd.apache

#或者
salt '*' state.highstate 
#前提是存在top.sls 去指定minion運行的是哪個文件
#top.sls
base:
  '*':
    - webserver

state.sls默認的運行環(huán)境是base環(huán)境饱须,但是它并不讀取top.sls(top.sls定義了運行環(huán)境以及需要運行的sls)

state.sls也可以指定讀取哪個環(huán)境:state.sls salt_env='prod' xxxx.sls,這個xxxx.sls可以不在top.sls中記錄台谊。

state.highstate: 這個是全局的所有環(huán)境蓉媳,以及所有狀態(tài)都生效。它會讀取每一個環(huán)境的top.sls锅铅,并且對所有sls都生效酪呻。不在top.sls文件里面記錄的sls則不會被執(zhí)行;

閱讀后寫的版本

webserver:
  pkg:
    - name: httpd
    - installed
  service:
    - name: httpd
    - running
    - reqire:
      -pkg: httpd

[root/srv/salt] ]$salt 'minion-02' state.sls webserver
minion-02:
----------
          ID: webserver
    Function: pkg.installed
        Name: httpd
      Result: True
     Comment: The following packages were installed/updated: httpd
     Started: 18:24:07.033564
    Duration: 65091.443 ms
     Changes:   
              ----------
              httpd:
                  ----------
                  new:
                      2.4.6-45.el7.centos
                  old:
              httpd-tools:
                  ----------
                  new:
                      2.4.6-45.el7.centos
                  old:
              mailcap:
                  ----------
                  new:
                      2.1.41-2.el7
                  old:
----------
          ID: webserver
    Function: service.running
        Name: httpd
      Result: True
     Comment: Started Service httpd
     Started: 18:25:12.142495
    Duration: 5599.171 ms
     Changes:   
              ----------
              httpd:
                  True

Summary
------------
Succeeded: 2 (changed=2)
Failed:    0
------------
Total states run:     2
[root/srv/salt] ]$

較復雜的state

/srv/salt/ssh/init.sls

openssh-client:
  pkg.installed
/etc/ssh/ssh_config:
  file.managed:
    - user: root
    - group: root
    - mode: 644
    - source: salt://ssh/ssh_config
    - require:
      - pkg: openssh-client
#ssh/init.sls 意思是當執(zhí)行 salt '*' state.sls ssh的時候其實就是執(zhí)行init.sls
#第一行:文件名,全文件唯一,如果pkg等模塊沒跟- name 包名, 默認用的ID作為-name
#第二行: 簡寫,意思pkg下的installed函數(shù)
#第三行: ID 告訴minion下載的文件應該放哪里!
#第四行:簡寫
#第八行:source是告訴minion從哪里下載源文件!
#salt://ssh/ssh_config其實就是/srv/salt/ssh/ssh_config 前面/srv/salt這個路徑和file_roots的配置有關(guān)

/srv/salt/ssh/server.sls

include:
  - ssh
#include表示包含意思盐须,就是把ssh/init.sls直接包含進來

openssh-server:
 pkg.installed

sshd:
  service.running:
    - require:
      - pkg: openssh-client
      - pkg: openssh-server
      - file: /etc/ssh/banner
      - file: /etc/ssh/sshd_config

/etc/ssh/sshd_config:
  file.managed:
    - user: root
    - group: root
    - mode: 644
    - source: salt://ssh/sshd_config
    - require:
      - pkg: openssh-server
/etc/ssh/banner:
  file:
    - managed
    - user: root
    - group: root
    - mode: 644
    - source: salt://ssh/banner
    - require:
      - pkg: openssh-server

此時的目錄結(jié)構(gòu)應該是

├── ssh
│   ├── banner
│   ├── init.sls
│   ├── server.sls
│   ├── ssh_config
│   └── sshd_config

關(guān)于include古官網(wǎng)的demo

include:
  - ssh.server
extend:
  /etc/ssh/banner:
    file:
      - source: salt://ssh/custom-banner
 
#包含ssh/server.sls,擴展/etc/ssh/banner玩荠,重新其source而其它的如user,group等不變,與include一致贼邓。

include:
  - apache
extend:
  apache:
  service:
    - watch:
      - pkg: mod_python
#把apache.sls包含進來阶冈,想apache-service是追加了依賴關(guān)系(watch也是依賴系統(tǒng)的函數(shù)).

關(guān)于渲染器 render system

salt默認是用的yaml_jinja渲染器處理ss文件,會優(yōu)先使用jinjia處理,然后傳給yaml處理然后生成salt需要的python數(shù)據(jù)類型.

apache/init.sls

apache:
  pkg:installed:
    {% if grains['os'] == 'CentoOS' %}
    - name: httpd
    {% endif %}
  service.running:
    {% if grains['os'] == 'CentoOS' %}
    - name: httpd
    {% endif %}
    - watch:
      - pkg: apache
      
#簡單的例子,使用jinja結(jié)合grains進行判斷

user/init.sls

{% set users = ['jerry','tom','gaga'] %}
{% for user in users %}
{{ user }}:
 user.present:
   - shell: /bin/bash
   - home: /home/{{ user }}
{% endfor %}

---------------------------

{% if salt['cmd.run']('uname -i') == 'x86_64' %}
hadoop:
 user.present:
   - shell: /bin/bash
   - home: /home/hadoop
{% elif salt['cmd.run']('uname -i') == 'i386' %}
openstack:
 user.present:
   - shell: /bin/bash
- home: /home/openstack
{% else %}
django:
 user.present:
   - shell: /sbin/nologin
{% endif %}

py渲染器

純python寫的sls文件.如果使用其他的渲染器,需要在文件開頭聲明,!py就是聲明用的py渲染器,

py中可用的變量有salt,grains,pillar,opts,env,sls,前三個分別對應jinja里的salt,grains,pillar,opts是minion的配置文件的字典,env對應的是環(huán)境如base,sls對應的是sls的文件名

#!py
import os
def run():
   '''add user hadoop'''
platform = os.popen('uname -a').read().strip()
if platform == 'x86_64':
   return {'hadoop': {'user': ['present',{'shell': '/bin/bash'}, {'home': '/home/hadoop'}]}}
elif platform == 'i386':
       return {'openstack': {'user': ['present', {'shell': '/bin/bash'}, {'home': '/home/openstack'}]}}
else:
   return {'django': {'user': ['present', {'shell': '/sbin/nologin'}]}}

#注意的是return的數(shù)據(jù)結(jié)構(gòu){ID: {module: [func, arg1,arg2,...,]}} 或 {ID: {module.func: [arg1,arg2,..,]}} 塑径。表示的內(nèi)容與“示例女坑;salt字典”表達的相同

state的執(zhí)行順序

stata執(zhí)行,也就是.sls文件的執(zhí)行是無序的.為了保證每次的順序是一致的,就加入了state order ,

先了解下高級數(shù)據(jù)(High Data)和低級數(shù)據(jù)(Low Data).

高級數(shù)據(jù)就是指編寫的sls文件的數(shù)據(jù)

低級數(shù)據(jù)就是經(jīng)過render和parser編譯過的數(shù)據(jù)

[root~] ]$salt 'minion-01' state.show_highstate
minion-01:
    ----------
    webserver:
        ----------
        __env__:
            base
        __sls__:
            webserver
        pkg:
            |_
              ----------
              name:
                  httpd
            - installed
            |_
              ----------
              order:
                  10000
        service:
            |_
              ----------
              name:
                  httpd
            - running
            |_
              ----------
              -pkg:
                  httpd
              reqire:
                  None
            |_
              ----------
              order:
                  10001
[root~] ]$salt 'minion-01' state.show_lowstate
minion-01:
    |_
      ----------
      __env__:
          base
      __id__:
          webserver
      __sls__:
          webserver
      fun:
          installed
      name:
          httpd
      order:
          10000
      state:
          pkg
    |_
      ----------
      -pkg:
          httpd
      __env__:
          base
      __id__:
          webserver
      __sls__:
          webserver
      fun:
          running
      name:
          httpd
      order:
          10001
      reqire:
          None
      state:
          service
[root~] ]$

查看可知,里面有個order,這個是默認salt 會自動設置,從10000開始.可通過修改master state_auto_order: False來關(guān)閉

order的設定

  • include

被include的文件Order靠前,先執(zhí)行

  • 手動定義order
httpd:
  pkg:
    - installed
    - order: 1
#order的值越小,優(yōu)先級越高.但是-1 是最后!
  • 依賴關(guān)系系統(tǒng)

就是前面使用過的 - require

依賴關(guān)系系統(tǒng) requisite system

我們已經(jīng)使用過依賴關(guān)系系統(tǒng)了,就是定義狀態(tài)和狀態(tài)之間的依賴關(guān)系,常用的函數(shù)有 requirewatch 以及他們的變種require_inwatch-in

四者有何區(qū)別?

require,watch是指依賴,require_in,watch_in是指被依賴

watch 常用于service,而且當依賴條件發(fā)生變化的時候會執(zhí)行一些動作

/etc/httpd/httpd.conf:
  file:
    - managed
    - source: salt://httpd/httpd.conf
  pkg.installed
  service:
    - running
    - require:
      - pkg: httpd
    - watch:
      - file://etc/httpd/httpd.conf #當httpd.conf改變時统舀,重啟httpd服務
    
============================    

/etc/httpd/httpd.conf:
  file:
    - managed
    - source: salt://httpd/httpd.conf   
    - watch_in:
      - service: httpd
  httpd:
    pkg:
      - installed
      - require_in:
        - service: httpd
    service:
      - running
    
    
    
    
    
    
    

salt state多環(huán)境

針對不同的環(huán)境,應用不同state的file,比如開發(fā),測試,生產(chǎn)等.

通過修改master對不同的環(huán)境應用不通過的目錄

#官方demo
Example:
  file_roots:
    base:
      - /srv/salt/
    dev:
      - /srv/salt/dev/services
      - /srv/salt/dev/states
    prod:
      - /srv/salt/prod/services
      - /srv/salt/prod/states
#file_roots 配置salt配置的存放目錄, 其中base環(huán)境是必要的, 指定top.sls存放的位置.
#默認沒指定環(huán)境時則從base目錄獲取文件
#其它則是一些自定義的, 可以通過環(huán)境變量指定.
#這樣可以邏輯上隔離一些環(huán)境配置.
#每一個環(huán)境都可以定義多個目錄, 優(yōu)先級關(guān)系由定義目錄的順序決定.
file_roots:
  base:
    - /srv/salt/foo
    - /srv/salt/bar
#如果尋找 salt://file.sls, 如果都存在/srv/salt/foo/file.sls和/srv/salt/bar/file.sls, 則使用第一個找到的.    

另一個例子

file_roots:
  base:
    - /srv/salt/prod
  qa:
    - /srv/salt/qa
    - /srv/salt/prod
  dev:
    - /srv/salt/dev
    - /srv/salt/qa
    - /srv/salt/prod
#/srv/salt/prod 里的配置是在三種環(huán)境下都可以, /srv/salt/qa 只在qa和dev環(huán)境下可用, /srv/salt/dev則只在dev環(huán)境下可用.

簡答你的實施案例

#master配置
file_roots:
  base:
    - /home/base/
  dev:
    - /home/dev/
    - /home/base/
    
#base環(huán)境   
#/home/base
├── envtest.sls
└── top.sls

#cat /home/base/envtest.sls
envtest:
  cmd.run:
    - name: "echo '[base] env'"    

#dev環(huán)境
#/home/dev/
├── mytest.sls
└── top.sls

#cat /home/dev/mytest.sls
envtest:
  cmd.run:
    - name: "echo '[dev] env'"



##執(zhí)行效果如下,如果不添加環(huán)境變量,則提示找不到文件
[root/srv/salt/dev] ]$salt 'minion-01' state.sls mytest  test=True
minion-01:
    Data failed to compile:
----------
    No matching sls found for 'mytest' in env 'base'
ERROR: Minions returned with non-zero exit code

#加上環(huán)境變量執(zhí)行
[root/srv/salt/dev] ]$salt 'minion-01' state.sls mytest saltenv='dev' test=True
minion-01:
----------
          ID: mytest
    Function: cmd.run
        Name: echo dev-env
      Result: None
     Comment: Command "echo dev-env" would have been executed
     Started: 23:54:46.298421
    Duration: 0.422 ms
     Changes:   

Summary
------------
Succeeded: 1 (unchanged=1)
Failed:    0
------------
Total states run:     1
[root/srv/salt/dev] ]$

salt schedule(salt中的crontab)

周期性的執(zhí)行一些函數(shù),需要注意的是: 在minion上執(zhí)行salt可執(zhí)行模塊里的函數(shù),在master執(zhí)行的是runner模塊的函數(shù).

共有三種方式:master minion pillar

  • master端
  • minion端
  • pillar

一般而言,尤其是在minion端配置,基本不會用到的,主要還是一pillar為主

修改top.sls

#添加
  - schedule

/srv/pillar/schedule.sls

schedule:
  test-job:
    function: cmd.run
    seconds: 10
    args:
      - 'date >> /date.log'
      
#沒隔10S 在/目錄的date.log文件中記錄一條時間
salt "*" saltutil.refresh_pillar
#刷新pillar到minion

#回到minion 可以查看到
[root@minion-01 /]# ls
bin  boot  date.log  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
[root@minion-01 /]# cat date.log 
Fri Mar 24 02:27:40 CST 2017
Fri Mar 24 02:27:50 CST 2017
Fri Mar 24 02:28:00 CST 2017
....

salt ssh

salt-ssh 是 0.17.0 新出現(xiàn)的一個功能.對于有些不能安裝minion的機器,ssh不失為一種好的選擇但是SSH并不能取代minion,salt的有些功能不支持ssh.而且走的是SSH 并不是ZeroMQ,所以速度會有所影響

#首先安裝salt-ssh.
yum -y install salt-ssh
[root~] ]$cat /etc/salt/roster #roster文件名和路徑!
minion-01:
  host: 192.168.247.153
  user: root
  passwd: centos
minion-02:
  host: 192.168.247.154
  user: root
  passwd: centos
  sudo: True

#如果不給passwd的話,執(zhí)行salt-ssh會提示輸入密碼
#普通用戶給sudo權(quán)限
#第一次使用記得加參數(shù) -i 否則報錯如下
[root~] ]$salt-ssh 'minion-01' test.ping
minion-01:
    ----------
    retcode:
        254
    stderr:
    stdout:
        The host key needs to be accepted, to auto accept run salt-ssh with the -i flag:
        The authenticity of host '192.168.247.153 (192.168.247.153)' can't be established.
        ECDSA key fingerprint is 16:f6:f5:49:24:9c:91:da:d7:02:58:a2:14:08:e4:15.
        Are you sure you want to continue connecting (yes/no)? 
        
#第一次運行 添加-i參數(shù)
[root~] ]$salt-ssh 'minion-01' test.ping -i
minion-01:
    True
[root~] ]$salt-ssh 'minion-01' test.ping
minion-01:
    True
[root~] ]$

Returners

擴展salt

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末匆骗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子誉简,更是在濱河造成了極大的恐慌绰筛,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件描融,死亡現(xiàn)場離奇詭異铝噩,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門骏庸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毛甲,“玉大人,你說我怎么就攤上這事具被〔D迹” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵一姿,是天一觀的道長七咧。 經(jīng)常有香客問我,道長叮叹,這世上最難降的妖魔是什么艾栋? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮蛉顽,結(jié)果婚禮上蝗砾,老公的妹妹穿的比我還像新娘。我一直安慰自己携冤,他們只是感情好悼粮,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著曾棕,像睡著了一般扣猫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上翘地,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天申尤,我揣著相機與錄音,去河邊找鬼子眶。 笑死瀑凝,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的臭杰。 我是一名探鬼主播粤咪,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼渴杆!你這毒婦竟也來了寥枝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤磁奖,失蹤者是張志新(化名)和其女友劉穎囊拜,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體比搭,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡冠跷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜜托。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡抄囚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出橄务,到底是詐尸還是另有隱情幔托,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布蜂挪,位于F島的核電站重挑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏棠涮。R本人自食惡果不足惜谬哀,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望故爵。 院中可真熱鬧玻粪,春花似錦隅津、人聲如沸诬垂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽结窘。三九已至,卻和暖如春充蓝,著一層夾襖步出監(jiān)牢的瞬間隧枫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工谓苟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留官脓,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓涝焙,卻偏偏與公主長得像卑笨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子仑撞,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Salt使用PyAMl語法(http://pyyaml.org) 作為它的模板文件的格式赤兴,但是其他很多模板語言在S...
    白熊閱讀 2,452評論 0 1
  • 以前我給我媽說,我覺得你和我爸都不可靠隧哮。 因為3歲時候母親的不告而別桶良,讓我失去了對母親的信任,覺得她隨時都有可能離...
    尹莉莎閱讀 483評論 1 2
  • 大家好沮翔!我是位三年級的小學生陨帆,我喜歡跳舞、古箏、畫畫疲牵,還有閱讀和寫作岸浑,以后我會把自己寫的文字分享在這里,...
    睿子格格閱讀 187評論 1 1
  • 恰好今天是周末瑰步,鼓起很大的勇氣矢洲,從床上爬起來決定出去走走!明明知道缩焦,現(xiàn)在二月份各種公園都是淡季读虏,卻還是選擇了北京植...
    漂亮的姑娘閱讀 345評論 0 2