#cat blockinfile_ex.yml --- - name: blockinfile module test hosts: test tasks: - name: copy test.html to dest copy: src: files/test.html dest: /var/www/html/test.html - name: add block blockinfile: marker: "<!-- {mark} ANSIBLE MANAGED BLOCK -->" insertafter: "<body>" path: /var/www/html/test.html block: | <h1>Welcome to {{ ansible_hostname }}</h1> <p>Last updated on {{ ansible_date_time.iso8601 }}</p> 执行后结果如下: [root@app html]# cat test.html <html> <head> </head> <body> <!-- BEGIN ANSIBLE MANAGED BLOCK --> <h1>Welcome to app</h1> <p>Last updated on 2019-05-28T15:00:03Z</p> <!-- END ANSIBLE MANAGED BLOCK --> </body> </html>
2、Jinja2模板管理 介绍:Jinja2是基于python的模板引擎。那么什么是模板? 假设说现在我们需要一次性在10台主机上安装redis,这个通过playbook现在已经很容易实现。默认情况下,所有的redis安装完成之后,我们可以统一为其分发配置文件。这个时候就面临一个问题,这些redis需要监听的地址各不相同,我们也不可能为每一个redis单独写一个配置文件。因为这些配置文件中,绝大部分的配置其实都是相同的。这个时候最好的方式其实就是用一个通用的配置文件来解决所有的问题。将所有需要修改的地方使用变量替换,如下示例中redis.conf.j2文件: daemonize yes pidfile /var/run/redis.pid port 6379 logfile "/var/log/redis/redis.log" dbfilename dump.rdb dir /data/redis maxmemory 1G bind {{ ansible_eth0.ipv4.address }} 127.0.0.1 timeout 300 loglevel notice databases 16 save 900 1 save 300 10 save 60 10000 rdbcompression yes maxclients 10000 appendonly yes appendfilename appendonly.aof appendfsync everysec #那么此时,redis.conf.j2文件就是一个模板文件。{{ ansible_eth0.ipv4.address }}是一个fact变量,用于获取被控端ip地址以实现替换。
3、在playbook中使用jinja2 现在我们有了一个模板文件,那么在playbook中如何来使用呢? playbook使用template模块来实现模板文件的分发,其用法与copy模块基本相同,唯一的区别是,copy模块会将原文件原封不动的复制到被控端,而template会将原文件复制到被控端,并且使用变量的值将文件中的变量替换以生成完整的配置文件。 下面是一个完整的示例: # cat config_redis.yml - name: Configure Redis hosts: test tasks: - name: install redis yum: name: redis state: present - name: create data dir file: path: /data/redis state: directory recurse: yes owner: redis group: redis - name: copy redis.conf to dest template: src: templates/redis.conf.j2 dest: /etc/redis.conf notify: - restart redis - name: start redis service: name: redis state: started enabled: yes handlers: - name: restart redis service: name: redis state: restarted 执行完成之后,我们可以看到被控端/etc/redis.conf配置文件如下: daemonize yes pidfile /var/run/redis.pid port 6379 logfile "/var/log/redis/redis.log" dbfilename dump.rdb dir /data/redis maxmemory 1G bind 10.1.61.187 127.0.0.1 timeout 300 loglevel notice databases 16 save 900 1 save 300 10 save 60 10000 rdbcompression yes maxclients 10000 appendonly yes appendfilename appendonly.aof appendfsync everysec 关于template模块的更多参数说明:
backup:如果原目标文件存在,则先备份目标文件 dest:目标文件路径 force:是否强制覆盖,默认为yes group:目标文件属组 mode:目标文件的权限 owner:目标文件属主 src:源模板文件路径 validate:在复制之前通过命令验证目标文件,如果验证通过则复制
4、Jinja2条件语句 在上面的示例中,我们直接取了被控节点的eth0网卡的ip作为其监听地址。那么假如有些机器的网卡是bond0,这种做法就会报错。这个时候我们就需要在模板文件中定义条件语句如下:
daemonize yes pidfile /var/run/redis.pid port 6379 logfile "/var/log/redis/redis.log" dbfilename dump.rdb dir /data/redis
maxmemory 1G
{% if ansible_eth0.ipv4.address %} bind {{ ansible_eth0.ipv4.address }} 127.0.0.1 {% elif ansible_bond0.ipv4.address %} bind {{ ansible_bond0.ipv4.address }} 127.0.0.1 {% else%} bind 0.0.0.0 {% endif %}
timeout 300 loglevel notice
databases 16 save 900 1 save 300 10 save 60 10000
rdbcompression yes
maxclients 10000 appendonly yes appendfilename appendonly.aof appendfsync everysec 我们可以更进一步,让redis主从角色都可以使用该文件:
daemonize yes pidfile /var/run/redis.pid port 6379 logfile "/var/log/redis/redis.log" dbfilename dump.rdb dir /data/redis
maxmemory 1G
{% if ansible_eth0.ipv4.address %} bind {{ ansible_eth0.ipv4.address }} 127.0.0.1 {% elif ansible_bond0.ipv4.address %} bind {{ ansible_bond0.ipv4.address }} 127.0.0.1 {% else%} bind 0.0.0.0 {% endif %}
{% if redis_slave is defined %} slaveof {{ masterip }} {{ masterport|default(6379) }} {% endif %}
{% if masterpass is defined %} masterauth {{ masterpass }} {% endif %}
{% if requirepass is defined %} requirepass {{ requirepass }} {% endif %}
timeout 300 loglevel notice
databases 16 save 900 1 save 300 10 save 60 10000
rdbcompression yes
maxclients 10000 appendonly yes appendfilename appendonly.aof appendfsync everysec
stop-writes-on-bgsave-error no 我们定义一个inventory如下:
[redis] 10.1.61.27 redis_slave=true masterip=10.1.61.187 masterpass=123456 10.1.61.187 requirepass=123456
5、Jinja2循环语句 定义一个inventory示例如下:
[redis] 10.1.61.27 redis_slave=true masterip=10.1.61.187 masterpass=123456 10.1.61.187 requirepass=123456
5、Jinja2循环语句 定义一个inventory示例如下:
[proxy] 10.1.61.195
[webserver] 10.1.61.27 10.1.61.187 现在把proxy主机组中的主机作为代理服务器,安装nginx做反向代理,将请求转发至后面的两台webserver,即webserver组的服务器。
现在我们编写一个playbook如下:
#cat config_nginx.conf - name: gather facts gather_facts: Fasle hosts: webserver tasks: - name: gather facts setup: - name: Configure Nginx hosts: proxy tasks: - name: install nginx yum: name: nginx state: present - name: copy nginx.conf to dest template: src: templates/nginx.conf.j2 dest: /etc/nginx/nginx.conf notify: - restart nginx - name: start nginx service: name: nginx state: started enabled: yes handlers: - name: restart nginx service: name: nginx state: restarted 模板文件 templates/nginx.conf.j2示例如下:
# cat nginx.conf.j2 user nginx; worker_processes {{ ansible_processor_vcpus }}; error_log /var/log/nginx/error.log; pid /var/run/nginx.pid; include /usr/share/nginx/modules/*.conf; events { worker_connections 65535; use epoll; } http { map $http_x_forwarded_for $clientRealIP { "" $remote_addr; ~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr; } log_format real_ip '{ "datetime": "$time_local", ' '"remote_addr": "$remote_addr", ' '"source_addr": "$clientRealIP", ' '"x_forwarded_for": "$http_x_forwarded_for", ' '"request": "$request_uri", ' '"status": "$status", ' '"request_method": "$request_method", ' '"request_length": "$request_length", ' '"body_bytes_sent": "$body_bytes_sent", ' '"request_time": "$request_time", ' '"http_referrer": "$http_referer", ' '"user_agent": "$http_user_agent", ' '"upstream_addr": "$upstream_addr", ' '"upstream_status": "$upstream_status", ' '"upstream_http_header": "$upstream_http_host",' '"upstream_response_time": "$upstream_response_time", ' '"x-req-id": "$http_x_request_id", ' '"servername": "$host"' ' }'; access_log /var/log/nginx/access.log real_ip; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; include /etc/nginx/mime.types; default_type application/octet-stream; include /etc/nginx/conf.d/*.conf;
upstream web { {% for host in groups['webserver'] %} {% if hostvars[host]['ansible_bond0']['ipv4']['address'] is defined %} server {{ hostvars[host]['ansible_bond0']['ipv4']['address'] }}; {% elif hostvars[host]['ansible_eth0']['ipv4']['address'] is defined %} server {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}; {% endif %} {% endfor %} } server { listen 80 default_server; server_name _; location / { proxy_pass http://web; } } } 下面再给一个域名解析服务bind的配置文件 named.conf的jinja2模板示例:
options {
listen-on port 53 { 127.0.0.1; {% for ip in ansible_all_ipv4_addresses %} {{ ip }}; {% endfor %} };
listen-on-v6 port 53 { ::1; }; directory "/var/named"; dump-file "/var/named/data/cache_dump.db"; statistics-file "/var/named/data/named_stats.txt"; memstatistics-file "/var/named/data/named_mem_stats.txt"; };
zone "." IN { type hint; file "named.ca"; };
include "/etc/named.rfc1912.zones"; include "/etc/named.root.key";
{# Variables for zone config #} {% if 'authorativenames' in group_names %} {% set zone_type = 'master' %} {% set zone_dir = 'data' %} {% else %} {% set zone_type = 'slave' %} {% set zone_dir = 'slaves' %} {% endif %}
zone "internal.example.com" IN { type {{ zone_type }}; file "{{ zone_dir }}/internal.example.com"; {% if 'authorativenames' not in group_names %} masters { 192.168.2.2; }; {% endif %} };
6、Jinja2过滤器 (1) default过滤器 简单示例: "Host": "{{ db_host | default('lcoalhost') }}" (2)应用于注册变量的过滤器 正常情况下,当某个task执行失败的时候,ansible会中止运行。此时我们可以通过ignore_errors来捕获异常以让task继续往下执行。然后调用debug模块打印出出错时的内容,拿来错误结果后,主动失败。 - name: Run myprog command: /opt/myprog register: result ignore_errors: True - debug: var: result - debug: msg: "Stop running the playbook if myprog failed" failed_when: result|failed 任务返回值过滤器: failed: 如果注册变量的值是任务failed则返回True changed: 如果注册变量的值是任务changed则返回True success:如果注册变量的值是任务succeeded则返回True skipped:如果注册变量的值是任务skipped则返回True
(3)应用于文件路径的过滤器 basename:返回文件路径中的文件名部分 dirname:返回文件路径中的目录部分 expanduser:将文件路径中的~替换为用户目录 realpath:处理符号链接后的文件实际路径 下面是一个示例:
- name: test basename hosts: test vars: homepage: /usr/share/nginx/html/index.html tasks: - name: copy homepage copy: src: files/index.html dest: {{ homepage }} 可以通过basename改写成如下方式:
- name: test basename hosts: test vars: homepage: /usr/share/nginx/html/index.html tasks: - name: copy homepage copy: src: files/{{ homepage | basename }} dest: {{ homepage }} (4)自定义过滤器 举个简单的例子,现在有一个playbook如下:
- name: test filter hosts: test vars: domains: ["www.example.com","example.com"] tasks: template: src: templates/test.conf.j2 dest: /tmp/test.conf templates/test.conf.j2如下: hosts = [{{ domains | join(',') }}] 执行playbook后,在目标机上的test.conf如下: hosts = [www.example.com,example.com] 现在如果希望目标机上的test.conf文件返回结果如下: hosts = ["www.example.com","example.com"] 没有现成的过滤器来帮我们做这件事情。我们可以自己简单写一个surround_by_quote.py内容如下:
# 定义过滤器执行的操作 def surround_by_quote(a_list): return ['"%s"' % an_element for an_element in a_list] class FilterModule(object): def filters(self): return {'surround_by_quote': surround_by_quote} 我们需要开启ansible.cfg的配置项: filter_plugins = /usr/share/ansible/plugins/filter 将刚刚编写的代码文件放入/usr/share/ansible/plugins/filter目录下,然后修改templates/test.conf.j2如下: hosts = [{{ domains | join(',') }}] 再次执行playbook,最后返回结果: hosts = ["www.example.com","example.com"]
十五、Playbook高级用法 1、本地执行 如果希望在控制主机本地运行一个特定的任务,可以使用local_action语句。 假设我们需要配置的远程主机刚刚启动,如果我们直接运行playbook,可能会因为sshd服务尚未开始监听而导致失败,我们可以在控制主机上使用如下示例来等待被控端sshd端口监听: - name: wait for ssh server to be running wait_for port: 22 host: "{{ inventory_hostname }}" search_regex: OpenSSH connection: local
2、任务委托 在有些时候,我们希望运行与选定的主机或主机组相关联的task,但是这个task又不需要在选定的主机或主机组上执行,而需要在另一台服务器上执行。 这种特性适用于以下场景: 在告警系统中启用基于主机的告警 向负载均衡器中添加或移除一台主机 在dns上添加或修改针对某个主机的解析 在存储节点上创建一个存储以用于主机挂载 使用一个外部程序来检测主机上的服务是否正常 可以使用delegate_to语句来在另一台主机上运行task: - name: enable alerts for web servers hosts: webservers tasks: - name: enable alerts nagios: action=enable_alerts service=web host="{{ inventory_hostname }}" delegate_to: nagios.example.com 如果delegate_to: 127.0.0.1的时候,等价于local_action
3、任务暂停 有些情况下,一些任务的运行需要等待一些状态的恢复,比如某一台主机或者应用刚刚重启,我们需要需要等待它上面的某个端口开启,此时就需要将正在运行的任务暂停,直到其状态满足要求。 Ansible提供了wait_for模块以实现任务暂停的需求
wait_for模块常用参数: connect_timeout:在下一个任务执行之前等待连接的超时时间 delay:等待一个端口或者文件或者连接到指定的状态时,默认超时时间为300秒,在这等待的300s的时间里,wait_for模块会一直轮询指定的对象是否到达指定的状态,delay即为多长时间轮询一次状态。 host:wait_for模块等待的主机的地址,默认为127.0.0.1 port:wait_for模块待待的主机的端口 path:文件路径,只有当这个文件存在时,下一任务才开始执行,即等待该文件创建完成 state:等待的状态,即等待的文件或端口或者连接状态达到指定的状态时,下一个任务开始执行。当等的对象为端口时,状态有started,stoped,即端口已经监听或者端口已经关闭;当等待的对象为文件时,状态有present或者started,absent,即文件已创建或者删除;当等待的对象为一个连接时,状态有drained,即连接已建立。默认为started timeout:wait_for的等待的超时时间,默认为300秒 示例:
#等待8080端口已正常监听,才开始下一个任务,直到超时 - wait_for: port: 8080 state: started #等待8000端口正常监听,每隔10s检查一次,直至等待超时 - wait_for: port: 8000 delay: 10 #等待8000端口直至有连接建立 - wait_for: host: 0.0.0.0 port: 8000 delay: 10 state: drained #等待8000端口有连接建立,如果连接来自10.2.1.2或者10.2.1.3,则忽略。 - wait_for: host: 0.0.0.0 port: 8000 state: drained exclude_hosts: 10.2.1.2,10.2.1.3 #等待/tmp/foo文件已创建 - wait_for: path: /tmp/foo
#等待/tmp/foo文件已创建,而且该文件中需要包含completed字符串 - wait_for: path: /tmp/foo search_regex: completed
#等待/var/lock/file.lock被删除 - wait_for: path: /var/lock/file.lock state: absent #等待指定的进程被销毁 - wait_for: path: /proc/3466/status state: absent #等待openssh启动,10s检查一次 - wait_for: port: 22 host: "{{ ansible_ssh_host | default(inventory_hostname) }}" search_regex: OpenSSH delay: 10
4、滚动执行 默认情况下,ansible会并行的在所有选定的主机或主机组上执行每一个task,但有的时候,我们会希望能够逐台运行。最典型的例子就是对负载均衡器后面的应用服务器进行更新时。通常来讲,我们会将应用服务器逐台从负载均衡器上摘除,更新,然后再添加回去。我们可以在play中使用serial语句来告诉ansible限制并行执行play的主机数量。 下面是一个在amazon EC2的负载均衡器中移除主机,更新软件包,再添加回负载均衡的配置示例: - name: upgrade pkgs on servers behind load balancer hosts: myhosts serial: 1 tasks: - name: get the ec2 instance id and elastic load balancer id ec2_facts:
- name: take the host out of the elastic load balancer id local_action: ec2_elb args: instance_id: "{{ ansible_ec2_instance_id }}" state: absent
- name: upgrade pkgs apt: update_cache: yes upgrade: yes
- name: put the host back n the elastic load balancer local_action: ec2_elb args: instance_id: "{{ ansible_ec2_instance_id }}" state: present ec2_elbs: "{{ items }}" with_items: ec2_elbs 在上述示例中,serial的值为1,即表示在某一个时间段内,play只在一台主机上执行。如果为2,则同时有2台主机运行play。 一般来讲,当task失败时,ansible会停止执行失败的那台主机上的任务,但是继续对其他 主机执行。在负载均衡的场景中,我们会更希望ansible在所有主机执行失败之前就让play停止,否则很可能会面临所有主机都从负载均衡器上摘除并且都执行失败导致服务不可用的场景。这个时候,我们可以使用serial语句配合max_fail_percentage语句使用。max_fail_percentage表示当最大失败主机的比例达到多少时,ansible就让整个play失败。示例如下:
- name: upgrade pkgs on fservers behind load balancer hosts: myhosts serial: 1 max_fail_percentage: 25 tasks: ...... 假如负载均衡后面有4台主机,并且有一台主机执行失败,这时ansible还会继续运行,要让Play停止运行,则必须超过25%,所以如果想一台失败就停止执行,我们可以将max_fail_percentage的值设为24。如果我们希望只要有执行失败,就放弃执行,我们可以将max_fail_percentage的值设为0。
5、只执行一次 某些时候,我们希望某个task只执行一次,即使它被绑定到了多个主机上。例如在一个负载均衡器后面有多台应用服务器,我们希望执行一个数据库迁移,只需要在一个应用服务器上执行操作即可。 可以使用run_once语句来处理: - name: run the database migrateions command: /opt/run_migrateions run_once: true 还可以与local_action配合使用,如下:
- name: run the task locally, only once command: /opt/my-custom-command connection: local run_once: true 还可以与delegate_to配合使用,让这个只执行一次的任务在指定的机器上运行:
- name: run the task locally, only once command: /opt/my-custom-command run_once: true delegate_to: app.a1-61-105.dev.unp
6、设置环境变量 我们在命令行下执行某些命令的时候,这些命令可能会需要依赖环境变量。比如在安装某些包的时候,可能需要通过代理才能完成完装。或者某个脚本可能需要调用某个环境变量才能完成运行。 ansible 支持通过environment关键字来定义一些环境变量。 在如下场景中可能需要用到环境变量: 运行shell的时候,需要设置path变量 需要加载一些库,这些库不在系统的标准库路径当中 下面是一个简单示例: --- - name: upload a remote file to aws s3 hosts: test tasks: - name: install pip yum: name: python-pip state: installed - name: install the aws tools pip: name: awscli state: present - name upload file to s3 shell aws s3 put-object --bucket=my-test-bucket --key={{ ansible_hostname }}/fstab --body=/etc/fstab --region=eu-west-1 environment: AWS_ACCESS_KEY_ID: xxxxxx AWS_SECRET_ACCESS_KEY: xxxxxx 事实上,environment也可以存储在变量当中: - hosts: all remote_user: root vars: proxy_env: http_proxy: http://proxy.example.com:8080 https_proxy: http://proxy.bos.example.com:8080 tasks: - apt: name=cobbler state=installed environment: proxy_env 交互式提示 在少数情况下,ansible任务运行的过程中需要用户输入一些数据,这些数据要么比较秘密不方便,或者数据是动态的,不同的用户有不同的需求,比如输入用户自己的账户和密码或者输入不同的版本号会触发不同的后续操作等。ansible的vars_prompt关键字就是用来处理上述这种与用户交互的情况的。
- hosts: all remote_user: root vars_prompt: - name: share_user prompt: "what is your network username?" private: yes - name: share_pass prompt: "what is your network password" private: yes tasks: - debug: var: share_user - debug: var: share_pass vars_prompt常用选项说明: private: 默认为yes,表示用户输入的值在命令行不可见 default:定义默认值,当用户未输入时则使用默认值 confirm:如果设置为yes,则会要求用户输入两次,适合输入密码的情况
十六、Playbook之tags 简介: 在大型项目当中,通常一个playbook会有非常多的task。而我们每次执行这个playbook时,都会将所有task运行一遍。而事实上,在实际使用过程中,我们可能只是想要执行其中的一部分任务而已,并不想把整个playbook完整跑一遍。这个时候就需要用到tags。 通过tags,我们可以给playbook中的某一些任务打上“标签”,而在执行playbook的时候,我们可以通过选定标签的方式指定只执行哪一些任务或者不执行哪一些任务。
1、为task打tag: 下面是一个安装httpd的简单示例: # cat /etc/ansible/playbook/install_web.yml - name: configure webservers hosts: all remote_user: ansible tasks: - name: Install httpd yum: name: httpd state: present tags: install_httpd - name: Cofiguration httpd copy: src: /root/httpd.conf dest: /etc/httpd/conf/httpd.conf tags: conf_httpd notify: - restart httpd - name: Start httpd service: name: httpd state: started enabled: no tags: start_httpd handlers: - name: restart httpd service: name=httpd state=restart 在上面的示例中,我们为两个task定义了三个tags:install_httpd、conf_httpd以及start_httpd。
2、执行指定tag的task 有了tags之后,我们就可以只运行playbook中指定标签的task了:
# ansible-playbook --tags="start_httpd" install_web.yml PLAY [configure webservers] ******************************************************************************* TASK [Gathering Facts] ***************************************************************************************************** ok: [10.1.61.187] TASK [Start httpd] ************************************************************************************************************** changed: [10.1.61.187] PLAY RECAP ******************************************************************************************************** 10.1.61.187 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 也可以一次指定多个tag执行:
# ansible-playbook --tags="conf_httpd,start_httpd" install_web.yml PLAY [configure webservers] *********************************************************************************************************** TASK [Gathering Facts] **************************************************************************************************************** ok: [10.1.61.187] TASK [Cofiguration httpd] ************************************************************************************************************************* ok: [10.1.61.187] TASK [Start httpd] ******************************************************************************************************************************* ok: [10.1.61.187] PLAY RECAP **************************************************************************************************************** 10.1.61.187 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
3、排除指定tag的task 通过下面的方式可以排除指定了tag的task,即除了指定tag的task不执行,其他task都执行: # ansible-playbook --skip-tags="install_httpd" install_web.yml PLAY [configure webservers] ************************************************************************************************************** TASK [Gathering Facts] ******************************************************************************************************************* ok: [10.1.61.187] TASK [Cofiguration httpd] **************************************************************************************************************** ok: [10.1.61.187] TASK [Start httpd] *********************************************************************************************************************** ok: [10.1.61.187] PLAY RECAP ******************************************************************************************************************************* 10.1.61.187 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 执行效果跟上面一样。
4、查看playbook中的所有tag 可以通过--list-tags参数列出指定的playbook中所有的tag # ansible-playbook --list-tags install_web.yml playbook: install_web.yml play #1 (all): configure webservers TAGS: [] TASK TAGS: [conf_httpd, install_httpd, start_httpd]
5、打tag的几种方式 (1)为一个任务指定一个标签 tags: conf_httpd
(2)为一个任务指定多个标签 #可以通过列表的方式为一个任务指定多个标签: tags: - install_httpd - install_web tags: ['install_httpd','install_web'] tags: install_httpd,install_web
(3)为一个play指定一组标签 #当为一个play指定一组标签后,该play下的所有task都会自动继承该标签,各task也可以自定义自己的标签。 - name: configure webservers hosts: all remote_user: ansible tags: - httpd tasks: ...
6、ansible内置tag 除了用户自定义tag,ansible也内置了几个tag,这些tag都包含特殊含义: always:一旦某个task被打上了always的tag,则无论是playbook的完整执行,还是指定tag执行,不管你指定的tag是啥,该任务总是会被执行。除非明确指定"--sk ip-tags=always"选项,才不会执行该task。 never:该标签与always正好相反,总是不会执行,除非明确指定"--tags=never"选项。
tagged:在调用时使用 ```sh # 所有打了tag的任务都会被执行,包含never tag的除外,没有标签的不会被执行 ansible-playbook --tags tagged install_web.yaml # 所有打了tag的任务都不会被执行,包括always tag也不会被执行 ansible-playbook --skip-tags tagged install_web.yaml
untagged:在调用时使用 ```sh # 所有未打tag的任务都会被执行,打了always tag的也会被执行 ansibl-playbook --tags untagged install_web.yaml # 所有未打tag的任务都不会被执行 ansibl-playbook --skip-tags untagged install_web.yaml ``` all:表示所有任务都会被执行,在默认情况下,不指定任何标签,则使用的就是该标