ansible
安装ansible
yum install epel-release -y
yum install ansible -y
如果被控主机密码不同,则需要做ssh-keygen和ssh-copy-id到被控主机
ansible配置文件
主配置文件说明:
主配置文件ansible.cfg(主配置文件的内容可以参考/etc/ansible/ansible.cfg)
ansible配置文件查找顺序
首先检测ANSIBLE_CONFIG变量定义的配置文件(默认没有这个变量)
其次检查当前目录下的./ansible.cfg文件
再次检查当前用户家目录下~/ansible.cfg文件
最后检查/etc/ansible/ansible.cfg文件
推荐使用默认的/etc/ansible/ansible.cfg文件
默认配置
这里的配置项有很多,这里主要介绍一些常用的
[defaults]
#inventory      = /etc/ansible/hosts                        #被控端的主机列表文件
#library        = /usr/share/my_modules/                    #库文件存放目录
#remote_tmp     = ~/.ansible/tmp                            #临时文件远程主机存放目录
#local_tmp      = ~/.ansible/tmp                            #临时文件本地存放目录
#forks          = 5                                         #默认开启的并发数
#poll_interval  = 15                                        #默认轮询时间间隔(单位秒)
#sudo_user      = root                                      #默认sudo用户
#ask_sudo_pass = True                                       #是否需要sudo密码
#ask_pass      = True                                       #是否需要密码
#transport      = smart                                     #传输方式
#remote_port    = 22                                        #默认远程主机的端口号
建议开启修改以下配置参数(取消掉注释即可)
#host_key_checking = False                                  #检查对应服务器的host_key
#log_path=/var/log/ansible.log                              #开启ansible日志
#inventory      = /etc/ansible/hosts
#remote_tmp     = ~/.ansible/tmp
#local_tmp      = ~/.ansible/tmp
#forks          = 300
#ask_sudo_pass = True
#remote_port    = 22
取消启用警告
deprecation_warnings = False
提权,使普通用户也能使用ansible管理远程主机
[privilege_escalation]
#become=True
#become_method=sudo
#become_user=root
#become_ask_pass=False
ansible的hosts文件配置
/etc/ansible/hosts
常用参数配置:
    ansible_ssh_host                    # 目标主机地址
    ansible_ssh_port                    # 目标主机端口,默认22
    ansible_ssh_user                    # 目标主机用户
    ansible_ssh_pass                    # 目标主机ssh密码
    ansible_sudo_pass                 	# sudo密码
    ansible_sudo_exe                    
    ansible_connection               	# 与主机的连接类型,比如:local,ssh或者paramiko
    ansible_ssh_private_key_file  		# 私钥地址
    ansible_shell_type                 	# 目标系统的shell类型
    ansible_python_interpreter   		# python版本
例1:
[test]
192.168.0.111							#也可以这样写192.168.0.[111:112]
192.168.0.112
[test:vars]								#给test组添加变量,可以写成all全部
ansible_become=true
ansible_ssh_port=22
ansible_ssh_user="zzy"
ansible_ssh_pass="1qaz@WSX"				#需要所有主机的密码是一样的
ansible_sudo_pass="1qaz@WSX"
例2:
[test1]
192.168.0.111
[test2]
192.168.0.112
[group:children]						#嵌套组
test1
test2
[group:vars]
ansible_become=true
ansible_ssh_port=22
ansible_ssh_user="zzy"
ansible_ssh_pass="zzyaiziji520"
ansible_sudo_pass="zzyaiziji520"
测试连通性
ansible all -m ping
ansible 192.168.0.111 -m ping					#可以指定hosts文件中的其中一台主机
常用命令
ansible all --list-hosts
ansible all -m ping
模块
ansible-doc获取帮助
ansible-doc -l							#列出所有模块
ansible-doc -l | grep yum            	#在所有模块中过滤关键词
ansible-doc yum                      	#查看模块帮助
command模块(默认模块)
ansible all -m command -a 'uptime'
等同于
ansible all -a 'uptime'
shell模块
command和shell模块的区别,command不支持bash的特性,如管道、重定向和&等功能,但是shell模块可以支持。
不可以使用shell模块执行交互命令,如vim、top等。
ansible all -m shell -a  "ps aux | wc -l"       #进程数量
ansible all -m shell -a  "who"                  #登陆信息
ansible all -m shell -a  "touch /tmp/txt.txt"  	#使用shell模块创建文件会有Warning警告提示,正常!!!
script模块
script模块会把-a后面的脚本拷贝到被管理端主机,然后执行这个脚本,不需要x权限。
ansible  all  -m script  -a  "./test.sh"    
#-a后面的./test.sh是上面创建脚本的相对路径和文件名
#./是当前目录的意思,在当前目录下有个脚本叫test.sh
幂等性
幂等性:任意次执行所产生的影响均与一次执行的影响相同。
command、shell、script模块缺点:不支持幂等性。
file模块
file模块可以创建文件、目录、链接;修改权限与属性等(ansible-doc file)
ansible  all  -m  file  -a  "path=/tmp/file.txt state=touch"         
#远程所有主机,新建文件,path后面指定要创建的文件或目录的名称
#state=touch是创建文件,state=directory是创建目录
ansible  all  -m  file  -a  "path=/tmp/file.txt state=file" 
#state=file即使文件不存在,也不会被创建
ansible  all  -m  file  -a  "path=/tmp/mydir state=directory"
#创建目录
ansible  all  -m  file 	-a  "path=/tmp/file.txt owner=sshd group=adm mode=0777" 
#修改文件或目录权限(0777中第一个0代表的是无特殊权限,如SUID、SGID等)
ansible all -m file -a "path=/tmp/mydir state=absent"
#state=absent代表删除(删除目录)
ansible all -m file -a "path=/tmp/file.txt state=absent"
# state=absent代表删除(删除文件)
ansible all -m file -a "src=/etc/hosts  path=/tmp/host.txt state=link"
#给/etc/hosts文件创建一个链接文件/tmp/host.txt(src指定源文件,path是软链接文件名)
#相当于执行命令 ln -s  /etc/hosts  /tmp/host.txt
copy模块
copy模块可以将文件拷贝到远程主机 (ansible-doc copy)。
ansible all -m copy -a "src=~/1.txt dest=/root/"
#把管理端本机的a3.txt文件,拷贝到test组中所有主机的/root/目录
#src代表源文件,dest代表目标文件
copy模块还可以使用content将内容拷贝到远程主机。
- name: copy with content
  hosts: test
  tasks:
    - name: copy
      copy:
        dest: /var/www/html/index.html
        content: >												
          hello world!
          Nihao
          welcome to china!
>和|的含义
>表示多行合并成一行,不保留换行符
|表示保留多行,保留换行符
fetch模块
fetch模块与copy类似,但是作用相反,可以将其他主机的文件拷贝到本地(ansible-doc fetch)。
ansible all -m copy -a "src=~/1.txt dest=/root/"
#把管理端本机的a3.txt文件,拷贝到test组中所有主机的/root/目录
#src代表源文件,dest代表目标文件
lineinfile|replace模块
在修改单个文件的单行内容时可以使用lineinfile模块(ansible-doc lineinfile)。
ansible all -m lineinfile -a "path=/etc/issue line='hello world'"
#在/etc/issue文件中添加一行内容hello world,默认添加到最后,line后面跟的是需要添加的文件内容
ansible all -m lineinfile -a 'path=/etc/selinux/config regexp="^SELINUX=" line="SELINUX=disabled"'
#将/etc/selinux/config文件中的以SELINUX开头的行替换(regexp是启用正则表达式)
lineinfile会替换一整行,replace可以替换关键词(ansible-doc replace)。
ansible all -m replace -a "path=/etc/issue.net regexp=Kernel replace=Ocean"
#将/etc/issue.net文件全文所有的Kernel替换为Ocean
#regexp后面是需要替换的旧内容;replace后面是需要替换的新内容
ansible all -m replace -a 'path=/root/test.txt regexp="^(.+)$" replace="# \1"'
#将/root/test.txt文件中所有行加注释
user模块
user模块可以实现Linux系统账户管理(ansible-doc user)。
ansible all -m user -a "name=tom"
#远程所有主机并创建系统账户tom,默认state的值为present,代表创建用户
ansible all -m user -a "name=jerry uid=1010 group=adm groups=daemon,root home=/home/jerry"
#创建账户并设置对应的账户属性,uid指定用户ID号,group指定用户属于哪个基本组
#groups指定用户属于哪些附加组,home指定用户的家目录
ansible all -m user -a "name=tom password={{'abc'| password_hash('sha512')}}"
#修改账户密码,用户名是tom,密码是abc,密码经过sha512加密
ansible all -m user -a "name=tom state=absent"
#删除账户tom,state=absent代表删除账户的意思,name指定要删除的用户名是什么
#账户的家目录不会被删除,相当于执行userdel tom
ansible all -m user -a "name=jerry state=absent remove=true"
#删除jerry账户同时删除家目录、邮箱,相当于执行userdel  -r  jerry
yum_repository模块
使用yum_repository可以创建或修改yum源配置文件(ansible-doc yum_repository)。
ansible test -m yum_repository -a "file=myyum name=myyum description=hello baseurl=ftp://192.168.4.254/centos gpgcheck=no"
#新建一个yum源配置文件/etc/yum.repos.d/myyum.repo
#yum源文件名为myyum,该文件的内容如下:
[myyum]
baseurl = ftp://192.168.4.254/centos
gpgcheck = 0
name = hello
ansible test -m yum_repository -a "file=myyum name=myyum description=test baseurl=ftp://192.168.4.254/centos gpgcheck=yes gpgkey=…"
#修改yum源文件内容
ansible test -m yum_repository -a "file=myyum name=myyum state=absent"
#删除yum源文件myyum
yum模块
使用yum模块可以安装、卸载、升级软件包(ansible-doc yum),state: present(安装)|absent(卸载)|latest(升级)。
ansible test -m yum -a "name=unzip state=present"
#安装unzip软件包,state默认为present,也可以不写
## 验证:到被控主机执行命令rpm -q unzip查看是否有该软件
ansible test -m yum -a "name=unzip,httpd state=present"
#可以同时装多个软件
ansible test -m yum -a "name=unzip state=latest"
#升级unzip软件包,软件名称可以是*,代表升级所有软件包
ansible test -m yum -a "name=unzip state=absent"
#调用yum模块,卸载unzip软件包,state=absent代表卸载软件
service模块
service为服务管理模块(启动、关闭、重启服务等),
state:started|stopped|restarted,
enabled:yes设置开机启动。
ansible test -m yum -a "name=httpd"
#调用yum模块,安装httpd软件包
ansible test -m service -a "name=httpd state=started"
#调用service模块,启动httpd服务
ansible test -m service -a "name=httpd state=stopped"
#调用service模块,关闭httpd服务
ansible test -m service -a "name=httpd state=restarted"
#调用service模块,重启httpd服务
ansible test -m service -a "name=httpd enabled=yes"
#调用service模块,设置httpd服务开机自启
parted模块
用于磁盘分区
ansible test -m parted -a "device=/dev/sdb number=1 state=present part_start=1MiB part_end=3GiB"
#为/dev/sdb创建编号为1的分区(第一个分区),part_start为分区开始的位置,part_end为结束的位置
ansible test -m parted -a "device=/dev/sdb number=1 state=absent"
#删除分区
逻辑卷相关模块(lvg、lvol)
提示:做实验之前需要给对应的虚拟机添加额外磁盘,并创建磁盘2个分区
提示:可以使用前面学习过的parted或fdisk命令给磁盘创建分区
提示:这里的磁盘名称仅供参考,不要照抄!!!
lvg模块:创建、删除卷组(VG),修改卷组大小,
state:present(创建)|absent(删除)。
ansible test -m yum -a "name=lvm2"
#安装lvm2软件包,安装了lvm2软件后,才有pvcreate、vgcreate、lvcreate等命令
ansible test -m lvg -a "vg=myvg pvs=/dev/vdb1"
#创建名称为myvg的卷组,该卷组由/dev/vdb1组成
#注意:这里的磁盘名称要根据实际情况填写
ansible test -m lvg -a "vg=myvg pvs=/dev/vdb1,/dev/vdb2"
#修改卷组大小,往卷组中添加一个设备/dev/vdb2
lvol模块:创建、删除逻辑卷(LV),修改逻辑卷大小,
state:present(创建)|absent(删除)。
ansible test -m lvol -a "lv=mylv vg=myvg size=2G"
#使用myvg这个卷组创建一个名称为mylv的逻辑卷,大小为2G
## 验证:到被控主机执行命令lvs查看是否有对应的LV逻辑卷
ansible test -m lvol -a "lv=mylv vg=myvg size=4G"
#修改LV逻辑卷大小
ansible test -m lvol -a "lv=mylv vg=myvg state=absent force=yes"
#删除逻辑卷,force=yes是强制删除
ansible test -m lvg -a "vg=myvg state=absent"
#删除卷组myvg
filesystem
格式化逻辑卷
ansible test -m filesystem -a "fstype=xfs dev=/dev/myvg/mylv"
mount模块
挂载和卸载,并写进fstab文件中,完成开机自动挂载(present|mounted|unmounted|absent)
ansible test -m mount -a "src=/dev/mapper/myvg-mylv path=/mnt fstype=xfs state=mounted"
#挂载并写进fstab文件中
ansible test -m mount -a "src=/dev/mapper/myvg-mylv path=/mnt fstype=xfs state=present"
#将mount写进fstab文件中,不挂载
ansible test -m mount -a "src=/dev/mapper/myvg-mylv path=/mnt fstype=xfs state=unmounted"
#卸载,不删除fstab文件中的mount信息
ansible test -m mount -a "src=/dev/mapper/myvg-mylv path=/mnt fstype=xfs state=absent"
#卸载并删除fstab文件中的mount信息,如果mount点非空会报错,但实际上已经卸载
firewalld模块
使用firewalld模块可以配置防火墙策略。
vim ~/ansible/firewall.yml
---
- hosts: test                           #hosts定义需要远程的主机
  tasks:                                 #tasks定义需要执行哪些任务
    - name: install firewalld.         #name为第一个任务定义描述信息
      yum:                               #第一个任务调用yum模块安装软件
        name: firewalld                 #需要安装的软件名称为firewalld
        state: present                  #state等于present代表安装软件
    - name: run firewalld.             #定义第二个任务的描述信息
      service:                          #第二个任务调用service模块启动服务
        name: firewalld                #启动的服务名称为firewalld
        state: started                 #state等于started代表启动服务
        enabled: yes                    #enabled等于yes是设置服务为开机自启动
    - name: set firewalld rule.       #第三个任务的描述信息
      firewalld:                        #第三个任务调用firewalld模块设置防火墙规则
        port: 80/tcp                    #在防火墙规则中添加一个放行tcp,80端口的规则
        permanent: yes                  #permaenent 是设置永久规则
        immediate: yes                  #immediate 是让规则立刻生效
        state: enabled                  #state等于enabled是添加防火墙规则
#最终:在默认zone中添加一条放行80端口的规则
template模块
template模块可以传递变量,copy模块不行
copy模块可以将一个文件拷贝给远程主机,但是如果希望每个拷贝的文件内容都不一样呢?如何给所有web主机拷贝index.html内容是各自的IP地址?
Ansible可以利用Jinja2模板引擎读取变量,之前在playbook中调用变量,也是Jinja2的功能,Jinja2模块的表达式包含在分隔符"{{ }}"内。
这里,我们给test主机拷贝首页,要求每个主机内容不同。
vim ~/ansible/index.html
Welcome to {{ ansible_hostname }} on {{ ansible_eth0.ipv4.address }}. 
#注意网卡名称根据实际情况填写,不可以完全照抄,不知道网卡名可以通过ip a s查询!
#{{ansible_hostname}}和{{ ansible_eth0.ipv4.address }}是ansible自动的facts变量。  
编写Playbook将网页模板文件拷贝到远程主机。
vim ~/ansible/template.yml
---
- hosts: test
  tasks:
    - name: use template copy index.html to webserver.
      template:
        src: ~/ansible/index.html
        dest: /tmp/index.html
#hosts定义需要远程的目标主机是谁;tasks定义需要执行的任务是什么
#- name定义任务的描述信息;任务需要调用的模块是template模块
#template模块需要两个参数,src指定需要拷贝的源文件,dest指定需要拷贝的目标位置
#src: ~/ansible/template/index.html是上面创建的文件,文件中包含变量
#dest: /tmp/index.html拷贝到目标主机放在/tmp目录下
特殊模块
setup模块
ansible_facts用于采集被管理设备的系统信息,所有收集的信息都被保存在变量中,每次执行playbook默认第一个任务就是Gathering Facts,使用setup模块可以查看收集到的facts信息。
ansible test -m setup								#查看所有facts变量
192.168.4.10 | SUCCESS => {
"ansible_facts": {
   "ansible_all_ipv4_addresses": [
… 省略部分内容…
ansible test -m setup -a 'filter=ansible_all_ipv4_addresses'	#只查看ip地址
- ansible_all_ipv4_addresses #IP地址
- ansible_bios_version #主板BIOS版本
- ansible_memtotal_mb #总内存
- ansible_hostname #主机名
- ansible_fqdn #主机的域名
- ansible_devices.vda.partitions.vda1.size #某个磁盘分区的大小
debug模块
debug模块可以显示变量的值,可以辅助排错,通过msg可以显示变量的值,变量需要使用{{}}扩起来。
vim ~/ansible/debug.yml
---
- hosts: test
  tasks:
    - debug:
        msg: "主机名是:{{ ansible_hostname }}"
    - debug:
        msg: "总内存大小:{{ ansible_memtotal_mb }}"
#备注调用debug模块显示某些具体的变量值
#debug模块可以显示变量的值,可以辅助排错
变量
Ansible支持十几种定义变量的方式,这里我们仅介绍其中一部分变量。
下面是根据优先级排序的定义方式:
- Inventory变量
- Host Facts变量
- Playbook变量
- 变量文件
Inventory变量(在主机清单配置文件中定义变量)。
vim ~/ansible/inventory
[test]
node1  iname="nb" 
[proxy]
node2
[webserver]
node[3:4]
[webserver:vars]
iname="dachui"
#备注,在node1主机后面给该主机添加变量iname,值为nb.
#给webserver组定义变量,vars是关键词不可以改变,webserver是上面定义的组
#给这个组定义变量iname="dachui"
... ...<部分后面的内容省略>... ...
下面编写剧本调用刚才的变量:(在剧本中需要调用变量是要使用{{}})
vim ~/ansible/inventory_var.yml
---
- hosts: node1,webserver                         #定义需要远程管理的主机是谁               
  tasks:                                           #剧目要完成哪些任务
    - name: create a user with var.              #剧目中的第一个任务描述信息
      user:                                        #调用user模块创建用户
        name: "{{ iname }}"                      #需要创建的用户名是iname这个变量
#注意事项:
#在ansible剧本中当调用变量时,开始位置就调用变量,就需要在{{}}外面加双引号
#如果是在后面或者中间位置调用变量{{}}外面可以不加双引号,也可以加双引号
#如:
#  "{{ iname }}"
#  nihao {{ iname }}
Host Facts变量(可以直接调用ansible收集的系统信息)
vim ~/ansible/facts_var.yml
---
- hosts: test
  tasks:
    - name: create user.
      user:
        name: "{{ ansible_hostname }}"
#定义剧本,远程所有被管理主机,调用user模块,创建用户
#需要创建的用户名ansible_hostname是一个ansible_facts变量
#验证: 到node1主机查看是否有一个与主机名同名的用户
Playbook变量(使用vars关键词可以在playbook内定义变量)。
vim ~/ansible/playbook_var.yml
---
- hosts: test
  vars:                                     #vars是关键词,用来定义变量用的
    iname: heal                            #具体变量名是iname,值是heal
    ipass: '123456'                       #再定义一个变量名是ipass,值是123456
#注意密码必须是字符串,需要引号                           
  tasks:                                   #tasks定义需要执行的任务
    - name: Use variables create user.  #给任务写个描述信息   
      user:                                #调用user模块创建用户
        name: "{{ iname }}"               #用户名的是前面定义的变量
        password: "{{ ipass | password_hash('sha512') }}"
#密码是前面定义好的ipass,管道给password_hash把密码加密.
单独定义个变量文件,在playbook中用vars_files调用该文件。
vim ~/ansible/file_var.yml
---
- hosts: test
  vars_files: variables.yml             #当变量比较多时,专门定义一个文件用来存变量
  tasks:
    - name: create user.
      user:
        name: "{{ iname }}"
        password: "{{ ipass | password_hash('sha512') }}"
#调用user模块创建用户
#用户名是变量文件variables.yml中定义的变量iname,密码也是变量文件中定义的变量
[root@control ansible]# vim  ~/ansible/variables.yml
---
iname: cloud
ipass: '123456'
Playbook
Ansible ad-hoc可以通过命令行形式远程管理其他主机,适合执行一些临时性简单任务。另外还有一种远程管理的方式叫Playbook,Ansible Playbook中文名称叫剧本,它将经常需要执行的任务写入一个文件,这个文件就叫剧本。
编写yaml文件之前的设置(可选)
默认一个tab键是8个空格,不方便,可以修改
- 按tab键跳2个空格
- 自动缩进
- 将tab转换为空格
cat > ~/.vimrc <<EOF
set ai
set ts=2
set et
EOF
编写第一个Playbook(剧本)
hosts、tasks、name是关键词(不可修改),ping是模块,调用不同模块完成不同任务。
vim /etc/ansible/test.yaml 
---
- hosts: all                                #hosts定义要远程谁?
  tasks:                                    #tasks定义远程后要执行的任务有哪些?
    - name: This is my first playbook     #name后面的具体内容可以任意
      ping:
运行playbook:
ansible-playbook /etc/ansible/test.yml
例2:
在test组中创建/tmp/demo目录,权限755,将本机的/etc/hosts拷贝到目标主机的/tmp/demo中
- name: create dir and copy file
  hosts: test
  tasks:
    - name: create
      file: path=/tmp/demo state=directory mode=0755
      
    - name: copy
      copy: src=/etc/hosts dest=/tmp/demo/
例3:
在test组中安装lamp,并启动服务
- name: deploy lamp
  hosts: test
  tasks:
    - name: install lamp
      yum: name=httpd,mariadb,mariadb-server,mariadb-devel,php,php-mysql
    - name: start httpd
      service: name=httpd state=started enabled=yes
    - name: start mariadb
      service: name=mariadb state=started enabled=yes
例4:
创建用户
- name: add user
  hosts: test
  tasks:
    - name: add user tom
      user: name=tom password={{ '123' | password_hash('sha512') }}
例5:
给/dev/sdb磁盘分一个2G的分区sdb1,做成卷组,创建逻辑卷,格式化并挂载到/mnt下
- name: create lvm
  hosts: test
  tasks:
    - name: parted
      parted: device=/dev/sdb number=1 state=present part_start=1MiB part_end=2GiB
    - name: create vg
      lvg: vg=myvg pvs=/dev/sdb1
    - name: create lvm
      lvol: vg=myvg lv=mylv size=1.99G
    - name: filesystem
      filesystem: fstype=xfs dev=/dev/myvg/mylv
    - name: mount
      mount: src=/dev/mapper/myvg-mylv path=/mnt fstype=xfs state=mounted
例6:
安装rpm组包,并更新软件
- name: rpms manage
  hosts: test
  tasks:
    - name: install rpm group
      yum: name="@Development Tools" state=present     //@符号表示这是一组rpm包
    - name: update rpms
      yum: name='*' state=latest
Ansible高级语法应用
error错误处理
默认ansible在遇到error会立刻停止playbook,使用ignore_errors可以忽略错误,继续后续的任务。
如果一个剧本里面有20个任务,执行到第3个时失败,则不再往下执行。
下面这个这个Playbook在执行时会意外中断。
vim ~/ansible/error.yml
---
- hosts: test
  tasks:
    - name: start a service that does not exist.
      service:
        name: hehe         #注意:没有这个服务(启动一个不存在的服务)                           
        state: started
    - name: touch a file.
      file:
        path: /tmp/service.txt
        state: touch
下面这个Playbook在执行时因为忽略了错误(针对某一个任务),不会被中断。
vim ~/ansible/error.yml
---
- hosts: test
  tasks:
    - name: start a service that does not exist.
      service:
        name: hehe
        state: started
      ignore_errors: true       #针对某一个任务忽略错误(ignore_errors是关键词),值可以是ture或yes                          
    - name: touch a file.
      file:
        path: /tmp/service.txt
        state: touch
下面这个Playbook在执行时因为忽略了错误,不会被中断。
cat ~/ansible/error.yml
---
- hosts: test
  ignore_errors: true      #针对playbook全局忽略错误                             
  tasks:
    - name: start a service that does not exist.
      service:
        name: hehe
        state: started
    - name: touch a file.
      file:
        path: /tmp/service.txt
        state: touch
handlers
主要用户有依赖性的任务
在剧本中tasks用来定义任务(一定会执行),handlers也可以定义任务(不一定执行),handlers任务要想执行必须要被别人触发才能执行。
实例草稿:
---
- hosts: test
  tasks:
    - 任务1
       notify:任务5
    - 任务2
  handlers:
    - 任务5
    - 任务6
可以通过handlers定义一组任务,仅当某个任务触发(notify)handlers时才执行相应的任务,如果有多个notify触发执行handlers任务,也仅执行一次。
仅当任务的执行状态为changed时handlers任务才执行,handlers任务在所有其他任务都执行后才执行。
下面编写一个通过notify触发执行handlers任务的案例。
 vim ~/ansible/handlers.yml
---
- hosts: test
  tasks:
    - name: create directory.           #多次执行playbook该任务状态不再是changed
      file:                               #调用file模块创建目录
        path: /tmp/parents/subdir/      #需要创建的具体目录名称
        state: directory                #state等于directory代表创建目录
      notify: touch file                #notify后面名称必须和handlers中的任务名称一致           
  handlers:                              #通过handlers再定义一组任务
    - name: touch file                  #给任务写描述信息(任务的名字,名字可以任意)
      file:                              #调用file模块创建文件
        path: /tmp/parents/subdir/new.txt    #需要创建的文件名
        state: touch                           #state等于touch代表创建文件
#备注:仅当file模块执行成功,
#并且状态为changed时才会通过notify触发执行handlers下面的任务,
#所以多次执行该剧本时,handlers任务不会被重复执行,
#notity后面的名称必须和handlers下面name定义的任务名称一致(名称可以任意)。
when条件判断
when可以定义判断条件,条件为真时才执行某个任务。
常见条件操作符有:==、!=、>、>=、<、<=。
多个条件可以使用and(并且)或or(或者)分割,when表达式中调用变量不要使用{{ }}。
下面编写Playbook,远程主机剩余内存不足700M则关闭NetworkManager服务
vim ~/ansible/when_1.yml
---
- hosts: test
  tasks:
    - name: check memory size.
      service:
        name: NetworkManager
        state: stopped
      when: ansible_memfree_mb < 700
#被管理端主机剩余内存不足700M则关闭NetworkManager服务(也可以关闭别的不需要的服务)
#ansible_memfree_mb这个是ansible自带的facts变量,代表剩余内存的容量。
下面再编写一个Playbook,判断操作系统是RedHat8则创建测试文件。YAML的语法格式中>支持多行输入,但不保留换行符。
vim ~/ansible/when_2.yml
---
- hosts: test
  tasks:
    - name: touch a file
      file:
        path: /tmp/when.txt
        state: touch
      when:  >
        ansible_distribution == "RedHat"
        and
        ansible_distribution_major_version == "8"
#判断操作系统是RedHat8则创建测试文件
#YAML的语法格式中>支持多行输入,但不保留换行符(计算机会认为实际是一行内容)
#ansible_distribution和ansible_distribution_major_version都是自带的facts变量
#可以使用setup模块查看这些变量
block任务块
如果我们需要当条件满足时执行N个任务,我们可以给N个任务后面都加when判断(但是很麻烦),此时可以使用block定义一个任务块,当条件满足时执行整个任务块.
任务块就是把一组任务合并为一个任务组,使用block语句可以将多个任务合并为一个任务组。
vim ~/ansible/block_1.yml
---
- hosts: test
  tasks:
    - name: define a group of tasks.
      block:                                          #block是关键词,定义任务组
        - name: install httpd                       #任务组中的第一个任务
          yum:                                        #调用yum模块安装httpd软件包
            name: httpd
            state: present
        - name: start httpd                          #任务组中的第二个任务
          service:                                    #调用service模块启动httpd服务
            name: httpd
            state: started
      when: ansible_distribution == "RedHat"       #仅当条件满足再执行任务组
#注意:when和block是对齐的,他们在一个级别,当条件满足时要执行的是任务组(不是某一个任务)
#判断条件是看远程的目标主机使用的Linux发行版本是否是RedHat.
对于block任务块,我们可以使用rescue语句定义在block任务执行失败时要执行的其他任务,还可以使用always语句定义无论block任务是否成功,都要执行的任务。
rescue语句定义在block任务执行失败时要执行的其他任务
always语句定义无论block任务是否成功,都要执行的任务
下面编写一个包含rescue和always的示例。
vim ~/ansible/block_2.yml
---
- hosts: test
  tasks:
    - block:
        - name: touch a file test1.txt
          file:
            name: /tmp/test1.txt      #如果修改为/tmp/xyz/test1.txt就无法创建成功             
            state: touch
      rescue:
        - name: touch a file test2.txt
          file:
            name: /tmp/test2.txt
            state: touch
      always:
        - name: touch a file test3.txt
          file:
            name: /tmp/test3.txt
            state: touch
#默认在/tmp/目录下创建test1.txt会成功,所以不执行rescue(创建test2.txt)
#如果我们把block中的任务改为创建/tmp/xyz/test1.txt(因为没有xyz目录所以会失败)
#当block默认任务失败时就执行rescue任务(创建test2.txt)
#但是不管block任务是否成功都会执行always任务(创建test3.txt)
loop循环
ansible中的loop相当于shell中的for循环
for循环可以自定义变量,loop只能使用默认变量item
相同模块需要反复被执行怎么处理?使用loop循环可以避免重复。
编写Playbook,循环创建目录。
vim ~/ansible/simple_loop.yml
---
- hosts: test
  tasks:
    - name: mkdir multi directory.
      file:
        path=/tmp/{{item}}       #注意,item是关键字,调用loop循环的值                                
        state=directory
      loop:                       #loop是关键词,定义循环的值,下面是具体的值
        - School
        - Legend
        - Life
#最终在/tmp目录下创建三个子目录.file模块被反复执行了三次。
#mkdir  /tmp/School;  mkdir  /tmp/Legend;   mkdir  /tmp/Life。
编写Playbook,循环创建用户并设置密码。
vim ~/ansible/complex_loop.yml
---
- hosts: test
  tasks:
    - name: create multi user.
      user:
        name: "{{item.iname}}"
        password: "{{item.ipass | password_hash('sha512')}}"
      loop:
        - { iname: 'term', ipass: '123456' }
        - { iname: 'amy' , ipass: '654321' }
#loop循环第一次调用user模块创建用户,user模块创建用户会读取loop里面的第一个值.
#loop第一个值里面有两个子值,iname和ipass
#创建用户item.iname就是loop第一个值里面的iname=term
#修改密码item.ipass就是loop第一个值里面的ipass=123456
#loop循环第二次调用user模块创建用户,user模块创建用户会读取loop里面的第二个值.
#loop第二个值里面有两个子值,iname和ipass
#创建用户item.iname就是loop第二个值里面的iname=amy
#修改密码item.ipass就是loop第二个值里面的ipass=654321
Ansible Roles
- 
为了实现playbook的重用,可以使用role角色 
- 
roles就是拆解playbook中的内容,放到不同的文件中 
- 
再把固定的一些值,如用户名、服务、软件包名等用变量来表示 
- 
调用role的时候,只要改变变量名就行了 
Roles目录结构中主要文件的作用是什么呢?
- defualts/main.yml:定义变量的缺省值,优先级较低
- files目录:存储静态文件的目录,如tar包、音乐、视频等
- handlers/main.yml: 定义handlers
- meta/main.yml: 写作者、版本等描述信息
- README.md: 整个角色(role)的描述信息
- tasks/main.yml: 定义任务的地方
- templates目录:存放动态数据文件的地方(文件中包含了变量的模板文件)
- vars/main.yml: 定义变量,优先级高
Role应用案例
创建Roles
下面这个案例目的:编写一个包含变量的模板文件,编写任务调用template模块,将模板文件拷贝给被管理端主机。
ansible-galaxy命令可以创建、管理自己的roles。
mkdir ~/ansible/roles
ansible-galaxy init  ~/ansible/roles/issue
#创建一个Role,该Role的目的是拷贝自己新建的一个模板文件到远程主机的/etc/issue
tree  ~/ansible/roles/issue/
#查看目录结构,如果没有tree命令则需要使用yum安装该软件
修改Role文件
定义名称为myfile.txt的模板文件(该文件包含变量,因此必须放置templates目录)
vim ~/ansible/roles/issue/templates/myfile.txt
This is the system {{ ansible_hostname }}
Today's date is:{{ ansible_date_time.date }}
Contact to {{ admin }}
自定义变量文件(前面调用了admin这个变量,这里需要定义admin变量并赋值)
vim ~/ansible/roles/issue/vars/main.yml
---
# vars file for /root/ansible/roles/issue
admin: test@qq.com
#变量名为admin,变量的值为test@qq.com
文件准备好了,计算机不会自动将文件拷贝给被管理端主机!需要编写任务调用模块实现拷贝的功能。
修改任务文件,任务文件中不需要tasks关键词,Role的各个文件之间相互调用不需要写文件的路径。
vim ~/ansible/roles/issue/tasks/main.yml
---
# tasks file for /root/ansible/roles/issue
-  name: delever issue file
   template:
     src: myfile.txt
     dest: /etc/issue
#调用template模块将myfile.txt文件拷贝给被管理端主机.
在Playbook中调用Role
Role创建好了,role不会自己运行,需要编写一个剧本调用上面的role。
编写playbook剧本文件,通过roles关键词调用role。
vim  ~/ansible/issue.yml
---
- hosts: test
  roles:
    - issue
#- role2              #支持加载多个role
修改ansible.cfg配置文件,定义roles目录。
vim  ~/ansible/ansible.cfg 
[defaults]
inventory = ./inventory
roles_path = ./roles                    #指定到哪个目录下找role
ansible-galaxy命令
ansible-galaxy  search 'httpd' 
#联网搜索roles
ansible-galaxy info acandid.httpd 
#查看roles基本信息
ansible-galaxy install acandid.httpd -p ~/ansible/roles/
#下载roles到特定的目录,-p可以指定下载到哪个目录
使用ansible-galaxy install可以直接下载Role,也可以编写requirements.yml文件下载Role。
vim ~/ansible/roles/requirements.yml
#格式一:可以直接从Ansible Galaxy官网下载
- src: acandid.httpd
#格式二:可以从某个git服务器下载
- src: http://gitlab.com/xxx/xxx.git
  scm: git
  version: 56e00a54
  name: nginx-acme
  
#格式三:可以指定位置下载tar包,支持http、https、file
- src:  http://example.com/myrole.tar
  name:  myrole
  
ansible-galaxy install \
-r ~/ansible/roles/requirements.yml \
-p roles
# -r后面跟文件名,该文件中包含了需要下载哪些role以及他们的链接位置
# -p 指定将下载的role保存到哪个目录
加密敏感数据
使用ansible-vault处理敏感数据
- 加密敏感数据。
encrypt(加密)、decrypt(解密)、view(查看),rekey(重置密码)。
ansible-vault encrypt data.txt      #加密文件
ansible-vault view data.txt         #查看加密文件
- 修改密码(rekey)
ansible-vault rekey data.txt             #修改密码
Vault password: <旧密码>
New Vault password: <新密码>
Confirm New Vault password:<确认新密码>
- 解密文件
ansible-vault decrypt data.txt      #解密文件
- 
使用密码文件 加密、解密每次都输入密码很麻烦,可以将密码写入文件。 
echo "I'm secret data" > data.txt       #需要加密的敏感数据
echo 123456 > pass.txt                   #加密的密码
ansible-vault  encrypt --vault-id=pass.txt  data.txt 
cat data.txt
ansible-vault decrypt --vault-id=pass.txt data.txt
cat data.txt
扩展练习
自动化部署Web集群
部署两台后端http服务器
创建role角色
ansible-galaxy  init  ~/ansible/roles/http
修改role配置文件,准备2台http网站的素材
安装httpd,拷贝一个网页文件。
vim roles/http/tasks/main.yml
---
- name: install httpd
  yum:
    name: httpd
    state: present
- name: create index.html
  copy:
    content: "{{ansible_hostname}}"
    dest: /var/www/html/index.html
- name: set firewalld
  firewalld:
    service: http
    state: enabled
    permanent: yes
    immediate: yes
- name: start httpd
  service:
    name: httpd
    state: started
    enabled: yes
#文件中包含多个任务,每个任务可以设置一个name名字(也可以没有name)
#第一个任务调用yum模块安装httpd软件包
#第二个任务调用copy模块创建一个新的网页文件(index.html)
#调用copy模块时可以在没有源文件的情况下,直接使用content指定文件的内容
#将该内容直接拷贝到被管理主机的某个文件中(/var/www/html/index.html)
#第三个任务调用firewalld模块,设置防火墙规则,允许访问http服务
#第四个任务调用service模块将httpd服务启动,并设置开机自启。
编写Playbook调用role,并执行Playbook。
vim web.yml
---
- hosts: webserver
  roles:
    - http
ansible-playbook web.yml
部署nginx代理服务器
创建role角色
ansible-galaxy  init  ~/ansible/roles/proxy
准备代理服务器需要的素材
下载Nginx源码包,编写一个源码编译安装nginx的shell脚本。
wget http://nginx.org/download/nginx-1.20.2.tar.gz
vim ~/ansible/roles/proxy/files/nginx_install.sh
#!/bin/bash
yum -y install gcc pcre-devel openssl-devel
useradd -s /sbin/nologin nginx
tar -xf nginx-1.20.2.tar.gz
cd nginx-1.20.2/
./configure --prefix=/usr/local/nginx --user=nginx --with-http_ssl_module --with-http_stub_status_module --with-stream
make && make install
新建一个Nginx代理服务器的配置文件模板。
vim ~/ansible/roles/proxy/files/nginx.conf
worker_processes  2;
#error_log  logs/error.log;
events {
    worker_connections  65535;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    tcp_nopush     on;
    keepalive_timeout  65;
    #gzip  on;
upstream webs {
   server 192.168.4.13;
   server 192.168.4.14;
}
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://webs;
            root   html;
            index  index.html index.htm;
        }
        error_page  404              /404.html;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}
修改role配置文件。
vim roles/proxy/tasks/main.yml
---
- name: copy nginx-1.20.2.tar.gz to proxy.
  copy:
    src: nginx-1.20.2.tar.gz
    dest: /tmp/
#拷贝源码包软件
- name: install nginx through shell script.
  script: nginx_install.sh
  args:
    creates: /usr/local/nginx/sbin/nginx
#执行源码编译安装脚本,如果已经安装nginx,则不再执行安装脚本.
#args是关键词,设置script模块的参数,通过creates参数做判断,creates也是关键词
#creates后面跟文件名,如果creates判断文件存在的话就不再执行script模块对应的命令。
- name: copy nginx.conf to destination host.
  copy:
    src: nginx.conf
    dest: /usr/local/nginx/conf/nginx.conf
- name: run nginx service.
  shell: /usr/local/nginx/sbin/nginx
  args:
    creates: /usr/local/nginx/logs/nginx.pid
#nginx.pid存在,说明nginx已经启动。如果该文件存在,则不再启动nginx。
- name: set firewalld
  firewalld:
    service: http
    state: enabled
    permanent: yes
    immediate: yes
编写Playbook调用role,并执行Playbook。
vim proxy.yml
---
- hosts: proxy
  roles:
    - proxy
ansible-playbook proxy.yml
配置sudo权限
sudo(superuser or another do)让普通用户可以以超级管理员或其他人的身份执行命令。
sudo基本流程如下:
- 管理员需要先授权(修改/etc/sudoers文件)
- 普通用户以sudo的形式执行命令
修改/etc/sudoers的方法如下:
- visudo(带语法检查,默认没有颜色提示)
- vim /etc/sudoers(不带语法检查,默认有颜色提示)
授权格式如下:
用户或组 主机列表=(提权身份) [NOPASSWD]:命令列表
注意事项:命令需要写绝对路径,对组授权需要在组名称前面加%。
配置sudo提权
远程所有被管理主机批量创建系统账户,账户名称为alice,密码为123456。
ansible all -m user -a "name=alice password={{ '123456' | password_hash('sha512') }}"
配置alice账户可以提权执行所有命令(control批量授权,node1主机验证)。
使用lineinfile模块修改远程被管理端主机的/etc/sudoers文件,line=后面的内容是需要添加到文件最后的具体内容。
等于是在/etc/sudoers文件末尾添加一行:alice ALL=(ALL) NOPASSWD:ALL
ansible all -m lineinfile -a "path=/etc/sudoers line='alice  ALL=(ALL) NOPASSWD:ALL'"
修改Ansible配置
配置普通用户远程管理其他主机
修改主配置文件,配置文件文件的内容可以参考/etc/ansible/ansible.cfg。
vim ~/ansible/ansible.cfg
[defaults]
inventory = ~/ansible/inventory
remote_user = alice                #以什么用户远程被管理主机(被管理端主机的用户名),默认为root
[privilege_escalation]
become = true                    #alice没有特权,是否需要切换用户提升权限
become_method = sudo                #如何切换用户(比如用su就可以切换用户,这里是sudo)
become_user = root                #切换成什么用户(把alice提权为root账户)
become_ask_pass = no                #执行sudo命令提权时是否需要输入密码
修改inventory主机清单配置文件(参考即可,不需要操作)。
cat  ~/ansible/inventory
[test]                    
node1           ansible_ssh_port=端口号                      #自定义远程SSH端口
[proxy]
node2           ansible_ssh_user=用户名                    #自定义远程连接的账户名
[webserver]
node[3:4]       ansible_ssh_pass=密码                     #自定义远程连接的密码
[database]
node5
[cluster:children]                
webserver
database
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号