ansible笔记

ansible笔记

 

 

ansible失败的地方
被控机器是Windows
1、被控机要开winrm端口
2、Windows的模块非常少

 

如果某个主机执行失败了,会生成retry文件,然后用--limit参数来重新执行即可

to retry, use: ansible  --limit @xxx.retry

 

 

解决:
一大堆脚本,而且服务器上脚本不是最新
预估停机执行时间


注意:
任务重复执行问题
安全:密码和密钥,ansible管理机权限
ansible没有回滚操作

 

 

ansible属于非登录shell

ansible笔记_第1张图片

 

ansible笔记_第2张图片

ansible执行过程大体过程如下图,其中暖色调的代表已经模块化

ansible笔记_第3张图片

ansible笔记_第4张图片

 

 

 

 

https://www.jianshu.com/p/575ced3a08fa

特点

无客户端
无服务器端
基于模块,用任意语言开发模块
yaml订制playbook
基于ssh工作
实现多级指挥

连接插件,连接被管理端
核心模块
自定义模块
插件完成模块功能补充
playbooks定义任务
主机清单

ansible笔记_第5张图片

 

架构图

ansible笔记_第6张图片

f

ansible笔记_第7张图片

管理端支持local(连接管理机即自己连接自己的时候) 、ssh、zeromq 三种方式连接被管理端,默认使用基于ssh的连接---这部分对应基本架构图中的连接模块;

 


ansible安装

主控机
管理节点操作
yum install -y epel-release
yum install -y ansible

 

 

任务执行模式

ansible系统由控制主机对被管节点的操作方式有两种ad_hoc和playbook

ad_hoc单命令模式 可以对多台主机执行单个命令
ansible all -a "/bin/echo hello"

playbook模式
playbook通过多个tasks的集合完成一类功能如web的安装部署,数据库服务器的批量备份等



ansible提供了10个命令
ansible
ansible-doc
ansible-vault
ansible-pull
ansible-config
ansible-galaxy
ansible-playbook
ansible-connection
ansible-inventory
ansible-console

 

 

 

1、ansible

ansible命令管理主机
在官方文档中命令行的名字是:Ad-Hoc Commands


ansible是指令核心部分,其主要用于执行ad-hoc命令,即单条命令。默认后面需要跟主机和选项部分,默认不指定模块时,使用的是command模块。如:
[root@localhost ~]# ansible 127.0.0.1 -a 'date'
-m:后面接调用模块的名字
-a:后面接调用模块的参数
ansible all -m shell -a "sh /tmp/kel.sh >>/tmp/kel.log"
参数:
-a 'Arguments', --args='Arguments' 命令行参数
-m NAME, --module-name=NAME 执行模块的名字,默认使用 command 模块,所以如果是只执行单一命令可以不用 -m参数
-M MODULE_PATH, --module-path=MODULE_PATH 要执行的模块的路径,默认为/usr/share/ansible/
-i PATH, --inventory=PATH 指定主机文件、主机目录、可执行文件的路径,默认为/etc/ansible/hosts.
-u Username, --user=Username 执行用户,使用这个远程用户名而不是当前用户
-U --sud-user=SUDO_User sudo到哪个用户,默认为 root
-k --ask-pass 登录密码,提示输入SSH密码而不是基于密钥的验证
-K --ask-sudo-pass 提示密码使用sudo -s --sudo sudo运行
-s --snippet 指定模块显示playbook片段
-S --su 用 su 命令
-f --forks=NUM 并行任务数。NUM被指定为一个整数,默认是5。 #ansible testhosts -a "/sbin/reboot" -f 10 重启testhosts组的所有机器,每次重启10台
--private-key=PRIVATE_KEY_FILE 私钥路径,使用这个文件来验证连接
-vvv --verbose 查看详细的报错信息,类似/usr/local/mysql/bin/mysqlbinlog  -vv
all 针对hosts 定义的所有主机执行
-o --one-line 压缩输出,摘要输出.尝试一切都在一行上输出。
-t Directory, --tree=Directory 执行过程中的日志保存到这个目录
-B 后台运行超时时间,ansible all  -m ping  -B 3600 -P 0
-T Seconds, --timeout=Seconds 时间,单位秒s
-P NUM, --poll=NUM 调查背景工作每隔数秒,调查后台程序时间。需要- b
-c Connection, --connection=Connection 连接类型使用。可能的选项是paramiko(SSH),SSH和地方。
--version  ansible版本号
-C, --check 只是测试一下,不会真正去执行
-l SUBSET, --limit=SUBSET 进一步限制所选主机/组模式 --limit=192.168.0.15 只对这个ip执行或--limit=kvm 只对这个kvm组执行 ,除了kvm组不执行,其他组都执行ansible all -l kvm -m shell -a "sh /tmp/kel.sh >>/tmp/kel.log"

ansible --version
ansible 2.4.2.0
config file = /etc/ansible/ansible.cfg
configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python2.6/site-packages/ansible
executable location = /usr/bin/ansible
python version = 2.6.6 (r266:84292, Aug 18 2016, 15:13:37) [GCC 4.4.7 20120313 (Red Hat 4.4.7-17)]

 

查后台任务id
ansible all  -m ping  -B 3600 -P 0
ansible_job_id:"123"
xxx

#查看后台任务id为123的任务状态
ansible all  -m async_status -a "jid='123'"

 

 

2、ansible-doc
查看模块信息
-l:列出所有已安装的模块
-s :查看具体某模块的用法,如果是extra模块,必须cd到extra模块目录下面再执行,ansible-doc -s command


3、ansible-galaxy
ansible-galaxy 指令用于方便的从https://galaxy.ansible.com/ 站点下载官方收录的playbooks,我们可以形象的理解其类似于centos下的yum、python下的pip。

ansible-galaxy install aeriscloud.docker
这个安装了一个aeriscloud.docker组件,前面aeriscloud是galaxy上创建该模块的用户名,后面对应的是其模块。


初始化一个role的目录结构
ansible-galaxy init role_name

 

列出已安装的roles
ansible-galaxy list

查看已安装的roles信息
ansible-galaxy info bennojoy.mysql

 

卸载roles
ansible-galaxy remove bennojoy.mysql


安装一个role
参数-p 指定role下载的目录。
如果没有指定-p , 那么role 会被自动下载到环境变量ANSIBLE_ROLES_PATH 定义的目录下,
或者默认目录/etc/ansible/roles 下。
ansible-galaxy -p /tmp/roles install bennojoy.nginx

 

安装多个role
将所有依赖的role放在一个role.txt里面,-r参数指定role列表文件role.txt,类似pip install -r requirements.txt

ansible-galaxy -p /tmp/roles install  -r /tmp/role.txt  
cat role.txt
bennojoy.nginx
bennojoy.mysql
bennojoy.mongodb

或者
ansible-galaxy -p /tmp/roles install -r requirements.yml
cat requirements.yml 
# from galaxy
- src: yatesr.timezone

# from GitHub
- src: https://github.com/bennojoy/nginx

# from GitHub , overriding the name and specifying a  specific tag
- src: https://github.com/bennojoy/nginx
  version: master
  name: nginx role

# from a webserver , where the role is packaged in a tar.gz
- src: https://some.webserver.example.com/files/master.tar.gz
  name: http-role

 


4、ansible-playbook
该指令是使用最多的指令,其通过读取playbook 文件后,执行相应的动作
--tags=TAGS 只执行指定标签的任务 例子:ansible-playbook test.yml --tags=copy 只执行标签为copy的那个任务
--list-hosts 只打印有哪些主机会执行这个 playbook 文件,不是实际执行该 playbook 文件
--list-tasks 列出所有将被执行的任务
--syntax-check 对playbook执行语法检查,但不执行它
--skip-tags=SKIP_TAGS 只运行任务不匹配这些值的标签的playbook --skip-tags=copy_start
-e EXTRA_VARS, --extra-vars=EXTRA_VARS 额外的变量设置为键=值或YAML / JSON
--syntax-check 检查yaml文件的语法
--verbose 查看输出细节
--step  每执行一个任务后停止,等待用户确认
-f 用多少个线程执行playbook任务,比如用10个线程 ansible-playbook -f 10 ngxin.yml

#ansible-playbook update.yml --extra-vars "hosts=vipers user=admin" 传递{{hosts}}、{{user}}变量,hosts可以是 ip或组名


 


5、ansible-pull
Ansible的另一种工作模式,pull模式,ansible默认使用push模式
支持直接从git下载playbook执行,需要遵循其规定的目录格式,用处不是特别大,可以不关注



6、ansible-vault
ansible-vault主要应用于配置文件中含有敏感信息,又不希望他能被人看到,vault可以帮你加密/解密这个配置文件,属高级用法。
主要对于playbooks里比如涉及到配置密码或其他变量时,可以通过该指令加密,这样我们通过cat看到的会是一个密码串类的文件,编辑的时候需要输入事先设定的密码才能打开。
这种playbook文件在执行时,需要加上 –ask-vault-pass参数,同样需要输入密码后才能正常执行。具体该部分可以参查官方博客。

[root@node1 ansible]# ansible-vault encrypt db_hosts
New Vault password:
Confirm New Vault password:
Encryption successful
[root@node1 ansible]# ansible -i db_hosts localhost -m ping
ERROR! Decryption failed
Decryption failed
[root@node1 ansible]# ansible -i db_hosts --ask-vault-pass localhost -m ping
Vault password:
localhost | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

[root@node1 ansible]# cat db_hosts
$ANSIBLE_VAULT;1.1;AES256
61663966666265363465653064386666326234353433346163633838366532366236313032303636
6437313333333936396164663031633566613233343161650a333163333732616130343762636135
30303864663138643661393234336433313465623830333832663165393964353961323261373130
3135626236626435640a396338616563646532623966333337366365636665663563666432333539
61663632633130623733316232353836663366623136636432616332376266383263356264303765
6133616235363066356164653232326139643862653464623037

 



 


 

ansible核心模块

 

 

Ansible 对远程服务器的操作实际是通过模块完成的。其工作原理如下
1、将模块拷贝到远程服务器
2、执行模块定义的操作,完成对服务器的修改
3、在远程服务器中删除模块

所有模块官方文档:http://docs.ansible.com/ansible/latest/modules/list_of_all_modules.html

 


ping模块
ping一下你的远程主机,尝试ssh登录远程节点并检查python版本,如果连接成功,返回pong,不需要任何参数
调试和测试类模块



setup模块
获取远程机器的facts数据,也就是机器配置
参数
filter:只返回符合过滤规则的facts数据

# 显示所有机器的facts数据并存储在/tmp/facts目录下
# ansible all -m setup --tree /tmp/facts

# 只显示内存相关内容
# ansible all -m setup -a 'filter=ansible_*_mb'

# 只显示和facter有关的内容.
# ansible all -m setup -a 'filter=facter_*'

# 只显示网卡信息
# ansible all -m setup -a 'filter=ansible_eth[0-2]'



debug模块
参数
msg:定义打印的字符串
var:定义需要打印的变量
regester:定义注入变量

# Example that prints the loopback address and gateway for each host
- debug:
msg: "System {{ inventory_hostname }} has uuid {{ ansible_product_uuid }}"

- debug:
msg: "System {{ inventory_hostname }} has gateway {{ ansible_default_ipv4.gateway }}"
when: ansible_default_ipv4.gateway is defined

- shell: /usr/bin/uptime
register: result #将执行结果注入result变量

- debug:
var: result #打印result的内容
verbosity: 2

- name: Display all variables/facts known for a host
debug:
var: hostvars[inventory_hostname]
verbosity: 4

 

 

 

script模块
在远程节点上执行主控机上的脚步,其功能相当于scp + shell 的组合,脚本执行完成以后会在远程服务器上删除脚本文件
参数
chdir:在执行脚步之前先cd到这个目录

- script: /some/local/script.sh --some-arguments 1234

# Run a script that creates a file, but only if the file is not yet created
- script: /some/local/create_file.sh --some-arguments 1234
  args:
    creates: /the/created/file.txt

# Run a script that removes a file, but only if the file is not yet removed
- script: /some/local/remove_file.sh --some-arguments 1234
  args:
    removes: /the/removed/file.txt

 

 

 

copy模块
复制前会比较远程文件的checksum,如果相同则不复制,返回ok,如果不同才复制,返回changed
参数
mode:可以是数字形式也可以是符号形式,0644 ,01777,u+rwx , u=rw,g=r,o=r
owner:属主
group:属组
backup:默认no,先备份目标节点的源文件再复制,如果拷贝过程失败,则源文件还能使用,而不是对目标文件备份一份!
validate:默认none,验证复制后的文件,%s指代复制后的文件
dest:目标文件,必须是绝对路径
src:源文件,可以是绝对路径,也可以是相对路径,/tmp/表示拷贝文件夹里内容,/tmp表示把文件夹也拷贝过去
force:默认yes,当目标文件跟源文件不一样时会强制覆盖目标文件,如果一样,不会执行复制,别名thirsty,也就是thirsty等于force
follow:默认no,当拷贝的文件夹内有link存在的时候,那么拷贝过去的也会有link

- name: example copying file with owner and permissions
copy:
src: /srv/myfiles/foo.conf
dest: /etc/foo.conf
owner: foo
group: foo
mode: 0644

- name: The same example as above, but using a symbolic mode equivalent to 0644
copy:
src: /srv/myfiles/foo.conf
dest: /etc/foo.conf
owner: foo
group: foo
mode: u=rw,g=r,o=r

- name: Another symbolic mode example, adding some permissions and removing others
copy:
src: /srv/myfiles/foo.conf
dest: /etc/foo.conf
owner: foo
group: foo
mode: u+rw,g-wx,o-rwx

- name: Copy a new "ntp.conf file into place, backing up the original if it differs from the copied version
copy:
src: /mine/ntp.conf
dest: /etc/ntp.conf
owner: root
group: root
mode: 0644
backup: yes

- name: Copy a new "sudoers" file into place, after passing validation with visudo
copy:
src: /mine/sudoers
dest: /etc/sudoers
validate: /usr/sbin/visudo -cf %s

- name: Copy a "sudoers" file on the remote machine for editing
copy:
src: /etc/sudoers
dest: /etc/sudoers.edit
remote_src: yes
validate: /usr/sbin/visudo -cf %s

- name: Copy using the 'content' for inline data
copy:
content: '# This file was moved to /etc/other.conf'
dest: /etc/mine.conf'

 

 

template模块
在复制的同时根据实际情况修改部分内容,这时不能用copy模块,要用template模块
template模块使用的是python的jinja2模板引擎,这里不需要了解jinja2,只需要知道变量的表示法
{{}}就可以了
template模块同样具备copy模块的权限设置,文件备份,验证功能

参数
mode:可以是数字形式也可以是符号形式,0644 ,01777,u+rwx , u=rw,g=r,o=r
owner:属主
group:属组
backup:默认no,先备份目标节点的源文件再复制
validate:默认none,验证复制后的文件,%s指代复制后的文件
dest:目标文件
src:源文件
force:默认yes,当目标文件跟源文件不一样时会强制覆盖目标文件,如果一样,不会执行复制
follow:默认no,当拷贝的文件夹内有link存在的时候,那么拷贝过去的也会有link

 
roles/templates/server.xml中的template文件关键部分如下:




当这个文件还没被template执行的时候,本地的admin_username及admin_password 都是变量状态。
当playbook执行完template的时候,远程的admin_username*及admin_password 会变成变量所对应的值。
么在执行这个Playbook前,对应的那个template文件(俗称模版),将在本地保持{{ admin_username }}及{{ admin_password }}的状态。在Ansible调用template模版执行的时候,这里将由Jinjia2从”tomcat-servers”读取对应的值,然后替换掉模版里的变量,
然后把这个替换变量值后的文件拷贝到远程节点。

 




 

file模块
用来设置远程主机上的文件,软链接symlinks,文件夹权限,也可以创建和删除文件夹,文件
参数
path:目标路径,别名dest,,name,也就是dest,,name等于path
recurse:递归设置文件属性,当state为directory时候
mode:可以是数字形式也可以是符号形式,0644 ,01777,u+rwx , u=rw,g=r,o=r
owner:属主
group:属组
src:源链接,只能应用于state=link 、 state=hard的情况
force:默认yes,当目标文件跟源文件不一样时会强制覆盖目标文件,如果一样,不会执行复制
follow:默认no,当拷贝的文件夹内有link存在的时候,那么拷贝过去的也会有link
state:默认file,file/link/directory/hard/touch/absent
file代表拷是文件;
link代表是个软链接;
directory代表文件夹;
hard代表硬链接;
touch代表生成一个空文件;
absent代表删除

#改变文件权限,数字权限必须以0开头
- file:
path: /etc/foo.conf
owner: foo
group: foo
mode: 0644
#创建软链接,这里的src和dest参数含义和copy模块不一样,file模块里所操作的文件都是远程节点上的文件
- file:
src: /file/to/link/to
dest: /path/to/symlink
owner: foo
group: foo
state: link


# 创建一个新文件 touch命令
- file:
path: /etc/foo.conf
state: touch
mode: "u=rw,g=r,o=r"

# touch the same file, but add/remove some permissions
- file:
path: /etc/foo.conf
state: touch
mode: "u+rw,g-wx,o-rwx"

# 创建一个文件夹
- file:
path: /etc/some_directory
state: directory
mode: 0755

 



user模块/group模块
user 模块请求的是useradd , userdel , usermod 三个命令
group 模块请求的是groupadd,groupdel, groupmod 三个命令

参数
group:主属组
groups:多个附加属组,用逗号分隔
home:家目录
createhome:默认yes,是否创建家目录
name:用户名或群组名,必须,别名user,也就是user等于name
password:密码,必须是加密过的密码,python -c "from passlib.hash import sha512_crypt; import getpass; print(sha512_crypt.using(rounds=5000).hash(getpass.getpass()))"
remove:把家目录也删除,前提state为absent,相当于userdel -r
shell:设置用户的登录shell
uid:设置uid
gid:设置gid
state:创建或删除用户/群组,取值包括present 和absent
generate_ssh_key:是否生成密钥
ssh_key_bits:sshkey的位数,最好设置为2048
ssh_key_file:默认.ssh/id_rsa,
ssh_key_type:默认rsa, 可选dsa,rsa
ssh_key_passphrase:sshkey的密码
expires :用户过期时间,过期时间为时间戳

- name: 添加用户johnd,设置uid为1040,组为admin
user:
name: johnd
comment: John Doe
uid: 1040
group: admin

- name: 添加用户james,设组为admins,developers
user:
name: james
shell: /bin/bash
groups: admins,developers
append: yes

- name: 删除用户johnd
user:
name: johnd
state: absent
remove: yes

- name: 创建密钥是 2048位 SSH key 的用户jsmith,保存位置在~jsmith/.ssh/id_rsa
user:
name: jsmith
generate_ssh_key: yes
ssh_key_bits: 2048
ssh_key_file: .ssh/id_rsa

- name: 创建用户james18,设置过期时间为2015/1/28 8:3:7
user:
name: james18
shell: /bin/zsh
groups: developers
expires: 1422403387


#创建群组
ansible test -m group -a "name=ansible state=present gid=l234" -become


#删除群组
ansible test -m group -a "name=ansible state=absent" -become

 

 

 

yum模块
管理yum包,fedora从版本22开始使用dnf代替yum,如果用fedora22推荐用dnf模块来进行安装包

参数
conf_file:yum的配置文件
disable_gpg_check:默认no, 只有state 为present 或 latest.才能指定disable_gpg_check
list:相当于yum list
name:包名, 可以是'*' 表示yum -y update 更新所有包 或 rpm 文件的url路径或本地路径 ,同时 state=present ,别名pkg,也就是pkg等于name
state:用于描述安装包最终状态,present/latest/installed用于安装包,removed/absent用于remove安装包
update_cache:默认no,用于安装包前执行更新list,只会影响state参数为present/latest的时候

- name: 安装最新版本的Apache,如果已经安装了老版本,那么会更新到最新版本
yum:
name: httpd
state: latest

- name: 删除 Apache 包
yum:
name: httpd
state: absent

- name: 从testing repo 安装Apache
yum:
name: httpd
enablerepo: testing
state: present

- name: 安装指定版本的 Apache
yum:
name: httpd-2.2.29-1.4.amzn1
state: present

- name: 更新所有包
yum:
name: '*'
state: latest

- name: 更新所有包 除外 kernel & foo
yum:
name: '*'
state: latest
exclude: kernel*,foo*

- name: 从url中安装nginx rpm 包
yum:
name: http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm
state: present

- name: 从本地文件中安装 nginx rpm 包
yum:
name: /usr/local/src/nginx-release-centos-6-0.el6.ngx.noarch.rpm
state: present

- name: 安装一组包 group install 'Development tools'
yum:
name: "@Development tools"
state: present

- name: 安装一组包 group install 'Gnome desktop'
yum:
name: "@^gnome-desktop-environment"
state: present

- name: yum list 出ansible相关的包并注入 register result ,之后用 debug 模块打印
yum:
list: ansible
register: result

- name: 使用多个仓库来安装包
yum:
name: sos
enablerepo: "epel,ol7_latest"

- name: 从多个禁用仓库安装包
yum:
name: sos
disablerepo: "epel,ol7_latest"

 



 

service模块
管理远程节点上的服务,比如httpd,sshd,nfs,crond等

参数
arguments:给命令行提供一些选项,别名: args,也就是args等于arguments
enabled:是否开机启动
name:必选项,服务名称
state:对当前服务执行启动,停止、重启、重新加载等操作(started,stopped,restarted,reloaded)
pattern:定义一个模式,如果通过status指令来查看服务的状态时,没有响应,就会通过ps指令在进程中根据该模式进行查找,如果匹配到,则认为该服务依然在运行

- name: 开启服务
service:
name: httpd
state: started

- name: 关服务
service:
name: httpd
state: stopped

- name: 重启服务
service:
name: httpd
state: restarted

- name: 重载服务
service:
name: httpd
state: reloaded

- name: 设置开机启动服务
service:
name: httpd
enabled: yes

- name: 启动foo服务,根据/usr/bin/foo
service:
name: foo
pattern: /usr/bin/foo
state: started

- name: 重启网络服务的eth0网卡
service:
name: network
state: restarted
args: eth0

ansible all -m service -a 'name=network state=restarted args=eth0'

 

 

 

firewalld模块
为某服务和端口添加firewalld规则,firewalld中有正在运行的规则和永久的规则,firewalld都支持
firewalld模块要求远程节点上的firewalld版本在0.2.11以上

参数
permanent:默认None,是否保存设置即使重启机器
interface,默认None,从zone里增删网卡
port:端口,形式端口/协议 ,端口范围 端口-端口/协议
rich_rule:复杂的firewalld规则
service:服务名,为服务添加firewall规则
source:ip来源
state:状态,enabled、disabled、present、absent
zone:work、drop、internal、external、trusted、home、dmz、public、block

#为https服务添加firewall规则
- firewalld:
service: https
permanent: true
state: enabled
#区域dmz
- firewalld:
zone: dmz
service: http
permanent: true
state: enabled

#为端口号8081/tcp,范围161-162/udp添加firewall规则
- firewalld:
port: 8081/tcp
permanent: true
state: disabled

- firewalld:
port: 161-162/udp
permanent: true
state: enabled

#其他复杂规则
- firewalld:
rich_rule: 'rule service name="ftp" audit limit value="1/m" accept'
permanent: true
state: enabled

- firewalld:
source: 192.0.2.0/24
zone: internal
state: enabled

- firewalld:
zone: trusted
interface: eth2
permanent: true
state: enabled

- firewalld:
masquerade: yes
state: enabled
permanent: true
zone: dmz

- firewalld:
zone: custom
state: present
permanent: true

 

 

 

shell模块/raw模块
在远程节点通过/bin/sh执行命令,如果一个操作可以通过模块yum,copy实现,那么建议不要用shell或command这样的通用命令模块
因为通用命令模块不会根据具体操作的特点进行状态status判断,所以当没有必要再重新执行的时候,他还是会重新执行一次
shell/raw 模块相当于使用SSH 直接执行Linux 命令,不会进入到Ansible 的模块子系统中,也可以执行远程机器的shell脚本,不过shell脚本需要绝对路径

shell:在节点执行shell命令,支持$HOME、<、>、|、;、&  ,不支持传入参数

参数
chdir:默认None,运行shell之前cd到某个目录
creates:默认None,创建一个文件,如果这个文件存在则不运行shell
removes:默认None,删除一个文件,如果这个文件不存在则不运行shell
executable:默认None,指定用哪种shell来执行命令,给出shell的绝对路径,比如/bin/bash

- name: 执行shell命令,把标准输出重定向到文件
shell: somescript.sh >> somelog.txt

- name: 改变工作目录在执行shell命令前
shell: somescript.sh >> somelog.txt
args:
chdir: somedir/


- name: somelog.txt不存在并改变工作目录
shell: somescript.sh >> somelog.txt
args:
chdir: somedir/
creates: somelog.txt

- name: bash处理重定向
shell: cat < /tmp/*txt
args:
executable: /bin/bash

- name: 使用模板变量 (always use quote filter to avoid injection)
shell: cat {{ myfile|quote }}


- name: Run expect to wait for a successful PXE boot via out-of-band CIMC
shell: |
set timeout 300
spawn ssh admin@{{ cimc_host }}

expect "password:"
send "{{ cimc_password }}\n"

expect "\n{{ cimc_name }}"
send "connect host\n"

expect "pxeboot.n12"
send "\n"

exit 0
args:
executable: /usr/bin/expect
delegate_to: localhost

 

 

 

 

command模块
在远程节点执行命令,不支持家目录,重定向,管道 $HOME、<、>、|、;、&
和shell模块类似,但有一个和shell模块不同,command模块多了一个传入参数的方式

参数
chdir:默认None,运行shell之前cd到某个目录
creates:默认None,创建一个文件,如果这个文件存在则不运行shell
removes:默认None,删除一个文件,如果这个文件不存在则不运行shell

- name: 返回执行结果到变量 registered var
command: cat /etc/motd
register: mymotd

- name: command模块多了一个传入参数的方式
command: /usr/bin/make_database.sh arg1 arg2 creates=/path/to/database


- name: This command will change the working directory to somedir/ and will only run when /path/to/database doesn't exist.
command: /usr/bin/make_database.sh arg1 arg2
args:
chdir: somedir/
creates: /path/to/database

- name: safely use templated variable to run command. Always use the quote filter to avoid injection issues.
command: cat {{ myfile|quote }}
register: myoutput

 

 

 

cron模块
管理cron.d 目录 crontab 文件
参数
cron_file:如果指定该选项,则用该文件替换远程主机上的cron.d目录下的用户任务计划,与user参数一起用
backup:默认no,对远程主机上的原任务计划内容修改之前做备份,万一出故障还原回来,而不是备份任务计划
job:要执行的任务,依赖于state=present  别名value ,也就是job等于value  
user:默认root,操作哪个用户的crontab
disabled:禁用job,默认no,job状态必须是state=present
name:该任务的描述(必须项)
minute:分钟(0-59,*,*/2,……),不写默认为*
hour:小时(0-23,*,*/2,……),不写默认为*
day:日(1-31,*,*/2,……),不写默认为*,别名dom ,也就是day等于dom
month:月(1-12,*,*/2,……),不写默认为*
weekday:周(0-7,*,……),不写默认为*,别名dow,也就是weekday等于dow
special_time:指定什么时候执行,参数:reboot/yearly/annually/monthly/weekly/daily/hourly
state:状态,参数:present:创建任务 、absent:删除任务

# 5点和2点运行 一个ls命令"0 5,2 * * ls -alh > /dev/null"
- cron:
    name: "check dirs"
    minute: "0"
    hour: "5,2"
    job: "ls -alh > /dev/null"

# 移除名叫an old job的作业
- cron:
    name: "an old job"
    state: absent

# 创建一个作业 "@reboot /some/job.sh"
- cron:
    name: "a job for reboot"
    special_time: reboot
    job: "/some/job.sh"

# 建一个环境变量 "PATH=/opt/bin"
- cron:
    name: PATH
    env: yes
    value: /opt/bin

# Creates an entry like "APP_HOME=/srv/app" and insert it after PATH
# declaration
- cron:
    name: APP_HOME
    env: yes
    value: /srv/app
    insertafter: PATH

# 建一个cron文件存放在 /etc/cron.d
- cron:
    name: yum autoupdate
    weekday: 2
    minute: 0
    hour: 12
    user: root
    job: "YUMINTERACTIVE: 0 /usr/sbin/yum-autoupdate"
    cron_file: ansible_yum-autoupdate

# 删除一个cron文件 /etc/cron.d
- cron:
    name: "yum autoupdate"
    cron_file: ansible_yum-autoupdate
    state: absent

# 删除 "APP_HOME" 环境变量
- cron:
    name: APP_HOME
    env: yes
    state: absent

 

 

 

 

unarchive模块
unarchive模块用于解压文件,其作用类似于Linux 下的tar 命令。
默认情况下,unarchive的作用是将主控机的压缩包拷贝到远程服务器,然后进行解压。
远程机器需要已经安装gtar/unzip

参数:
remote_src: 默认no,表示在解压文件之前,先将控制节点上的文件复制到远程主机中,然后再进行解压,即使远程主机已经有这个压缩文件也不检查,yes表示解压远程主机上的文件,即使远程主机无这个文件
src: 必须,指定压缩文件的路径,该选项的取值取决于remote_src 的取值, 如果remote_src:取值为yes ,则src 指定的是远程服务器中压缩包的地址,如果remote_src取值为no, 则src 指向的是控制节点中的路径;
dest: 必须,该选项指定的是远程服务器上的绝对路径,表示压缩文件解压的路径
list_files:默认no ,如果该选项取值为yes ,也会解压文件,并且在ansible 的返回值中列出压缩包里的文件
exclude: 解压文件时排除exclude 选项指定的文件或目录列表;
keep_newer:默认False ,如果该选项取值为True ,那么,当目标地址中存在同名的文件,并且文件比压缩包中的文件更新时,不进行覆盖;
owner: 文件或目录解压以后的所有者
group:文件或目录解压以后所属的群组
mode: 文件或目录解压以后的权限

- name: Extract foo.tgz into /var/lib/foo
  unarchive:
    src: foo.tgz
    dest: /var/lib/foo

- name: Unarchive a file that is already on the remote machine
  unarchive:
    src: /tmp/foo.zip
    dest: /usr/local/bin
    remote_src: yes

- name: Unarchive a file that needs to be downloaded (added in 2.0)
  unarchive:
    src: https://example.com/example.zip
    dest: /usr/local/bin
    remote_src: yes

 

 

 

stat模块
stat 模块用于获取远程服务器上的文件信息,其作用类似于Linux 下的stat 命令。
stat模块可以获取atime 、ctime 、mtime 、checksum 、size 、uid 、gid 等信息

参数:
path: 指定文件或目录的路径

# Obtain the stats of /etc/foo.conf, and check that the file still belongs
# to 'root'. Fail otherwise.
- stat:
    path: /etc/foo.conf
  register: st
- fail:
    msg: "Whoops! file ownership has changed"
  when: st.stat.pw_name != 'root'

# Determine if a path exists and is a symlink. Note that if the path does
# not exist, and we test sym.stat.islnk, it will fail with an error. So
# therefore, we must test whether it is defined.
# Run this to understand the structure, the skipped ones do not pass the
# check performed by 'when'
- stat:
    path: /path/to/something
  register: sym

- debug:
    msg: "islnk isn't defined (path doesn't exist)"
  when: sym.stat.islnk is not defined

- debug:
    msg: "islnk is defined (path must exist)"
  when: sym.stat.islnk is defined

- debug:
    msg: "Path exists and is a symlink"
  when: sym.stat.islnk is defined and sym.stat.islnk

- debug:
    msg: "Path exists and isn't a symlink"
  when: sym.stat.islnk is defined and sym.stat.islnk == False


# Determine if a path exists and is a directory.  Note that we need to test
# both that p.stat.isdir actually exists, and also that it's set to true.
- stat:
    path: /path/to/something
  register: p
- debug:
    msg: "Path exists and is a directory"
  when: p.stat.isdir is defined and p.stat.isdir

# Don't do md5 checksum
- stat:
    path: /path/to/myhugefile
    get_md5: no

# Use sha256 to calculate checksum
- stat:
    path: /path/to/something
    checksum_algorithm: sha256

 

 

 

mount 模块
在远程服务器上挂载磁盘,当进行挂盘操作时,如果挂载点指定的路径不存在,将创建该路径

参数
state:可以取值为present, absent, mounted, unmounted , 其中, mounted 与unmounted 用来处理磁盘的挂载和卸载, 并且会正确配置fstab 文件, preset 与absent 只会设置fstab 文件, 不会去操作磁盘
fstype:指定文件系统类型, 当state 取值为present 或mounted 时,该选项为必填选项
src:挂载的设备
path: 挂载点的路径

# Before 2.3, option 'name' was used instead of 'path'
- name: Mount DVD read-only
  mount:
    path: /mnt/dvd
    src: /dev/sr0
    fstype: iso9660
    opts: ro,noauto
    state: present

- name: Mount up device by label
  mount:
    path: /srv/disk
    src: LABEL=SOME_LABEL
    fstype: ext4
    state: present

- name: Mount up device by UUID
  mount:
    path: /home
    src: UUID=b3e48f45-f933-4c8e-a700-22a159ec9077
    fstype: xfs
    opts: noatime
    state: present
    

 

 

 

synchronize模块
synchronize 模块是对rsync 命令的封装,以便对常见的rsync 任务进行处理
注意:主控机和远程主机都需要安装rsync,rsync daemon必须在主控机和远程主机都运行
synchronize模块不一定能完全代替rsync命令
    
参数
src : 需要同步到远程服务器的文件或目录
dest :远程服务器保存数据的路径
archive :默认yes ,相当于同时开启recursive 、links 、perms 、times 、owner 、group 、-D 等选项
compress :默认yes ,表示在文件同步过程中是否启用压缩
delete :默认no ,当取值为yes 时,表示删除dest 中存在而src 中不存在的文件
过滤:    (.rsync-filter )files to the source directory,排除同步rsync-filter 文件里面指定的文件

- name: Synchronization of src on the control machine to dest on the remote hosts
  synchronize:
    src: some/relative/path
    dest: /some/absolute/path

- name: Synchronization using rsync protocol (push)
  synchronize:
    src: some/relative/path/
    dest: rsync://somehost.com/path/

- name: Synchronization using rsync protocol (pull)
  synchronize:
    mode: pull
    src: rsync://somehost.com/path/
    dest: /some/absolute/path/

- name:  Synchronization using rsync protocol on delegate host (push)
  synchronize:
    src: /some/absolute/path/
    dest: rsync://somehost.com/path/
  delegate_to: delegate.host

- name: Synchronization using rsync protocol on delegate host (pull)
  synchronize:
    mode: pull
    src: rsync://somehost.com/path/
    dest: /some/absolute/path/
  delegate_to: delegate.host

- name: Synchronization without any --archive options enabled
  synchronize:
    src: some/relative/path
    dest: /some/absolute/path
    archive: no

- name: Synchronization with --archive options enabled except for --recursive
  synchronize:
    src: some/relative/path
    dest: /some/absolute/path
    recursive: no

- name: Synchronization with --archive options enabled except for --times, with --checksum option enabled
  synchronize:
    src: some/relative/path
    dest: /some/absolute/path
    checksum: yes
    times: no

- name: Synchronization without --archive options enabled except use --links
  synchronize:
    src: some/relative/path
    dest: /some/absolute/path
    archive: no
    links: yes

- name: Synchronization of two paths both on the control machine
  synchronize:
    src: some/relative/path
    dest: /some/absolute/path
  delegate_to: localhost

- name: Synchronization of src on the inventory host to the dest on the localhost in pull mode
  synchronize:
    mode: pull
    src: some/relative/path
    dest: /some/absolute/path

- name: Synchronization of src on delegate host to dest on the current inventory host.
  synchronize:
    src: /first/absolute/path
    dest: /second/absolute/path
  delegate_to: delegate.host

- name: Synchronize two directories on one remote host.
  synchronize:
    src: /first/absolute/path
    dest: /second/absolute/path
  delegate_to: "{{ inventory_hostname }}"

- name: Synchronize and delete files in dest on the remote host that are not found in src of localhost.
  synchronize:
    src: some/relative/path
    dest: /some/absolute/path
    delete: yes
    recursive: yes

# This specific command is granted su privileges on the destination
- name: Synchronize using an alternate rsync command
  synchronize:
    src: some/relative/path
    dest: /some/absolute/path
    rsync_path: "su -c rsync"

# Example .rsync-filter file in the source directory
# - var       # exclude any path whose last part is 'var'
# - /var      # exclude any path starting with 'var' starting at the source directory
# + /var/conf # include /var/conf even though it was previously excluded

- name: Synchronize passing in extra rsync options
  synchronize:
    src: /tmp/helloworld
    dest: /var/www/helloworld
    rsync_opts:
      - "--no-motd"
      - "--exclude=.git"

# Hardlink files if they didn't change
- name: Use hardlinks when synchronizing filesystems
  synchronize:
    src: /tmp/path_a/foo.txt
    dest: /tmp/path_b/foo.txt
    link_dest: /tmp/path_a/

 

 

 

get_url模块
类似于wget和curl的功能,可以进行下载以及webapi交互等操作
支持HTTP, HTTPS, FTP
参数:
backup:默认no,创建一个包括时间戳信息的备份文件,这样你可以得到原始文件,如果你不正确地弄错了
checksum:
如果将校验和传递给此参数,则将在下载目标文件的摘要后计算摘要,以确保其完整性。格式:,例如:checksum =“sha256:D98291AC [...] B6DC7B97”如果你担心可移植性,只有sha1算法可用于所有平台和python版本。可以安装第三方hashlib库以访问其他算法。此外,如果将校验和传递给此参数,并且文件位于dest位置下,则将计算destination_checksum,如果校验和等于destination_checksum,则将跳过文件下载(除非force为true)
dest:目标文件,必须
force:默认no,当目标文件跟源文件不一样时会强制覆盖目标文件,如果一样,不会执行复制,别名thirsty,也就是thirsty等于force
mode:可以是数字形式也可以是符号形式,0644 ,01777,u+rwx , u=rw,g=r,o=r
owner:属主
group:属组
timeout:默认10秒,单位秒 超时请求
url:HTTP, HTTPS, FTP形式URL
url_username:用于HTTP基本认证的用户名。对于允许空密码的站点,可以在不使用url_password的情况下使用此参数。
url_password:用于HTTP基本认证的密码。如果未指定url_username参数,则不会使用url_password参数。
use_proxy:默认yes,如果no,它将不使用代理,即使在目标主机上的环境变量中定义了一个代理。
validate_certs:默认yes,如果no,SSL证书将不会验证。这只应在使用自签名证书的个人控制站点上使用。
tmp_dest:临时文件下载到的绝对路径。默认为TMPDIR,TEMP或TMP env变量或特定于平台的值

- name: 下载文件 foo.conf
  get_url:
    url: http://example.com/path/file.conf
    dest: /etc/foo.conf
    mode: 0440


- name: 下载并校验文件(sha256)
  get_url:
    url: http://example.com/path/file.conf
    dest: /etc/foo.conf
    checksum: sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c

- name: D下载并校验文件 (md5)
  get_url:
    url: http://example.com/path/file.conf
    dest: /etc/foo.conf
    checksum: md5:66dffb5228a211e61d6d7ef4a86f5758

- name: 用ftp下载一个文件
  get_url:
    url: file:///tmp/afile.txt
    dest: /tmp/afilecopy.txt

 

 

fetch模块
从远程机器拷贝文件到控制机
参数
dest:必选项,必须是一个目录,假如目录是/backup/,文件名是/etc/profile,机器是192.168.3.6,那么保存的路径是/backup/192.168.3.6/etc/profile
flat:如果dest是/backup/而不是/backup,那么就会修改默认行为,那么保存的路径是/backup/profile
src :必选项,远程机器的文件位置,必须是一个文件,不能是目录,后续可能会支持目录
validate_checksum:默认yes,验证源文件和目标文件的校验和

# 存储文件在目标路径/tmp/fetched/192.168.3.6/tmp/somefile
- fetch:
    src: /tmp/somefile
    dest: /tmp/fetched

# 直接定义一个路径
- fetch:
    src: /tmp/somefile
    dest: /tmp/prefix-{{ inventory_hostname }}
    flat: yes

# 定义一个目标路径
- fetch:
    src: /tmp/uniquefile
    dest: /tmp/special/
    flat: yes

# 存储在针对playbook的相对路径
- fetch:
    src: /tmp/uniquefile
    dest: special/prefix-{{ inventory_hostname }}
    flat: yes

 

 

core模块和extra模块
在ansible文档上查看单个模块会显示是core模块还是extra模块
yum就是一个core模块,archive就是一个extra模块


core模块
不需要额外下载和配置,安装ansible后就可以直接使用


extra模块
需要进行下载和额外配置才能使用
额外的模块托管在Github上的,ansible-modules-extras
使用方法
1、下载extra模块
cd /tmp/
git clone   https://github.com/ansible/ansible-modules-extras.git

2、修改配置文件
/etc/ansible/ansible.cfg
#library = /tmp/ansible-modules-extras/



 


ansible配置文件 ansible.cfg
注意:修改了配置文件不需要重启ansible,因为ansible不是服务端,没有后台运行,修改了配置文件马上生效

 

ansible.cfg提供的是默认配置,在inventory文件里或playbook里没有定义的配置,ansible.cfg提供默认配置,例如sudo_user这个选项      



module_set_locale  设置本地的环境变量  
inventory      = /etc/ansible/hosts     这个一个静态的ini格式的文件,指定主机文件、主机目录、可执行文件的路径,默认为/etc/ansible/hosts.
#library        = /usr/share/my_modules/   Ansible默认搜寻extra模块的位置  
remote_tmp     = $HOME/.ansible/tmp   Ansible 通过远程传输模块到远程主机,然后远程执行,执行后在清理现场,所以你看不到远程主机有python脚本或模块存在  
pattern        = *    如果没有提供“hosts”节点,这是playbook要通信的默认主机组.默认值是对所有主机通信  
forks          = 5    在与主机通信时的默认并行进程数 ,默认是5
poll_interval  = 15    当具体的poll interval 没有定义时,多少时间回查一下这些任务的状态, 默认值是15秒  
sudo_user      = root   sudo使用的默认用户 ,默认是root  
#ask_sudo_pass = True   用来控制Ansible playbook 在执行sudo之前是否询问sudo密码.默认为False
#ask_pass      = True    控制Ansible playbook 是否会自动默认弹出密码,默认为False,如果改为true,ansible命令就不需要用-k选项
transport      = smart   通信机制.默认 值为’smart’。如果本地系统支持   ControlPersist技术的话,将会使用(基于OpenSSH)‘ssh’,如果不支持讲使用‘paramiko’.其他传输选项包括‘local’, ‘chroot’,’jail’等等  
#remote_port    = 22    远程SSH端口。 默认是22  
module_lang    = C   模块和系统之间通信的计算机语言,默认是C语言  
gathering = implicit   控制默认facts收集(远程系统变量). 默认值为’implicit’, 每一次play,facts都会被收集
#roles_path    = /etc/ansible/roles   roles 路径指的是’roles/’下的目录,用于playbook搜索Ansible roles
#host_key_checking = False    检查主机密钥
sudo_exe = sudo     如果在其他远程主机上使用另一种方式执sudo操作.可以使用该参数进行更换
#what flags to pass to sudo   传递sudo之外的参数
#sudo_flags = -H

#SSH timeout    SSH超时时间
timeout = 10
#remote_user = root   使用/usr/bin/ansible-playbook链接的默认用户名,如果不指定,会使用当前登录的用户名
#log_path = /var/log/ansible.log     日志文件存放路径
#module_name = command     ansible命令执行默认的模块
#executable = /bin/sh     在sudo环境下产生一个shell交互接口. 用户只在/bin/bash的或者sudo限制的一些场景中需要修改
#jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n      允许开启Jinja2拓展模块
#private_key_file = /root/.ssh/id_rsa        私钥文件存储位置
ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}   这个设置可以告知用户,Ansible修改了一个文件,并且手动写入的内容可能已经被覆盖.
#display_skipped_hosts = True     显示任何跳过任务的状态 ,默认是显示
#error_on_undefined_vars = False      如果所引用的变量名称错误的话, 将会导致ansible在执行步骤上失败
#system_warnings = True    允许禁用系统运行ansible相关的潜在问题警告
#deprecation_warnings = True     允许在ansible-playbook输出结果中禁用“不建议使用”警告
#command_warnings = False    当shell和命令行模块被默认模块简化时,Ansible 将默认发出警告
#nocows = 1    默认ansible可以调用一些cowsay的特性   开启/禁用:0/1
#nocolor = 1  输出带上颜色区别, 开启/关闭:0/1


 

 

 

在线查看ansible.cfg文件
https://raw.githubusercontent.com/ansible/ansible/devel/examples/ansible.cfg

ansible配置文件解释列表
http://docs.ansible.com/ansible/latest/installation_guide/_config.html
http://docs.ansible.com/ansible/latest/installation_guide/intro_configuration.html


ansible会按照下面顺序查找配置文件
ANSIBLE_CONFIG (environment variable if set)
ansible.cfg (in the current directory)
~/.ansible.cfg (in the home directory)
/etc/ansible/ansible.cfg

 


 

inventory配置

 

 

指定inventory配置文件
1、在/etc/ansible/ansible.cfg中修改
inventory = /etc/ansible/hosts

2、命令行中传递inventory配置文件
利用参数-i传递主机清单配置文件
ansible-playbook -i /etc/ansible/hosts  site.yml

 

 

静态inventory

一组相似的 hostname , 可简写如下,一般建议使用ip,而不使用主机名或域名,需要反解析:
[webservers]
www[01:50].example.com


数字的简写模式中,01:50 也可写为 1:50,意义相同.你还可以定义字母范围的简写模式:
[databases]
db-[a:f].example.com

 

inventory变量

主机变量
分配变量给主机很容易做到,这些变量定义后可在 playbooks 中使用:
[atlanta]
host1 http_port=80 maxRequestsPerChild=808   #maxRequestsPerChild 称为主机变量
host2 http_port=303 maxRequestsPerChild=909


组变量
也可以定义属于整个组的变量,这样就不用单独一台一台主机定义变量:
[atlanta]
host1
host2

[atlanta:vars]
ntp_server=ntp.atlanta.example.com
proxy=proxy.atlanta.example.com

[docker] #定义了一组叫docker
172.16.1.11 #组下面的主机
172.16.1.12
#172.11.11.11 # ansible_ssh_pass='123456'

[docker:vars] #针对docker组使用inventroy内置变量定义了ssh登陆密码,中括号里写组名:vars
ansible_ssh_pass='123456'

[kvm] 
172.16.1.13
172.16.1.14

[docker:children] #组的继承,docker下面包含一个kvm组,kvm组里会继承docker组所有变量ansible_ssh_pass
kvm

 

 


按目录结构定义变量

通过/etc/ansible/ 定义文件表示变量

创建:/etc/ansible/host_vars/
创建:/etc/ansible/group_vars/

host_vars目录下文件名,要和/etc/ansible/hosts文件里的host名一致,并且是yaml格式文件
group_vars目录下文件名,要和/etc/ansible/hosts文件里的group名一致,并且是yaml格式文件
host_vars目录下目录名,ansible会读取目录下所有文件内容,并且是yaml格式文件
group_vars目录下目录名,ansible会读取目录下所有文件内容,并且是yaml格式文件

 

比如
主机变量
/etc/ansible/hosts
client_105 #主机名


/etc/ansible/host_vars/client_105.yml
---
client_105_key: 105

 

组变量
/etc/ansible/hosts
[kvm]
192.168.3.6


/etc/ansible/group_vars/kvm.yml
---
ntp_server: ntp.atlanta.example.com


目录下
/etc/ansible/host_vars/client_105/1.yml
/etc/ansible/host_vars/client_105/2.yml
/etc/ansible/host_vars/client_105/x.yml

/etc/ansible/group_vars/kvm/1.yml
/etc/ansible/group_vars/kvm/2.yml
/etc/ansible/group_vars/kvm/x.yml

 

 

 

Inventory行为参数(behavioral inventory parameters )
覆盖Ansible 默认配置时
ansible_ssh_host
将要连接的远程主机名.与你想要设定的主机的别名不同的话,可通过此变量设置.

ansible_ssh_port
ssh端口号.如果不是默认的端口号,通过此变量设置.

ansible_ssh_user
默认的 ssh 用户名

ansible_ssh_pass
ssh 密码(这种方式并不安全,我们强烈建议使用ansible命令行中 -k 参数 或 SSH 密钥)

ansible_sudo_pass
sudo 密码(这种方式并不安全,我们强烈建议使用 -K)

ansible_sudo_exe (new in version 1.8)
sudo 命令路径(适用于1.8及以上版本)

ansible_become
强制使用sudo提权

ansible_become_method
使用哪种提权方法,例如sudo

ansible_become_user
sudo用户,切换到哪个用户执行命令

ansible_become_pass
sudo用户密码

ansible_connection
与主机的连接类型.比如:local, ssh ,winrm, paramiko. Ansible 1.3 以前默认使用 paramiko.1.3 以后默认使用 'smart','smart' 方式会根据是否支持 ControlPersist, 来判断'ssh' 方式是否可行.从Ansible 1.3 版本开始, Ansible 默认使用Open SSH实现各服务器间通信

ansible_ssh_private_key_file
ssh 使用的私钥文件.适用于有多个密钥,而你不想使用 SSH 代理的情况.

ansible_shell_type
目标系统的shell类型.默认情况下,命令的执行使用 'bash' 语法,可设置为 'csh' 或 'fish'.

ansible_python_interpreter
目标主机的 python 路径.适用于的情况: 系统中有多个 Python, 或者命令路径不是"/usr/bin/python",比如 \*BSD, 或者 /usr/bin/python
不是 2.X 版本的 Python.我们不使用 "/usr/bin/env" 机制,因为这要求远程用户的路径设置正确,且要求 "python" 可执行程序名不可为 python以外的名字(实际有可能名为python26).
与 ansible_python_interpreter 的工作方式相同,可设定如 ruby 或 perl 的路径

 

 

多个inventory列表
配置支持多个inventory
首先需要修改ansible.cfg中inventory 的定义改成一个目录
inventory = /etc/ansible/inventory   #/etc/ansible/inventory 是一个目录


然后我们在目录里面放入多个hosts文件
[root@ceshi2 ansible]# tree inventory
inventory
├── docker
└── kvm

cat docker
[docker] 
172.16.1.11 
172.16.1.12
#172.11.11.11 # ansible_ssh_pass='123456'

[docker:vars] 
ansible_ssh_pass='123456'


cat kvm
[kvm] 
172.16.1.13
172.16.1.14

ansible all  -i  /etc/ansible/inventory  -a 'uptime'  # -i可以指定目录

 

 

 

 

动态inventory

动态inventory的意思是所有的变量可以从外部获取,也就是说我们可以从CMDB以及zabbix系统拉取所有的主机信息然后使用ansible进行管理。
引用inventory只需要把ansible.cfg文件中的inventory定义值改成一个执行脚本即可。

inventory = /etc/ansible/inventory.py   #/etc/ansible/inventory.py 是一个py文件

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Author : huazai
# @Time : 2017/11/27 14:11
# @File : inventory.py
# @Description :

import json
host1ip = ['192.168.1.15']
host2ip = ['192.168.1.110']
group = 'test11'    # 给一个组名
group2 = 'test22'
hostdata = {group:{"hosts":host1ip},group2:{"hosts":host2ip}}
print json.dumps(hostdata,indent=4)


ansible -i inventory.py test11  -a 'uptime' -k
SSH password:
192.168.1.15 | SUCCESS | rc=0 >>
 07:25:27 up  3:56,  3 users,  load average: 0.00, 0.00, 0.00
192.168.1.110 | SUCCESS | rc=0 >>
 07:25:27 up 7 min,  3 users,  load average: 0.00, 0.02, 0.00

 

按照Ansible 的约定,我们需要写一个动态脚本来获取服务器的列表。这个脚本必须支持 --list 选项和--host 选项。
--list 选项以json 的格式返回以组名为key ,服务器列表为value 的数据。
--host 返回一个字典,该字典中包含了这个服务器的详细信息。

python hosts.py --list
{
    "webservers":[
     "foo.example.com",
     "bar.example.com"
    ],
    "dbservers ": [
     "one.example.com",
     "two.example.com",
     "three.example.com"
    ]
}


python hosts.py  --host=foo.example.com
{
    "ansible_user":"lmx",
    "ansible_port":2099
}

 

表结构

mysql > show create table hosts\G
Create Table: CREATE TABLE 'hosts'(
`id`int(11) NOT NULL AUTO_INCREMENT,
`host` varchar(15) DEFAULT NULL ,
`groupname` varchar(15) DEFAULT NULL ,
`username` varchar(15) DEFAULT NULL ,
`port` int(11) DEFAULT  '22',
PRIMARY KEY ('id')
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4


id 是表的主键,
host 是服务器的ip 地址或主机名称 
groupname 是Ansible 中的组名
username  SSH 连接的用户名
port SSH 连接的端口号

 

hosts.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Author : huazai
# @Time : 2018/5/31 9:47
# @File : dd.py
# @Description :


from __future__ import  print_function
import argparse
import json
from collections import  defaultdict
from contextlib import  contextmanager

import pymysql


def to_json(in_dict):
    return json.dumps(in_dict,sort_keys=True,indent=2)


@contextmanager
def get_conn(**kwargs):
    conn=pymysql.connect(**kwargs)
    try:
        yield  conn   #关键是yield 和contextmanager
    finally:
        conn.close()


def parse_args():
    parse=argparse.ArgumentParser(description='openstack inventory module')
    group=parse.add_mutually_exclusive_group(required=True)
    group.add_argument('--list',action='store_true',help='list active servers')
    group.add_argument('--host',help='list details about the specific host')
    return parse.parse_args()


def list_all_hosts(conn):
    hosts=defaultdict(list)

    with conn as cur:   #上下文管理
        cur.execute('select * from hosts')
        rows=cur.fetchall()
        for row in rows:
            no,host,group,user,port = row
            hosts[group].append(host)
    return  hosts




def get_host_detail(conn,host):
    details = {}
    with conn as cur:
        cur.execute("select * from hosts where host='{0}'".format(host))
        rows = cur.fetchall()
        if rows:
            no,host,group,user,port=rows[0]
            details.update(ansible_user=user,ansible_port=port)

    return details


def main():
    parser=parse_args()
    with get_conn(host='127.0.0.1',user='abc',passwd='abc',db='test') as conn:
        if parser.list:
            hosts=list_all_hosts(conn)
            print(to_json(hosts))
        else:
            details = get_host_detail(conn,parser.host)
            print (to_json(details))

if __name__=='__main__':
    main()

 

ansible test -i hosts.py  -m ping

 

 

主机分组切片

- command: /opt/application/migrate_db.py
  when: inventory_hostname == webservers[0]

 


playbook剧本

 

playbook选项

https://github.com/lorin/ansible-quickref

 

注意:
yaml文件里  等于号 等价于 冒号,  mode=600  等价于   mode: 600

 

 

playbook剧本主要三部分
1、在什么机器以什么身份执行,hosts,users,sudo,sudo_user
2、执行的任务是什么,tasks
3、善后的任务有什么,handlers


YAML语法
1、文件开始符
---

2、数组list,横线后面有空格
- element1
- element2
- element3

3、字典 hash or dictionary,冒号后面有空格
key: value

4、复杂的字典
字典嵌套
languages:
ruby: Elite
python: Elite
dotnet: Lame
字典和数组的嵌套
- languages:
ruby: Elite
python: Elite
dotnet:
- lisp
- fortran
- erlang

5、注意
变量里有冒号:时要加引号
foo: "somebody i a colon here: so i did"
变量以为"{"开头时要加引号
foo: "{{ variable }}"

 

 

 

 

YAML 的语法规则如下
---:声明这是一个yaml文件,非必须
YAML 中的字段大小写敏感
YAML 与Python 一样,使用缩进表示层级关系
YAML 的缩进不允许使用Tab 键,只允许使用空格,且空格的数目不重要,只要相同层级的元素左侧对齐即可
"#"表示注释,从这个字符一直到行尾都会被解析器忽略
hosts:被控主机ip,或主机组,或all
remote_user:以某个用户身份执行
vars:变量
tasks:playbook核心,定义顺序执行的动作action,每个action调用一个ansible模块
action语法
name:每个action最好有name属性,这是供人类读的,写name是个好习惯,playbook执行时会显示对应的name
module:module_parameter=module_value
handers:playbook的event处理操作,有且仅有在action触发时才会执行,但多次触发只执行一次,并按照声明顺序执行
参数:传入到模块执行的参数,比如,copy模块需要src,dest,owner,group,mode等参数



YAML 支持三种格式的数据,分别是:
对象:键值对的集合,又称为映射,类似于Python 中的字典
数组: 一组按次序排列的值,又称为序列(sequence), 类似于Python 中的列表
纯量(scalars): 单个的、不可再分的值,如字符串、布尔值与数字

 

安装PyYAML模块
pip install PyYAML

#读取yaml文件
import yaml
with open('data.yaml') as f:
    print (yaml.load(f))
['Apple','Orange','Strawberry','Mango']

 



YAML 中可以使用多种方式指定布尔值。例如,下面的YAML 格式都是合法的:
create_key: yes
needs_agent:no
needs_agent:NO
knows_oop: True
likes_emacs: TRUE
uses_cvs: false

转换为Python 代码以后,对变量的取值进行了格式化
{'create_key': True ,
'know_oop': True,
'likes_emacs': True ,
'needs_agent': False,
'uses_cvs': False}

 

注意:当playbook使用vars.yml外部变量文件的时候,变量文件里面的yes、no要加单引号否则会当成布尔值

cat  vars.yml
---
redis_settings:
 appendonly: 'no' #no和yes需要使用加单引号的字符串,否则会当成布尔值

 

 



双引号引用字符串
如果字符串中包含特殊字符,需要使用双引号包含起来。
例如,下面的例子中,字符串包含冒号
冒号对于YAML 来说是一个特殊字符,因此,需要使用双引号包含起来。
foo: "somebody said I should put a colon here: so I did"


如果字符串的内容比较长,可以使用">"来折叠换行。也就是说,接下来缩进的内容都是这个字符串的一部分。

that: >
  Foo
  Bar

 

 

针对每一组主机的一个action组成一个play,一般一个playbook中只包含一个play
tasks中每个action都是对模块的调用,在每个action中
冒号前是模块的名字
冒号后是模块的参数

web.yml
- hosts: test \\主机组,在/etc/ansible/hosts定义
remote_user: root \\远端执行任务的用户
tasks: \\任务
-name: install httpd \\任务描述
command: yum -y install httpd \\调用ansible的command模块安装httpd
-name: provide httpd.conf \\任务描述
copy:src="/root/httpd.conf" dest="/etc/httpd/conf/httpd.conf" \\调用ansible的copy模块,httpd安装完成后将事先准备好的httpd.conf文件复制到/etc/httpd/conf目录下
tags: conf \\给此任务打标记,可单独执行标记的任务
notify: \\文件内容变更通知
- server restart \\通知到指定的任务
- name: server start \\任务描述
service: name=httpd state=started enabled=true \\调用ansible的service模块的属性定义安装完成httpd以后httpd服务的管理
handlers: \\定义接受关注的资源变化后执行的动作
- name: server restart \\任务描述
service: name=httpd state=restarted \\当关注的资源发生变化后调用service模块,采取的响应的动作

 

 

例子
heartbeat.yaml
    - hosts: hbhosts
    remote_user: root
    tasks:
        - name: ensure heartbeat latest version
          yum: name=heartbeat state=present
        - name: authkeys configure file
          copy: src=/root/hb_conf/authkeys dest=/etc/ha.d/authkeys
        - name: authkeys mode 600
          file: path=/etc/ha.d/authkeys mode=600
          notify:
            - restart heartbeat
        - name: ha.cf configure file
          copy: src=/root/hb_conf/ha.cf dest=/etc/ha.d/ha.cf
          notify:
            - restart heartbeat
    handlers:
        - name: restart heartbeat
            service: name=heartbeat state=restarted

 或

例子
heartbeat.yaml
---
- hosts: hbhosts
  remote_user: root
  become: yes
  become_method: sudo
  tasks:
      - name: ensure heartbeat latest version
        yum: name=heartbeat state=present
      - name: authkeys configure file
        copy: src=/root/hb_conf/authkeys dest=/etc/ha.d/authkeys
      - name: authkeys mode 600
        file: path=/etc/ha.d/authkeys mode=600
        notify:
          - restart heartbeat
      - name: ha.cf configure file
        copy: src=/root/hb_conf/ha.cf dest=/etc/ha.d/ha.cf
        notify:
          - restart heartbeat
  handlers:
      - name: restart heartbeat
          service: name=heartbeat state=restarted

 

 参数的写法


key=value
file: path=/etc/ha.d/authkeys mode=600 owner=root
或
多行
file: path=/etc/ha.d/authkeys mode=600 owner=root 或
yaml字典格式
file: path: /etc/ha.d/authkeys mode: 600 owner: root

 

 


handler
每个编程语言都有event机制,handler就是playbook的event
handlers里面每个handler都是对模块的一次调用
只有在action的状态为changed时才会执行该action的handler

 
testhandler.yaml
    - hosts: lb
    remote_user: root
    vars:
        random_number: "{{ 1000 | random }}"
    tasks:
        - name: copy the /etc/hosts to /tmp/hosts.{{ 1000 | random }}
          copy: src=/etc/hosts dest=/tmp/hosts.{{ 1000 | random }}
          notify:   #因为文件名是随机数,源和目标永远都不一样,所以永远都会执行这个handler
            - call by /tmp/hosts.random_number
        - name: copy the /etc/hosts to /tmp/hosts
          copy: src=/etc/hosts dest=/tmp/hosts
          notify:
            - call by /tmp/hosts


    handlers:
        - name: call by /tmp/hosts
            debug: msg="call by /tmp/hosts"
        - name: call by /tmp/hosts.random_number
            debug: msg="call by /tmp/hosts.random_number"

notify里的名字跟handlers里的name一一对应
call by /tmp/hosts -》call by /tmp/hosts
call by /tmp/hosts.random_number -》 call by /tmp/hosts.random_number

handler按一定顺序执行,即使call by /tmp/hosts.random_number先触发,handler依然先执行call by /tmp/hosts

 handler 只会在所有task 执行完后执行。并且,即便一个handler 被触发多次,它也只会执行一次。handler 并不是在被触发时立即执行,而是按照action中定义的顺序执行。
一般情况下, handler 都位于action的最后,即在所有任务执行完成以后再执行。
Ansible 官方文档提到handler 的唯一用途,就是重启服务与服务器

 

 

serial选项
串行执行,serial指定串行值
无论webservers 组中有多少服务器,无论--forks 选项取值为多少, 一次只更新一台服务器,更新完成以后再更新下一台服务器

- name: test play
  hosts: webservers
  serial: 1

 


local_action选项
使用local_action只在主控机执行操作

tasks:
- name: take out of load balancer pool
  local_action: command /usr/bin/take_out_of_pool    {{inventory_hostname}}

 

 

 

playbook变量
1、用户自定义变量,vars, vars_files ,vars_prompt,这些变量都可以用在jinja2文件里
2、来自远程主机收集到的facts变量
3、内置系统变量,template模块
4、把action结果传入变量,叫注册变量
5、在执行playbook时传入变量,叫额外变量

 

变量名约束
变量名称应为字母,数字和下划线。
变量应始终以字母开头。
变量名不应与python属性和方法名冲突。

 

 

变量作用域
global全局作用域: 设置在config, 环境变量, 和命令行中的变量
play: 作用于play和包含的structure, 变量, role中的default和vars
host: inventory, facts和register产生的变量, 只作用于某个host

 

变量优先级(从低到高)
role defaults
inventory vars
inventory group_vars
inventory host_vars
playbook group_vars
playbook host_vars
host facts
play vars
play vars_prompt
play vars_files
registered vars
set_facts
role and include vars
block vars (only for tasks in block)
task vars (only for the task)
extra vars (always win precedence)



用户自定义变量
vars关键字自定义变量,使用{{}}引用起来

cat a.yml
---
    - hosts: test
      gather_facts: False
      vars:
        var1: haha
        var2: your name is
      tasks:
        - debug: msg="{{var2}} {{var1}}"



 
cat b.yml 
---
- hosts : test
  gather_facts: False 
  vars_files:
    - /tmp/var_file1.yml   #建议用绝对路径
  tasks:
    - debug: msg="{{var1}}"


var_file1.yml 里的内容:
var1:  hello Han×××



cat b.yml
---
- hosts: client_105
  gather_facts: False
  vars_prompt:
    - name: 'client_105_key'
      prompt: 'Input key'  #交互时提示信息
      private: no          #输入数据是否显示
  tasks:
    - name: print 105_key
      debug: msg="{{ client_105_key }}"
      

 

 


facts变量
ansible通过setup模块收集主机信息,这些主机信息叫facts
每个playbook执行前都会默认执行setup模块,所以这些facts信息可以直接以变量形式使用
setup 模块收集到的信息,是 json 格式
说明:通常facts数据的顶级key为ansible_facts,在引用时应该将其包含在变量表达式中。
但自动收集的facts比较特殊,它以ansible_facts作为key,ansible每次收集后会自动将其注册为变量,
所以facts中的数据都可以直接通过变量引用,甚至连顶级key ansible_facts都要省略。

cat a.yml 
---
    - hosts: test
      tasks:
        - debug: msg="{{ansible_all_ipv4_addresses}} {{ansible_os_family}}"
 
 

 

搜集facts信息会额外消耗时间,如果不需要facts信息,在playbook中通过关键字
gather_facts来控制是否收集

cat a.yml 
---
    - hosts: test
      gather_facts: no

 

 

内置系统变量,template模块
在playbook中定义的变量,可以直接在template中使用,同时facts变量也可以直接在template中使用
当然也包含在inventory里面定义的host和group变量。只要是在playbook中可以访问的变量,都可以在template文件中使用。


下面使用template模块复制文件index.html.j2,并且替换index.html.j2中的变量

facts变量 
{{ ansible_hostname }} 
{{ ansible_default_ipv4.address }} 
用户自定义的变量 
{{ defined_name }} 

 
index.html.j2文件

Demo

"block" style="height: 99%;">
"centered">

#46 Demo \{\{ defined_name \}\}

Served by \{\{ ansible_hostname \}\} (\{\{ ansible_default_ipv4.address \}\}).

 

a.yml

 
cat a.yml 
---
- hosts : web
  vars:
      defined_name: "hello lin"
  tasks:
      - name: write the default index.html file
      template: src=/tmp/index2.html.j2  dest=/var/www/html/index.html
      
 

 

 



注册变量
register关键字,多个action之间传递变量,上一个action结果传递到下个action

cat a.yml 
---
- hosts : test
  tasks:
    - shell : ls /tmp/
      register: result
      ignore_errors: True
    - debug: msg="{{result.stdout}}"

说明: 
执行shell 模块后,返回信息是 json 格式的。再通过 register 注册到 result 变量。
然后,通过 debug 模块,输出 result 变量的stdout的值。
 

 

 




额外变量
第一种情况:hosts和user必须从命令行传入值,如果在命令行中不传入值则playbook报错
第二种情况:var1可以从命令行传入值,如果在命令行传入值则会覆盖playbook里面var1定义值(变量优先级)

 
 
第一种情况
cat a.yml 
---
- hosts : "{{hosts}}"
  remote_user: "{{user}}"
  gather_facts: False 
  tasks:

    - debug: msg="{{var1}}"
    
第二种情况
cat a.yml 
---
- hosts : web
  gather_facts: False 
  vars:
      var1: "AAA"
  tasks:

    - debug: msg="{{var1}}"

 

命令行传入变量值三种方式
普通方式传入
ansible-playbook a.yml -e 'hosts=web user=root'
json方式传入
ansible-playbook a.yml -e '{'hosts':'web','user':'root'}'
外部文件方式传入
ansible-playbook a.yml -e '@/tmp/vars.json'

 

 

 

 



playbook引用json格式变量

facts变量或者register返回的变量都是json格式

在playbook里面引用json格式变量,有三种方法
例子
注册变量返回的结果是json格式

 
- shell : ls /tmp/
  register: result
- debug: msg="{{result}}"
TASK [debug] **************************************************
ok: [192.168.188.109] => {
    "msg": {
        "changed": true, 
        "cmd": "echo hello world", 
        "delta": "0:00:00.001890", 
        "end": "2018-01-18 17:12:00.178123", 
        "failed": false, 
        "rc": 0, 
        "start": "2018-01-18 17:12:00.176233", 
        "stderr": "", 
        "stderr_lines": [], 
        "stdout": "hello world", 
        "stdout_lines": [
            "hello world"
        ]
    }
}

 

1、点号
- debug: msg="{{result.stdout}} "

2、中括号
- debug: msg="{{result['stdout']}} "

3、访问json里面的列表
- debug: msg="{{result['stdout_lines'][0]}} "

 

 

playbook逻辑控制语句
when:条件判断语句
loop:循环语句,vars插件和lookup插件提供
block:把几个action组成一个代码块,便于针对一组操作异常进行处理

 

注意:{{ item }}这个变量名是ansible内置的,不能变


when语句

远程主机是debian就立刻关机

tasks:
  - name: "shutdown debian systems"
    command: /sbin/shutdown  -t now
    when: ansible_os_family == "Debian"

判断真假

vars:
   epic: true
tasks:
   - shell: echo "HELLO"
   when: epic
   或
   when: not epic


判断变量是否已经定义了

tasks:
   - shell: echo "HELLO {{ foo }}"
   when: foo is defined

tasks:
   - shell: echo "not defined foo"
   when: foo is not  defined

使用and ,or进行判断

tasks:
   - shell: echo "HELLO {{ foo }}"
   when: foo is defined

tasks:
   - shell: echo "not defined foo"
   when: (foo is not  defined) or (ansible_distribution=="Debian")

 

 

loop语句

with_lines
with_fileglob
with_first_found
with_diet
with_flattened
with_indexed_items
with_nested
with_random_choice
with_sequence
with_together
with_subelements
with_file


列表循环

- name: add users
  user: name={{item}}  state=present  groups=wheel
  with_items:
    - testuser1
    - testuser2

vars:
  somelist: ['testuser1','testuser2']
tasks:
- name: add users
  user: name={{item}}  state=present  groups=wheel
  with_items: "{{somelist}}"

 

#如果列表里面的hash类型

- name: add users
  user: name={{ item.name }}  state=present  groups={{ item.groups }}
  with_items:
    - { name : 'testuser1',groups : 'wheel'}
    - { name : 'testuser2',groups : 'vsftp'}

 

 

#如果某个选项大于3就打印
vars:
  item: [1,2,3,4,5,6,7]
tasks:
- name: add users
  command: echo {{item}} 
  with_items: item
  when: item > 3

 

 

嵌套循环

- name: give users access to multiple databases
  mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo
  with_nested:
    - [ 'alice', 'bob' ]
    - [ 'clientdb','employeedb','providerd' ]

 

哈希循环

---
- hosts: all
  gather_facts: False
  vars:
   user:
    Bob_hou:
     name: Bob_Hou
     shell: bash
    Jmilk:
     name: Jmilk
     shell: zsh
  tasks:
  - name: debug loops
    debug: "msg=\"name -----> {{item.key }} value -----> {{item.value.name }} shell -----> {{ item.value.shell }}\""
    with_dict: "{{ user }}"

 

文件循环

---
- hosts: all
  gather_facts: False
  tasks:
  - name: debug loops
    debug: "msg=\"files -----> {{ item }}\""
    with_fileglob:
    - /root/playbook/*.yaml

 

条件判断循环,until\retries\delay

---
- hosts: all
  gather_facts: False
  tasks:
  - name: debug loops
    shell: cat /root/ansible
    register: host
    until: host.stdout.startswith("cat")
    retries: 5
    delay: 5

 

 

序列循环
类似于Python 中的range 函数。
在使用with_sequence 时,我们可以指定起点、终点和步长。

# create some test users
- user:
  name:"{{item}}"
  state: present
  groups: "events"
  with_sequence: start=0 end=32 format=testuser%02x

 

 

 

register循环,接受多个task的结果
这个是jinja2的语法:{% for i in ret.results %}

---
- hosts: all
  gather_facts: True
  tasks:
  - name: debug loops
    shell: "{{ item }}"
    with_items:
    - hostname
    - uname
    register: ret
  - name: display loops
    debug: "msg=\"{% for i in ret.results %} {{ i.stdout }} {% endfor %}\""

 

 

 


标签
标签tags执行部分action,tags可以和一个play(就是很多个task)或者一个task进行捆绑
ansible-playbook提供了“--skip-tags”和“--tags” 来指明是跳过特定的tags还是执行特定的tags。
always
tagged
untagged
all

- hosts: test-agent
  tasks:
    - command: echo test1
      tags:
         - test1
    - command: echo test2
      tags:
         - test2
    - command: echo test3
      tags:
         - test3

当执行  ansible-playbook test1.yml --tags="test1,test3"  ,则只会执行 test1和test3的echo命令
当执行  ansible-playbook test1.yml --skip-tags="test2"  ,同样只会执行 test1和test3的echo命令



- hosts: test-agent1
  tags:
     - play1
  tasks:
     - command: echo This is
     - command: echo agent1
- hosts: test-agent2
  tags:
     - play2
  tasks:
     - command: echo This is
     - command: echo agent2
- hosts: test-agent3
  tags:
     - play3
  tasks:
     - command: echo This is
     - command: echo agent3

当执行  ansible-playbook test2.yml --tags="play1,play3"  ,则只会执行 play1和play3的tasks。
当执行  ansible-playbook test2.yml --skip-tags="play2"  ,同样只会执行 test1和test3的tasks。




always标签
即使只执行某个标签,always标签也会被执行

tasks:
  - debug: msg="always print this debug message"
    tags:
     - always
    
  - yum: name{{item}} state=installed
    with_items:
      - httpd
    tags:
      - packages
      

ansible-playbook test_always.yml --tags="packages"




tasks:
  - yum: name{{item}} state=installed
    with_items:
      - httpd
    tags:
      - tagged

ansible-playbook test_always.yml --tags tagged
执行所有标记了标签的任务

tasks:
  - yum: name{{item}} state=installed
    with_items:
      - httpd
    tags:
      - tagged
ansible-playbook test_always.yml --tags untagged 

执行所有没有标记标签的任务


tasks:
  - yum: name{{item}} state=installed
    with_items:
      - httpd
    tags:
      - tagged

ansible-playbook test_always.yml --tags all
不管有无标记标签都执行所有任务


include语句使用标签

- included: foo.yml
  tags: [web,foo]

role使用标签

roles:
  - { role: webserver,port: 5000 tags: ['web','foo']}

 

 

 

 

 

插件
插件是对ansible功能的补充,在ansible的配置文件中,每种类型的插件都有自己的配置变量
因此放置的目录并不相同

插件类型
#action_plugins = /usr/share/ansible/plugins/action
#cache_plugins = /usr/share/ansible/plugins/cache
#callback_plugins = /usr/share/ansible/plugins/callback
#connection_plugins = /usr/share/ansible/plugins/connection
#lookup_plugins = /usr/share/ansible/plugins/lookup
#inventory_plugins = /usr/share/ansible/plugins/inventory
#vars_plugins = /usr/share/ansible/plugins/vars
#filter_plugins = /usr/share/ansible/plugins/filter
#test_plugins = /usr/share/ansible/plugins/test
#terminal_plugins = /usr/share/ansible/plugins/terminal
#strategy_plugins = /usr/share/ansible/plugins/strategy
#inventory_plugins  = /usr/share/ansible/plugins/inventory
#enable_plugins = host_list, virtualbox, yaml, constructed


action插件
和模块使用方法类似,只不过执行目标不是远程主机,而是在ansible的控制节点上

cache插件
为facts提供缓存,以避免多次执行playbook时搜集facts上有过多开销

callback插件
执行playbook后,提供额外行为,例如,将执行结果发送到email,或写入log中
官方提供的一些callback插件
https://github.com/ansible/ansible/tree/devel/lib/ansible/plugins/callback


connection插件
ansible为管理节点和远程节点之间提供了更多连接方法。默认的连接协议是基于paramiko的ssh协议
paramiko基本已经够用,如果有高级需求,则可以通过自定义插件来提供snmp,message bus等传输协议


filters插件
过滤器提供更多功能

lookup插件
lookup功能提供更多功能

 

strategy插件
为执行playbook提供更多执行策略,控制play在执行时的策略,默认策略是linear
即所有远程主机都执行完某一个任务之后,再执行下一个任务,ansible官方的strategy插件
提供了另外一个策略free,允许每个远程主机尽快地执行到play的结尾
从ansible2.0开始,支持free策略,允许执行较快的远程主机提前完成action部署,不用等其他远程主机一起执行action
- hosts: all
strategy: free
task:
...

 

shell插件
通过shell插件提供远程主机上更多类型的shell(csh,ksh,tcsh)

test插件
jinja2 test提供更多功能


vars插件
ansible将inventory/playbook命令行中的变量注入ansible中,通过vars插件
实现更多变量注入功能

 

如何使用callback 插件
timer:记录playbook执行时间
log plays:把执行结果记录在/var/log/ansible/hosts对应主机名文件里

1、下载 callback plugins 文件
使用callback插件,首先从官网提供的callback插件下载需要使用的插件对应的python文件
将下载的文件保存在 /usr/share/ansible/plugins/callback目录下

2、配置 ansible.cfg
配置放置 callback 插件的 Python 文件的位置
在ansible.cfg里启用插件
callback_plugins = /usr/share/ansible/plugins/callback
callback_whitelist = timer, log plays

3、playbook执行的时候自动会记录timer 和 log plays

 

 

 

lookup 插件

1、file:获取文件内容

---
- hosts: all
tasks:
- debug: msg="the value of foo.txt is {{ lookup('file', '/etc/foo.txt') }}"


2、password:生成密码字符串
如果文件已存在,则不会向其写入任何数据。 如果文件有内容,那些内容将作为密码读入。 空文件导致密码以空字符串返回。

---
- hosts: all
tasks:
# 使用只有ascii字母且长度为8的随机密码创建一个mysql用户:
    - mysql_user: name={{ client }}
                  password="{{ lookup('password', '/tmp/passwordfile chars=ascii_letters length=8') }}"
                  priv={{ client }}_{{ tier }}_{{ role }}.*:ALL
# 使用只有数字的随机密码创建一个mysql用户:
    - mysql_user: name={{ client }} password="{{ lookup('password', '/tmp/passwordfile chars=digits') }}" priv={{ client }}_{{ tier }}_{{ role }}.*:ALL # 使用许多不同的字符集使用随机密码创建一个mysql用户: - mysql_user: name={{ client }} password="{{ lookup('password', '/tmp/passwordfile chars=ascii_letters,digits,hexdigits,punctuation') }}" priv={{ client }}_{{ tier }}_{{ role }}.*:ALL



3、env :读取环境变量

tasks:
- debug: msg="{{ lookup('env','HOME') }} is an environment variable"

 

4、pipe :读取Linux命令执行结果 

tasks:
- debug: msg="{{ lookup('pipe','date') }} is the raw result of running this command"

 

5、csvfile :读取csv文件

- debug: msg="The atomic number of Lithium is {{ lookup('csvfile', 'Li file=/tmp/elements.csv delimiter=,') }}"
- debug: msg="The atomic mass of Lithium is {{ lookup('csvfile', 'Li file=/tmp/elements.csv delimiter=, col=2') }}"

参数    默认值    描述
file    ansible.csv    要加载的文件名称
col    1    要输出的列,索引从0开始
delimiter    TAB    文件的分隔符
default    empty string    如果key不在csv文件中,则为默认返回值
encoding    utf-8    使用的CSV文件的编码(字符集)(added in version 2.1)

 

6、ini :读取ini/properties文件
在section下查找以key1 = value1的格式来读取文件的内容。
ini 参数格式
lookup('ini', 'key [type=] [section=section] [file=file.ini] [re=true] [default=]')
第一个值必须是ini文件里的key
字段 默认值 描述
type ini 文件类型。 可以是ini或properties (对于java)。
file ansible.ini 要加载的文件名称
section global 在哪里查找key
re False 开启正则匹配
default empty string 如果key不在文件中,则为默认返回值

users.ini
[production]
# My production information
user=robert
pass=somerandompassword

[integration]
# My integration information
user=gertrude
pass=anotherpassword

tasks:
- debug: msg="User in integration is {{ lookup('ini', 'user section=integration file=/tmp/users.ini') }}" - debug: msg="User in production is {{ lookup('ini', 'pass section=production file=/tmp/users.ini') }}"

 

7、dig:dns查询
此模块依赖dnspython 库

- debug: msg="The IPv4 address for example.com. is {{ lookup('dig', 'example.com.')}}"
- debug: msg="The TXT record for example.org. is {{ lookup('dig', 'example.org.', 'qtype=TXT') }}"
- debug: msg="The TXT record for example.org. is {{ lookup('dig', 'example.org./TXT') }}"
其他
tasks:
- debug: msg="{{ lookup('pipe','date') }} is the raw result of running this command"
# redis_kv lookup requires the Python redis package
     - debug: msg="{{ lookup('redis_kv', 'redis://localhost:6379,somekey') }} is value in Redis for somekey"
# dnstxt lookup requires the Python dnspython package
     - debug: msg="{{ lookup('dnstxt', 'example.com') }} is a DNS TXT record for example.com" - debug: msg="{{ lookup('template', './some_template.j2') }} is a value from evaluation of this template" # loading a json file from a template as a string - debug: msg="{{ lookup('template', './some_json.json.j2', convert_data=False) }} is a value from evaluation of this template" - debug: msg="{{ lookup('etcd', 'foo') }} is a value from a locally running etcd" # shelvefile lookup retrieves a string value corresponding to a key inside a Python shelve file - debug: msg="{{ lookup('shelvefile', 'file=path_to_some_shelve_file.db key=key_to_retrieve') }}

 

 

 

过滤器插件
过滤器对普通变量的操作

default:为未定义的变量提供默认值
omit:与default一起使用,忽略变量的占位符,ansible会按照没有传这个参数的值来处理
{{ result.cmd|default(5) }}
result.cmd 如果没有定义的话,则其默认值为5

- name: touch files with an optional mode
  file: dest={{item.path}} state=touch mode={{item.mode|default(omit)}}
   with_items:
    - path: /tmp/foo
    - path: /tmp/bar
    - path: /tmp/baz
      mode: "0444"

对于列表中的前两个文件,默认mode将由系统的umask确定,因为mode=parameter 不会发送到文件模块,而最后的文件将接收mode=0444选项
default(omit)在没有定义mode时忽略mode变量

 

 

mandatory:强制定义变量,否则报错
如果变量未定义,则来自ansible和ansible.cfg的默认行为失败,但您可以将其关闭。
默认ansible.cfg中的error_on_undefined_vars=True

---
- hosts: localhost
  remote_user: root
  tasks:
    - debug: msg="{{ variable | mandatory }}"

 

int:将变量变为整型

vars:
  string_value: "6"
tasks:
  - name: "yes"
    debug: msg='YES'
      when:  string_value | int > 5

 

 
bool:判断变量是否为布尔类型

- debug: msg=test
  when: some_string_value | bool

 

 ternary:类似于编程语言中的(A?B:C)

vars:
  name: "John"
tasks:
  - name: "true of false take different value"
    debug: msg="{{ ('name' == 'John') | ternary('Mr','Ms') }}"

 

 
过滤器对文件路径的操作
Linux
获取路径的文件名:{{ path | basename }}
获取路径中的目录:{{ path | dirname }}
扩展为实际的目录:{{ path | expanduser }}
获取软连接的真实路径:{{ path | realpath }}
获取文件名的名称和扩展名:{{ path | splitext }}


windows
获取路径的文件名:{{ path | win_basename }}
获取路径的目录名:{{ path | win_dirname }}
分割成多个部分:{{ path | win_splitdrive }}
分割成多个部分驱动器部分:{{ path | win_splitdrive | first }}
分割成多个部分文件名部分:{{ path | win_splitdrive | last }}

 

 
过滤器对字符串变量的操作

caplitalize:为字符串首字母大写

vars:
  string_value: "john"
tasks:
  - name: "cap"
    shell: echo {{ string_value | caplitalize }}


quote:为字符串增加双引号

vars:
  string_value: "John"
tasks:
  - name: "quote"
    shell: echo {{ string_value | quote }}

 

 hash:获取字符串得hash值

tasks:
  - name: "hash"
    shell: echo {{ 'test1'|hash('sha1') }}
    shell: echo {{ 'test1'|hash('md5') }}

 
comment:注释
支持各种语言的注释

{{ "this is the comment" | comment }}
{{ "this is the comment" | comment('c') }}
{{ "this is the comment" | comment('cblock') }}
{{ "this is the comment" | comment('erlang') }}
{{ "this is the comment" | commen('xml')t }}
{{ "this is the comment" | comment('plain', prefix='#######\n#', postfix='#\n#######\n ###\n #') }}

 

regex_replace:用正则表示对字符串进行替换

# convert "ansible" to "able"
{{ 'ansible' | regex_replace('^a.*i(.*)$', 'a\\1') }}

# convert "foobar" to "bar"
{{ 'foobar' | regex_replace('^f.*o(.*)$', '\\1') }}

# convert "localhost:80" to "localhost, 80" using named groups
{{ 'localhost:80' | regex_replace('^(?P.+):(?P\\d+)$', '\\g, \\g') }}

 

ip:字符串ip地址转换

vars:
  myvar: "192.168.3.6"
  myvar6: "fe80:dcfd:1e54:728d:a99e"
  CIDR: "192.0.2.1/24'"
tasks:
  - name: "check ip"
    debug: msg={{ myvar | ipaddr }} debug: msg={{ myvar | ipv4 }} debug: msg={{ myvar6 | ipv6 }} debug: msg={{ CIDR | ipaddr('address') }}

 

 to_datetime:字符串转换为时间戳

tasks:
  - name: "datetime filter"
    debug: msg={{ "2016-06-06  20:00:12" | to_datetime }}

 

 

 

过滤器对json的操作
format:将变量值按照json/yaml输出
更改数据格式,其结果是字符串

{{  some_variable | to_json  }} 
{{  some_variable | to_yaml  }}
对于人类可读的输出
{{  some_variable | to_nice_json  }} 
{{  some_variable | to_nice_yaml  }}

 

 

 合并两个json对象

tasks:
  - name: "combine"
    debug: msg={{ {'a':1,'b':2}| combine({'b':3}) }}

 

 

 

过滤器对数据结构的操作

数据结构:list、set

random:取随机数
从列表中随机获取元素
{{ ['a','b','c','d','e','f']|random }}

从0-59 的整数中随机获取一个数
{{ 59 |random}}

从0-100 中随机获取能被10 整除的数(可以理解为0 10 20 30 40 50 ...100 的随机数)
{{ 100 |random(step=10) }}

从0-100 中随机获取1 开始步长为10 的数(可以理解为1 11 21 31 41...91 的随机数)
{{ 100 |random(1, 10) }}
{{ 100 |random(start=1, step=10) }}

取最小的值
{{ [3, 4, 2] | min }}

取最大的值
{{ [3, 4, 2] | max }}

列表转换字符
{{ [3, 4, 2] | join(" ") }}

对列表唯一过滤
{{ [3, 4, 2] | unique }}

 

 

 

过滤器测试功能

测试字符串

when: ansible_os_family | match("Red[Hh]at" )
when: url | search("/users/.*/resources/.*")

 

 版本比较

检查ansible_distribution_version版本是否大于或等于'12 .04',条件成立返回True。
{{ ansible_distribution_version | version_compare('12.04', '>=') }}

进行严格的版本检查
{{ sample_version_var | version_compare('1.0', operator='lt', strict=True) }}

 

 测试list的包含关系

vars:
    a: [1,2,3,4,5]
    b: [2,3]
tasks:
    - debug: msg="A includes B"
      when: a|issuperset(b)

    - debug: msg="B is included in A" when: b|issubset(a)

 

 测试文件路径

- debug: msg="path is a directory"
  when: mypath|is_dir
- debug: msg="path is a file"
  when: mypath|is_file
- debug: msg="path is a symlink"
  when: mypath|is_link - debug: msg="path already exists" when: mypath|exists - debug: msg="path is {{ (mypath|is_abs)|ternary('absolute','relative')}}" - debug: msg="path is the same file as path2" when: mypath|samefile(path2) - debug: msg="path is a mount" when: mypath|ismount

 

测试任务执行结果

tasks:
- shell: /usr/bin/foo
    register: result
    ignore_errors: True
- debug: msg="it failed"
    when: result|failed
  - debug: msg="it changed"
    when: result|changed - debug: msg="it succeeded in Ansible >= 2.1" when: result|succeeded - debug: msg="it succeeded" when: result|success - debug: msg="it was skipped" when: result|skipped

 

 

变量可以通过过滤器修改。过滤器与变量用管道符号( | )分割,并且也可以用圆括号传递可选参数。
多个过滤器可以链式调用,前一个过滤器的输出会被作为 后一个过滤器的输入

vars:
  string_value: "steven"
tasks:
  - name: "quote"
    shell: echo {{ string_value | | caplitalize|quote |hash('md5') }}

 



jinja2 模版语法


读取json格式变量

下面2种方式效果是一样的,如果变量或属性不存在,会返回一个未定义值。

{{ foo.bar }}
{{ foo['bar'] }}

 

注释
要注释模板中一行,默认使用 {# ... #} 注释语法。

 

转义
简单的使用单引号进行转义
对于较大的段落,使用raw进行转义

{% raw %}
    
    {% for item in seq %}
  • {{ item }}
  • {% endfor %}
{% endraw %}

包含 > 、 < 、 & 或 " 字符的变量,必须要手动转义

{{ user.username|e }} 

 

 

控制结构
控制结构指的是所有的那些可以控制程序流的东西 —— 条件(比如 if/elif/ekse )、 for 循环、以及宏和块之类的东西。控制结构在默认语法中以 {% .. %} 块的形式出现。

 

for语句
遍历序列

{% for user in users %}
  
  • {{ user.username|e }}
  • {% endfor %}

     

    在模板中如何遍历某一组内的所有主机?
    {% for host in groups['db_servers'] %}
      {{ host }}
    {% endfor %}
    
    
    获取ip地址
    {% for host in groups['db_servers'] %}
      {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
    {% endfor %}
    获取组中第一个主机的ip地址
    {{ hostvars[groups['webservers'][0]]['ansible_eth0']['ipv4']['address'] }}

     

    迭代字典

    {% for key, value in my_dict.iteritems() %}
        
    {{ key|e }}
    <dd>{{ value|e }}dd> {% endfor %}

     

    循环 10 次迭代之后会终止处理,注:使用break, 需要开启轮询控制. 具体是在ansible.cfg的jinja2_extensions变量加上jinja2.ext.loopcontrols.

    {% for user in users %}
        {%- if loop.index >= 10 %}{% break %}{% endif %}
    {%- endfor %}
    
    {% for user in users if loop.index <= 10 %}
        {{ loop.index }}
    {%- endfor %}

     

    在一个 for 循环块中你可以访问这些特殊的变量

    loop.index    当前循环迭代的次数(从 1 开始)
    loop.index0    当前循环迭代的次数(从 0 开始)
    loop.revindex    到循环结束需要迭代的次数(从 1 开始)
    loop.revindex0    到循环结束需要迭代的次数(从 0 开始)
    loop.first    如果是第一次迭代,为 True 。
    loop.last    如果是最后一次迭代,为 True 。
    loop.length    序列中的项目数。
    loop.cycle    在一串序列间期取值的辅助函数。

     

     

    if 语句

    Jinja 中的 if 语句类似 Python 中的 if 语句。

    {% if kenny.sick %}
        Kenny is sick.
    {% elif kenny.dead %}
        You killed Kenny!  You bastard!!!
    {% else %}
        Kenny looks okay --- so far
    {% endif %}

     

     

    过滤器
    过滤器段允许你在一块模板数据上应用常规 Jinja2 过滤器。只需要把代码用 filter 节包裹起来

    {% filter upper %}
        This text becomes uppercase
    {% endfilter %}

     

     

    变量赋值
    在代码块中,你也可以为变量赋值。在顶层的(块、宏、循环之外)赋值是可导出的,即 可以从别的模板中导入。
    赋值使用 set 标签,并且可以为多个变量赋值

    {% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
    {% set key, value = call_something() %}

     

     

    表达式
    {% ... %} 用于执行诸如 for 循环 或赋值的语句
    {{ ... }} 把表达式的结果打印到模板上

     

    if 表达式
    一般的语法是

    <do something> if true> else <do something else>

    例如

    {{ '[%s]' % page.title if page.title is defined else 'undefined' }}

     

    字面量
    表达式最简单的形式就是字面量。字面量表示诸如字符串和数值的 Python 对象。

    "Hello World"    双引号或单引号中间的一切都是字符串。无论何时你需要在模板中使用一个字 符串(比如函数调用、过滤器或只是包含或继承一个模板的参数),它们都是 有用的。
    42/42.23    直接写下数值就可以创建整数和浮点数。如果有小数点,则为浮点数,否则为 整数。记住在 Python 里, 42 和 42.0 是不一样的。
    ['list','of','objects']    一对中括号括起来的东西是一个列表。列表用于存储和迭代序列化的数据。
    ('tuple','of','values')    元组与列表类似,只是你不能修改元组。如果元组中只有一个项,你需要以逗号 结尾它。元组通常用于表示两个或更多元素的项。更多细节见上面的例子。
    {dict':'of','key':'and','value':'pairs'}    Python 中的字典是一种关联键和值的结构。键必须是唯一的,并且键必须只有一个 值。字典在模板中很少使用,罕用于诸如 xmlattr() 过滤器之类。
    true/false    true 永远是 true ,而 false 始终是 false,true 、 false 和 none 实际上是小写的,为了一致性(所有的 Jinja 标识符是小写的),你应该使用小写的版本

     

    算术
    Jinja 允许你用计算值。支持下面的 运算符

    +    把两个对象加到一起。通常对象是素质,但是如果两者是字符串或列表,你可以用这 种方式来衔接它们。无论如何这不是首选的连接字符串的方式!连接字符串见 ~ 运算符。 {{ 1 + 1 }} 等于 2 。
    -    用第一个数减去第二个数。 {{ 3 - 2 }} 等于 1 。
    /    对两个数做除法。返回值会是一个浮点数。 {{ 1 / 2 }} 等于 {{ 0.5 }} 。
    //    对两个数做除法,返回整数商。 {{ 20 // 7 }} 等于 2 。
    %    计算整数除法的余数。 {{ 11 % 7 }} 等于 4 。
    *    用右边的数乘左边的操作数。 {{ 2 * 2 }} 会返回 4 。也可以用于重 复一个字符串多次。 {{ ‘=’ * 80 }} 会打印 80 个等号的横条。
    **    取左操作数的右操作数次幂。 {{ 2**3 }} 会返回 8 。

     

    比较
    ==    比较两个对象是否相等。
    !=    比较两个对象是否不等。
    >    如果左边大于右边,返回 true 。
    >=    如果左边大于等于右边,返回 true 。
    <    如果左边小于右边,返回 true 。
    <=    如果左边小于等于右边,返回 true 。

     

    逻辑
    对于 if 语句,在 for 过滤或 if 表达式中,它可以用于联合多个表达式

    and    如果左操作数和右操作数同为真,返回 true 。
    or    如果左操作数和右操作数有一个为真,返回 true 。
    not    对一个表达式取反(见下)。
    (expr)    表达式组。
    is 和 in 运算符同样支持使用中缀记法: foo is not bar 和 foo not in bar 而不是 not foois bar 和 not foo in bar 。所有的 其它表达式需要前缀记法 not (foo and bar)

     

     

    其它运算符
    in    运行序列/映射包含检查。如果左操作数包含于右操作数,返回 true 。比如 {{ 1 in[1,2,3] }} 会返回 true 。
    is    运行一个 测试 。
    应用一个 过滤器 。
    ~    把所有的操作数转换为字符串,并且连接它们。 {{ "Hello " ~ name ~ "!" }} 会返回(假设 name 值为 ''John' ) Hello John! 。
    ()    调用一个可调用量:{{ post.render() }} 。在圆括号中,你可以像在 python 中一样使用位置参数和关键字参数: {{ post.render(user, full=true) }} 。
    . / []    获取一个对象的属性。

     

     


     

    role 和ansible galaxy


    重用playbook
    1、include语句
    主要重用action,可以将多个action放在多个文件,避免playbook过于臃肿

    include指令类似如下,可以像普通tasks命令一样在playbook中混合使用

    tasks:
    - include: /tmp/tasks/foo.yml
    cat /tmp/tasks/foo.yml
    ---
    # possibly saved as /tmp/tasks/foo.yml
      - name: placeholder foo
        command: /bin/foo
      - name: placeholder bar
        command: /bin/bar

     

    在include中使用参数,parameterized include

    tasks:
    - include: wordpress.yml wp_user=timmy
    - include: wordpress.yml wp_user=alice
    - include: wordpress.yml wp_user=bob
    
    或者
    ---
    - hosts: lb
    vars:
      wp_user: "timmy"
      remote_user: root
    tasks:
    - include: wordpress.yml

     

     

    2、role
    role类似于python的package,可以重用一组文件,形成完整功能

    ansible会按照下面顺序查找roles_path

    1、ANSIBLE_ROLES_PATH #环境变量
    2、/etc/ansible/ansible.cfg : #roles_path = /etc/ansible/roles #如果没有设置环境变量就用配置文件里,可以设置多个路径,用逗号隔开
    3、/etc/ansible/roles #如果没有设置环境变量也没有配置文件里设置roles_path 那么使用默认的路径

     

     

    调整role和任务顺序
    pre_tasks
    post_tasks
    如果在执行一个role时,需要在其前或其后依然要执行某些任务,我们可以使用pre_tasks及post_tasks来声明。
    pre_tasks是在role之前执行,而post_tasks则在role之后执行

    - name: deply webservers
     host: webservers
     vars_files:
       - secrets.yml
    
     pre_tasks:
       - name: update yum cache
         yum: update_cache=yes
    
     roles:
       - role: apache
         database_host: {{ hostvars.db.ansible_eth0.ipv4.address }}
         domains:
           - exampel.com
           - www.example.com
    
      post_tasks:
        - name: print something
          shell: echo "The roles have been updated!"

     

     

    条件测试调用role

    vim roles.yml
    ---
    - hosts:web
      remote_user: root
      roles:
      - role: nginx
        when: "ansible_distribution_major_version == '7'"

     

     

     

     

    完整的role目录结构
    这里面任何一个文件都不是必须的,如果文件不存在,则跳过该文件的加载

    调用:ansible-playbook  -i  hosts  site.yml
    
    主机清单设置group_vars目录,分类设置各类主机的变量
    group_vars:
    nginx
    mysql
    all
    
    
    
    ├── library  #目录下存放callback二次开发,mysqldb,filter plugin等库文件
    ├── simple
    │   ├── defaults #文件中的变量都会被加入play,这个是默认变量,优先级比vars低
    │   │   └── main.yml
    │   ├── files  #copy、script模块使用这个目录下的文件
    │   ├── handlers    #文件中的handlers都会被加入play
    │   │   └── main.yml
    │   ├── meta  #文件中的所有依赖的role都会被加入play
    │   │   └── main.yml
    │   ├── README.md
    │   ├── tasks   #文件中的任务都会被加入play,role的入口文件
    │   │   └── main.yml
    │   ├── templates  #存放jinja2模版文件,例如nginx.conf.j2
    │   ├── tests
    │   │   ├── inventory
    │   │   └── test.yml
    │   └── vars   #文件中的变量都会被加入play
    │       └── main.yml
    ├── site.yml  #总入口文件
    ├── group_vars
    │   └── nginx  #主机清单的nginx组的组变量
    ├── hosts   #主机清单

     

     

    role目录标准化

    ansible笔记_第8张图片

    ansible笔记_第9张图片

     

    引用其他main.yml 文件
    Ansible 提供了两个关键字, include和include_vars ,来分别引入role 中非main.yml 其他文件包含的tasks和vars

    默认

    ansible笔记_第10张图片

    引入其他文件yaml文件

    ansible笔记_第11张图片

     

    role的依赖
    role 依赖关系的定义文件是x/meta/main.yml
    如果role x 依赖role y ,那么在Playbook 中调用role x 之前会先调用role y。
    当多个role 依赖同一个role 时, Ansible 会自动进行过滤,避免重复调用相同参数的role

     

    第一种方法:dependencies
    db 和Web 都依赖role common。如果在Playbook中调用db 和Web,
    那么Ansible 会保证在 db 和Web 运行前,先运行 common ,并且只运行一次。
    ansible笔记_第12张图片

    依赖关系中和role 的调用一样,也是可以加入参数的,下面是加入参数的meta/main.yml 文件的例子。
    在Ansible 的去重机制中,只有对个相同的role 进行参数相同的调用时,才算是重复的。

    ---
    dependencies :
    - { role: common, name:"NAME IN DB"

     

     

    第二种方法:把引用的role放在最前面,先执行

    site.yml
    roles: 
     - common  #先执行common role
     - db
     - web 

     

     

     

    ansible galaxy网站
    访问https://galaxy.ansible.com/ ,通过BROWSE ROLES 找到你需要的role 。

    windows相关的role
    https://galaxy.ansible.com/mrlesmithjr/windows-iis/

    ansible笔记_第13张图片

     

     


     

    windows远程管理

     

    https://www.jianshu.com/p/4dcdf2a5cfe5

     



    Windows Playbook示例

    执行powershell脚本
    - name: test script module
      hosts: windows
      tasks:
        - name: run test script
          script: files/test_script.ps1

    获取ip地址信息
    - name: test raw module
      hosts: windows
      tasks:
        - name: run ipconfig
          win_command: ipconfig
          register: ipconfig
        - debug: var=ipconfig

    使用dos命令,移动文件
    - name: another raw module example
      hosts: windows
      tasks:
         - name: Move file on remote Windows Server from one location to another
           win_command: CMD /C "MOVE /Y C:\teststuff\myfile.conf C:\builds\smtp.conf"

    使用powershell命令,移动文件
    - name: another raw module example demonstrating powershell one liner
      hosts: windows
      tasks:
         - name: Move file on remote Windows Server from one location to another
           win_command: Powershell.exe "Move-Item C:\teststuff\myfile.conf C:\builds\smtp.conf"



    https://www.jianshu.com/p/4dcdf2a5cfe5
    add_host
    assert
    async
    debug
    fail
    fetch
    group_by
    include_vars
    meta
    pause
    raw
    script
    set_fact
    setup
    slurp
    template (also: win_template)

    windows模块
    http://docs.ansible.com/ansible/latest/modules/list_of_windows_modules.html

    win_acl - Set file/directory/registry permissions for a system user or group
    win_acl_inheritance - Change ACL inheritance
    win_audit_policy_system - Used to make changes to the system wide Audit Policy.
    win_audit_rule - Adds an audit rule to files, folders, or registry keys
    win_certificate_store - Manages the certificate store
    win_chocolatey - Manage packages using chocolatey
    win_command - Executes a command on a remote Windows node
    win_copy - Copies files to remote locations on windows hosts
    win_defrag - Consolidate fragmented files on local volumes
    win_disk_facts - Show the attached disks and disk information of the target host
    win_disk_image - Manage ISO/VHD/VHDX mounts on Windows hosts
    win_dns_client - Configures DNS lookup on Windows hosts
    win_domain - Ensures the existence of a Windows domain.
    win_domain_controller - Manage domain controller/member server state for a Windows host
    win_domain_group - creates, modifies or removes domain groups
    win_domain_membership - Manage domain/workgroup membership for a Windows host
    win_domain_user - Manages Windows Active Directory user accounts
    win_dotnet_ngen - Runs ngen to recompile DLLs after .NET updates
    win_dsc - Invokes a PowerShell DSC configuration
    win_environment - Modify environment variables on windows hosts
    win_eventlog - Manage Windows event logs
    win_eventlog_entry - Write entries to Windows event logs
    win_feature - Installs and uninstalls Windows Features on Windows Server
    win_file - Creates, touches or removes files or directories.
    win_file_version - Get DLL or EXE file build version
    win_find - Return a list of files based on specific criteria
    win_firewall - Enable or disable the Windows Firewall
    win_firewall_rule - Windows firewall automation
    win_get_url - Fetches a file from a given URL
    win_group - Add and remove local groups
    win_group_membership - Manage Windows local group membership
    win_hotfix - install and uninstalls Windows hotfixes
    win_iis_virtualdirectory - Configures a virtual directory in IIS.
    win_iis_webapplication - Configures IIS web applications
    win_iis_webapppool - configures an IIS Web Application Pool
    win_iis_webbinding - Configures a IIS Web site binding.
    win_iis_website - Configures a IIS Web site.
    win_lineinfile - Ensure a particular line is in a file, or replace an existing line using a back-referenced regular expression.
    win_mapped_drive - maps a network drive for a user
    win_msg - Sends a message to logged in users on Windows hosts.
    win_msi - Installs and uninstalls Windows MSI files (D)
    win_nssm - NSSM - the Non-Sucking Service Manager
    win_owner - Set owner
    win_package - Installs/uninstalls an installable package
    win_pagefile - Query or change pagefile configuration
    win_path - Manage Windows path environment variables
    win_ping - A windows version of the classic ping module
    win_power_plan - Changes the power plan of a Windows system
    win_product_facts - Provides Windows product information (product id, product key)
    win_psexec - Runs commands (remotely) as another (privileged) user
    win_psmodule - Adds or removes a Powershell Module.
    win_rabbitmq_plugin - Manage RabbitMQ plugins
    win_reboot - Reboot a windows machine
    win_reg_stat - returns information about a Windows registry key or property of a key
    win_regedit - Add, change, or remove registry keys and values
    win_region - Set the region and format settings
    win_regmerge - Merges the contents of a registry file into the windows registry
    win_robocopy - Synchronizes the contents of two directories using Robocopy
    win_route - Add or remove a static route.
    win_say - Text to speech module for Windows to speak messages and optionally play sounds
    win_scheduled_task - Manage scheduled tasks
    win_scheduled_task_stat - Returns information about a Windows Scheduled Task
    win_security_policy - changes local security policy settings
    win_service - Manage and query Windows services
    win_share - Manage Windows shares
    win_shell - Execute shell commands on target hosts.
    win_shortcut - Manage shortcuts on Windows
    win_stat - returns information about a Windows file
    win_tempfile - Creates temporary files and directories.
    win_template - Templates a file out to a remote server.
    win_timezone - Sets Windows machine timezone
    win_toast - Sends Toast windows notification to logged in users on Windows 10 or later hosts
    win_unzip - Unzips compressed files and archives on the Windows node
    win_updates - Download and install Windows updates
    win_uri - Interacts with webservices
    win_user - Manages local Windows user accounts
    win_user_right - Manage Windows User Rights
    win_wait_for - Waits for a condition before continuing
    win_wakeonlan - Send a magic Wake-on-LAN (WoL) broadcast packet
    win_webpicmd - Installs packages using Web Platform Installer command-line
    win_whoami - Returns information about the current user and process

     

    具体步骤

    1、在管理机安装pywinrm
    pip install  pywinrm

     

    2、在被控机执行winrm设置脚本

    
    checkclientenv.bat
    winrm set winrm/config/service/auth @{Basic="true"}
    winrm set winrm/config/service @{AllowUnencrypted="true"}
    checkclientenv.ps1
    
    $RootDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
    $RootDir
    ""
    sleep 1; Write-Host -ForegroundColor Yellow "正在检查当前数据库服务器的环境......"; sleep 2
    ""
    Get-WmiObject -Class Win32_OperatingSystem | Select-Object -ExpandProperty Caption | Out-Null
    $ComInfo =(gwmi -Class win32_operatingsystem).Caption
    Write-Host "当前系统版本为:$($ComInfo)";
    ""
    #获取当前系统的外网IP
    $ExternalIP = ''
    Try {$ExternalIP = Invoke-WebRequest http://myip.ipip.net| Select -ExpandProperty Content -ErrorAction SilentlyContinue}
    catch [System.Net.WebException] { Write-Host -ForegroundColor Red  "当前主机无法访问外网!!"}
    IF ($ExternalIP -ne '') { Write-Host -ForegroundColor Green  "当前主机外网IP为$($ExternalIP)" }
    ""
    #获取当前系统的Powershell版本
    $PSVersion = $PSVersionTable.PSVersion.Major
    Write-Host -ForegroundColor Yellow "当前Powershell版本为$PSVersion"
    
    ##安装Powershell更新补丁
    IF ($PSVersion -lt 5)
        {  
         Write-Host -ForegroundColor Yellow "当前Powershell版本需要更新,正在打开Window update功能......";sleep 3
         set-Service -name wuauserv -StartupType Manual
         start-Service -name wuauserv
         Write-Host ""
        switch($ComInfo)  
            {  
            {$ComInfo.contains("2012") -and  $ComInfo.contains("R2")}                 {wusa.exe "$RootDir\PowerShell_v5\Win8.1AndW2K12R2-KB3134758-x64.msu"} 
            {$ComInfo.contains("2012") -and  $ComInfo.notcontains("R2") }             {wusa.exe "$RootDir\PowerShell_v5\W2K12-KB3134759-x64"}
            {$ComInfo.contains("2008") -and  $ComInfo.contains("R2") }                {wusa.exe "$RootDir\PowerShell_v5\Win7AndW2K8R2-KB3134760-x64"}    
            {$ComInfo.contains("Windows 8") -or $ComInfo.contains("Windows 8.1")}     {wusa.exe "$RootDir\Tools\PowerShell_v5\Win8.1-KB3134758-x86"}  
            {$ComInfo.contains("Windows 7")}                                          {wusa.exe "$RootDir\PowerShell_v5\Win7-KB3134760-x86.msu"}   
            Default { -ForegroundColor Red "当前系统无法自动更新Powershell V5版本,请手动更新后再重新运行脚本!等待5秒后脚本自动退出!";sleep 5;exit}  
            }  
        Write-Host "补丁安装完成后需要重启服务器!等待5秒后脚本自动退出!";sleep 5;exit
        }
    
    ""
    #判断WinRm服务是否启动
    $WinRMstatus  = Get-Service WinRM | Where-Object {$_.Status -eq "running"}
    
    IF($WinRMstatus.count -eq 0)
        {
        Write-Host "正在开启WinRM服务......";Enable-PSRemoting –Force
        }
        
    
    #判断WinRM服务是否开启监听端口
    $WinRMport = winrm e winrm/config/listener
    
    IF($WinRMport.count -eq 0)
        {
        Write-Host -NoNewline  "正在开启WinRM监听端口......";Enable-PSRemoting –Force;Write-Host -ForegroundColor Green "OK"
        }
        
    Write-Host ""
    Write-Host "测试本机的WinRM服务是否能正常连接......"
    Try {Test-WsMan 127.0.0.1  -ErrorAction Stop | Out-Null}
    Catch [System.InvalidOperationException] 
    { Write-Host -ForegroundColor Red "WinRM服务连接异常,请检查相关配置,等待15秒后脚本自动退出!";sleep 15;exit}
    Write-Host -ForegroundColor Green "本机的WinRM服务连接正常!"
    ""
    
    ##添加WinRM信任列表,在客户端添加,而不是服务端
    $tr = Get-Item wsman:\localhost\client\trustedhosts | Select -Property value
    IF($tr.value  -ne "*")
        {
        Write-Host "正在添加WinRM信任列表......"
        Set-Item wsman:\localhost\client\trustedhosts * -Force
        Restart-Service WinRM
        }
    ""
    Write-Host "WinRM服务连接配置完成!";
    ""
    Write-Host -ForegroundColor Yellow "请手动允许数据库服务器上的防火墙通过WinRM端口(允许外部网络,默认5895)和数据库镜像端口(允许内部网络,默认5022)通过!";sleep 5;
    ""
    Write-Host "当前数据库服务器的环境完成!等待10秒后脚本自动退出!";sleep 10;exit
    
      



    3、inventory 文件

    [windows]
    10.154.175.156
    
    [linux]
    192.168.3.9
    
    [windows:vars]
    ansible_user=Administrator
    ansible_password=1qaz2wsx!@#123
    ansible_port=5985   #winrm的端口
    ansible_connection=winrm
    ansible_winrm_server_cert_validation=ignore

     

     

    4、Windows Playbook

    获取ip地址信息
    ---
    - hosts: windows
      tasks:
        - name: run ipconfig
          win_command: ipconfig
          register: result
        - name: print ipconfig
          debug: msg="{{ result }}"

     

    5、执行playbook
    ansible-playbook   /tmp/site.yml

     

     

    补充

    对命令输出的信息进行utf-8编码,修改winrm模块的protocol.py
    sed -i "s#tdout_buffer.append(stdout)#tdout_buffer.append(stdout.decode('gbk').encode('utf-8'))#g" /usr/lib/python2.6/site-packages/winrm/protocol.py
    sed -i "s#stderr_buffer.append(stderr)#stderr_buffer.append(stderr.decode('gbk').encode('utf-8'))#g" /usr/lib/python2.6/site-packages/winrm/protocol.py



    ansible的优化


    1、开启SSH长连接
    ansible是通过使用ssh和远程主机进行通信,所以对ssh有这很强的依赖。在OpenSSH 5.6以后支持Multiplexing这个特性,可以通过在ansible配置中设置以支持该特性。
    ansible中控机上执行一次与远程主机的连接之后,这个连接会持久保持设定时间之久。可以通过netstat命令查看到ESTABLISHED状态的连接信息。

    如下是配置参数,设置长连接保持时间为5天;control_path指定socket文件所保存的位置。
    cat /etc/ansible/ansible.cfg
    ssh_args = -o ControlMaster=auto -o ControlPersist=5d
    control_path = /etc/ansible/ssh-socket/   #control_path需要预先创建给写权限





    2、开启pipelining
    默认情况下,ansible的执行流程是把生成好的本地python脚本push到远程服务器然后运行。如果开启了pipelining,整个流程少了一个push脚本到远程服务器的步骤,直接在SSH的会话中进行,可以提高整个执行效率。
    cat /etc/ansible/ansible.cfg
    pipelining = True

    需要注意的是:如果开启pipelining,需要被控的远程服务器将/etc/sudoers中的”Defaults requiretty”注释掉,否则会出现类似如:you must have a tty to run sudo 的报错。


     
     

    3、开启accelerate模式
    accelerate模式类似于SSH的Multiplexing功能,都是使ansible控制服务器和远程服务器之间保持长连接。
    accelerate模式是使用python程序在远程服务器上运行一个守护进程,ansible通过这个守护进程监听的端口进行通信。
    使用accelerate模式,需要控制服务器和远程服务器都安装python-keyczar包

    accelerate模式的开启方法很简单,只要在playbook中配置accelerate: true即可。
    cat /etc/ansible/ansible.cfg
    [accelerate]
    accelerate_port = 5099
    accelerate_timeout = 30
    accelerate_connect_timeout = 5.0





    4、对facts设置优化
    playbook默认第一个task是Gathering Facts收集各主机的facts信息,以方便我们在paybook中直接引用facts里的信息。
    如果既想用facts信息,有希望能提高playbook的效率的话,可以采用facts缓存来实现。
    facts缓存支持多种方式:json文件方式,redis方式,memcache方式等。
    facts存储不支持远端的redis/memcache,需要在ansible的控制服务器上安装redis/memcache;同时,还需要安装python的redis模块/memcache模块
    1)json文件方式
    cat /etc/ansible/ansible.cfg
    gathering=smart
    fact_caching_timeout=86400
    fact_caching=jsonfile
    fact_caching_connection=/path/to/ansible_fact_cache  #这是一个目录,目录下会生成各个机器会以机器名为文件名


    2)redis方式
    cat /etc/ansible/ansible.cfg
    gathering=smart
    fact_caching_timeout=86400
    fact_caching=redis


    3)memcache方式
    cat /etc/ansible/ansible.cfg
    gathering=smart
    fact_caching_timeout=86400
    fact_caching=memcached


    pycharm的yaml插件

     

    插件地址:https://github.com/vermut/intellij-ansible

     

    安装

    ansible笔记_第14张图片

     

    设置

    Editor → File Types → YAML/Ansible

    添加   *.yaml 、*.yml   ,提示已经关联过yaml,yml,直接覆盖即可

    ansible笔记_第15张图片

    ansible笔记_第16张图片

     ansible笔记_第17张图片

     新建一个a.yml自动关联插件

    ansible笔记_第18张图片

    ansible笔记_第19张图片

     

    右上角显示语法是否正确

    ansible笔记_第20张图片

     

    f

    你可能感兴趣的:(ansible笔记)