ansible笔记
ansible失败的地方
被控机器是Windows
1、被控机要开winrm端口
2、Windows的模块非常少
如果某个主机执行失败了,会生成retry文件,然后用--limit参数来重新执行即可
to retry, use: ansible --limit @xxx.retry
解决:
一大堆脚本,而且服务器上脚本不是最新
预估停机执行时间
注意:
任务重复执行问题
安全:密码和密钥,ansible管理机权限
ansible没有回滚操作
ansible属于非登录shell
ansible执行过程大体过程如下图,其中暖色调的代表已经模块化
https://www.jianshu.com/p/575ced3a08fa
特点
无客户端
无服务器端
基于模块,用任意语言开发模块
yaml订制playbook
基于ssh工作
实现多级指挥
连接插件,连接被管理端
核心模块
自定义模块
插件完成模块功能补充
playbooks定义任务
主机清单
架构图
f
管理端支持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:
如果将校验和传递给此参数,则将在下载目标文件的摘要后计算摘要,以确保其完整性。格式:
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=
第一个值必须是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 %}
包含 > 、 < 、 & 或 " 字符的变量,必须要手动转义
{{ user.username|e }}
控制结构
控制结构指的是所有的那些可以控制程序流的东西 —— 条件(比如 if/elif/ekse )、 for 循环、以及宏和块之类的东西。控制结构在默认语法中以 {% .. %} 块的形式出现。
for语句
遍历序列
{% for user in users %}
在模板中如何遍历某一组内的所有主机? {% 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() %}
循环 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> iftrue> 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目录标准化
引用其他main.yml 文件
Ansible 提供了两个关键字, include和include_vars ,来分别引入role 中非main.yml 其他文件包含的tasks和vars
默认
引入其他文件yaml文件
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 ,并且只运行一次。
依赖关系中和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/
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
安装
设置
Editor → File Types → YAML/Ansible
添加 *.yaml 、*.yml ,提示已经关联过yaml,yml,直接覆盖即可
新建一个a.yml自动关联插件
右上角显示语法是否正确
f