Ansible Playbook 拓展
Handlers
使用 notify 触发 handlers
可以同时调用多个 handlers 或 使用 handlers 调用其他的 handlers
1 2 3 4 5 |
- name: 调用多个handlers示例 command: /opt/test.sh notify: - restart apache - restart memcached |
1 2 3 4 5 |
# handlers中调用handlers handlers: - name: restart apache service: name=httpd state=restarted notify: restart memcached |
重要:
1、handlers只有其所在任务被执行时,才会被运行
2、handlers只会在play的末尾运行一次,若要在play中间运行handlers,需要使用meta模块来实现,如 - meta: flush_handlers
3、若一个play在运行到调用handlers语句之前就失败,该handlers将不会被执行
可以使用meta 模块的 --force-handlers选项强制执行handlers,即使handlers所在play中途失败也能执行
环境变量
可以使用lineinfile模块直接修改远程主机 ~/.bash_profile 文件添加环境变量,如
lineinfile: dest=~/.bash_profile regexp=^ENV_VAR= line=ENV_VAR=value
为在后续任务中使用此前定义过的变量,可以使用 register 选项将环境变量存储到自定义的变量中,如
1 2 3 4 5 6 7 8 9 10 11 12 |
tasks: - name: 为远程主机上的用户指定环境变量 lineinfile: dest: ~/.bash_profile regexp: "^ENV_VAR=" line: ENV_VAR=value - name: 获取刚刚指定的环境变量 并保存到自定义变量中 shell: 'source ~/.bash_profile && echo $ENV_VAR' register: foo - name: 打印出环境变量 debug: msg: "The variable is {{ foo.stdout }}" |
linux中可用来设置环境变量的文件:
~/.bash_profile ~/.bashrc ~/.profile /etc/profile /etc/environment
其中 /etc/environment 用于系统全局的环境变量设置
预定义环境变量
对一个play,可以使用 environment 选项为其设置单独的环境变量
# 为一个下载任务设置 http 代理
1 2 3 4 5 6 |
- name: 使用指定的代理服务器下载文件 get_url: url: http://www.example.com/file.tar.gz dest: /opt environment: http_proxy: http://example-proxy:80/ |
若要为整个playbook设置环境变量,可以使用 vars 块或引用外部变量文件
测试远程主机设置的环境变量是否生效:
ansible all -m shell -a 'echo $ENV_VAR
变量
建议变量字母都用小写,避免大小写混合的驼峰式写法,尽量避免在变量中出现数字,尽量让数字出现在变量名末尾
inventory中,变量使用=赋值
playbook中,变量使用:赋值
playbook变量
1、运行playbook时,使用 --extra-vars "boo=bar" 指定额外的变量
2、运行playbook时,也可将变量写在json或yaml中,如 --extra-vars "@even_more_vars.json"
3、在playbook中,使用 vars 代码块,或者引入变量文件 vars_files (当使用变量文件时,变量是顶格写的)
4、在playbook中,可以通过内置环境变量实现变量配置文件的有条件导入,如
1 2 3 4 5 6 |
- hosts: all vars_files: - [ "apache_{{ ansible_os_family }}.yml","apache_default.yml"] tasks: - name: 根据不同系统启动apache service: name={{ apache }} state=running |
变量 ansible_os_family 会返回远程主机的系统类型,如 CentOS ,然后就可以通过该值导入变量文件,若没有匹配到合适的文件名,将默认读取 apache_default.yml
Inventory 中定义变量
1 2 3 4 5 6 7 |
# 为某台主机定义变量 [shanghai] app.example.com proxy_state=present api.example.com proxy_state=absent # 为主机组定义变量 [shanghai:vars] cdn_host=sh.cdn.example.com |
不建议把变量直接定义在hosts文件中
Ansible默认从 /etc/ansible/host_vars/ 和 /etc/ansible/group_vars/ 目录下读取变量定义
在该文件夹下创建与host文件中主机名或组名同名的文件来定义变量
高阶变量
ansible命令行设定的、hosts文件中定义的、playbook和变量文件中定义的,这些变量称为简单变量或普通变量
数组变量(列表变量)
1 2 3 4 |
foo_list: - one - two - three |
访问方法: foo[0] 或 foo|first #Jinja2语法
字典变量(二维字典)
如 ansible_ens32 ansible_eth0 变量这种
1 2 3 |
# 显示 ansible_ens32 变量 tasks: - debug: var=ansible_ens32 |
读取变量的方法:
{{ ansible_ens32.ipv4.address }}
{{ ansible_ens32['ipv4']['address'] }}
主机变量和组变量
主机较少时,可以直接在 hosts 文件中定义
group_vars 和 host_vars
与hosts文件同一目录下的目录 group_vars 和 host_vars
可在这些文件夹下放以yml格式的且以对应主机名和主机组名命名的文件,存放变量
还可以在 group_vars 和 host_vars 两个文件夹下定义 all 文件,为所有主机、主机组定义变量
变量 hostvars 包含指定主机上所定义的所有变量
ansible常用内置变量:
groups,包含所有hosts文件中主机组的列表
group_names,包含当前主机所在所有主机组名的一个列表
inventory_hostname,通过hosts文件写义主机的主机名(与ansible_home不一定相同)
inventory_hostname_short,变量inventory_hostname的第一部分
play_hosts,将执行当前任务的所有主机
facts 收集系统信息
facts信息包括(不限于)远程主机cpu类型、ip地址、磁盘空间、操作系统信息、网终接口信息等
可以根据这些信息决定是否要继续运行下一步,或者将这些信息写入某个配置文件中
# 获取对应主机的所有可用facts信息
ansible 192.168.1.110 -m setup -u test|more
在用不到facts信息的playbook中,可以设置 gather_facts:no 跳过这一步,可为任务节省几秒钟时间
1 2 |
- hosts: all gather_facts: no |
常用的facts变量有 ansible_os_family、ansible_hostname、ansible_memtotal_mb等,这些变量常用于 when 语句判断条件,决定下一步操作
若远程主机上安装有Facter或Ohai,也会收集它们的信息,变量名以facter_和ohai_开头
注意,不同操作系统上获取到的Facts变量格式不一样
在远程主机上定义Facts变量:
将变量写入远程主机 /etc/ansible/facts.d/目录中并以.fact结尾,文件可以是json或ini文件或可返回json的可执行文件
ansible在执行任务时会自动到这个目录下读取变量信息
# 查看定义的本地变量
ansible 192.168.1.120 -m setup -a "filter=ansible_local" -u test|more
# playbook中 要使用本地变量时
setup: filter=ansible_local
vault 加密模块
ansible-vault encrypt tomcat.service
# 查看加密文件
ansible-vault view tomcat.service
# 编辑加密文件
ansible-vault edit tomcat.service
ansible-vault命令常用选项:
edit,编辑加密过的文件
rekey,修改已加密文件的密码
create,创建新文件 并直接对其进行加密
view,查看经过加密的文件
decrypt,解密文件
可跟多个文件进行操作
以密码文件的形式解密
密码文件位于:~/.ansible/ 目录,权限为 600
# 使用密码文件自动解密
ansible-playbook b.yml --vault-password-file ~/.ansible/vault_pass.txt
注,也可以使用可执行脚本生成单行密码,如~/.ansible/vault_pass.py
若安装 pip install cryptography 可加快vault的运行速度
ansible vault加密使用AES-256加密码算法,极其安全
变量优先级
高 -> 低
1、命令行中定义的变量 -e
2、inventory中定义的连接变量,如ansible_ssh_user
3、大多数的其他变量(命令行转换、play中的变量、included的变量、role中的变量等)
4、inventory中定义的其他变量
5、系统通过gather_facts方法发现的facts
6、Role默认变量(默认的值)
定认变量的技巧:
1、Role中默认变量应设置得尽可能合理
2、playbook中应尽量少定义变量,建议定义到引用文件中或inventory文件中
3、只有真正与主机或主机组强相关的变量才定义在inventory文件中
4、尽量少在动态或静态inventory源文件中定义变量,特别是很少在playbook中用到的变量
5、避免在命令行中使用 -e 选项定义变量,只有在不关心项目可维护性和任务幂等性时,才建议这种变量定义方式
流程控制 if/then/when
幂等性检查
shell模块、command模块中,需要对用户的输入内容或任务的运行结果进行判断
Jinja2正则表达、Python内置函数和逻辑判断
Jinja2支持的数据类型:字符串、整数、浮点数、列表、元组、字典、布尔型
Jinja2支持基本数据运算,加减乘除和比较;逻辑运算符:and or not
1 in [1,2,3]
'see' in 'Can you see me?'
foo != bar
# 返回false
(foo != foo) or (a in [1,2,3])
# 判断变量foo是否被定义过
foo is defined #定义过返回true
foo is undefined
equalto 与 == 等效
even 判断对象是否是偶数
iterable 判断对象是否可迭代
Python内置方法:
string.split
[number].is_signed()
1 2 3 |
- name: 当软件主版本号为4时进行操作 # 任务内容 when: software_version.split('.')[0] == '4' # software_version "4.6.1" |
通常建议使用更加简洁的Jinja2语句进行判断
变量注册器 register
注册一个变量,存储运行结果,该注册变量在随后的任务中将像普通变量一样被使用
1 2 3 |
- name: 用注册器接收shell命令返回的结果 shell: ... register: shell_result |
shell_result.stdout 标准输出的内容
shell_result.stderr 标准错误输出的内容
条件判断 when
让任务在满足when条件时才被执行
when: (is_db_server is defined) and is_db_server #当变量is_db_server定义且值为true时 任务执行
when: "forever_list.stdout.find('/path/to/app/app.js') == -1"
when: "(is_app_server == true) and ('interesting-branch' not in git_branches.stdout)"
when: hosts_file.stat.exists == false #hosts_file变量中项目stat不存在时
1 2 3 4 5 6 |
- name: 判断某应用状态并注册到变量中 ... register: app_result - name: 当应用状态为ready时执行 ... when: "'ready' in app_result.stdout" |
changed_when failed_when
对命令运行结果进行判断
对command和shell模块,若不使用changed_when语句,将永远返回changed
1 2 3 4 |
- name: 使用composer安装软件 command: "/usr/local/bin/composer global require phpunit/phpunit --prefer-dist" register: composer changed_when: "'Nothing to install or update' not in composer.stdout" |
1 2 3 4 5 6 |
- name: 通过cli导入jenkins任务 shell: > java -jar /opt/jinkins-cli.jar -s http://localhost:8080/ create-job "My Job" < /usr/local/my-job.xml register: import failed_when: "import.stderr and 'already exists' not in import.stderr" |
ignore_errors
ignore_errors: true
屏蔽所有错误信息,ansible会认为任务运行成功,并继续运行下面的任务
任务间流程控制
任务委托
默认ansible所有任务都是在我们指定的机器上运行
当任务需要在特定主机上运行而非一开始指定的所有主机时,用到任务委托
1 2 3 4 5 |
- hosts: webservers tasks: - name: 添加监控主机 command: monitor-server webservers {{ inventory_hostname }} delegate_to: "{{ monitoring_master }}" |
playbook定义在webservers上执行,但当执行到该任务时,运行任务的主机是 monitoring_master
1 2 3 4 5 |
- name: 执行本地任务 command: remove-from-lb {{ inventory_hostname }} delegate_to: 127.0.0.1 - name: 执行本地任务 local_action: command remove-from-lb {{ inventory_hostname }} |
任务暂停
等待一些状态时
1 2 3 4 5 6 7 8 |
- name: 等待webserver启动 local_action: module: wait_for host: webserver1 port: 80 delay: 10 timeout: 300 state: started |
每10秒检查一次主机webserver1上80端口是否开启,若超过300s,80端口仍未开启,将返回失败信息
wait_for模块应用场景:
1、判断一段时间内主机端口是否可用,host,port,timeout组合使用
2、使用path选项(可结合search_regx选项进行正则匹配)和timeout选项判断某个路径下文件是否存在
3、使用host,port,stat选项的drained值判断活动连接数是否被耗尽
4、使用delay选项来指定在timeout时间内进行检测的时间间隔,单位秒
交互式提示 变量手动赋值
手动为变量赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
--- # tomcat.yml - hosts: 192.168.1.120 remote_user: test become: yes become_user: root become_method: sudo gather_facts: no vars_prompt: - name: share_user prompt: "为变量share_user赋值" private: yes - name: share_pass prompt: "为变量share_pass赋值" private: no tasks: - debug: var=share_user |
private,默认为yes,输入内容不显示
default,为变量设置默认值
confirm,两次输入 适合输入密码的情况
prompt,提示信息
name,变量名
Tags 标签
为Roles、文件、单独的任务甚至整个playbook打上标签,然后利用标签来指定要运行playbook中个别任务或不执行指定的任务
1 2 3 4 5 6 7 8 9 10 11 12 13 |
--- - hosts: all tags: deploy # 整个playbook的标签 ... roles: - { role: tomcat,tags:['tomcat','app']} # 为Roles打的标签会应用于Roles下所有任务 ... tasks: - name: 为某个任务打标签 command: ... tags: - notifications - say |
只执行标签处任务:
ansible-playbook tags.yml --tags "say"
跳过打有某标签的任务:
ansible-playbook tags.yml --skip-tags "notifications"
1 2 3 4 5 6 |
# 添加多标签语法 tags: ['one','two','three'] tags: - one - two - three |
Block 块
块可以将任务进行分组,可在块级别上应用任务变量
可以集中处理块级别的内部任务异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
--- - hosts: web tasks: - block: - yum: name=httpd state=present - template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf - service: name=httpd state=started enabled=yes when: ansible_os_family == 'RedHat' sudo: yes - block: - apt: name=apache2 state=present - template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf - service: name=apache2 state=started enabled=yes when: ansible_os_family == 'Debian' sudo: yes |
块功能适合于多个任务共用相同一套任务参数的情况
块功能处理异常:
1 2 3 4 5 6 7 8 9 10 11 12 |
--- - hosts: web tasks: - block: - name: 一个任务 script: monitoring-connect.sh rescue: - name: 只有脚本报错时才执行 debug: msg="something wrong" always: - name: 无论结果总是执行 debug: msg="always executes" |
转载请注明:轻风博客 » Ansible使用指南-03-Playbook拓展