Ansible使用02

1、忽略错误 ignore_errors

如果一个task出错,默认将不会继续执行后续的其它task
利用 ignore_errors:yes 可以忽略此task的错误,继续向下执行playbook其它task

[root@ansible ansible]#vim test_ignore.yml
---
- hosts: websrvs

 tasks:
   - name: error
     command: /bin/false
     ignore_errors: yes
   - name: continue
     command: wall continue

2、Playbook中使用handlers和notify

2.1、handlers和notify

Handlers本质是task list ,类似于MySQL中的触发器触发的行为,其中的task与前述的task并没有本质上的不同,只有在关注的资源发生变化时,才会采取一定的操作。
Notify对应的action 在所有task都执行完才会最后被触发,这样可避免多个task多次改变发生时每次都触发执行指定的操作,Handlers仅在所有的变化发生完成后一次性地执行指定操作。
在notify中列出的操作称为handler,也即notify中调用handler中定义的操作

注意:

  • 如果多个task通知了相同的handlers, 此handlers仅会在所有task结束后运行一 次。
  • 只有notify对应的task发生改变了才会通知handlers, 没有改变则不会触发handlers
  • handlers 是在所有前面的tasks都成功执行才会执行,如果前面任何一个task失败,会导致handler跳过执行
[root@ansible ansible]#cat install_nginx.yml
---
#install nginx

- hosts: webservers
  gather_facts: no

  tasks:
    - name: install package
      yum:
        name: nginx
    - name: config file
      copy:
        src: nginx.conf
        dest: /etc/nginx/nginx.conf
      notify:
        - restart nginx
        - notify message
      tags:
        - config
    - name: Web Content
      copy:
        content: "App Version {{ ansible_eth0.ipv4.address.split('.')[-1]}}"
        dest: /usr/share/nginx/html/index.html
    - name: start service
      service:
        name: nginx
        state: started
        enabled: yes

  handlers:
    - name: restart nginx
      service: name=nginx state=restarted
    - name: notify message
      debug: msg="nginx is restarted"

2.2、force_handlers

如果不论前面的task成功与否,都希望handlers能执行,可以使用force_handlers: yes 强制执行handler

- hosts: webservers
  force_handlers: yes
  tasks:
    - name: config file
      copy:
        src: nginx.conf
        dest: /etc/nginx/nginx.conf
      notify: restart nginx
    - name: install package
      yum:
        name: no_exist_package
  handlers:
    - name: restart nginx
      service:
        name: nginx
        state: restarted

3、Playbook中使用tags组件

默认情况下, Ansible 在执行一个 playbook 时,会执行 playbook 中所有的任务
在playbook文件中,可以利用tags组件,为特定 task 指定标签,当在执行playbook时,可以只执行特定tags的task,而非整个playbook文件
可以一个task对应多个tag,也可以多个task对应同一个tag
还有另外3个特殊关键字用于标签, tagged, untagged 和 all,它们分别是仅运行已标记,只有未标记和所有任务。
tags 主要用于调试环境

[root@ansible ansible]# vim install_httpd.yml

---

- hosts: webservers
  remote_user: root
  gather_facts: no

  tasks:
    - name: install httpd
      yum:
        name: httpd
    - name: modify config listen port
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: '^Listen'
        line: 'Listen 8080'
    - name: modify config data1
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: '^DocumentRoot "/var/www/html"'
        line: 'DocumentRoot "/data/html"'
    - name: modify config data2
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: '^<Directory "/var/www/html">'
        line: '<Directory "/data/html">'
    - name: mkdir website dir
      file:
        path: /data/html
        state: directory
    - name: web html
      copy:
        src: files/index.html
        dest: /data/html/
      tags: copy
    - name: start service
      service:
        name: httpd
        state: started
        enabled: yes
      tags: service
# 列出标签
[root@ansible ansible]# ansible-playbook --list-tags install_httpd.yml

playbook: install_httpd.yml

  play #1 (webservers): webservers	TAGS: []
      TASK TAGS: [copy, service]

# 执行有指定标签的任务
[root@ansible ansible]# ansible-playbook -t service install_httpd.yml

# 忽略执行指定标签的task
[root@ansible ansible]# ansible-playbook --skip-tags service install_httpd.yml

# 忽略执行没有标签的任务,即只执行有标签的任务
[root@ansible ansible]# ansible-playbook install_httpd.yml --skip-tags untagged

4、Playbook中使用变量

Playbook中同样也支持变量

变量名:仅能由字母、数字和下划线组成,且只能以字母开头

变量定义

variable=value
variable: value

http_port=80
http_port: 80

变量调用方式

通过 {{ variable_name }} 调用变量,且变量名前后建议加空格,有时用”{{ variable_name }}”才生效

变量来源

  • ansible 的 setup facts 远程主机的所有变量都可直接调用
  • 通过命令行指定变量,优先级最高
ansible-playbook -e varname=value test.yml
  • 在playbook文件中定义
vars:
 var1: value1
 var2: value2
  • 在独立的变量YAML文件中定义
- hosts: all
  vars_files:
    - vars.yml
  • 在主机清单文件中定义
主机(普通)变量:主机组中主机单独定义,优先级高于公共变量
组(公共)变量:针对主机组中所有主机定义统一变量
  • 在项目中针对主机和主机组定义
在项目目录中创建 host_vars和group_vars目录
  • 在role中定义
变量的优先级从高到低如下
-e 选项定义变量 -->playbook中vars_files --> playbook中vars变量定义 -->host_vars/主机名文件 -->主机清单中主机变量--> group_vars/主机组名文件-->group_vars/all文件--> 主机清单组变量

4.1、使用 setup 模块中变量

使用 facts 变量

本模块自动在playbook调用,生成的系统状态信息, 并将之存放在facts变量中
facts 包括的信息很多,如: 主机名,IP,CPU,内存,网卡等

facts 变量的实际使用场景案例

  • 通过facts变量获取被控端CPU的个数信息,从而生成不同的Nginx配置文件
  • 通过facts变量获取被控端内存大小信息,从而生成不同的memcached的配置文件
  • 通过facts变量获取被控端主机名称信息,从而生成不同的Zabbix配置文件
  • 通过facts变量获取被控端网卡信息,从而生成不同的主机名
# 范例
[root@ansible ansible]# ansible localhost -m setup -a 'filter="ansible_default_ipv4"'
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_default_ipv4": {
            "address": "192.168.1.11",
            "alias": "eth0",
            "broadcast": "192.168.1.255",
            "gateway": "192.168.1.2",
            "interface": "eth0",
            "macaddress": "00:0c:29:33:e3:25",
            "mtu": 1500,
            "netmask": "255.255.255.0",
            "network": "192.168.1.0",
            "type": "ether"
        }
    },
    "changed": false
}

[root@ansible ansible]# ansible localhost -m setup -a "filter=ansible_nodename"
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_nodename": "ansible"
    },
    "changed": false
}
# 范例
# 用户和目录要提前存在
[root@ansible ansible]# vim var.yml

---

- hosts: all
  remote_user: root
  gather_facts: yes

  tasks:
    - name: create log file
      file:
        name: /data/{{ ansible_nodename }}.log
        state: touch
        owner: devop
        mode: 600
# 显示 eth0 网卡的 IP 地址
[root@ansible ansible]# vim show_ip.yml

---

- hosts: all

  tasks:
    - name: show eht0 ip address
      debug:
        msg: IP address {{ ansible_eth0.ipv4.address }}
        #msg: IP address {{ ansible_facts["eth0"]["ipv4"]["address"] }}
        #msg: IP address {{ ansible_facts.eth0.ipv4.address }}
        #msg: IP address {{ ansible_default_ipv4.address }}
        #msg: IP address {{ ansible_eth0.ipv4.address }}
        #msg: IP address {{ ansible_eth0.ipv4.address.split('.')[-1] }}#取IP中的
最后一个数字

[root@ansible ansible]# ansible-playbook show_ip.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [all] ************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************
ok: [192.168.1.12]
ok: [192.168.1.21]
ok: [192.168.1.13]
ok: [192.168.1.11]

TASK [show eht0 ip address] ******************************************************************************************
ok: [192.168.1.13] => {
    "msg": "IP address 13"
}
ok: [192.168.1.12] => {
    "msg": "IP address 12"
}
ok: [192.168.1.11] => {
    "msg": "IP address 11"
}
ok: [192.168.1.21] => {
    "msg": "IP address 21"
}

PLAY RECAP ************************************************************************************************************************
192.168.1.11               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
192.168.1.12               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
192.168.1.13               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
192.168.1.21               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
# 修改主机名形式为 web_随机数
[root@ansible ansible]# vim hostname.yml

---

- hosts: webservers

  tasks:
    - name: 定义一个随机数,设定为变量,然后后续调用
      shell: echo $((RANDOM%255))
      register: web_number
    - name: 使用debug输出变量结果
      debug:
        msg: "{{ web_number }}"
    - name: 使用hostname模块将主机名修改为web_随机数
      hostname:
        name: web_{{ web_number.stdout }}

性能优化

每次执行playbook,默认会收集每个主机的所有facts变量,将会导致速度很慢,可以采用下面方法加速

  • 关闭facts采集加速执行,此方法将导致无法使用facts变量
- hosts: all
  gather_facts: no
  • 当使用 gather_facts: no 关闭 facts,确实能加速 Ansible 执行,但是有时候又需要使用 facts 中的内容,还希望执行的速度快,这时候可以设置facts 的缓存,将facts变量信息存在redis服务器中
[root@ansible ~]# cat /etc/ansible/ansible.cfg
[defaults]
# smart 表示默认收集 facts,但 facts 已有的情况下不会收集,即使用缓存 facts
# implicit 表示默认收集 facts,要禁止收集,必须使用 gather_facts: False
# explicit 则表示默认不收集,要显式收集,必须使用gather_facts: True

gathering = smart              #在使用 facts 缓存时设置为smart
fact_caching_timeout = 86400   #缓存时长
fact_caching = redis           #缓存存在redis中
fact_caching_connection = 192.168.1.41:6379:0  #0表示redis的0号数据库
#若redis设置了密码
fact_caching_connection = 192.168.1.41:6379:0:password

4.2、在playbook 命令行中定义变量

vim var2.yml

---
- hosts: websrvs
  remote_user: root
  tasks:
    - name: install package
      yum: name={{ pkname }} state=present

[root@ansible ~]#ansible-playbook -e pkname=httpd var2.yml
# 可以将多个变量放在一个文件中
[root@ansible ~]#cat vars
pkname1: memcached
pkname2: vsftpd

[root@ansible ~]#vim var2.yml
---
- hosts: websrvs
  remote_user: root
  tasks:
    - name: install package {{ pkname1 }
      yum: name={{ pkname1 }} state=present
    - name: install package {{ pkname2 }
      yum: name={{ pkname2 }} state=present

[root@ansible ~]#ansible-playbook -e pkname1=memcached -e pkname2=httpd 
var2.yml
[root@ansible ~]#ansible-playbook -e '@vars' var2.yml

4.3、在playbook文件中定义变量

此方式定义的是私有变量,即只能在当前playbook中使用,不能被其它Playbook共用

[root@ansible ~]#vim var3.yml
---
- hosts: websrvs
  remote_user: root
  vars:
    username: user1
    groupname: group1

tasks:
  - name: create group {{ groupname }}
    group: name={{ groupname }} state=present
  - name: create user {{ username }}
    user: name={{ username }} group={{ groupname }} state=present

[root@ansible ~]#ansible-playbook -e "username=user2 groupname=group2" var3.yml
# 变量的相互调用
[root@ansible ~]#cat var4.yaml
---
- hosts: websrvs
  remote_user: root
  vars:
    collect_info: "/data/test/{{ansible_default_ipv4['address']}}/"

  tasks:
    - name: create IP directory
      file: name="{{collect_info}}" state=directory

# 执行结果
tree /data/test/
/data/test/
└── 192.168.1.12

└── 192.168.1.13
1 directory, 0 files
# 安装多个包
[root@ansible ~]#cat install.yml
- hosts: websrvs
  vars:
    web: httpd
    db: mariadb-server

  tasks:
    - name: install {{ web }} {{ db }}
      yum:
        name:
          - "{{ web }}"
          - "{{ db }}"
        state: latest

4.4、使用专用的公共的变量文件

可以在一个独立的playbook文件中定义公共变量,在其它的playbook文件中可以引用变量文件中的变量
此方式比playbook中定义的变量优化级高

vim vars.yml
---
# variables file
package_name: mariadb-server
service_name: mariadb


vim var5.yml
---
#install package and start service
- hosts: dbsrvs
  remote_user: root
  vars_files:
    - vars.yml

  tasks:
    - name: install package
      yum: name={{ package_name }}
      tags: install
    - name: start service
      service: name={{ service_name }} state=started enabled=yes

4.5、在主机清单中定义主机和主机组的变量

所有项目的主机变量

在inventory 主机清单文件中为指定的主机定义变量以便于在playbook中使用

[websrvs]
www1.test.org http_port=80 maxRequestsPerChild=808
www2.test.org http_port=8080 maxRequestsPerChild=909

所有项目的组(公共)变量

在inventory 主机清单文件中赋予给指定组内所有主机上的在playbook中可用的变量,如果和主机变是同名,优先级低于主机变量

# 范例
[websrvs:vars]
http_port=80
ntp_server=ntp.wang.org
nfs_server=nfs.wang.org

[all:vars]
# --------- Main Variables ---------------
# Cluster container-runtime supported: docker, containerd
CONTAINER_RUNTIME="docker"

# Network plugins supported: calico, flannel, kube-router, cilium, kube-ovn
CLUSTER_NETWORK="calico"

# Service proxy mode of kube-proxy: 'iptables' or 'ipvs'
PROXY_MODE="ipvs"

# K8S Service CIDR, not overlap with node(host) networking
SERVICE_CIDR="192.168.0.0/16"

# Cluster CIDR (Pod CIDR), not overlap with node(host) networking
CLUSTER_CIDR="172.16.0.0/16"

# NodePort Range
NODE_PORT_RANGE="20000-60000"

# Cluster DNS Domain
CLUSTER_DNS_DOMAIN="magedu.local."
[root@ansible ~]#vim /etc/ansible/hosts
[websrvs]
192.168.1.12 hname=www1 domain=test.io
192.168.1.13 hname=www2

[websrvs:vars]
mark="-"

[all:vars]
domain=wang.org

[root@ansible ~]#ansible websrvs -m hostname -a 'name={{ hname }}{{ mark }}{{ domain }}'

# 命令行指定变量:
[root@ansible ~]#ansible websrvs -e domain=magedu.cn -m hostname -a 'name={{ hname }}{{ mark }}{{ domain }}'

4.6、针对当前项目的主机和主机组的变量

上面的方式是针对所有项目都有效,而官方更建议的方式是使用ansible特定项目的主机变量和组变量
生产建议在每个项目对应的目录中创建额外的两个变量目录,分别是host_vars和group_vars

  • host_vars下面的文件名和主机清单主机名一致,针对单个主机进行变量定义,格式:host_vars/hostname
  • group_vars下面的文件名和主机清单中组名一致, 针对单个组进行变量定义,格式:gorup_vars/groupname
  • group_vars/all文件内定义的变量对所有组都有效

4.7、register 注册变量

在playbook中可以使用register将捕获命令的输出保存在临时变量中,方便后续调用此变量,比如可以使用debug模块进行显示输出

# 利用debug 模块输出变量
[root@ansible ~]#cat register1.yml
---
- hosts: dbsrvs
  tasks:
    - name: get variable
      shell: hostname
      register: name
    - name: "print variable"
      debug:
        msg: "{{ name }}"                  #输出register注册的name变量的全部信息,注意变量要加" "引起来
        #msg: "{{ name.cmd }}"            #显示命令
        #msg: "{{ name.rc }}"             #显示命令成功与否
        #msg: "{{ name.stdout }}"         #显示命令的输出结果为字符串形式,所有结果都放在一行里显示,适合于结果是单行输出
        #msg: "{{ name.stdout_lines }}"   #显示命令的输出结果为列表形式,逐行标准输出,适用于多行显示
        #msg: "{{ name['stdout_lines'] }}" #显示命令的执行结果为列表形式,和效果上面相同
        #msg: "{{ name.stdout_lines[0] }}" #显示命令的输出结果的列表中的第一个元素

说明

第一个 task 中,使用了 register 注册变量名为 name ;当 shell 模块执行完毕后,会将数据放到该变量中。
第二个 task 中,使用了 debug 模块,并从变量name中获取数据。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享