1、ansible

ansible的配置文件

/etc/ansible/ansible.cfg     主配置文件,配置ansible工作特性,也可以在项目的目录中创建此文件,当前目录下如果也有ansible.cfg,则此文件优先生效,建议每个项目目录下,创建独有的ansible.cfg文件
/etc/ansible/hosts           主机清单
/etc/ansible/roles/          存放角色的目录

1.1 Ansible 的默认配置文件/etc/ansible/ansible.cfg,其中大部分的配置内容无需进行修改

[defaults]
#inventory     = /etc/ansible/hosts 	#主机列表配置文件
#library = /usr/share/my_modules/ 		#库文件存放目录
#remote_tmp = $HOME/.ansible/tmp 		#临时py命令文件存放在远程主机目录
#local_tmp     = $HOME/.ansible/tmp 	#本机的临时命令执行目录
#forks         = 5   					#默认并发数
#sudo_user     = root 					#默认sudo 用户
#ask_sudo_pass = True 					#每次执行ansible命令是否询问ssh密码
#ask_pass     = True
#remote_port   = 22
#host_key_checking = False     			#检查对应服务器的host_key,建议取消此行注释,实现第一次连接自动信任目标主机
#log_path=/var/log/ansible.log 			#日志文件,建议启用
#module_name = command   				#默认模块,可以修改为shell模块

[privilege_escalation] 					#普通用户提权配置
#become=True
#become_method=sudo
#become_user=root
#become_ask_pass=False

1.2 inventory 主机清单文件

默认的inventory file为/etc/ansible/hosts inventory file可以有多个,且也可以通过Dynamic Inventory来动态生成

注意: 生产建议在每个项目目录下创建项目独立的hosts文件

官方文档:

https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html

Inventory 参数说明

ansible_ssh_host 				#将要连接的远程主机名.与你想要设定的主机的别名不同的话,可通过此变量设置.
ansible_ssh_port 				#ssh端口号.如果不是默认的端口号,通过此变量设置.这种可以使用 ip:端口 192.168.1.100:2222
ansible_ssh_user 				#默认的 ssh 用户名
ansible_ssh_pass 				#ssh 密码(这种方式并不安全,我们强烈建议使用 --ask-pass 或 SSH 密钥)
ansible_sudo_pass 				#sudo 密码(这种方式并不安全,我们强烈建议使用 --ask-sudo-pass)
ansible_sudo_exe (new in version 1.8)	 #sudo 命令路径(适用于1.8及以上版本)
ansible_connection 				#与主机的连接类型.比如:local, ssh 或者 paramiko. Ansible 1.2 以前默认使用 paramiko.1.2 以后默认使用 'smart','smart' 方式会根据是否支持 ControlPersist, 来判断'ssh' 方式是否可行
ansible_ssh_private_key_file	#ssh 使用的私钥文件.适用于有多个密钥,而你不想使用 SSH 代理的情况.
ansible_shell_type 				#目标系统的shell类型.默认情况下,命令的执行使用 'sh' 语法,可设置为 '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 的路径....

1.3 ansible相关工具

#查看帮助工具
ansible-doc [options] [module...]
#列出所有模块
ansible-doc -l  
#查看指定模块帮助用法
ansible-doc ping  
#查看指定模块帮助用法
ansible-doc -s  ping 

#ansible执行工具
ansible <host-pattern> [-m module_name] [-a args]

#剧本执行工具
ansible-playbook

#命令行工具连接 https://galaxy.ansible.com 
ansible-galaxy

#加密解密yml文件
ansible-vault [create|decrypt|edit|encrypt|rekey|view]

#交互执行命令,支持tab,ansible 2.0+新增
ansible-console

2、ansible常用模块

2.1 Command模块

功能:在远程主机执行命令,此为默认模块,可忽略-m选项 注意:次命令不支持$VARNAME < > | ; & 等,可能用shell模块实现 注意:模块不具备幂等性 范例:

[root@ansible ~]#ansible web -m command -a 'chdir=/etc cat centos-release'
10.0.0.133 | CHANGED | rc=0 >>
CentOS Linux release 8.3.2011
10.0.0.126 | CHANGED | rc=0 >>
CentOS Linux release 8.3.2011

[root@ansible ~]#ansible web -m command -a 'echo magedu |passwd --stdin wang'
10.0.0.133 | CHANGED | rc=0 >>
magedu |passwd --stdin wang
10.0.0.126 | CHANGED | rc=0 >>
magedu |passwd --stdin wang

2.2 shell模块

功能:和command相似,用shell执行命令,支持各种符号,比如:*,$, > 注意:此模块不具有幂等性 范例:

[root@ansible ~]#ansible web -m shell -a 'echo $HOSTNAME'
10.0.0.133 | CHANGED | rc=0 >>
client
10.0.0.126 | CHANGED | rc=0 >>
mycat
[root@ansible ~]#ansible web -m shell -a "echo $HOSTNAME"
10.0.0.133 | CHANGED | rc=0 >>
ansible
10.0.0.126 | CHANGED | rc=0 >>
ansible

[root@ansible ~]#ansible web -m shell -a 'echo Centos | passwd --stdin wang'
10.0.0.126 | CHANGED | rc=0 >>
Changing password for user wang.
passwd: all authentication tokens updated successfully.
10.0.0.133 | CHANGED | rc=0 >>
Changing password for user wang.
passwd: all authentication tokens updated successfully.
[root@ansible ~]#ansible web -m shell -a 'ls -l /etc/shadow'
10.0.0.133 | CHANGED | rc=0 >>
---------- 1 root root 806 Jul 21 09:33 /etc/shadow
10.0.0.126 | CHANGED | rc=0 >>
----------. 1 root root 785 Jul 21 09:33 /etc/shadow
[root@ansible ~]#ansible web -m shell -a 'echo hello >/data/hello.log'
10.0.0.133 | CHANGED | rc=0 >>

10.0.0.126 | CHANGED | rc=0 >>

[root@ansible ~]#ansible web -m shell -a 'cat /data/hello.log'
10.0.0.133 | CHANGED | rc=0 >>
hello
10.0.0.126 | CHANGED | rc=0 >>
hello

调用bash执行命令 类似 cat /tmp/test.md | awk -F'|' '{print $1,$2}' &> /tmp/example.txt 这些复杂命令,即使使用shell也可能会失败,解决办法:写到脚本时,copy到远程,执行,再把需要的结果拉回执行命令的机器 范例:将默认的command改为shell模块

[root@ansible ~]#vim /etc/ansible/ansible.cfg
#修改下面一行
module_name = shell

2.3 Script模块

功能:在远程主机上运行ansible服务器上的脚本(无需执行权限) 注意:此模块不具有幂等性 范例:

ansible web -m script -a /data/test.sh

2.4 Copy模块

功能:从ansible服务器主控端复制文件到远程主机 注意: src=file 如果是没指明路径,则为当前目录或当前目录下的files目录下的file文件 范例:

#如目标存在,默认覆盖,此处指定先备份
[root@ansible ~]#ansible web -m copy -a "src=/root/ssh_key.sh dest=/tmp/test.sh owner=wang mode=600 backup=yes"
#指定内容,直接生成目标文件 
[root@ansible ~]#ansible web -m copy -a "content='test line1\ntest line2\n' dest=/tmp/test.txt"
[root@ansible ~]#ansible web -a 'cat /tmp/test.txt'
10.0.0.133 | CHANGED | rc=0 >>
test line1
test line2
10.0.0.126 | CHANGED | rc=0 >>
test line1
test line2

#复制/etc目录自身,注意/etc/后面没有/
ansible web -m copy -a "src=/etc dest=/backup"
#复制/etc/下的文件,不包括/etc/目录自身,注意/etc/后面有/
ansible web -m copy -a "src=/etc/ dest=/backup"

2.5 Get_url模块

功能: 用于将文件从http、https或ftp下载到被管理机节点上 常用参数如下:

url        	     下载文件的URL,支持HTTP,HTTPS或FTP协议
dest       	     下载到目标路径(绝对路径),如果目标是一个目录,就用服务器上面文件的名称,如果目标设置了名称就用目标设置的名称
owner      	     指定属主
group      	     指定属组
mode       	     指定权限
force       	 如果yes,dest不是目录,将每次下载文件,如果内容改变,替换文件。如果否,则只有在目标不存在时才会下载该文件
checksum   	     对目标文件在下载后计算摘要,以确保其完整性
	示例: checksum="sha256:D98291AC[...]B6DC7B97",
	checksum="sha256:http://example.com/path/sha256sum.txt"
url_username     用于HTTP基本认证的用户名。 对于允许空密码的站点,此参数可以不使用`url_password'
url_password	 用于HTTP基本认证的密码。 如果未指定`url_username'参数,则不会使用`url_password'参数
validate_certs 	 如果“no”,SSL证书将不会被验证。 适用于自签名证书在私有网站上使用timeout: URL请求的超时时间,秒为单位

范例:

[root@ansible ~]#ansible web -m get_url -a 'url=http://nginx.org/download/nginx-1.18.0.tar.gz dest=/usr/local/src/nginx.tar.gz checksum="md5:b2d33d24d89b8b1f87ff5d251aa27eb8"'

2.6 Fetch模块

功能:从远程主机提取文件至ansible的主控端,copy相反,目前不支持目录 范例:

[root@ansible ~]#ansible web -m fetch -a 'src=/etc/redhat-release dest=/data/os'
[root@ansible ~]#tree /data/os/
/data/os/
├── 10.0.0.126
│   └── etc
│       └── redhat-release
└── 10.0.0.133
    └── etc
        └── redhat-release

4 directories, 2 files

2.7 File模块

功能:设置文件属性,创建软链接等 范例:

#创建文件
[root@ansible ~]#ansible all -m file -a 'path=/data/test.txt state=touch'
[root@ansible ~]#ansible all -m file -a 'path=/data/test.txt state=absent'
[root@ansible ~]#ansible all -m file -a 'path=/root/test.sh owner=wang mode=755 state=touch'
#创建目录
[root@ansible ~]#ansible all -m file -a "path=/data/mysql state=directory owner=mysql group=mysql"
#创建软链接,src在当前ansible要存在
[root@ansible ~]#ansible all -m file -a 'src=/data/testfile path=/data/testfile-link state=link'
#递归修改目录属性,但不递归至子目录
[root@ansible ~]#ansible all -m file -a "path=/data/mysql state=directory owner=mysql group=mysql"
#递归修改目录及子目录的属性
[root@ansible ~]#ansible all -m file -a "path=/data/mysql state=directory owner=mysql group=mysql recurse=yes"

2.8 stat模块

功能:检查文件或文件系统的状态 注意:对于Windows目标,请改用win_stat模块 选项:

path      文件/对象的完整路径(必须)

常用的返回值判断:

exists    判断是否存在
isuid     调用用户的ID与所有者ID是否匹配

范例:

[root@ansible ~]#ansible 127.0.0.1 -m stat -a 'path=/etc/passwd'
127.0.0.1 | SUCCESS => {
    "changed": false,
    "stat": {
        "atime": 1626783161.0525913,
        "attr_flags": "",
        "attributes": [],
        "block_size": 4096,
        "blocks": 8,
        "charset": "us-ascii",
        "checksum": "e4017c7406a7f0e7e52be462ba51fc0f6d4c3e78",
        "ctime": 1625395551.69196,
        "dev": 2049,
        "device_type": 0,
        "executable": false,
        "exists": true,
        "gid": 0,
        "gr_name": "root",
        "inode": 134699518,
        "isblk": false,
        "ischr": false,
        "isdir": false,
        "isfifo": false,
        "isgid": false,
        "islnk": false,
        "isreg": true,
        "issock": false,
        "isuid": false,
        "mimetype": "text/plain",
        "mode": "0644",
        "mtime": 1625395551.69196,
        "nlink": 1,
        "path": "/etc/passwd",
        "pw_name": "root",
        "readable": true,
        "rgrp": true,
        "roth": true,
        "rusr": true,
        "size": 1106,
        "uid": 0,
        "version": "2221273259",
        "wgrp": false,
        "woth": false,
        "writeable": true,
        "wusr": true,
        "xgrp": false,
        "xoth": false,
        "xusr": false
    }
}

经典案例:

[root@ansible ~]#cat stat.yml
- hosts: web
  tasks:
    - name: check file
      stat: path=/data/mysql
      register: st
    - name: debug
      debug: 
        msg: "/data/mysql is not exits"
      when: not st.stat.exits
[root@ansible ~]#ansible-playbool stat.yml

2.9 unarchive模块

功能:解包解压缩 实现有两种用法: 1、将ansible主机上的压缩包传到远程主机后解压缩至特定目录,设置copy=yes,此为默认值,可省略 2、将远程主机上的某个压缩包解压缩到指定路径下,设置copy=no 常见参数:

copy           默认为yes,当copy=yes,拷贝的文件是从ansible主机复制到远程主机上,如果设置为copy=no,会在远程主机上寻找src源文件
remote_src     和copy功能一样且互斥,yes表示在远程主机,不在ansible主机,no表示文件在ansible主机上
src            源路径,可以是ansible主机上的路径,也可以是远程主机(被管理端或者第三方主机)上的路径,如果是远程主机上的路径,则需要设置copy=no
dest           远程主机上的目标路径
mode           设置解压缩后的文件权限

范例:

[root@ansible ~]#ansible all -m unarchive -a 'src=/data/foo.tgz dest=/var/lib/foo owner=wang group=bin'
[root@ansible ~]#ansible all -m unarchive -a 'src=/tmp/foo.zip dest=/data copy=no mode=0777'
[root@ansible ~]#ansible all -m unarchive -a 'src=https://example.com/example.zip dest=/data copy=no'
[root@ansible ~]#ansible web -m unarchive -a 'src=https://releases.ansible.com/ansible/ansible-2.1.6.0-0.1.rc1.tar.gz dest=/data/   owner=root remote_src=yes'
[root@ansible ~]#ansible web -m unarchive -a 'src=http://nginx.org/download/nginx-1.18.0.tar.gz dest=/usr/local/src/ copy=no'

2.10 Archive 模块

功能:打包压缩保存在被管理节点 范例:

[root@ansible ~]#ansible web -m archive  -a 'path=/var/log/ dest=/data/log.tar.bz2 format=bz2 owner=wang mode=0600'

2.11 Hostname模块

功能:管理主机名 范例:

10.0.0.133 | CHANGED | rc=0 >>
client
10.0.0.126 | CHANGED | rc=0 >>
mycat
[root@ansible ~]#ansible 10.0.0.133  -m hostname -a "name=websrv"
[root@ansible ~]#ansible web -m shell -a 'echo $HOSTNAME'
10.0.0.133 | CHANGED | rc=0 >>
websrv
10.0.0.126 | CHANGED | rc=0 >>
mycat

2.12 Cron模块

功能:计划任务 支持时间:minute,hour,day,month,weekday 范例:

#备份数据库脚本
[root@ansible ~]#cat /root/mysql_backup.sh
#!/bin/bash
mysqldump -A -F --single-transaction --master-data=2 -q -uroot |gzip > /data/mysql_`date +%F_%T`.sql.gz

#创建任务
[root@ansible ~]#ansible 10.0.0.8 -m cron -a 'hour=2 minute=30 weekday=1-5 name="backup mysql" job=/root/mysql_backup.sh'
[root@ansible ~]#ansible web -m cron -a "minute=*/5 job='/usr/sbin/ntpdate ntp.aliyun.com &>/dev/null' name=Synctime"

#禁用计划任务
[root@ansible ~]#ansible web -m cron -a "minute=*/5 job='/usr/sbin/ntpdate 172.20.0.1 &>/dev/null' name=Synctime disabled=yes"

#启用计划任务
[root@ansible ~]#ansible web -m cron -a "minute=*/5 job='/usr/sbin/ntpdate 172.20.0.1 &>/dev/null' name=Synctime disabled=no"

#删除任务
ansible web -m cron -a "name='backup mysql' state=absent"
ansible web -m cron -a 'state=absent name=Synctime'

2.13 Yum和Apt模块

功能:yum 管理软件包,只支持RHEL,CentOS,fedora,不支持Ubuntu其它版本 apt 模块管理 Debian 相关版本的软件包 范例:

#安装
[root@ansible ~]#ansible web -m yum -a 'name=httpd state=present'
#启用epel源进行安装
[root@ansible ~]#ansible web -m yum -a 'name=nginx state=present enablerepo=epel'
#升级除kernel和foo开头以外的所有包
[root@ansible ~]#ansible websrvs -m yum -a 'name=* state=lastest exclude=kernel*,foo*'
#删除
[root@ansible ~]#ansible websrvs -m yum -a 'name=httpd state=absent'

范例:

[root@ansible ~]#ansible web -m yum -a "name=https://mirror.tuna.tsinghua.edu.cn/zabbix/zabbix/5.2/rhel/7/x86_64/zabbix-agent-5.2.5-1.el7.x86_64.rpm"
[root@ubuntu ~]#ansible web -m apt -a 'name=rsync,psmisc state=absent'

查看包

[root@ansible ~]#ansible localhost -m yum -a "list=tree"
localhost | SUCCESS => {
    "ansible_facts": {
        "pkg_mgr": "dnf"
    },
    "changed": false,
    "msg": "",
    "results": [
        {
            "arch": "x86_64",
            "epoch": "0",
            "name": "tree",
            "nevra": "0:tree-1.7.0-15.el8.x86_64",
            "release": "15.el8",
            "repo": "@System",
            "version": "1.7.0",
            "yumstate": "installed"
        },
        {
            "arch": "x86_64",
            "epoch": "0",
            "name": "tree",
            "nevra": "0:tree-1.7.0-15.el8.x86_64",
            "release": "15.el8",
            "repo": "base",
            "version": "1.7.0",
            "yumstate": "available"
        }
    ]
}

2.14 yum_repository模块

- name: Add multiple repositories into the same file (1/2)
  yum_repository:
    name: epel
    description: EPEL YUM repo
    file: external_repos
    baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/
    gpgcheck: nogpgcheck: no

- name: Add multiple repositories into the same file (2/2)
  yum_repository:
    name: rpmforge
    description: RPMforge YUM repo
    file: external_repos
    baseurl: http://apt.sw.be/redhat/el7/en/$basearch/rpmforge
    mirrorlist: http://mirrorlist.repoforge.org/el7/mirrors-rpmforge
    enabled: no

- name: Remove repository from a specific repo file
  yum_repository:
    name: epel
    file: external_repos
    state: absent

范例:创建和删除仓库

[root@ansible ~]#cat yum_repo.yml
- hosts: web
  tasks:
    - name: Add multiple repositories into the same file 
      yum_repository:
	name: test
	description: EPEL YUM repo
	file: external_repos
	baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/
	gpgcheck: no

[root@ansible ~]#cat remove_yum_repo.yml
- hosts: web
  tasks:
    - name: remove repo 
      yum_repository:
	name: test
	file: external_repos
	state: absent

2.15 Service模块

功能:管理服务 范例:

[root@ansible ~]#ansible all -m service -a "name=httpd state=started enabled=yes"
[root@ansible ~]#ansible all -m service -a 'name=httpd state=stopped'
[root@ansible ~]#ansible all -m service -a 'name=httpd state=reloaded'
[root@ansible ~]#ansible all -m shell -a "sed -i 's/^Listen 80/Listen 8080/' /etc/httpd/conf/httpd.conf"
[root@ansible ~]#ansible all -m service -a 'name=httpd state=restarted'

2.16 User模块

功能:管理用户 范例:

#创建用户
[root@ansible ~]#ansible all -m user -a 'name=user1 comment="test user" uid=2048 home=/app/user1 group=root'
[root@ansible ~]#ansible all -m user -a 'name=nginx comment=nginx uid=88 group=nginx groups="root,daemon" shell=/sbin/nologin system=yes create_home=no home=/data/nginx non_unique=yes'

#remove=yes表示删除用户及家目录等数据,默认remove=no
[root@ansible ~]#ansible all -m user -a 'name=nginx state=absent remove=yes'

#生成123456加密的密码

#创建用户test,并生成4096bit的私钥
[root@ansible ~]#ansible web -m user -a 'name=test generate_ssh_key=yes ssh_key_bits=4096 ssh_key_file=.ssh/id_rsa'

2.17 Group模块

功能:管理组 范例:

#创建组
[root@ansible ~]#ansible web -m group  -a 'name=nginx gid=88 system=yes'
#删除组
[root@ansible ~]#ansible web -m group -a "name=nginx state=absent"

2.18 Lineinfile模块

一般在ansible当中去修改某个文件的单行进行替换的时候需要使用lineinfile模块 如果想进行多行匹配进行替换需要使用replace模块 这两个都支持正则表达式 功能:相当于sed,可以修改文件内容 范例:

[root@ansible ~]#ansible web -m lineinfile -a "path=/etc/httpd/conf/httpd.conf regexp='^Listen' line='Listen 80'"
[root@ansible ~]#ansible all -m   lineinfile -a "path=/etc/selinux/config regexp='^SELINUX=' line='SELINUX=disabled'"
[root@ansible ~]#ansible all -m lineinfile  -a 'dest=/etc/fstab state=absent regexp="^#"'

2.19 Replace 模块

该模块有点类似于sed命令,主要也是基于正则进行匹配和替换,建议使用 范例:

[root@ansible ~]#ansible all -m replace -a "path=/etc/fstab regexp='^(UUID.*)' replace='#\1'"
[root@ansible ~]#ansible all -m replace -a "path=/etc/fstab regexp='^#(UUID.*)' replace='\1'"

2.20 SELinux模块

该模块管理 SELInux 策略 范例:

[root@ansible ~]#ansible 10.0.0.133 -m selinux -a 'state=disabled'

2.21 reboot模块

[root@ansible ~]#ansible web -m reboot

2.22 mount挂载和卸载

功能: 挂载和卸载文件系统 范例:

#临时挂载
[root@ansible ~]#mount websrvs -m mount -a 'src=UUID="bb4f92fa-6073-4f84-9266-6b87e79e6104" path=/home fstype=xfs opts=noatime state=present'

#临时取消挂载
[root@ansible ~]#mount web -m mount -a 'path=/home fstype=xfs opts=noatime state=unmounted'

#永久挂载
[root@ansible ~]#ansible web -m mount -a 'src=10.0.0.133:/data/wordpress path=/var/www/html/wp-content/uploads opts="_netdev" state=mounted'

#永久卸载
[root@ansible ~]#ansible web -m mount -a 'src=10.0.0.133:/data/wordpress path=/var/www/html/wp-content/uploads state=absent'

2.23 Setup模块

功能: setup 模块来收集主机的系统信息,这些 facts 信息可以直接以变量的形式使用,但是如果主机较多,会影响执行速度 可以使用gather_facts: no 来禁止 Ansible 收集 facts 信息 范例:

[root@ansible ~]#ansible all -m setup
[root@ansible ~]#ansible all -m setup -a "filter=ansible_nodename"
[root@ansible ~]#ansible all -m setup -a "filter=ansible_hostname"
[root@ansible ~]#ansible all -m setup -a "filter=ansible_domain"
[root@ansible ~]#ansible all -m setup -a "filter=ansible_memtotal_mb"
[root@ansible ~]#ansible all -m setup -a "filter=ansible_memory_mb"
[root@ansible ~]#ansible all -m setup -a "filter=ansible_memfree_mb"
[root@ansible ~]#ansible all -m setup -a "filter=ansible_os_family"
[root@ansible ~]#ansible all -m setup -a "filter=ansible_distribution_major_version"
[root@ansible ~]#ansible all -m setup -a "filter=ansible_distribution_version"
[root@ansible ~]#ansible all -m setup -a "filter=ansible_processor_vcpus"
[root@ansible ~]#ansible all -m setup -a "filter=ansible_all_ipv4_addresses"
[root@ansible ~]#ansible all -m setup -a "filter=ansible_architecture"
[root@ansible ~]#ansible all -m setup -a "filter=ansible_uptime_seconds"
[root@ansible ~]#ansible all -m setup -a "filter=ansible_processor*"
[root@ansible ~]#ansible all -m setup -a 'filter=ansible_env'

范例:

[root@ansible ~]#ansible all -m setup -a 'filter=ansible_python_version'
10.0.0.135 | SUCCESS => {
    "ansible_facts": {
        "ansible_python_version": "3.6.8",
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}
10.0.0.133 | SUCCESS => {
    "ansible_facts": {
        "ansible_python_version": "3.6.8",
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}
10.0.0.134 | SUCCESS => {
    "ansible_facts": {
        "ansible_python_version": "3.6.8",
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}
10.0.0.126 | SUCCESS => {
    "ansible_facts": {
        "ansible_python_version": "3.6.8",
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}

范例:取IP地址

#取所有IP
[root@ansible ~]#ansible 10.0.0.133 -m setup -a 'filter=ansible_all_ipv4_addresses'
10.0.0.133 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "10.0.0.133"
        ],
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}

#取默认IP
[root@ansible ~]#ansible all -m setup -a 'filter="ansible_default_ipv4"'
10.0.0.135 | SUCCESS => {
    "ansible_facts": {
        "ansible_default_ipv4": {
            "address": "10.0.0.135",
            "alias": "eth0",
            "broadcast": "10.0.0.255",
            "gateway": "10.0.0.2",
            "interface": "eth0",
            "macaddress": "00:0c:29:a7:b0:c1",
            "mtu": 1500,
            "netmask": "255.255.255.0",
            "network": "10.0.0.0",
            "type": "ether"
        },
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}
10.0.0.126 | SUCCESS => {
    "ansible_facts": {
        "ansible_default_ipv4": {
            "address": "10.0.0.126",
            "alias": "ens33",
            "broadcast": "10.0.0.255",
            "gateway": "10.0.0.2",
            "interface": "ens33",
            "macaddress": "00:0c:29:29:82:48",
            "mtu": 1500,
            "netmask": "255.255.255.0",
            "network": "10.0.0.0",
            "type": "ether"
        },
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}
10.0.0.134 | SUCCESS => {
    "ansible_facts": {
        "ansible_default_ipv4": {
            "address": "10.0.0.134",
            "alias": "eth0",
            "broadcast": "10.0.0.255",
            "gateway": "10.0.0.2",
            "interface": "eth0",
            "macaddress": "00:0c:29:8b:00:8c",
            "mtu": 1500,
            "netmask": "255.255.255.0",
            "network": "10.0.0.0",
            "type": "ether"
        },
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}
10.0.0.133 | SUCCESS => {
    "ansible_facts": {
        "ansible_default_ipv4": {
            "address": "10.0.0.133",
            "alias": "eth0",
            "broadcast": "10.0.0.255",
            "gateway": "10.0.0.2",
            "interface": "eth0",
            "macaddress": "00:0c:29:09:26:9f",
            "mtu": 1500,
            "netmask": "255.255.255.0",
            "network": "10.0.0.0",
            "type": "ether"
        },
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}

2.24 debug模块

此模块可以用于输出信息,并且通过 msg 定制输出的信息内容 注意: msg后面的变量有时需要加 " " 引起来 范例: debug 模块默认输出Hello world

[root@ansible ~]#ansible 10.0.0.133 -m debug
10.0.0.133 | SUCCESS => {
    "msg": "Hello world!"
}

[root@ansible ~]#cat debug.yml
- hosts: web
  
  tasks: 
    - name: output Hello world
      debug:
#默认没有指定msg,默认输出"Hello world!"
[root@ansible ~]#ansible-playbook debug.yml 

PLAY [web] ***************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************
ok: [10.0.0.126]
ok: [10.0.0.133]

TASK [output Hello world] ************************************************************************************************************
ok: [10.0.0.126] => {
    "msg": "Hello world!"
}
ok: [10.0.0.133] => {
    "msg": "Hello world!"
}

PLAY RECAP ***************************************************************************************************************************
10.0.0.126                 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
10.0.0.133                 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

范例:利用debug模块输出变量

[root@ansible ~]#cat debug.yml 
- hosts: web
  
  tasks: 
    - name: output variables
      debug:
        msg: Host "{{ ansible_nodename }}" Ip "{{ ansible_default_ipv4.address }}"
[root@ansible ~]#ansible-playbook debug.yml 

PLAY [web] ***************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************
ok: [10.0.0.133]
ok: [10.0.0.126]

TASK [output variables] **************************************************************************************************************
ok: [10.0.0.126] => {
    "msg": "Host \"mycat\" Ip \"10.0.0.126\""
}
ok: [10.0.0.133] => {
    "msg": "Host \"client\" Ip \"10.0.0.133\""
}

PLAY RECAP ***************************************************************************************************************************
10.0.0.126                 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
10.0.0.133                 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

范例:显示字符串特定字符

[root@ansible ~]#cat debug.yml 
- hosts: all
  gather_facts: no
  vars:
    a: "12345"
  
  tasks: 
  - debug:
      msg: "{{a[2]}}"
#定义了一个字符串变量a,如果想要获取a字符串的第3个字符,则可以使用”a[2]”获取,索引从0开始,执行上例playbook,debug的输出信息如下:
[root@ansible ~]#ansible-playbook debug.yml 

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

TASK [debug] *************************************************************************************************************************
ok: [10.0.0.134] => {
    "msg": "3"
}
ok: [10.0.0.126] => {
    "msg": "3"
}
ok: [10.0.0.133] => {
    "msg": "3"
}
ok: [10.0.0.135] => {
    "msg": "3"
}

PLAY RECAP ***************************************************************************************************************************
10.0.0.126                 : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
10.0.0.133                 : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
10.0.0.134                 : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
10.0.0.135                 : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

3、ansible-playbook

playbook核心组件

官方文档

https://docs.ansible.com/ansible/latest/reference_appendices/playbooks_keywords.html#playbook-keywords

一个playbook 中由多个组件组成,其中所用到的常见组件类型如下:

Hosts         执行的远程主机列表
Tasks         任务集,由多个task的元素组成的列表实现,每个task是一个字典,一个完整的代码块功能需最少元素需包括 name 和 task,一个name只能包括一个task
Variables     内置变量或自定义变量在playbook中调用
Templates     模板,可替换模板文件中的变量并实现一些简单逻辑的文件
Handlers      和 notify 结合使用,由特定条件触发的操作,满足条件方才执行,否则不执行
tags          标签 指定某条任务执行,用于选择运行playbook中的部分代码。ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地长。此时,如果确信其没有变化,就可以通过tags跳过此些代码片断

hosts 组件

Hosts:playbook中的每一个play的目的都是为了让特定主机以某个指定的用户身份执行任务。hosts用于指定要执行指定任务的主机,须事先定义在主机清单中

one.example.com
one.example.com:two.example.com
192.168.1.50
192.168.1.*
Websrvs:dbsrvs            #或者,两个组的并集
Websrvs:&dbsrvs           #与,两个组的交集
webservers:!dbsrvs        #在websrvs组,但不在dbsrvs组

案例:

- hosts: websrvs:appsrvs

remote_user 组件

remote_user: 可用于Host和task中。也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或某任务;此外,甚至可以在sudo时使用sudo_user指定sudo时切换的用户

- hosts: websrvs
  remote_user: root

  tasks:
    - name: test connection
      ping:
      remote_user: magedu
      sudo: yes                  #默认sudo为root
      sudo_user:wang             #sudo为wang

task列表和action组件

play的主体部分是task list,task list中有一个或多个task,各个task 按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个task后,再开始第二个task task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致 每个task都应该有其name,用于playbook的执行结果输出,建议其内容能清晰地描述任务执行步骤。如果未提供name,则action的结果将用于输出 task两种格式:

action: module arguments
module: arguments                #建议使用

注意:shell和command模块后面跟命令,而非key=value 范例:

- hosts: websrvs
  remote_user: root
  gather_facts: no

  tasks:
    - name: install httpd
      yum: name=httpd
    - name: start httpd
      service: name=httpd state=started enabled=yes

其它组件

某任务的状态在运行后为changed时,可通过"notify"通知给相应的handlers任务可以通过"tags"打标签,可在ansible-playbook命令上使用-t指定进行调用

Playbook中使用handlers和notify

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

- hosts: web
  remote_user: root
  gather_facts: no
  tasks:
    - name: Install httpd
      yum: name=httpd state=present
    - name: Install configure file
      copy: src=files/httpd.conf dest=/etc/httpd/conf/
      notify: restart httpd
    - name: ensure apache is running
      service: name=httpd state=started enabled=yes
  
  handlers:
    - name: restart httpd
      service: name=httpd state=restarted

Playbook中使用tags组件

在playbook文件中,可以利用tags组件,为特定 task 指定标签,当在执行playbook时,可以只执行特定tags的task,而非整个playbook文件 案例:

- hosts: websrvs
  remote_user: root
  gather_facts: no

  tasks:
    - name: Install httpd
      yum: name=httpd state=present
    - name: Install configure file
      copy: src=files/httpd.conf dest=/etc/httpd/conf/
      tags: conf
    - name: start httpd service
      tags: service
      service: name=httpd state=started enabled=yes

Playbook中使用变量

使用 setup 模块中变量

本模块自动在playbook调用,不要用ansible命令调用 案例:使用setup变量

[root@ansible ~]#ansible 10.0.0.126 -m setup -a 'filter="ansible_default_ipv4"'
10.0.0.126 | SUCCESS => {
    "ansible_facts": {
        "ansible_default_ipv4": {
            "address": "10.0.0.126",
            "alias": "ens33",
            "broadcast": "10.0.0.255",
            "gateway": "10.0.0.2",
            "interface": "ens33",
            "macaddress": "00:0c:29:29:82:48",
            "mtu": 1500,
            "netmask": "255.255.255.0",
            "network": "10.0.0.0",
            "type": "ether"
        },
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}

[root@ansible ~]#ansible 10.0.0.126 -m setup -a 'filter=ansible_nodename'
10.0.0.126 | SUCCESS => {
    "ansible_facts": {
        "ansible_nodename": "mycat",
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}

范例:

- hosts: all
  remote_user: root
  gather_facts: yes

  tasks:
    - name: create log file
      file: name=/data/{{ ansible_nodename }}.log state=touch owner=wang mode=600
在playbook 命令行中定义变量

范例:

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

[root@ansible ~]#ansible-playbook -e pkname=httpd var2.yml
在playbook文件中定义变量

范例:

- hosts: web
  remote_user: root

  vars:
    username: user1
    groupname: group1

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

[root@ansible ~]#ansible-playbook -e "username=user2 groupname=group2" var3.yml

范例:

- hosts: web
  remote_user: root
  vars:
    collect_info: "/data/test/{{ansible_default_ipv4['address']}}/"                                                                   

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

#执行结果
[root@client ~]#tree /data/test/
/data/test/
└── 10.0.0.133

1 directory, 0 files
使用变量文件

可以在一个独立的playbook文件中定义变量,在另一个playbook文件中引用变量文件中的变量,比playbook中定义的变量优化级高 范例:

[root@ansible ~]#cat vars.yml 
#variables file
package_name: mariadb-server
service_name: mariadb

[root@ansible ~]#cat var5.yml 
#install package and start service
- hosts: db
  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

范例2:

[root@ansible ~]#cat vars2.yml
var1: httpd
var2: nginx

[root@ansible ~]#cat var6.yml
- hosts: web
  remote_user: root
  vars_files:
    - vars2.yml
  
  tasks:
    - name: create httpd log
      file: name=/app/{{ var1 }}.log state=touch
    - name: create nginx log
      file: name=/app/{{ var2 }}.log state=touch
主机清单文件中定义变量

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

[web]
www1.jiangfeng.com http_port=80 maxRequestsPerChild=808
www2.jiangfeng.com http_port=8080 maxRequestsPerChild=909

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

[web]
www1.jiangfeng.com http_port=8080
www2.jiangfeng.com

[web:vars]
http_port=80
ntp_server=ntp.jiangfeng.com
nfs_server=nfs.jiangfeng.com

范例:

[root@ansible ~]#vim /etc/ansible/hosts
[web]
10.0.0.126 hname=www1 domain=jiangfeng.com
10.0.0.133 hname=www2

[web:vars]
mark="-"
domain=jiangfeng.org 

[root@ansible ~]#ansible web -m hostname -a 'name={{ hname }}{{ mark }}{{ domain }}'
10.0.0.126 | CHANGED => {
    "ansible_facts": {
        "ansible_domain": "com.com",
        "ansible_fqdn": "www1-jiangfeng.com",
        "ansible_hostname": "www1-jiangfeng",
        "ansible_nodename": "www1-jiangfeng.com",
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": true,
    "name": "www1-jiangfeng.com"
}
10.0.0.133 | CHANGED => {
    "ansible_facts": {
        "ansible_domain": "org",
        "ansible_fqdn": "www2-jiangfeng.org",
        "ansible_hostname": "www2-jiangfeng",
        "ansible_nodename": "www2-jiangfeng.org",
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": true,
    "name": "www2-jiangfeng.org"
}

#命令行指定变量
10.0.0.126 | CHANGED => {
    "ansible_facts": {
        "ansible_domain": "cn",
        "ansible_fqdn": "ec2-54-153-56-183.us-west-1.compute.amazonaws.com",
        "ansible_hostname": "www1-jiangfeng",
        "ansible_nodename": "www1-jiangfeng.cn",
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": true,
    "name": "www1-jiangfeng.cn"
}
10.0.0.133 | CHANGED => {
    "ansible_facts": {
        "ansible_domain": "cn",
        "ansible_fqdn": "www2-jiangfeng.cn",
        "ansible_hostname": "www2-jiangfeng",
        "ansible_nodename": "www2-jiangfeng.cn",
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": true,
    "name": "www2-jiangfeng.cn"
}

范例:k8s的ansible变量文件

[etcd]
10.0.0.104 NODE_NAME=etcd1
10.0.0.105 NODE_NAME=etcd2
10.0.0.106 NODE_NAME=etcd3

[kube-master]
10.0.0.103 NEW_MASTER=yes 
10.0.0.101
10.0.0.102

[kube-node]
10.0.0.109 NEW_NODE=yes 
10.0.0.107
10.0.0.108

[harbor]

[ex-lb]
10.0.0.111 LB_ROLE=master EX_APISERVER_VIP=10.0.0.100 EX_APISERVER_PORT=8443
10.0.0.112 LB_ROLE=backup EX_APISERVER_VIP=10.0.0.100 EX_APISERVER_PORT=8443

[chrony]

[all:vars]
CONTAINER_RUNTIME="docker"
CLUSTER_NETWORK="calico"
PROXY_MODE="ipvs"
SERVICE_CIDR="192.168.0.0/16"
CLUSTER_CIDR="172.16.0.0/16"
NODE_PORT_RANGE="20000-60000"
CLUSTER_DNS_DOMAIN="magedu.local."
bin_dir="/usr/bin"
ca_dir="/etc/kubernetes/ssl"
base_dir="/etc/ansible"

template 模板

模板是一个文本文件,可以做为生成文件的模版,并且模板文件中还可嵌套jinja语法 template功能:可以根据和参考模块文件,动态生成相类似的配置文件 template文件必须存放于templates目录下,且命名为 .j2 结尾 yaml/yml 文件需和templates目录平级,目录结构如下示例: ./ ├── temnginx.yml └── templates └── nginx.conf.j2 范例:利用template 同步nginx配置文件

#准备templates/nginx.conf.j2文件
[root@ansible ~]#vim temnginx.yml
- hosts: web
  remote_user: root

  tasks:
    - name: template config to remote hosts
      template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf

 [root@ansible ~]#ansible-playbook temnginx.yml
template算术运算
vim nginx.conf.j2 
worker_processes {{ ansible_processor_vcpus**2 }};
worker_processes {{ ansible_processor_vcpus+2 }};

template中使用流程控制 for 和 if

template中也可以使用流程控制 for 循环和 if 条件判断,实现动态生成文件功能 范例:

#temlnginx2.yml

- hosts: websrvs
  remote_user: root
  vars:
    nginx_vhosts:
      - 81
      - 82
      - 83
  tasks:
    - name: template config
      template: src=nginx.conf2.j2 dest=/data/nginx.conf

#templates/nginx.conf2.j2
{% for vhost in nginx_vhosts %}
server {
   listen {{ vhost }}
}
{% endfor %}

ansible-playbook -C templnginx2.yml --limit 10.0.0.126

#生成的结果:
server {
   listen 81
}
server {
   listen 82
}
server {
   listen 83
}

在模版文件中还可以使用 if条件判断,决定是否生成相关的配置信息 范例:

#templnginx5.yml
- hosts: web
  remote_user: root
  vars:
    nginx_vhosts:
      - web1:
        listen: 8080
        root: "/var/www/nginx/web1/"
      - web2:
        listen: 8080
        server_name: "web2.jiangfeng.com"
        root: "/var/www/nginx/web2/"
      - web3:
        listen: 8080
        server_name: "web3.jiangfeng.com"
        root: "/var/www/nginx/web3/"
  tasks:
    - name: template config to
      template: src=nginx.conf5.j2 dest=/data/nginx5.conf

#templates/nginx.conf5.j2
{% for vhost in nginx_vhosts %}
server {
   listen {{ vhost.listen }}
   {% if vhost.server_name is defined %}
server_name {{ vhost.server_name }}   #注意缩进
   {% endif %}
root  {{ vhost.root }}                #注意缩进
}
{% endfor %}

#生成的结果
server {
   listen 8080
   root /var/www/nginx/web1/
}
server {
   listen 8080
   server_name web2.jiangfeng.com
   root /var/www/nginx/web2/
}
server {
   listen 8080
   server_name web3.jiangfeng.com
   root /var/www/nginx/web3/
}

playbook使用 when

when语句,可以实现条件测试。如果需要根据变量、facts或此前任务的执行结果来做为某task执行与否的前提时要用到条件测试,通过在task后添加when子句即可使用条件测试,jinja2的语法格式 范例:

- hosts: websrvs
  remote_user: root
  tasks:
    - name: "shutdown RedHat flavored systems"
      command: /sbin/shutdown -h now
      when: ansible_os_family == "RedHat"

范例:

- hosts: websrvs
  remote_user: root
  tasks:
    - name: "shut down CentOS 6 and Debian 7 systems"
      command: /sbin/shutdown -t now
      when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")

playbook 使用迭代 with_items(loop)

迭代:当有需要重复性执行的任务时,可以使用迭代机制 对迭代项的引用,固定内置变量名为"item" 要在task中使用with_items给定要迭代的元素列表 注意: ansible2.5版本后,可以用loop代替with_items

列表元素格式:
  • 字符串
  • 字典 范例:
- hosts: web
  remote_user: root

  tasks:
    - name: add several users
      user: name={{ item }} state=present groups=wheel
      with_items:
	- testuser1
	- testuser2
	- testuser3

#上面语句的功能等同于下面的语句
    - name: add several users
      user: name=testuser1 state=present groups=wheel
    - name: add several users
      user: name=testuser2 state=present groups=wheel
    - name: add several users
      user: name=testuser3 state=present groups=wheel

范例:卸载 mariadb

#remove mariadb server
- hosts: app:!10.0.0.126
  remote_user: root

  tasks:
    - name: stop service
      shell: /etc/init.d/mysqld stop
    - name: delete files and dir
      file: path={{item}} state=absent
      with_items:
        - /usr/local/mysql
        - /usr/local/mariadb-10.2.27-linux-x86_64
        - /etc/init.d/mysqld
        - /etc/profile.d/mysql.sh
        - /etc/my.cnf
        - /data/mysql
    - name: delete user
      user: name=mysql state=absent remove=yes

迭代嵌套子变量:在迭代中,还可以嵌套子变量,关联多个变量在一起使用 示例:

- hosts: websrvs
  remote_user: root
  tasks:
    - name: add some groups
      group: name={{ item }} state=present
      with_items:
        - nginx
        - mysql
        - apache
    - name: add some users
      user: name={{ item.name }} group={{ item.group }} state=present
      with_items:
        - { name: 'nginx', group: 'nginx' }
        - { name: 'mysql', group: 'mysql' }
        - { name: 'apache', group: 'apache' }

管理节点过多导致的超时问题解决方法

默认情况下,Ansible将尝试并行管理playbook中所有的机器。对于滚动更新用例,可以使用serial关键字定义Ansible一次应管理多少主机,还可以将serial关键字指定为百分比,表示每次并行执行的主机数占总数的比例 范例:

#vim test_serial.yml
- hosts: all
  serial: 2  #每次只同时处理2个主机,将所有task执行完成后,再选下2个主机再执行所有task,直至所有主机
  gather_facts: False
  
  ...

范例:

- name: test serail
  hosts: all
  serial: "20%"   #每次只同时处理20%的主机

4、 roles 角色

运维复杂的场景:建议使用 roles,代码复用度高 roles:多个角色的集合目录, 可以将多个的role,分别放至roles目录下的独立子目录中,如下示例

roles/
    mysql/
    nginx/
    tomcat/
    redis/

默认roles存放路径

/root/.ansible/roles
/usr/share/ansible/roles	
/etc/ansible/roles

5.1 Ansible Roles目录编排

roles目录结构:
playbook.yml
roles/
  project/
    tasks/
    files/
    vars/
    templates/
    handlers/
    default/
    meta/ 
Roles各目录作用

roles/project/ :项目名称,有以下子目录

files/ :       存放由copy或script模块等调用的文件 
templates/:    template模块查找所需要模板文件的目录
tasks/:        定义task,role的基本元素,至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含
handlers/:     至少应该包含一个名为main.yml的文件;此目录下的其它的文件需要在此文件中通过include进行包含
vars/:         定义变量,至少应该包含一个名为main.yml的文件;此目录下的其它的变量文件需要在此文件中通过include进行包含
meta/:         定义当前角色的特殊设定及其依赖关系,至少应该包含一个名为main.yml的文件,其它文件需在此文件中通过include进行包含
default/:      设定默认变量时使用此目录中的main.yml文件,比vars的优先级低

5.2 创建 role

创建role的步骤

1 创建以roles命名的目录
2 在roles目录中分别创建以各角色名称命名的目录,如mysql等
3 在每个角色命名的目录中分别创建files、handlers、tasks、templates和vars等目录;用不到的目录可以创建为空目录,也可以不创建
4 在每个角色相关的子目录中创建相应的文件,如 tasks/main.yml,templates/nginx.conf.j2
5 在playbook文件中,调用需要的角色

针对大型项目使用Roles进行编排 范例:roles的目录结构

nginx-role.yml
roles/
└── nginx
     ├── files
     │   └── main.yml 
     ├── tasks
     │   ├── groupadd.yml
     │   ├── install.yml
     │   ├── main.yml
     │   ├── restart.yml
     │   └── useradd.yml 
     └── vars 
         └── main.yml

5.3 playbook 调用角色

调用角色方法1:

- hosts: websrvs
  remote_user: root
  roles:
    
    - mysql
    - memcached
    - nginx

调用角色方法2: 键role用于指定角色名称,后续的k/v用于传递变量给角色

- hosts: all  
  remote_user: root
  roles:
    - mysql
    - { role: nginx, username: nginx }

调用角色方法3: 还可基于条件测试实现角色调用

- hosts: all
  remote_user: root
  roles:

    - { role: nginx, username: nginx, when: ansible_distribution_major_version == '7' }

5.4 roles 中 tags 使用

[root@ansible ~]#vim app-role.yml
- hosts: app
  remote_user: root
  roles:
    - { role: nginx ,tags: [ 'nginx', 'web' ] ,when: ansible_distribution_major_version == "6" }
    - { role: httpd ,tags: [ 'httpd', 'web' ] }
    - { role: mysql ,tags: [ 'mysql', 'db' ] }
    - { role: mariadb ,tags: [ 'mariadb', 'db' ] }

[root@ansible ~]#ansible-playbook --tags="nginx,httpd,mysql" app-role.yml

5.5 实战案例

实现 httpd 角色
#创建角色相关的目录
[root@ansible ~]#mkdir -pv /data/ansible/roles/httpd/{tasks,handlers,files}

#创建角色相关的文件
[root@ansible ~]#cd /data/ansible/roles/httpd/

#main.yml 是task的入口文件
[root@ansible httpd]#vim tasks/main.yml
- include: group.yml
- include: user.yml
- include: install.yml
- include: config.yml
- include: index.yml
- include: service.yml

[root@ansible httpd]#vim tasks/group.yml
- name: create apache group
  group: name=apache system=yes gid=80 

[root@ansible httpd]#vim tasks/user.yml
- name: create apache user
  user: name=apache system=yes shell=/sbin/nologin home=/var/www/ uid=80 group=apache 

[root@ansible httpd]#vim tasks/install.yml
- name: install httpd package
  yum: name=httpd 

[root@ansible httpd]#vim tasks/config.yml
- name: config file
  copy: src=httpd.conf dest=/etc/httpd/conf/ backup=yes
  notify: restart 

[root@ansible httpd]#vim tasks/index.yml
- name: index.html
  copy: src=index.html dest=/var/www/html/

[root@ansible httpd]#vim tasks/service.yml
- name: start service
  service: name=httpd state=started enabled=yes  

[root@ansible httpd]#vim handlers/main.yml
- name: restart
  service: name=httpd state=restarted 

#在files目录下准备两个文件
[root@ansible httpd]#ls files/
httpd.conf  index.html

[root@ansible httpd]#tree /data/ansible/roles/httpd/
/data/ansible/roles/httpd/
├── files
│   ├── httpd.conf
│   └── index.yml
├── handlers
│   └── main.yml
└── tasks
    ├── config.yml
    ├── group.yml
    ├── index.yml
    ├── install.yml
    ├── main.yml
    ├── service.yml
    └── user.yml

3 directories, 10 files

#在playbook中调用角色
[root@ansible httpd]#vim /data/ansible/role_httpd.yml
# httpd role
- hosts: web
  remote_user: root

  roles:
    - httpd

#运行playbook
[root@ansible httpd]#ansible-playbook /data/ansible/role_httpd.yml

3、ansible-playbook批量安装httpd,按主机名提供不同的index.html(如node1的index.html欢迎页面为welcome node1)

#我这个方法解题思路是利用模板
[root@ansible ~]#vim install_httpd.yml
- hosts: app
  remote_user: root
  gather_facts: yes                                                                                                                   

  tasks:
    - name: Install httpd
      yum: name=httpd state=present
    - name: modify config
      lineinfile: path=/etc/httpd/conf/httpd.conf regexp='^Listen' line='Listen 8080'
    - name: template html to remote hosts
      template: src=index.html.j2 dest=/var/www/html/index.html
    - name: start service
      service: name=httpd state=started enabled=yes

[root@ansible ~]#vim templates/index.html.j2
welcome {{ ansible_hostname }}   

[root@ansible ~]#tree
.
├── templates
│   └── index.html.j2
├── install_httpd.yml

[root@ansible ~]#ansible-playbook install_httpd.yml

#测试
[root@ansible ~]#ansible app -m shell -a 'curl 127.0.0.1:8080'
[WARNING]: Consider using the get_url or uri module rather than running 'curl'.  If you need to use command because get_url or uri is
insufficient you can add 'warn: false' to this command task or set 'command_warnings=False' in ansible.cfg to get rid of this
message.
10.0.0.134 | CHANGED | rc=0 >>
welcome ansible  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    16  100    16    0     0  16000      0 --:--:-- --:--:-- --:--:-- 16000
10.0.0.135 | CHANGED | rc=0 >>
welcome 10  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    11  100    11    0     0  11000      0 --:--:-- --:--:-- --:--:-- 11000
10.0.0.126 | CHANGED | rc=0 >>
welcome www1-jiangfeng  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    23  100    23    0     0  23000      0 --:--:-- --:--:-- --:--:-- 23000