【02】Ansible 自动化配置进阶

一、Ansible Playbook

1、Playbook 书写格式

1、yml语法

1)缩进:每个缩进有2个空格组成(ansible没有那么严格有缩进即可,saltstack必须两个空格)

2)冒号:所有冒号后面必须有空格,以冒号结尾除外。

3)短横线:表示列表项,使用一个短横杠加一个空格。福讴歌想使用通用的缩进级别作为同一列表。

-----------------------------------------------------------------------------------------

2、官方推荐写法

[[email protected] project]# cat p1.yml
---
#play
- hosts: webservers

#tasks  
  tasks:
    - name: Installed Httpd Server
      yum:
        name: httpd
        state: present

    - name: Start Httpd Server
      systemd:
         name: httpd
         state: started
         enabled: yes
         
-----------------------------------------------------------------------------------------
3、多paly语法示例

[[email protected] project]# cat p1.yml
#play
- hosts: webservers

  tasks:
    - name: Installed Httpd Server
      yum: name=httpd state=present

    - name: Start Httpd Server   
      systemd: name=httpd state=started enabled=yes

    - name: Start Firewalld Server
      systemd: name=firewalld state=started enabled=yes

    - name: Configure Firewalld Server
      firewalld: service=http immediate=yes permanent=yes state=enabled

- hosts: web01
  tasks:
    - name: Configure web01 Website
      copy: content='This is Web01' dest=/var/www/html/index.html

- hosts: web02
  tasks:
    - name: Cofnigure web02 weisite
      copy: content='This is Web02' dest=/var/www/html/index.html
      
-----------------------------------------------------------------------------------------          
4、检查语法,只检查是否是yml语法格式,并不做逻辑校验。
[[email protected] project]# ansible-playbook --syntax-check p1.yml

playbook: p1.yml

5、模拟执行(不是真的执行)
[[email protected] project]# ansible-playbook -C  p1.yml 

6、真实的描述状态(被控端的状态必须与控制端描述的状态一致)
[[email protected] project]# ansible-playbook p1.yml 

2、Playbook 实践练习

1)Playbook-NFS

思路:
1)安装
2)配置(用户,/data,)
3)启动
4)firewalld

----------------------------------------------------------------------------------------- 
1、编写源文件
[[email protected] project]# cat exports.j2 
/data 172.16.1.0/24(rw,sync,all_squash,anonuid=666,anongid=666)

2、编写nfs的playbook
[[email protected] project]# cat nfs.yml 
###NFS###

- hosts: web01

  tasks:
    - name: Install NFS-utils Server
      yum: name=nfs-utils state=present

    - name: Configure NFS-utils Server
      copy: src=./exports.j2 dest=/etc/exports owner=root group=root mode=0644
      notify: Restart NFS Server
    
    - name: Create NFS Group
      group: name=www gid=666

    - name: Create NFS User
      user: name=www uid=666 group=www create_home=no shell=/sbin/nologin

    - name: Create Data Directory
      file: path=/data state=directory owner=www group=www mode=0755 recurse=yes

    - name: Start NFS Server
      systemd: name=nfs state=started enabled=yes

  handlers:
    - name: Restart NFS Server
      systemd: name=nfs state=restarted

- hosts: web02
  tasks:
    - name: Mount NFS Server
      mount: path=/opt src=172.16.1.7:/data fstype=nfs opts=defaults state=mounted

2)Playbook-LAMP

思路:
1)使用yum安装 httpd、php、php-mysql、mariadb、firewalld等
2)启动httpd、firewalld、mariadb等服务
3)添加防火墙规则,放行http的流量,并永久生效
4)使用get_url下载 http://fj.xuliangwei.com/public/index.php 文件
-----------------------------------------------------------------------------------------
1、编写nfs的playbook
[[email protected] project]# cat lamp.yml 
- hosts: webservers
- hosts: otherservers
  
  tasks:
    - name: Installed Web Packages
      yum: name=httpd,mariadb-server,php,php-mysql,php-pdo state=present

    - name: Start Web Serivce
      service: name=httpd state=started

    - name: Start Mariadb Service
      service: name=mariadb state=started

    - name: Get Wordpress
      unarchive: src=./wordpress-5.0.3-zh_CN.tar.gz dest=/var/www/html/ copy=yes mode=0755   #解压模块


  #  - name: Copy Index.php
  #    copy: src=./index.php.j2 dest=/var/www/html/index.php


  #  - name: Get Url index.php
  #    get_url: url="http://fj.xuliangwei.com/public/index.php" dest=/var/www/html/index.php

二、Ansible 变量解析

1、定义变量

  • 在playbook的开头通过vars进行定义

  • 在playbook中使用var_files指定文件作为变量文件(其他playbook可以调用)

  • 在inventory主机组中定义变量

  • 在项目目录中创建hosts_vars 和 group_vars

  • 在命令行使用--extra-vars或-e设定变量

1)vars

#安装两个软件包使用变量方式
[[email protected] project]# cat p2.yml 
- hosts: webservers
  vars:
    - web_package: httpd
    - ftp_package: vsftpd
  tasks:
    - name: Installed Packages
      yum: 
        name: 
          - "{{ web_package }}"
          - "{{ ftp_package }}"
        state: present

2)vars_files

[[email protected] project]# cat vars.yml   #变量文件
web_package: httpd
ftp_package: vsftpd

[[email protected] project1]# cat p2.yml 
- hosts: webservers
  vars_files: ./vars.yml
  tasks:
    - name: Installed Packages
      yum: 
        name: 
          - "{{ web_package }}"
          - "{{ ftp_package }}"
        state: present

3)inventory 定义变量

#主机变量优先级高于主机组变量(不推荐,容易将环境弄的特别乱)
[[email protected] project]# vim /etc/ansible/hosts 
[webservers]
web01 ansible_ssh_host=172.16.1.7
web02 ansible_ssh_host=172.16.1.8
[webservers:vars]
file_name=group_vars

[[email protected] project]# cat p3.yml 
- hosts: webservers
  tasks:
    - name: Create File
      file: path=/tmp/{{ file_name }} state=touch

4)hosts_vars 和 group_vars

1)更好的方式是在ansible的项目目录中创建额外的两个变量目录,分别是hosts_vars和group_vars
[[email protected] project]# mkdir hosts_vars
[[email protected] project]# mkdir group_vars

2)group_vars目录下必须存放和inventory清单文件中定义的组名一致,如下:
[[email protected] project]# cat /etc/ansible/hosts 
[webservers]
web01 ansible_ssh_host=172.16.1.7
web02 ansible_ssh_host=172.16.1.8

3)定义组变量
[[email protected] project]# cat group_vars/webservers 
web_package: httpd
ftp_package: vsftpd

4)组变量生效
[[email protected] project]#  cat p4.yml 
- hosts: webservers
  tasks:
    - name: Installed Packages
      yum: 
        name: 
          - "{{ web_package }}"
          - "{{ ftp_package }}"
        state: present

[[email protected] project]# ansible-playbook p4.yml 

5)系统提供了特殊的组,all,也就说在group_vars目录下创建一个all文件,定义变量对所有的主机都生效
[[email protected] project]# cat group_vars/all
web_package: httpd
ftp_package: vsftpd

6)创建hosts_vars变量
[[email protected] project1]# cat host_vars/web01 
web_package: zlib-static
ftp_package: zmap

[[email protected] project1]# cat group_vars/webservers 
web_package: httpd
ftp_package: vsftpd

7)结论:主机变量优先于主机组变量,不常用。

5)命令行定义变量

1)通过命令行覆盖变量,inventory的变量会被playbook文件中覆盖,这两种方式的变量都会被命令行直接指定变量所覆盖。
使用--extra-vars或-e设定变量。

[[email protected] project1]# ansible-playbook p4.yml -e "web_package=zarafa-devel" -e "ftp_package=zarafa-utils"

6)变量的优先级

命令行定义变量 > play文件定义的变量 > inventory文件定义的变量

【02】Ansible 自动化配置进阶

2、变量矩阵

变量也支持层级定义,使用"."可能会有问题,建议使用"[]"代替。

lamp.web.web_package

lamp.web.db_package

[[email protected] project]# cat vars1.yml 
rainbow:
  web:
    web_package: httpd
    db_package: mariadb

code:
  web:
    filename: code_web_filename

	
[[email protected] project1]# cat p8.yml 
- hosts: webservers
  vars_files: ./vars1.yml
  tasks:
    - name: Install Package
      yum: name= "{{ rainbow['web']['web_package'] }}"

    - name: create filename
      file: 
        path: /tmp/{{ code.web.filename }}
        state: touch

3、变量注册

1) register变量注册,通过debug显示输出,msg取值
[[email protected] project]# cat p9.yml 
- hosts: webservers
  tasks:
    - name: Get Network Port Status
      shell: netstat -lntp
      register: net_port

    - name: OutPut Network Port Status
      debug:
        msg: "{{ net_port.stdout_lines }}"     

4、Facts变量

1)基本概述

Ansible facts 是被管理主机上通过ansible自动采集发现的变量。

facts包含每台特定的主机信息:被控端主机的主机名,IP地址,系统版本,CPU数量,内存状态,磁盘状态等。

2)应用场景

  • 通过fact检查CPU,来生成对应的nginx配置文件

  • 通过facts检查主机名信息,来生成不同的zabbix配置文件

  • 通过facts检索的内存情况来自定义mysql的配置文件

3)变量获取

1)获取web01的facts变量信息
[[email protected] ~]# ansible web01 -m setup > /tmp/facts.txt

2)获取facts的变量,需要使用filter进行过滤
[[email protected] ~]# ansible web01 -m setup -a "filter='mb'"
[[email protected] ~]# ansible web01 -m setup|grep 172.16.1.7    #建议使用grep过滤

4)实践练习

1)facts基本用法:比如获取被控端的主机名与IP地址
[[email protected] ~]# cat facts.yml
- hosts: webservers
  tasks:
    - name: Output variables ansible facts
      debug:
        msg: >
         this default IPv4 address "{{ ansible_fgdn }}" is "{{ ansible_default_ipv4.
address}}"

-----------------------------------------------------------------------------------------
2)利用facts变量采集生成zabbix配置文件

[[email protected] project]# cat zabbix_agentd.conf
Server={{ zabbix_server }}
ServerActive{{ zabbix_server }}
Hostname={{ ansible_hostname }}   #facts变量获取

[[email protected] project]# cat p10.yml 
- hosts: webservers
  #gather_facts: no   关闭facts采集
  vars: 
    - zabbix_server: 172.16.1.71
  tasks:
    - name: Copy Zabbix Agent Configure
      template: src=./zabbix_agentd.conf dest=/tmp/zabbix_agent.conf

#facts变量默认采集
#解析变量必须使用template,copy模块不支持拷贝变量。

-----------------------------------------------------------------------------------------
2)playbook安装一个memcached

#1.安装确认memcached
[[email protected] ~]# yum install memcached -y
[[email protected] ~]# rpm -qc memcached
/etc/sysconfig/memcached
[[email protected] ~]# mv /etc/sysconfig/memcached /project/memcached.j2 

#2.修改配置文件的参数为facts变量
[[email protected] project]# cat memcached.j2 
PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="{{ ansible_memtotal_mb //2 }}"
OPTIONS=""

#3.查找memcached的facts变量
[[email protected] ~]# ansible web01 -m setup -a "filter='mb'"   

#4.编写playbook文件
[[email protected] project]# cat mem.yml 
- hosts: webservers
  tasks:
    - name: Installed Memcached
      yum: name=memcached state=present

    - name: Configure Memcached
      template: src=./memcached.j2 dest=/etc/sysconfig/memcached

    - name: Start Memcached
      service: name=memcached state=started enabled=yes

三、Ansible Task控制

1、Playbook 条件语句

when

实践案例一:根据不同操作系统,安装相同的软件包

#Centos:httpd
#Ubuntu:httpd2
	
[[email protected] project]# cat when.yml 
- hosts: webservers
  tasks:

    - name: Install httpd Server
      yum: name=httpd state=present
      when: ansible_distribution == "CentOS"

    - name: Install httpd Server
      apt: name=httpd2 state=present
      when: ansible_distribution == "Ubuntu"

实践案例二:所有为web主机名的添加nginx仓库,其余的都跳过添加

1.如何添加yum仓库

2.如何判断,判断什么内

---
- hosts: all
  tasks:
    - name: Add Nginx Repos
      yum_repository:
        name: nginx_test
        description: Nginx YUM repo
        baseurl: http://nginx.org/packages/centos/7/$basearch/
        gpgcheck: no
      when: (ansible_hostname is match ("web*")) or (ansible_hostname is match ("lb*"))

实践案例三:通过register将命令执行结果保存至变量,然后通过when语句进行判断

- hosts: webservers
  tasks:
    - name: Check Httpd Server
      command: systemctl is-active httpd
      ignore_errors: yes
      register: check_httpd

    #- name: debug outprint			#仅仅只是输出结果
    #  debug: var=check_httpd

    - name: Httpd Restart
      service: name=httpd state=restarted
      when: check_httpd.rc == 0

2、Playbook 循环语句

{{ item }} 循环变量

with_items:

实践案例一:使用循环启动多个服务

[[email protected] project]# cat with.yml 
- hosts: webservers
  tasks:
    - name: Start httpd mariadb
      systemd: name={{ item }} state=started
      with_items:
        - httpd
        - mariadb

实践案例二:使用定义变量方式循环安装软件包

- hosts: webservers
  tasks:
    - name: ensure a list of packages installed
      yum: name= "{{ packages }}" state=present
      vars:
        packages:
         - httpd
         - httpd-tools

#弃用的方式(会弹出警告)#

- hosts: webservers
  tasks:
    - name: ensure a list of packages installed
      yum: name= "{{ item }}" state=present
      with_items:
        - httpd
        - httpd-tools

实践案例三:使用字典循环方式创建用户和批量拷贝文件

[[email protected] project]# cat loop-user.yml
- hosts: webservers
  tasks:
    - name: Add Users
      user: name={{ item.name }} groups={{ item.groups }} state=present
      with_items:
        - { name: 'testuser1', groups: 'bin' }
        - { name: 'testuser2', groups: 'root' }
		

[[email protected] project]# cat with4.yml 
- hosts: webservers
  tasks:
    - name: Copy Rsync configure and Rsync passwd
      copy: src={{ item.src }} dest={{ item.dest }} mode={{ item.mode }}
      with_items:
        - { src: "./rsyncd.conf", dest: "/etc/rsyncd.conf", mode: "0644" }
        - { src: "./rsync.passwd", dest: "/tmp/rsync.passwd", mode: "0600" }

3、Playbook Handlers

notify

handlers

[[email protected] project]# cat han.yml 
- hosts: webservers
  vars:
    - http_port: 8083
  tasks:

    - name: Install Http Server
      yum: name=httpd state=present

    - name: Configure httpd server
      template: src=./httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
      notify: 
        - Restart Httpd Server
        - Restart PHP Server

    - name: Start httpd server
      service: name=httpd state=started enabled=yes

  handlers:
    - name: Restart Httpd Server
      systemd: name=httpd state=restarted 

    - name: Restart PHP Server
      systemd: name=php-fpm state=restarted

handlers注意事项

1.无论多少个task通知了相同的 handlers,handlers 仅会在所有 tasks 结束后运行一次。

2.只有task发生改变了才会通知 handlers,没有改变则不会触发 handlers

3.不能使用 handlers替代 tasks

 4、Playbook 任务标签

tags标记(用于调试的场景下)

-t 执行指定的tag标签任务

--skip-tags:执行--skip-tags之外的标签任务

[[email protected] project]# cat tag.yml 
- hosts: webservers
  vars:
    - http_port: 8083
  tasks:

    - name: Install Http Server
      yum: name=httpd state=present
      tags: 
        - install_httpd
        - httpd_server

    - name: configure httpd server
      template: src=./httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
      notify: Restart Httpd Server
      tags: 
        - confiure_httpd
        - httpd_server

    - name: start httpd server
      service: name=httpd state=started enabled=yes
      tags: service_httpd

  handlers:
    - name: Restart Httpd Server
      systemd: name=httpd state=restarted 

[[email protected] project]# ansible-playbook tag.yml --list-tags
[[email protected] project]# ansible-playbook tag.yml -t httpd_server
[[email protected] project]# ansible-playbook tag.yml -t install_httpd,confiure_httpd
[[email protected] project]# ansible-playbook tag.yml --skip-tags httpd_server

5、Playbook 文件复用

include文件复用

Include包含
include(import_playbook)老版
include_tasks 新版

[[email protected] project]# cat task.yml 
- hosts: webservers
  vars:
    - http_port: 801

  tasks:
    - include_tasks: task_install.yml
    - include_tasks: task_configure.yml
    - include_tasks: task_start.yml

  handlers:
    - name: Restart Httpd Server
      systemd: name=httpd state=restarted

[[email protected] project]# cat task_install.yml 
- name: Install Http Server
  yum: name=httpd state=present

[[email protected] project]# cat task_configure.yml 
- name: configure httpd server
  template: src=./httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
  notify: Restart Httpd Server

[[email protected] project]# cat task_start.yml 
- name: start httpd server
  service: name=httpd state=started enabled=yes

6、Playbook 忽略错误

忽略错误 ignore_errors

[[email protected] project]# cat f9.yml
---
- hosts: webservers
  tasks:
    - name: Ignore False
      command: /bin/false
      ignore_errors: yes
	  
    - name: touch new file
      file: path=/tmp/bgx_ignore state=touch


异常处理
force_handlers: yes		#强制调用handlers
changed_when: false		#被管理主机没有发生变化,抑制改变的状态的为ok(获取系统的信息状态时使用)
                        #检查服务的配置是否正常,正常则不处理,不正常则中断
changed_when: httpd_check.stdout.find('OK')	  #查看变量中的某个字符串

[[email protected] project]# cat changed_when.yml 
- hosts: webservers
  vars:
    - http_port: 8083
  tasks:

    - name: configure httpd server
      template: src=./httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
      notify: Restart Httpd Server

    - name: Check HTTPD
      shell: /usr/sbin/httpd -t
      register: httpd_check
      changed_when: 
	    - httpd_check.stdout.find('OK')
		- false

    - name: start httpd server
      service: name=httpd state=started enabled=yes

  handlers:
    - name: Restart Httpd Server
      systemd: name=httpd state=restarted 

7、Playbook 错误处理

failed_when

命令不依赖返回状态码来判定是否执行失败,而是要查看命令返回内容来决定,比如返回内容中包括 failed 字符串,则判定为失败。示例如下:

- name: this command prints FAILED when it fails 
  command: /usr/bin/example-command -x -y -z 
  register: command_result 
  failed_when: "'FAILED' in command_result.stderr"

8、Playbook Tasks总结

#tasks任务处理相关流程与控制参数
	when 			判断
	item 			循环
	handlers		触发器(需要task使用notify通知)
	tags			标签(调试使用)
	include_tasks	包含task任务
    ignore_errors   忽略错误
	
#错误处理
	force_handlers  #扩展
	changed_when	false   抑制改变的状态为ok(获取系统的状态信息时)
		                    重要(检查服务的配置是否正常,正常则不处理,不正常则中断)
	register: httpd_check
	changed_when: 
	        - httpd_check.stdout.find('OK')
	        - false