ansible学习笔记

发布时间 2023-10-20 16:28:59作者: 龙泉寺老方丈

ansible



安装

  • ansible的安装有两种方式:yum安装和pip安装

  • yum安装:

    • 安装epel源
      rpm -ivh https://mirrors.aliyun.com/centos/7/extras/x86_64/Packages/epel-release-7-11.noarch.rpm

    • 安装ansible
      yum -y install ansible

    • 查看ansible版本信息
      ansible --version


1. 配置

  • Inventory文件中填写需要被管理主机与主机组信息。
    默认Inventory文件在/etc/ansible/hosts,也可以自定义,使用 -i 指定Inventory文件位置

  • ansible基于密钥连接,需创建公钥私钥,并下发公钥至被控端

1.1 Inventory(主机清单)

# 密钥连接方式
[tomcat]
192.168.1.[20:21]:2200
[tomcat:vars]
ansible_ssh_user=tomcat


# 密码方式
[nginx]    
192.168.1.[100:101]:2200
[nginx:vars]
ansible_ssh_user=root
ansible_ssh_pass=abc123
ansible_ssh_port=2200
#ansible_su_pass=abc123
#ansible_sudo_pass=abc123


# 别名方式
[tomcat]
tomcat-node01 ansible_ssh_host=192.168.1.20 ansible_ssh_port=2200
tomcat-node02 ansible_ssh_host=192.168.1.21 ansible_ssh_port=2200


# 主机组方式(组合并)
[tomcat]
192.168.1.[20:21]:2200
[nginx]    
192.168.1.[100:101]:2200
[webserver:children]
tomcat
nginx


# 配置主机组变量
[nginx:vars]
key=nginx
[tomcat:vars]
key=tomcat

1.2 ansible.cfg

  • ansible.cfg读取顺序:
    1. 项目目录:./ansible.cfg
    2. 当前用户家目录:/home/$USER/.ansible.cfg
    3. 默认:/etc/ansible/ansible.cfg
inventory = /root/ansible/hosts  # 资源清单路径
host_key_checking = False        # 取消指纹认证
forks = 10                       # 并发进程数
roles_path = /etc/ansible/myroles/roles
log_path = /var/log/ansible.log  # 记录ansible日志的路径,默认不记录
timeout = 20                     # ssh连接的超时间隔
ssh_args = -o ControlMaster=auto -o ControlPersist=360s -o ServerAliveInterval=30 -o ServerAliveCountMax=2
#ControlPersist:超时时间
#ServerAliveInterval:客户端机器在使用SSH连接服务器时,每隔xxx秒给服务器发送一个"空包",保持它们之间的连接
#ServerAliveCountMax:最大超时时间

2. 模块

  • ansible-doc [模块名] | less

    定位到EXAMPLES,可查看模块使用示例


2.1 yum模块

  • 软件包管理

  • 参数说明:

    • name: 软件名/软件路径
    • state: [prensent(安装)|latest(安装+更新)|absent(卸载)]
    • exclude: 排除软件
# 只通过epel安装当前最新的Apache软件,如果存在则不安装
ansible tomcat -m yum -a "name=ntp state=present enablerepo=epel"

# 通过公网URL安装rpm软件
ansible tomcat -m yum -a "name=https://xx.rpm state=present"

# 安装最新版本的Apache软件,如果存在则更新Apache
ansible tomcat -m yum -a "name=ntp state=latest"

# 更新所有的软件包,但排除和kernel相关的
ansible tomcat -m yum -a "name=* state=latest exclude=kernel*"

# 删除Apache软件
ansible tomcat -m yum -a "name=ntp state=absent"

2.2 copy模块

  • 分发文件

  • 参数说明:

    • src: 源文件或目录,"无/代表整个目录,有/代表目录下所有文件"
    • dest: 目标服务器文件或目录
    • backup: 如果目标存在文件,那么覆盖前是否备份目标文件
    • owner: 拷贝到目标服务器后,文件或目录的所属用户
    • group: 拷贝到目标服务器后,文件或目录的所属群组
    • mode: 文件或目录权限
    • content: 写入/创建文件内容(可代替src)
    • directory_mode: 递归复制设置目录权限,默认为系统默认权限
    • force: 如果目标主机包含该文件,但内容不同,如果设置为yes,则强制覆盖,如果设置为no,则只有当目标主机的目标位置不存在该文件时,才复制。默认为yes
# 将本地test文件推送到远端服务并备份,属主属组look,权限644
ansible tomcat -m copy -a "src=/tmp/test dest=/tmp backup=yes owner=look group=look mode=0644"

# 向远处的主机文件写入内容(有则直接写入,没有则创建写入)
ansible tomcat -m copy -a "content=Ansible dest=/usr/tomcat/webapps/index.html"

# Playbook写法
'''
 name: copy content to cut_tomcat_log.sh
  copy:
    content: |
      #!/bin/bash
      echo "Test content"
    dest: /tmp/content.sh
    mode: 644
'''

2.3 get_url模块

  • 将文件或软件从httphttpsftp下载到本地节点上或被管理机节点上

  • 参数说明:

    • url: 文件路径,协议支持httphttpsftp
ansible tomcat -m get_url -a "url=http://xx/xx.html dest=/usr/tomcat/webapps/ROOT/index.html

2.4 fetch模块

  • 拉取文件

  • 参数说明:

    • src: 客户端文件
    • dest: 从远端主机上拉取的文件存在本地的位置,一般只能是目录
ansible tomcat -m fetch -a "src=/etc/hosts dest=/tmp/all_hosts"

2.5 file模块

  • 文件、目录创建及授权

  • 参数说明:

    • path: 目标服务器文件或目录路径
    • owner: 文件或目录的所属用户
    • group: 文件或目录的所属群组
    • mode:文件或目录权限
    • recurse: 递归
    • src: 要被链接到的路径,只应用与state=link的情况
    • force: 需要在两种情况下强制创建软连接,一种是源文件不存在但之后会建立的情况下;另一种是目标连接已存在,需要先取消之前的软连接,有两个选项[yes|no]
    • state:
      touch:文件不存在则会创建文件,文件存在则更新其最后修改时间
      directory:目录不存在则会创建目录,目录存在则更新其最后修改时间
      file:即使文件不存在,也不会被创建
      link:创建软连接
      hard:创建硬连接
      absent:删除目录、文件或者取消链接文件
# 创建目录、并设定属主属组、权限
ansible tomcat -m file -a "path=/tmp/file_test state=directory owner=tomcat group=root mode=755"

# 创建文件、并设定属主属组、权限
ansible tomcat -m file -a "path=/tmp/file_test/test state=touch owner=tomcat group=root mode=644"

# 递归授权目录
ansible tomcat -m file -a "path=/usr/tomcat owner=tomcat group=tomcat recurse=yes"

# 软链
ansible tomcat -m file -a "src=/etc/passwd dest=/etc/link_passwd state=link"

2.6 service模块

  • 服务管理

  • 参数说明:

    • name: 服务名
    • state: [started|stopped|reloaded|restarted]
    • enabled: 开机自启动
ansible tomcat -m service -a "name=ntpd state=started enabled=yes"

2.7 group模块

  • 组管理

  • 参数说明:

    • name: 组的名称
    • state:指定组的状态,默认present [present(创建)|absent(删除)]
    • gid: 指定组的gid
    • system: 是否为系统组,默认普通
ansible tomcat -m group -a "name=news system=yes state=present gid=300" 

2.8 user模块

  • 用户管理

  • 参数说明:

    • name: 创建的用户名
    • group: 属组,默认为用户名对应组,如指定组组需存在
    • groups: 附加组
    • password: 用户密码,需加密后的(注意写法,无引号)
    • shell: 登录的shell
    • home: 指定家目录
    • remove: 删除家目录
    • state: [present(添加)|absent(删除)] 默认present
    • create_home: [yes|no],默认yes(创建)
    • comment: 注释
    • generate_ssh_key: 创建密钥对
    • ssh_key_bits: 密码字节大小
    • ssh_key_file: 密码位置 .ssh/id_rsa
# 创建用户look,uid520,附加组bin,sys,登录shell为 bin/bash,创建家目录,密码为look


# 先使用Debug模块拿到加密密码
ansible localhost -m debug -a "msg={{ 'look' | password_hash('sha512','salt') }}"
localhost | SUCCESS => {
    "msg": "$6$salt$e2YCufFl6mFwJUwICWWt9Ap6ZJs3CJidpcpat0jvIg5Fk3MW0/HsPzjb1y5EfELmc8uMdsQBeyg7f6h0EyXVC0"
}

# 创建用户
ansible tomcat -m user -a 'name=look uid=525  groups=bin,sys shell=/bin/bash create_home=yes password=$6$salt$e2YCufFl6mFwJUwICWWt9Ap6ZJs3CJidpcpat0jvIg5Fk3MW0/HsPzjb1y5EfELmc8uMdsQBeyg7f6h0EyXVC0'


# 删除用户look并移除家目录
[root@ansible ~]# ansible tomcat -m user -a 'name=abc state=absent remove=yes'

2.9 cron模块

  • 计划任务

  • 参数说明:

    • name: 计划任务注释
    • minute: 分(*/3每隔3分钟)
    • hour: 时(*/3每隔3小时|2,5两点和五点|2-5两点到五点)
    • day: 日(*/3每隔3天)
    • month: 月(*/3每隔3个月)
    • weekdy: 周(*/3每隔3周)
    • user: 用户(默认为root)
    • job: 任务
    • disabled=yes: 关闭任务
    • state: 指定状态,prsent表示添加定时任务,也是默认设置,absent表示删除定时任务
# 添加定时任务。每分钟执行一次ls > /dev/null
ansible tomcat -m cron -a "name='ls_cron' job='ls > /dev/null'"

# 每天凌晨两点和凌晨五点执行一次ls > /dev/null
ansible tomcat -m cron -a "name='ls2_cron' minute=0 hour=2,5 job='ls > /dev/null'"

# 关闭定时任务,使定时任务失效
ansible tomcat -m cron -a "name='ls2_cron' minute=0 hour=2,5 job='ls > /dev/null' disabled=yes"

2.10 mount模块

  • 文件系统挂载

  • 参数说明:

    • src: 挂载源

    • path: 挂载的路径

    • fstype: 挂载文件系统类型(ext4、xfs、nfs...)

    • opts: 挂载参数(defaults、ro、rw...)

    • state:

      present(只加fstab文件,不挂载)

      absent(卸载并删fstab文件)

      mounted(挂载并写入fstab)

      unmounted(临时卸载-不删fstab)

# 挂载nfs至/opt目录,并实现开机自动挂载
ansible tomcat -m mount -a "src=192.168.1.70:/nfs_dir path=/opt fstype=nfs opts=deafults state=mounted"

2.11 firewalld模块


2.12 command/shell/raw模块

  • command模块: 执行客户端命令,不支持管道及重定向

  • shell/raw模块: 执行客户端命令,支持管道符及重定向

ansible tomcat -m command -a "who"
ansible tomcat -m row -a "who | grep root > /tmp/test.txt"
ansible tomcat -m shell -a "who | grep root > /tmp/test.txt"

2.13 script模块

  • 在远程主机上执行ansible主机上的脚本,且不需要将脚本复制到被执行的远程主机上

  • 参数说明:

    • creates: 当远程主机上的该文件存在时,不执行脚本,反之执行
    • removes: 当远程主机上的该文件不存在时,不执行脚本,反之执行
cat /tmp/test.sh
rpm -ivh /data/tools/ftp.rpm
rpm -qa | grep ftp 

ansible tomcat -m script -a 'creates=/data/tools/ftp.rpm /tmp/test.sh
ansible tomcat -m script -a 'removes=/data/tools/ftp.rpm /tmp/test.sh

2.14 unarchive模块

  • 用于解压文件

  • 参数说明:

    • copy: 默认为yes,当copy=yes,那么拷贝的文件是从ansible主机复制到远程主机上的,如果设置为copy=no,那么会在远程主机上寻找src源文件
    • src: 源路径,可以是ansible主机上的路径,也可以是远程主机上的路径,如果是远程主机上的路径,则需要设置copy=no
    • dest: 远程主机上的目标路径
    • mode: 设置解压缩后的文件权限
unarchive -a 'src=/srv/tomcat8/apache-tomcat-8.0.29.tar.gz dest=/usr/local copy=no mode=0755'

2.15 setup模块

  • 用于收集远程主机的一些基本信息
# 显示全部
ansible HOST -m setup

# 过滤
ansible HOST -m setup -a "filter=ansible_all_ipv4_addresses"

3. ansible命令参数

-v(--verbose)                           # 输出详细的执行过程信息,可以得到执行过程所有信息;
-i PATH(--inventory=PATH)               # 指定inventory信息,默认为/etc/ansible/hosts;
-f NUM(--forks=NUM)                     # 并发线程数,默认为5个线程;
--private-key=PRIVATE_KEY_FILE            # 指定密钥文件;
-m NAME,—module-name=NAME                # 指定执行使用的模块;
-M DIRECTORY(--module-path=DIRECTORY)   # 指定模块存放路径,默认为/usr/share/ansible;
-a ARGUMENTS(--args=ARGUMENTS)          # 指定模块参数;
-u USERNAME(--user=USERNAME)            # 指定远程主机以USERNAME运行命令;
-l subset(--limit=SUBSET)               # 限制运行主机;

-C (--check)                              # 只是测试一下会改变什么内容,不会真正去执行;相反,试图预
测一些可能发生的变化
--list-hosts                              # 只打印有哪些主机会执行这个 playbook 文件,不是实际执行
该 playbook 文件
--list-tasks                              # 列出所有将被执行的任务
--syntax-check                            # 执行语法检查的剧本,但不执行它
  • Ansible的返回结果非常友好,一般会用三种颜色来表示执行结果:

  • 红色:表示执行过程出现异常;

  • 黄色:表示命令执行后目标有状态变化;

  • 绿色:表示执行成功且没有目标机器做修改;


4. Ansible-playbook

4.1 playbook介绍

  • Ansible-playbook是日常应用中使用频率最高的命令,类似于Linux中的shell脚本或source命令,用来执行系列任务

  • 通过读取预先编写好的playbook文件实现集中处理任务

  • playbook配置文件使用YAML语法,具有简洁明了、结构清晰等特点

  • 当配置一些复杂任务时,在playbook配置文件中放置所有的任务代码,利用ansible-playbook命令执行该文件,可以实现自动化运维

  • YAML文件的扩展名通常为.yaml或.yml


4.2 Ansible-playbook字段

- hosts:                # 任务的目标主机,多个主机用冒号分隔,一般调用/etc/ansible/hosts中的分组信息
  remote_user/user:     # ansible 是通过ssh交互的,这里填的是ssh登录服务器的用户,默认身份为root
  become: yes/no         # 2.6版本以后的参数,之前是sudo,意思为切换用户运行
  become_user: look      # 指定sudo用户为look
  tasks:                # 任务,即定义的具体任务,由模块定义的操作列表;
  vars:                 # 自定义变量在playbook中调用
  handlers:             # 触发器,类似tasks,只是在特定的条件下才会触发的任务。某任务的状态在运行后为changed时,可通过“notify”通知给相应的handlers进行触发执行
  roles:                # 用于层次性、结构化地组织playbook
  gather_facts: yes/no   # facts变量开关
  force_handlers: yes    # 开启强制调用handlers
  max_fail_percentage: 1 # 最大失败百分比
  serial: 1              # 分批执行机器数
# play
any_errors_fatal:强制任何主机上的任何未处理任务错误传播到所有主机并结束播放。
become:布尔值,用于控制是否在任务执行时适用权限提升。
become_flags:当变为True的时候,要传递给权限提升程序的标志。
become_method:适用那种权限升级方法(例如sudo或su)
become_user:
check_mode:布尔值,控制任务是否以check的模式执行
collections
connection
debugger:调试器,根据任务结果的状态启用调试任务
diff
environment:转换未环境变量的字典,在执行时为任务提供。这不会影响Ansible本身及其配置,它只是为负责执行任务的代码设置变量。
fact_path:为gather_facts控制的事实收集插件设置事实路径选项。
force_handlers:即使在播放期间失败,也会强制通知处理程序执行主机
gather_facts
gather_subset:允许您将子集选项传递给gather_facts控制的事实收集插件。
gather_timeout:允许您设置由gather_facts控制的事实收集插件的超时
handlers:处理器,具有被视为处理程序的任务的部分,只有在每个任务部分完成后通知时才会正常执行。
hosts:主机或主机组列表
ignore_errors:布尔值,允许您忽略任务失败并继续执行。它不会影响连接错误。
ignore_unreachable:布尔值,允许您忽略无法访问的主机并继续播放
max_fail_percentage:可以用于在当前批次中给定百分比的主机发生故障后中止运行。
module_defaults:指定模块的默认参数值。
name:
no_log:控制信息泄露的布尔值。
order:控制主机在用于执行播放时的排序。可能的值是inventory(默认),sorted,reverse_sorted,reverse_inventory和shuffle。
port:用于覆盖连接中使用的默认端口。
post_tasks:任务部分后要执行的任务列表。
pre_tasks:在角色之前执行的任务列表。
remote_user:用户通过连接插件登录目标。
roles
run_once:布尔值,它将绕过主机循环,强制任务尝试在第一个可用主机上执行,然后将任何结果和事实应用于同一批次中的所有活动主机。
serial:批次执行
strategy:允许您选择用于播放的连接插件。
tags:应用于任务或包含任务的标签,允许从命令行选择任务子集。
tasks:在游戏中执行的任务的主要列表,它们在角色之后和post_tasks之前运行。
vars:变量
vars_files:变量文件
vars_prompt:交互式变量输入


# Role
any_errors_fatal:强制任何主机上的任何未处理任务错误传播到所有主机并结束播放。
become:布尔值,用于控制是否在任务执行时使用权限升级。
become_flags:当变为 True 时,要传递给权限提升程序的一串标志。
become_method:使用哪种权限升级方法(例如sudo或su)。
become_user:使用权限升级后您“成为”的用户。远程/登录用户必须具有成为此用户的权限。
check_mode:一个布尔值,用于控制任务是否以“检查”模式执行
collections
connection:允许您更改用于在目标上执行的任务的连接插件。
debugger:根据任务结果的状态启用调试任务
delegate_facts:布尔值,允许您将事实应用于委托主机而不是inventory_hostname。
delegate_to:主机执行任务而不是目标(inventory_hostname)。来自委派主机的连接变量也将用于该任务。
diff:切换以使任务返回'diff'信息与否。
environment:转换为环境变量的字典,在执行时为任务提供。这不会影响Ansible本身及其配置,它只是为负责执行任务的代码设置变量。
ignore_errors:布尔值,允许您忽略任务失败并继续播放。它不会影响连接错误。
ignore_unreachable:布尔值,允许您忽略无法访问的主机并继续播放。这不会影响其他任务错误
module_defaults:指定模块的默认参数值。
name:控制信息泄露的布尔值。
no_log:
port:用于覆盖连接中使用的默认端口。
remote_user
run_once
tags
vars
when


# Task
action:任务要执行的操作
any_errors_fatal:强制任何主机上的任何未处理任务错误传播到所有主机并结束播放
args:将参数传递到任务的第二种方法。
async:异步
become:是否在执行任务时进行权限升级
become_flags
become_method:适用那种权限升级的方法
become_user:权限升级后变更的用户
changed_when
check_mode
collections
connection:定义连接插件
debugger:调试器
delay:延迟的秒数
delegate_facts:布尔值,允许您将事实应用于委托主机而不是inventory_hostname。
delegate_to:委托
diff:
environment:配置环境信息
failed_when:任务失败的条件表达式
ignore_errors:
ignore_unreachable:布尔值,允许忽略无法访问的主机并继续播放。
local_action:
loop:循环取值的任务列表
loop_control
module_defaults:指定模块的默认参数值。
name
no_log:控制信息泄露的布尔值
notify:当任务返回changed=True状态时要通知处理程序
poll:轮询
port:连接节点的端口
register:寄存器,接收任务的返回值或者状态
remote_user:连接节点的用户
retries:重试次数,与until配合适用
run_once:运行批次中的一台主机,将结果同步给其它主机
tags:应用于任务或包含任务的标签,允许从命令行选择任务子集
until:直到...结束循环
vars:变量
when:条件表达式
with_<lookup_plugin>

4.3 使用任务计时插件

# 下载插件
cd /etc/ansible 
mkdir callback_plugins 
wget -o callback_plugins/profile_tasks.py  https://raw.githubusercontent.com/jlafon/ansible-profile/master/callback_plugins/profile_tasks.py 


# 修改配置(ansible.cfg 中:[defaults]加入)
callback_whitelist= profile_tasks


# 执行 ansible-playbook 时显示执行时长

5. 变量

5.1 定义变量方式

  • 通过playbook文件vars进行定义

  • 通过inventory主机清单进行定义

  • 通过执行playbook时使用-e参数指定变量


5.2 通过playbook文件vars定义变量

# 定义变量
vars:                     
- var1: 
  - value1
- value2
  - var2: value
  - var3: value
 
# 使用变量
{{ var1[0] }}             
{{ var2 }}


# 示例
- hosts: tomcat          # 执行任务主机
  vars:                  # 定义变量
  - Host:                # Host变量(定义多个值)
    - node01
    - node02
  - Type:                # Type变量
    - Linux
  # vars_files:                                  # 也可以指定文件引用
    #  - ./vars1.yaml
    #  - ./vars2.yaml
  tasks:                 # 执行的具体任务
  - name: Print VAR {{ hostname }},{{ systype }}    # 定义一个任务名
    shell: echo "{{  Host  }},{{ Host[0] }},{{ Host[1] }},{{ Type }}"
    register: Print
  - name: Debug
    debug: var=Print.stdout_lines


# 也可以不注册变量使用"ansible-playbook xxx.yml -v"

5.3 通过inventory主机清单定义变量

  • 在项目目录下创建两个变量的目录 host_varsgroup_vars

    • group_vars目录中的文件名与hosts清单中的组名保持一致,且变量只给对应组使用,

    • group_vars目录中的all文件可提供给所有组使用

    • host_vars目录中的文件名与hosts清单中的主机名保持一致,且变量只给对应主机使用

  • 使用IP定义主机时:找不到主机变量,会去找组变量,找不到组变量找组的all变量

# 目录结构
[root@ansible ~]# tree project/
project/
├── group_vars
│   ├── all
│   ├── tomcat1
│   └── tomcat2
├── hosts
├── host_vars
│   ├── 192.168.1.20
│   └── 192.168.1.21
└── test.yaml


# hosts配置
[root@ansible project]# cat hosts 
[tomcat1]
192.168.1.20:2200
[tomcat2]
192.168.1.21:2200


# group_vars变量
[root@ansible group_vars]# pwd
/root/project/group_vars
[root@ansible group_vars]# ll
total 12
-rw-r--r-- 1 root root 21 Feb  2 20:06 all
-rw-r--r-- 1 root root 24 Feb  2 20:02 tomcat1
-rw-r--r-- 1 root root 24 Feb  2 20:04 tomcat2
[root@ansible group_vars]# cat tomcat1 
file_name: /opt/tomcat1
[root@ansible group_vars]# cat tomcat2 
file_name: /opt/tomcat2
[root@ansible group_vars]# cat all 
file_name2: /opt/all


# host_vars变量
[root@ansible host_vars]# pwd
/root/preject/host_vars
[root@ansible host_vars]# ll
total 8
-rw-r--r-- 1 root root 22 Feb  2 20:25 192.168.1.20
-rw-r--r-- 1 root root 22 Feb  2 20:30 192.168.1.21
[root@ansible host_vars]# cat 192.168.1.20 
ip: /opt/192.168.1.20
[root@ansible host_vars]# cat 192.168.1.21 
ip: /opt/192.168.1.21


# yaml主文件
[root@ansible project]# pwd
/root/preject
[root@ansible project]# cat test.yaml 
- hosts: tomcat1
  tasks:
  - name: Touch {{ file_name }} File
    file: path={{ file_name }}  state=touch

- hosts: tomcat2
  tasks:
  - name: Touch {{ file_name }} File
    file: path={{ file_name }}  state=touch

- hosts: 192.168.1.20
  tasks:
  - name: Touch {{ ip }} File
    file: path={{ ip }}  state=touch

- hosts: 192.168.1.21
  tasks:
  - name: Touch {{ ip }} File
    file: path={{ ip }}  state=touch
- hosts: all
  tasks:
  - name: Touch {{ file_name2 }} File
    file: path={{ file_name2 }}  state=touch


# 验证结果
[root@ansible project]# ansible-playbook --syntax test.yaml  -i hosts 

playbook: test.yaml
[root@ansible project]# ansible-playbook test.yaml 
#192.168.1.20:/opt下创建 192.168.1.20,tomcat1,all三个文件
#192.168.1.21:/opt下创建 192.168.1.21,tomcat2,all三个文件


5.4 通过执行playbook时使用-e参数指定变量

[root@ansible project]# cat test2.yaml 
- hosts: "{{ hosts }}"
  tasks:
  - name: touch {{ hosts }} File
      file: path=/opt/test.txt state=touch
      
[root@ansible project]# ansible-playbook test2.yaml  -e "hosts=192.168.1.20"  --syntax

[root@ansible project]# ansible-playbook test2.yaml  -e "hosts=192.168.1.20"


5.5 变量优先级

  1. 外部传参

  2. playbook-vars_files

  3. playbook-vars

  4. host_vars

  5. group_vars-groupname

  6. group_vats-all


5.6 变量注册-register

  • register结合debug模块可以将执行结果打印出来

  • debug: 调试模块,用于在调试中输出信息,常用参数:

  • msg:调试输出的消息

    • var:将某个任务执行的输出作为变量传递给debug模块,debug会直接将其打印输出
    • verbosity:debug的级别(默认是0级,全部显示)
# register.yaml 
- hosts: k8s_node
  tasks:
  - name: Print String
    shell: echo "aaa"
    register: Test_register           # 注册任务执行结果至变量
  - name: Debug
    debug:                            # 调用debug模块
      msg: "{{ Test_register }}"      # 将注册的变量打印出来


# ansible-playbook  register.yaml -i hosts --syntax
...
...
TASK [Print] **********************************************************************************************************************************************************************
ok: [192.168.1.20] => {
    "msg": {                        # 字典
        "changed": true,            # 执行状态
        "cmd": "echo \"aaa\"",      # 执行的命令
        "delta": "0:00:00.003446",  # 执行了多久
        "end": "2021-02-02 08:38:44.273425",    # 执行结束时间
        "failed": false,            # 失败/否
        "rc": 0,                    # 执行命令结果[0成功|1失败]
        "start": "2021-02-02 08:38:44.269979",  # 执行开始时间 
        "stderr": "",               # 输出错误
        "stderr_lines": [],         # 输出错误行
        "stdout": "aaa",            # 输出结果
        "stdout_lines": [           # 列表的格式输出结果
            "aaa"
        ]
    }
}
...          


# 只输出列表的格式输出结果
msg: "{{ Test_register.stdout_lines }}"
或
var=Print.stdout_lines


# 只输出执行命令结果
msg: "{{ Test_register.rc }}"
或
var=Print.rc

5.7 facts变量

  • ansible内置,用来采集被控端的状态指标,例:IP、主机名称、CPU信息、内存等等

  • 查看所有facts变量:

    ansible user/group -m setup

  • 关闭facts变量:

    gather_facts: no

  • 不使用facts变量会加快ansible的执行速度,如不需要可关闭

# 使用facts变量打印IP地址
- hosts: 192.168.1.20
  tasks:
  - name: OUTPUT HOST IP
    debug:
      msg: IP Address is "{{ ansible_default_ipv4.address }}"


# 配置zabbix-agent
[root@ansible zbx_agent]# ll
total 8
-rw-r--r-- 1 root root 249 Feb  2 22:35 zabbix_agentd.conf.j2
-rw-r--r-- 1 root root 371 Feb  2 22:47 zabbix-agent.yaml


[root@ansible zbx_agent]# cat zabbix_agentd.conf.j2 
PidFile=/var/run/zabbix/zabbix_agentd.pid
LogFile=/var/log/zabbix/zabbix_agentd.log
LogFileSize=0
Server=192.168.1.70
ServerActive=192.168.1.70
Hostname={{ ansible_hostname }}
HostMetadataItem=system.uname
Include=/etc/zabbix/zabbix_agentd.d/*.conf


[root@ansible zbx_agent]# cat zabbix-agent.yaml 
- hosts: 192.168.1.21
  vars:
    conf_name: zabbix_agentd.conf
  tasks:
  - name: Configuer Zabbix Agent
    template: src=./{{ conf_name }}.j2 dest=/etc/zabbix/{{ conf_name }}
    register: zbx_agent
    
  - name: Start Zbx-agent
    service: name=zabbix-agent state=started enabled=yes      

  - name: DISPLAY
    debug:
      msg: "{{ zbx_agent }}"
        

# [root@ansible zbx_agent]# ansible-playbook zabbix-agent.yaml  --syntax

playbook: zabbix-agent.yaml
# [root@ansible zbx_agent]# ansible-playbook zabbix-agent.yaml


# 使用+、-、*、/运算
- hosts: 192.168.1.20
  tasks:
  - name: Test
    shell: echo "{{ ansible_memtotal_mb/2+1-1*2 }}"
    register: mem
    
  - name: OUT
    debug:
      msg: "{{ mem.stdout_lines }}"


# 设置随机8位主机名
- hosts: 192.168.1.21
  tasks:
  - name: Create Name
    shell: echo $RANDOM | md5sum | cut -c 5-12
    register: get_random

  - name: Debug
    debug:
      msg: "{{ get_random }}"

  - name: Set Hostname
    hostname: name={{ get_random.stdout_lines[0] }}
    #hostname: name={{ get_random.stdout }}
# 内置facts变量
# ansible xxx -m setup
ansible_all_ipv4_addresses         # 显示ipv4的信息
ansible_devices                    # 显示磁盘设备信息
ansible_distribution               # 显示是什么系统,例centos,suse等 
ansible_distribution_major_version # 显示是系统主版本
ansible_distribution_version       # 显示系统版本
ansible_machine                    # 显示系统类型,例32位,还是64位 
ansible_eth0                       # 显示eth0的信息
ansible_hostname                   # 显示主机名
ansible_kernel                     # 显示内核版本
ansible_lvm                        # 显示lvm相关信息
ansible_memtotal_mb                # 显示系统总内存
ansible_memfree_mb                 # 显示可用系统内存
ansible_memory_mb                  # 详细显示内存情况
ansible_swaptotal_mb               # 显示总的swap内存
ansible_swapfree_mb                # 显示swap内存的可用内存
ansible_mounts                     #显示系统磁盘挂载情况
ansible_processor                  # 显示cpu个数(具体显示每个cpu的型号)
ansible_processor_vcpus            # 显示cpu个数(只显示总的个数)

6. playbook Task任务控制语句

6.1 判断语句:when

  • 场景:

    • web服务器的角色需要安装nginx,其他服务器角色不需要

    • Centos和Ubuntu系统都需要安装nginx,但不同系统的安装包不一样,需要判断主机系统安装对应的软件包

  • 比较运算符

    • ==: 比较两个对象是否相等,相等为真
    • !=: 比较两个对象是否不等,不等为真
    • > : 比较两个值的大小,如果左边的值大于右边的值,则为真
    • < : 比较两个值的大小,如果左边的值小于右边的值,则为真
    • >=: 比较两个值的大小,如果左边的值大于右边的值或左右相等,则为真
    • <=: 比较两个值的大小,如果左边的值小于右边的值或左右相等,则为真
  • 逻辑运算符

    • and: 两个条件都为真
    • or: 一个条件为真即可
# 只在192.168.1.20这台机器创建文件
[root@ansible project]# cat when.yaml 
- hosts: tomcat
  tasks:
  - name: Test
    file: path=/opt/when.txt state=touch
    # 判断主机ip
    when: (ansible_default_ipv4.address == "192.168.1.20" and ansible_os_family == "RedHat")  
    
    # 判断主机名开头匹配tomcat
    # when: ( ansible_fqdn is match ("tomcat*") ) 
    
    # 根据action的执行结果,来决定接下来执行的action
    # when: result|failed
    # when: result|success
    # when: result|skipped
    
[root@ansible project]# ansible-playbook when.yaml 
...
...
skipping: [192.168.1.21]   #ip非192.168.1.20,跳过不执行
changed: [192.168.1.20]

PLAY RECAP ************************************************************************************************************************************************************************
192.168.1.20               : ok=2    changed=1    unreachable=0    failed=0   
192.168.1.21               : ok=1    changed=0    unreachable=0    failed=0   


# 判断之前的执行结果来决定是否继续执行
- hosts: tomcat
  tasks:
    - name: Test
      file: path=/opt/when.txt state=touch
      register: test_when               #执行结果注册为变量

    - name: Test2
      file: path=/opt/when2.txt state=touch
      when: test_when.failed == false   #判断上一个任务failed返回是否为false

6.2 循环语句:with_items

  • 用于循环处理任务
# 循环创建文件
- hosts: 192.168.1.20
  tasks:
  - name:
    with_items:                            # 定义一个循环列表
    - test_item1
    - test_item2
    file: name=/tmp/{{ item }} state=touch # 引用列表,列表有几次就循环几次
     
        
# 使用变量字典批量创建用户
- hosts: 192.168.1.20
  vars:
  - var1:
    - user3
    - root
  tasks:
  - name: Create Users
    user: name={{ item.user }} groups={{ item.groups }} state=present
    with_items:
    - { user: 'user1',groups: 'root' }
    - { user: 'user2',groups: 'root' }
    - { user: '{{ var1[0] }}',groups: '{{ var1[1] }}' }
      

# with_items结合stdout_lines批量删除用户
- hosts: 192.168.1.100
  tasks:
    - name: Filter User
      raw: "cat /etc/passwd | awk -F: '{print $1}' | grep test"
      register: users
      
    - name: Delete User
      raw: userdel -r {{ item }}
      with_items: "{{ users.stdout_lines }}"

6.3 handlers触发器

  • notify监控-->通知-->handlers触发

  • 应用场景:

    • 配置发生变化后重启服务,配置未变化不重启
  • 注意事项:

    • 无论多少个任务通知了相同的handlers ,handlers 仅会在所有tasks结束后运行一次
    • 只有当tasks中的action的执行状态是 changed 时,才会触发notify handler的执行,没有改变不触发
    • handlers 是一个特殊的 tasks,不能使用 handlers 替代 tasks
[root@ansible handlers]# ll
total 12
-rw-r--r-- 1 root root 238 Feb  3 03:26 handlers.yaml
-rw-r--r-- 1 root root  32 Feb  3 03:29 index.html.j2
-rw-r--r-- 1 root root 514 Feb  3 03:11 tomcat.sh

[root@ansible handlers]# cat index.html.j2 
hostname: {{ ansible_hostname}}

[root@ansible handlers]# cat handlers.yaml 
- hosts: tomcat
  tasks:
  - name: update_tomcat
    template: src=./index.html.j2 dest=/usr/tomcat/webapps/ROOT/index.html    
    notify: restart tomcat        # 监控执行任务是否发生改变
  handlers:                       # 触发器(发生改变后执行下面任务)
  - name: restart tomcat
    script: ./tomcat.sh restart   # 执行重启脚本

6.4 tags标签

  • 根据指定的标签执行,用于调试

    • 对一个tasks指定一个tags标签

    • 对一个tasks指定多个tags标签

    • 对多个tasks任务指定一个tags标签

  • 可将不想执行的task所指定同一个标签,使用--skip-tags "tags"跳过
    可通过-t "tags"指定标签只执行标签对应任务

- hosts: tomcat
  tasks:
  - name: task1
    modules1: - - -
    tags: 1
    tags: 11
  - name: task1
    modules1: - - -
    tags:1
    tags:2

6.5 include

  • 可多个playbook复用include
[root@ansible include]# ll
total 12
-rw-r--r-- 1 root root  58 Feb  3 04:11 include.yaml
-rw-r--r-- 1 root root 111 Feb  3 04:09 test2_include.yaml
-rw-r--r-- 1 root root 114 Feb  3 04:11 test_include.yaml

[root@ansible include]# cat include.yaml  # 只定义重启ntpd任务
- name: restart ntpd
  service: name=ntpd state=restarted
  
[root@ansible include]# cat test_include.yaml 
- hosts: tomcat
  tasks:
  - name: task1
    shell: echo "1"
  - name: restart ntpd
    include: ./include.yaml   # 调用include.yaml
    
[root@ansible include]# cat test2_include.yaml 
- hosts: tomcat
  tasks:
  - name: task2
    shell: echo "2"
  - name: restart ntpd
    include: include.yaml     # 调用Include.yaml

6.6 错误控制 (ignore_errors、force_handlers、failed_when、changed_when)

  • ignore_errors: 错误忽略,继续往下执行
- hosts: tomcat
  tasks:
  - name:
    shell: afsfs
    ignore_errors: yes
  - name:
    shell: echo aaa
  • force_handlers:当后续task返回为failed时,无论如何都执行handler
- hosts: tomcat
  force_handlers: yes           #开启强制调用handlers
  tasks:
  - name: update_tomcat
    template: src=./index.html.j2 dest=/usr/tomcat/webapps/ROOT/index.html  
    notify: restart tomcat
    - name: test
      shell: aaa
  handlers:
  - name: restart tomcat
    script: ./tomcat.sh restart

  • failed_when: 当对应的条件成立时,将对应任务的执行状态设置为失败
- hosts: 192.168.10.11
  tasks:
  - shell: echo | telnet 127.0.0.1 80
    register: check_failed_when
    failed_when: '"command not found" in check_failed_when.stderr'  #  stderr输出command not found才为failed,其余都为ok

  • changed_when: 控制changed返回

    • changed_when关键字的作用是在条件成立时,将对应任务的执行状态设置为changed

    • 当task真正执行后状态变为change时候 handler下的task生效,而将changed_whend状态改为ok,则不会触发

# 关闭changed状态
- hosts: tomcat
  tasks:
    - name: test
      shell: ps -ef | grep java
      changed_when: false   #关闭changed状态
      

# changed_when状态处理
- hosts: tomcat
  tasks:
  - name: test
    shell: id -u
    register: test_changed_when
    # changed_when: ( test_changed_when.stdout.find('0'))  # 包含指定字符状态为ok,其他仍为changed
    # changed_when: '"1" in test_changed_when   '          # 包含指定字符才能为changed,其他全为ok
    # changed_when: 1 > test_changed_when                  # 条件成立为ok,其他仍为changed
    notify: Print

  handlers:
  - name: Print
    debug: var=test_changed_when.stdout_lines

6.7 Delegate_to(任务委派)

  • Ansible 默认只会在定义好的 一组服务器 上执行相同的操作,。但如果在这过程中需要同时对 另外 1 台机器 执行操作时,就需要用到 Ansible 的任务委派功能 delegate_to

  • 使用 delegate_to 关键字可以委派任务到指定的机器上运行。

- hosts: k8s_node
  tasks:
  - name: add hosts to server
    shell: echo "{{ ansible_hostname }} xxx.com" >> /etc/hosts
    # 指定委派机器
    delegate_to: 192.168.10.10
    # 指定该task只能在某一台机器上执行一次. 可以和delegate_to 结合使用,如果没有delegate_to, 那么这个task会在第一台机器上执行
    run_once: true
    # 委派本机
    # local_action: shell 'echo "{{ ansible_hostname }} xxx.com" >> /etc/hosts'
    # 委派本机方法2
    # connection: local 


# 委派组
- hosts: xxx
  tasks:
    - name: xxx
      xxx: xxx
      delegate_to: "{{item}}"
      with_items: "{{groups['组名']}}"

6.8 serial(批次执行)和最大失败百分比(max_fail_percentage)

  • serial: 默认情况下,Ansible将尝试并行管理playbook中所有的机器。对于滚动更新用例,可以使用 serial 定义一次应管理多少主机

  • serial(单个play)这个并行数不能超过forks(全局)

  • 默认情况下, 只要group中还有server没有失败, ansible就继续执行tasks. 实际上, 用户可以通过"max_fail_percentage" 来定义, 只要超过max_fail_percentage台的server失败, ansible 就可以中止tasks的执行

- hosts: xxx
  # 最大失败百分比
  max_fail_percentage: 1

  # 批次执行机器数
  serial: 1
  # 也可以按百分比
  # serial: 50% 
  # 实际失败机器必须大于这个百分比时, tasks才会被中止. 等于时是不会中止tasks的.
  
  tasks:
  - name: xxx
    Module: xxx

6.9 Strategy执行策略

  • strategy的作用范围是一个play,通过设置不同参数,控制一个play内所有任务的执行策略,策略默认值为linear,可选参数为free

  • 执行策略:

    • linear策略: 即线性执行策略,线性执行策略指主机组内所有主机完成一个任务后才继续下一个任务的执行,在执行一个任务时,如果某个主机先执行完则会等待其他主机执行结束。说直白点就是第一个任务在指定的主机都执行完,再进行第二个任务的执行,第二个任务在指定的主机都执行完后,再进行第三个任务的执行…… 以此类推
    • free策略: 即自由策略,即在一个play执行完之前,每个主机都各顾各的尽可能快的完成play里的所有任务,而不会因为其他主机没执行完任务而等待,不受线性执行策略那样的约束。所以这种策略的执行结果给人感觉是无序的甚至是杂乱无章的,而且每次执行结果的task显示顺序很可能不一样。
# 格式
- hosts: xxx
  strategy: free
  tasks:
  ...

6.10 async、poll、wait_for

  • ansible的同步模式与异步模式执行区别:

    • 同步模式: 如果节点数太多,ansible无法一次在所有远程节点上执行任务,那么将先在一部分节点上执行一个任务(每一批节点的数量取决于fork进程数量,默认为5个,可设置),直到这一批所有节点上该任务完全执行完毕才会接入下一个批节点,直到所有节点将该任务都执行完毕,然后重新回到第一批节点开始执行第二个任务。依次类推,直到所有节点执行完所有任务,ansible端才会释放shell。这是默认的同步模式,也就是说在未执行完毕的时候,ansible是占用当前shell的,任务执行完毕后,释放shell了才可以输入其他命令做其他动作。

    • 异步模式:假如fork控制的并发进程数为5,远程控制节点为24个,则ansible一开始会将5个节点的任务扔在后台,并每隔一段时间去检查这些节点的任务完成情况,当某节点完成不会立即返回,而是继续等待直到5个进程都空闲了,才会将这5个节点的结果返回给ansible端,ansible会继续将下一批5个节点的任务扔在后台并每隔一段时间进行检查,依次类推,直到完成所有任务。

    • 在异步模式下,如果设置的检查时间间隔为0,在将每一批节点的任务丢到后台后都会立即返回ansible,并立即将下一批节点的任务丢到后台,直到所有任务都丢到后台完成后,会返回ansible端,ansible会立即释放占用的shell。也就是说,此时ansible是不会管各个节点的任务执行情况的,不管执行成功还是失败。因此,在轮训检查时间内,ansible仍然正在运行(尽管某批任务已经被放到后台执行了),当前shell进程仍被占用处于睡眠状态,只有指定的检查时间间隔为0,才会尽快将所有任务放到后台并释放shell。

  • ansible并发和异步:

    • StrategyForksSerial都是ansible同步阻塞模式下的优化方法,其中,strategy是通过控制任务执行策略进行优化,forksserial是通过控制并行数进行优化

    • ansible默认只会创建5个进程,所以一次任务只能同时控制5台机器执行。那如果你有大量的机器需要控制,或者你希望减少进程数,那你可以采取异步执行。

    • 使用asyncpoll这两个关键字便可以并行运行一个任务。 async这个关键字触发ansible并行运作任务,而async的值是ansible等待运行这个任务的最大超时值(如果执行超时任务会强制中断导致失败),而poll就是ansible检查这个任务是否完成的频率时间。

    • poll的缺省值为10,设置为0表示 不用等待结果,继续做下面的操作

    • async设置为0会一直等待命令结束

  • wait_for: 有些情况下,一些任务的运行需要等待一些状态的恢复,比如某一台主机或者应用刚刚重启,需要等待它的某个端口开启,此时就需要将正在运行的任务暂停,直到其状态满足要求

    • wait_for模块常用参数:

    • timeout:wait_for的等待的超时时间,默认为300秒,超时即失败,不再往后执行

    • connect_timeout:在下一个任务执行之前等待连接的超时时间

    • delay:开始轮询之前等待的秒数,检测间隔时间,默认是0

    • host:等待的主机的地址,默认为127.0.0.1

    • port:等待的主机的端口

    • path:文件路径,只有当这个文件存在时,下一任务才开始执行,即等待该文件创建完成

    • state:等待的状态,即等待的文件或端口或者连接状态达到指定的状态时,下一个任务开始执行。

      • 当等的对象为端口时,状态有startedstopeddrained,即端口监听|端口关闭|端口连接;

      • 当等待的对象为文件时,状态有present或者startedabsent,即文件已创建或者删除;

# 重启服务器
- hosts: k8s_node
  serial: 1
  tasks:
    - name:
      shell: reboot
      async: 1
      poll: 0
      notify:
        - wait_reboot

  handlers:
    - name: wait_reboot
      local_action:
        module: wait_for
        host: "{{ ansible_default_ipv4.address }}"
        port: 22
        delay: 10
        timeout: 300
        state: started

7. Jinjia2模板

  • ansible通常会使用 jinja2 模板来修改被管理主机的配置文件

  • 使用ansible的 jinja2 模板,需使用template模块。该模块和copy模块一样,区别是template模块可以获取要复制的文件中变量的值,而copy则是原封不动的把文件复制过去

  • ansible允许 jinja2 模板中使用条件判断循环,但是不允许在playbook中直接使用

  • 后缀为 .j2 代表jinja模板文件,当然这个后缀可以随便写,只是为了规范


7.1 使用Jinja2模版

  • playbook中的tasks必须使用template模块

  • 模板配置文件里使用变量,如 {{ var1 }} {{ facts变量 }}

  • 模板中可使用循环、判断表达式


7.2 Jinjia循环与判断

  • 判断语法
    {% if EXPR %}...{% elif EXPR %}...{% eles %}...{% endif %} #

  • 循环语法
    {% for i in EXPR %}...{% endfor % }

  • 注释
    {# COMMENT #}


7.3 Jinjia2模板示例

[root@ansible Jinja]# cat jinja.j2          #ansible的Jinja文件 
{% if ansible_fqdn == "tomcat-node01" %}    #判断主机名tomcat1配置:role:master
role:master
{% elif ansible_fqdn == "tomcat-node02" %}  #判断主机名tomcat2配置:role:slave
role:slave
{% else %}                                  #否则配置:role:backup
role:backup
{% endif %}                                 #结束

{% for i in range(0,3) %}                   #0-2循环三次
server 192.168.1.{{i}};                     #循环配置
{% endfor%}                                 #结束循环


[root@ansible Jinja]# cat jinja.yaml        #ansible的yaml文件
- hosts: tomcat
  tasks:
  - name: template
    template: src=./jinja.j2 dest=/tmp   


[root@ansible Jinja]# ansible-playbook jinja.yaml

# tomcat-node01节点
[root@tomcat-node01 ~]# cat /tmp/jinja.j2 
role:master
server 192.168.1.1;
server 192.168.1.2;

# tomcat-node02节点
[root@tomcat-node02 ~]# cat /tmp/jinja.j2 
role:slave
server 192.168.1.1;
server 192.168.1.2;

8. roles

  • roles功能可以用来规范playbook的编写

8.1 创建roles

[root@ansible ~]# mkdir /ansible_roles

[root@ansible ~]# cd /ansible_roles/

[root@ansible ansible_roles]# ansible-galaxy init ROLE_NAME #使用命令创建,也可以自己一个一个创建

[root@ansible ansible_roles]# touch install_tomcat.yml

[root@ansible ansible_roles]# tree /ansible_roles/
/ansible_roles/
│ main.yml                  # 主配置文件
├── install_tomcat          # 入口
│   ├── defaults
│   │   └── main.yml
│   ├── files               # copy模块和script模块的参数src默认会从这个文件夹查找
│   ├── handlers            # 用来存放notify
│   │   └── main.yml
│   ├── meta                # 用来存放依赖关系,可包含其他role
│   │   └── main.yml
│   ├── README.md
│   ├── tasks               # 用来存放ansible具体任务
│   │   └── main.yml
│   ├── templates           # 用来存Jinja2
│   ├── tests
│   │   ├── inventory
│   │   └── test.yml
│   └── vars                # 用来存变量
│       └── main.yml


8.2 案例:安装tomcat更新文件并启动

# 目录结构
[root@ansible /]# tree /ansible_roles/
/ansible_roles/
├── install_tomcat
│   ├── files
│   │   ├── apache-tomcat-8.5.40.tar.gz
│   │   ├── jdk-8u144-linux-x64.rpm
│   │   └── tomcat.sh
│   ├── handlers
│   │   └── main.yml
│   ├── tasks
│   │   └── main.yml
│   ├── templates
│   │   └── index.html.j2
│   └── vars
│       └── main.yml
└── main.yml

6 directories, 8 files


# 主配置文件
[root@ansible ansible_roles]# cat main.yml 
- hosts: tomcat
  serial: 1
  gather_facts: True
  roles:
  - install_tomcat
  #- update_tomcat


# vars文件
[root@ansible vars]# pwd
/ansible_roles/install_tomcat/vars

[root@ansible vars]# cat main.yml 
Tomcat_Package: apache-tomcat-8.5.40.tar.gz
Un_Tomcat_Package: apache-tomcat-8.5.40
Tomcat_Path: /usr/tomcat
Jdk_Package: jdk-8u144-linux-x64.rpm
my_name:
- shao
- chong


# files
[root@ansible files]# ll 
total 175456
-rw-r--r-- 1 root root   9690027 Feb  3 20:27 apache-tomcat-8.5.40.tar.gz
-rw-r--r-- 1 root root 169971490 Feb  3 20:27 jdk-8u144-linux-x64.rpm
-rw-r--r-- 1 root root       555 Feb  3 23:38 tomcat.sh
[root@ansible files]# cat tomcat.sh 
#!/bin/bash
Tomcat_Path=/usr/tomcat
Num=$(ps -ef | grep [j]ava | wc -l)

case $1 in
    start)
        [ $Num -eq 0 ] &&nohup sh $Tomcat_Path/bin/startup.sh || echo "tomcat alreadly startup,pid: $(jps | awk '/Bootstrap/{print $1}')"
    ;;
    stop)
        [ $Num -gt 0 ] &&nohup kill -9 $(jps | awk '/Bootstrap/{print $1}')
    ;;
    restart)
       if [ $Num -eq 0 ];then 
           nohup sh $Tomcat_Path/bin/startup.sh
       else 
           kill -9 $(pidof java)
           nohup sh $Tomcat_Path/bin/startup.sh
       fi
    ;;
esac


# handlers
[root@ansible handlers]# cat main.yml 
- name: start_tomcat
  script: tomcat.sh start
  remote_user: root
  sudo: yes
  sudo_user: tomcat

- name: stop_tomcat
  script: tomcat.sh stop
  remote_user: root
  sudo: yes
  sudo_user: tomcat

- name: restart_tomcat
  script: tomcat.sh restart
  remote_user: root
  sudo: yes
  sudo_user: tomcat
  

# template
[root@ansible templates]# cat index.html.j2 
{% if ansible_fqdn == "tomcat-node01" %}    
role: master
{% elif ansible_fqdn == "tomcat-node02" %}
role: slave
{% endif %}
host-name: {{ ansible_nodename }}
my-name: {{ my_name[0] }}


# tasks
- name: Copy Tomcat  Data
  copy: src={{ item.Packages }} dest=/tmp
  with_items:
  - { Packages: '{{ Tomcat_Package }}' }
  - { Packages: '{{ Jdk_Package }}' }

- name: Uncompress {{ Tomcat_Package }}
  unarchive: src=/tmp/{{ Tomcat_Package }} dest=/usr copy=no mode=0755
  tags: un 

- name: Rname {{ Tomcat_Package }} 
  shell: mv /usr/{{ Un_Tomcat_Package }} {{ Tomcat_Path }}


- name: Install {{ Jdk_Package }}
  shell: rpm -ivh /tmp/jdk-8u144-linux-x64.rpm
  ignore_errors: yes
  #register: check_failed_when
  #failed_when: ( check_failed_when.stdout.find('aa')) 
  tags: jdk

- name: Create Tomcat User
  user: name=tomcat uid=500 shell=/bin/bash create_home=yes
  
- name: Set Pwd
  shell: echo "tomcat" | passwd --stdin tomcat
  tags: passwd

- name: Set Tomcat Directory
  file: path={{ Tomcat_Path }} owner=tomcat group=tomcat recurse=yes
  tags: set_path

- name: Touch Index
  template: src=index.html.j2 dest={{ Tomcat_Path }}/webapps/ROOT/index.html
  remote_user: root
  sudo: yes
  sudo_user: tomcat
  notify: restart_tomcat

- name: Start Tomcat
  script:  tomcat.sh start
  remote_user: root
  sudo: yes
  sudo_user: tomcat
  register: start

- name: Check Tomcat-server Status
  shell: "ps -ef | grep [j]ava &> /dev/null ; echo $?"
  register: status_result
  changed_when: ( status_result.stdout.find('0'))

- name: Debug
  debug: #var=status_result
    msg: "{{ status_result }}"


#检查语法: 
[root@ansible ansible_roles]# ansible-playbook main.yml --syntax
playbook: main.yml

#执行
[root@ansible ansible_roles]# ansible-playbook main.yml

#跳过标签执行
[root@ansible ansible_roles]# ansible-playbook main.yml   --skip="jdk,passwd,se_path,un"