其实我是棵树 发表于 2024-4-5 18:35:01

RHCE(剧本和变量)

一:ansible剧本

1:简介

 一系列ansible命令的集合,使用yaml语言进行编写的,从上往下的执行,支持很多的特性,比如,将某个命令的状态作为变量给其他的任务执行,变量,循环,判断,错误纠正,可以是一个playbook或者是多个playbook执行
2:yaml基本语法

1、yaml约束


[*]编写yaml的时候,不能使用tab键,只能使用空格(vim版本有关系,vim会自动将tab转化为4个空格键)
[*]大小写严格区别,大写是大写,小写是小写;大写的变量和小写的变量是不一样的
[*]使用缩进来表示一个层级关系
[*]使用空格来表示层级关系,空格的数量没有限制,使用#表示注释
2、yaml数据类型


[*]纯量:类似于变量的值,最小的单位。无法进行切割
[*]数组(序列、列表):一组有次序的值,每一个 ‘-’ 就是一个列表
[*]对象(键值对)字典、哈希、映射:key=value
3:playbook

1、简单的案例

# cat file1.yaml
- name: touch file1
hosts: all
tasks:
    - name:
      file:
         path: /opt/file1
         state: touch  2、输出的信息解读

 
# ansible-playbook file1.yaml

##这个是playbook的名字
PLAY *************************************************************
#这个是playbook第一个任务,默认的任务。用于收集远程主机的各种信息,Ip等
TASK *********************************************************
ok:
#工作任务
TASK ********************************************************************
changed:
#这个就是执行命令后的总结
PLAY RECAP *********************************************************************
client                     : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


#rc为0的话代表这个执行成功,不为0的话,代表这执行失败了  可以使用-v查看详细的信息,但是最多是4个v
 
3、playbook执行之前的检查

在执行之前使用命令检查一下
#检查语法的问题(正常的情况下)
# ansible-playbookfile1.yaml--syntax-check

playbook: file1.yaml

#错误的情况
# ansible-playbookfile1.yaml--syntax-check
ERROR! A malformed block was encountered while loading tasks: {'-name': {'file': {'path': '/opt/file1', 'state': 'touch'}}} should be a list or None but is <class 'ansible.parsing.yaml.objects.AnsibleMapping'>

The error appears to be in '/mnt/file1.yaml': line 1, column 3, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:


- name: touch file1
^ here

#还有一个就是语法正确但是输出有问题的情况下
使用-C就是模拟执行的,但不是真正的执行
# ansible-playbook file1.yaml -C

PLAY ************************************************************************

TASK ********************************************************************
ok:

TASK *******************************************************************************
ok:

PLAY RECAP ********************************************************************************
client                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0     
 
4、多个剧本

 很多个剧本写在了一起,有很多的任务
# cat file1.yaml
- name: touch file1
hosts: client
tasks:
    - name:
      file:
         path: /opt/file1
         state: touch
- name: touch file2
hosts: server
tasks:
    - name:
      file:
      path: /opt/file2
      state: touch  
5、playbook结构上

1)主机或者主机组

定义play的名字,远程操作的主机,用户,提权等相关的配置
2)变量

定义变量,然后输出
# cat var.yaml
- name: var
hosts: client
vars:
    RHCE: rhel9
tasks:
    - name:
      debug:
      var: RHCE3)任务列表

就是tasks里面的
4)handler(特殊的任务)

 通过监听某个task或者几个task,然后这个tasks执行成功后,并且状态是chaged,所有的任务执行成功后,最后再来执行
 就是要先触发再来执行
# cat var.yaml
- name: var
hosts: client
tasks:
    - name: file1
      file:
      path: /mnt/file1
      state: touch
      notify: get_status
handlers:
    - name: get_status
      file:
      path: /mnt/file2
      state: touch

#要使用notify这个参数,来进行监听,类似与键值对的  必须是chaged才执行,并且其他的任务也没有出现错误,才行,
6、剧本出现了错误

现象:

# cat var.yaml
- name: var
hosts: client
tasks:
    - name: file1
      shell: ls /opt/qwqwqwq
    - name: file2
      file:
      path: /mnt/file2
      state: touch

#后面的命令不会执行  总结:

 1、就是运行剧本到一半的时候,出现了错误,会立刻的停止(对于当前任务的主机二样),不会影响到其他任务的主机(正常的运行),下次执行的话,就是从错误的地方开始执行
 2、按照主机划分,执行任务
3、具有幂等性,就是一次和多次执行的结果都一样
  如果期望值和执行的结果一致的话,则任务就是执行成功
  如果任务执行的前后,内容上的修改(文件的时间戳也算),那么再次执行的任务的话,就会发生覆盖
解决的方法

1)命令模块报错
使用||,配置/usr/bin/true,让rc的返回值为0
# cat var.yaml
- name: var
hosts: client
tasks:
    - name: file1
      shell: ls /opt/qwqwqwq || /usr/bin/true
    - name: file4
      file:
      path: /mnt/file4
      state: touch  
2)其他模块报错
 使用ignore_errors: true跳过错误的任务,继续执行后面的任务
# cat var.yaml
- name: var
hosts: client
tasks:
    - name: file1
      shell: ls /opt/qwqwqwq
      ignore_errors: true
    - name: file3
      file:
      path: /mnt/file3
      state: touch

执行后,会报错,但是执行了后面的创建file3的操作3)handler方法
使用handler来规避错误,如果有任务错误的话,handler也不会执行,所有的任务执行完成后,才会执行handler的task任务,强制去执行handler任务,加上一个字段force_handlers:yes即可
#中间有错误,依然执行
# cat var.yaml
- name: var
hosts: client
force_handlers: yes
tasks:
    - name: touch file10
      file:
      path: /opt/file10
      state: touch
      notify: get_status
    - name: chakan
      shell: ls /opt/121212
handlers:
    - name: get_status
      file:
      path: /opt/file11
      state: touch

#如果监听的是错误了
那就无法执行了 
二:ansibel变量的定义和引用

1:为什么需要变量

 ansible管理很多个主机的时候,就是他们的主机名或者顺序不一样什么的,就需要使用变量来进行一个统一的管理,或者端口什么的都可以使用变量来进行定义
1、变量有数字,字母,下划线,组成,
2、变量名可以是字母,不能以数字开头,严格区分大小写
3、在自定义变量的时候,不要以ansible开头,因为系统中有ansible开头的变量
案例:
# cat v1.yml
- name: use vars
hosts: client
vars:
    name_rhel: rhel9
tasks:
    - shell: touch "/opt/{{name_rhel}}"

#使用vars来定义变量,然后使用{{}}来引用变量 4、调试变量的方式
需要使用debug模块来进行调试,有2个参数来进行调试的,一个是var,另外一个是msg,2个不能一起使用
可以显示出变量的内容,但是其他的模块在执行剧本后,看不到返回的消息
msg:使用{{变量名}},来进行输出内容
var:name_rhel,age_rhel可以输出多个变量名
#专门打印变量的内容,无法打印信息
# cat v1.yml
- name: use vars
hosts: client
vars:
    name_rhel: rhel9
tasks:
    - name: debug
      debug:
      var: name_rhel

#mes可以打印内容,也可以打印信息
# cat v1.yml
- name: use vars
hosts: client
vars:
    name_rhel: rhel9
tasks:
    - name: debug
      debug:
      msg: "this is a {{name_rhel}}"  2:主机清单中定义变量

 内置变量:就是这些变量ansible都自定义好了的
ansible_become类似的,这种主机清单的优先级比配置文件的优先级高
1、定义主机变量

# cat hosts
client webserver=nginx
#使用ad-hoc调用debug模块,打印变量
client | SUCCESS => {
    "webserver": "nginx"
}   2、定义主机组变量

#使用[主机组:vars]这样的方式来进行定义
# cat hosts
client webserver=nginx

client

rhelname=rhce

当然,如果主机组的变量和主机发生了冲突的话,以主机的优先级高为主
# cat hosts
client rhelname=nginx

client

rhelname=rhce
client | SUCCESS => {
    "rhelname": "nginx"
}3、通过主机和主机组的目录文件定义变量

里面定义的都是键值对的方式来定义的,注意格式的规范
与hosts文件在同一个路径下创建2个目录,然后主机名为命名的文件即可
主机的目录hosts_vars,以主机名为命名的文件,只有该主机能够引用变量
mkdir /etc/ansible/host_vars
# cat client
name: rhel9

引用变量:
# ansible client -m shell -a 'echo "{{name}}"'
client | CHANGED | rc=0 >>
rhel9主机组的目录为group_vars,只有该主机组能引用变量
mkdir /etc/ansible/group_vars
# cat servers
age: 100

#引用变量
# ansible servers -m shell -a 'echo "{{age}}"'

client | CHANGED | rc=0 >>
1002、剧本中定义变量

1:vars来定义

就是在task任务之前定义即可,可以定义多次变量
就是定义了这个变量之后,然后可以不用重复的写这个内容,直接调用这个变量即可,代码就省略了很多,但是呢,这个变量是已经写死了的
#Vars定义,输出出来
# cat v1.yaml
- name: use vars
hosts: client
vars:
    rname: rhel9
    rage: 80
tasks:
    - name: use shell
      shell: echo "{{rname}} {{rage}}" > /opt/file111

#使用debug模块来进行引用变量
使用var来引用变量
# cat v1.yaml
- name: use vars
hosts: client
vars:
    rname: rhel9
    rage: rrr
tasks:
    - name: use debug
      debug:
      var: rname,rage

#使用msg来引用变量
# cat v1.yaml
- name: use vars
hosts: client
vars:
    rname: rhel9
    rage: rrr
tasks:
    - name: use debug
      debug:
      msg: this is "{{rname}} {{rage}}\n"2:vars_files来定义

引入外部的变量文件,外部的变量文件的内容是字典的形式,不能使用列表的形式(-)
使用vars_files这个参数
引用的话使用   键.键的方式来引用变量即可
文件的写法
第一种写法
# cat file1.yaml
user:
name: zhangshan
age: 18
sex: boy

引用变量
#使用{{user.name}}这中键的方式来获取值
# cat file1.yaml
user:
name: zhangshan
age: 18
sex: boy
# cat v1.yaml
- name: use vars
hosts: client
vars_files:
    - /mnt/file1.yaml
tasks:
    - name: shell
      shell: echo "{{user.name}}" >> /opt/file111

#第二种写法,就是一个大的字典
# cat file2.yaml
users:
job:
    name: aaa
    age: 90
joe:
    name: bbb
    age: 80


#输出
# cat v1.yaml
- name: use vars
hosts: client
vars_files:
    - /mnt/file2.yaml
tasks:
    - name: shell
      shell: echo "{{users.joe.name}}" >> /opt/file1113:注册变量

就是将一个任务的执行结果注册为一个变量
使用关键字register去得到任务的执行结果
#就是如果使用剧本来执行任务的话,就是不显示详细的信息,可以使用注册变量来让其显示详细的信息,并且也可以按照指定的变量来进行输出
# cat v1.yaml
- name: use vars
hosts: client
tasks:
    - name: shell
      shell: ls /etc/passwd
      register: get_status
    - debug:
      var: get_status

#执行这个剧本
ok: => {
    "get_status": {
      "changed": true,
      "cmd": "ls /etc/passwd",
      "delta": "0:00:00.002357",
      "end": "2024-03-26 16:43:34.086058",
      "failed": false,
      "rc": 0,
      "start": "2024-03-26 16:43:34.083701",
      "stderr": "",
      "stderr_lines": [],
      "stdout": "/etc/passwd",
      "stdout_lines": [
            "/etc/passwd"
      ]
    }
}



可以指定要的变量
# cat v1.yaml
- name: use vars
hosts: client
tasks:
    - name: shell
      shell: ls /etc/passwd
      register: get_status
    - debug:
      var: get_status.rc

#一般应用的场景就是需要收集被控节点的信息,并且将其保存到主控节点上面
#使用这个变量来对其进行操作
# cat v1.yaml
- name: use vars
hosts: client
tasks:
    - name: shell
      shell: ls /etc/passwd
      register: get_status
    - copy:
       content: "{{get_status.rc}}"
       dest: /opt/file222 
案例:就是收集被控节点的信息,然后拷贝到主控节点上面去
先使用copy模块,将信息存放到被控节点上面,在使用fetch模块,将信息存放到主控节点上面
  
4:命令模式来定义变量

使用临时定义的变量,非常的有用
# ansible --help|grep EXTRA_VARS
               [-e EXTRA_VARS] [--vault-id VAULT_IDS]
-e EXTRA_VARS, --extra-vars EXTRA_VARS

#使用剧本的方式,临时定义变量
# cat v1.yaml
- name: use vars
hosts: client
tasks:
    - name: file
      file:
      path: "{{path}}"
      state: touch

#如果有多个变量的话,就使用双引号加上逗号
# ansible-playbookv1.yaml -e "path=/opt/eeee"


#使用ad-hoc来定义变量
# ansible client -m debug -a 'var=rhel_name' -e rhel_name=90
client | SUCCESS => {
    "rhel_name": "90"
}  5:fact变量

事实变量,就是收集被控节点的主机的信息,然后定义一个变量,通过setup模块可以收集被控节点的主机信息,然后通过setup模块中中的facts参数,专门用来进行收集主机信息,通过这种方式收集到的信息称为facts变量(事实变量)
ansible_facts变量中有很多的信息,主机名、网卡设备、ip地址、磁盘和磁盘空间、文件系统bios版本,架构等
# ansible node1 -m setup| head -n 10
node1 | SUCCESS => {
    "ansible_facts": {
      "ansible_all_ipv4_addresses": [
            "172.25.250.20"
      ],
      "ansible_all_ipv6_addresses": [
            "fe80::20c:29ff:fea3:688c"
      ],
      "ansible_apparmor": {
            "status": "disabled"
很多的信息会被输出来  执行playbook的时候,默认会收集被控节点的主机信息(gather facts)
1、可以使用filter进行过滤

注意的就是只能过滤出ansible_facts的下一层级的变量,如果有多个层级的话,不能进行收集
1)通过指定的方式进行收集
使用的变量名就是精确的
# ansible node1 -m setup -a "filter=ansible_all_ipv4_addresses"
node1 | SUCCESS => {
    "ansible_facts": {
      "ansible_all_ipv4_addresses": [
            "172.25.250.20"
      ],
      "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false
}  2)通过通配符来收集信息
# ansible node1 -m setup -a "filter=ansible_*addresses"
node1 | SUCCESS => {
    "ansible_facts": {
      "ansible_all_ipv4_addresses": [
            "172.25.250.20"
      ],
      "ansible_all_ipv6_addresses": [
            "fe80::20c:29ff:fea3:688c"
      ],
      "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false
}2、导出facts变量到文件,引用facts变量

就是将变量文件导出来,然后存放到被控节点上面去
直接的引用fact变量,因为的话,就是在执行剧本的时候,会默认的收集这些信息,直接输出想要的信息即可
#导出facts变量
2钟的方式进行导出
//保存到主控节点上面鹅,但是都是一行,不容易的看
# ansible node1 -m setup --tree /opt/nod1-setup.txt

第二种方式直接使用重定向的,直接重定向到一个文件里面去即可
# ansible node1 -m setup > /opt/node1setup.txt
内容不在同一行上,可以使用搜索


#引用变量
# cat facts.yml
- name: facts
hosts: node1
tasks:
    - debug:
      var: ansible_all_ipv4_addresses

直接使用debug模块来进行引用3、禁用facts变量的收集

默认是开启的收集,禁用收集
# cat facts.yml
- name: facts
hosts: node1
gather_facts: no
tasks:
    - debug:
      var: ansible_all_ipv4_addresses
#再次执行这个剧本的时候,就会报错,说没有定义这个变量
ok: => {
    "ansible_all_ipv4_addresses": "VARIABLE IS NOT DEFINED!"
}使用模块,还是能够收集信息,但是这种情况不常见  
# cat facts.yml
- name: facts
hosts: node1
gather_facts: no
tasks:
    - setup:
    - debug:
      var: ansible_all_ipv4_addresses

#执行剧本
ok: => {
    "ansible_all_ipv4_addresses": [
      "172.25.250.20"
    ]
}  4、自定义的facts事实变量

让每一个主机都有自己的facts变量,每一个facts变量都是存放到/etc/ansible/facts.d目录下
定义的格式有要求的:ini或者yaml,文件的后缀必须是.fact结尾(ini格式的话就是网卡的配置文件的格式)
自定义facts事实变量
#创建一个自定义变量的文件,后缀为fact结尾的
# cat userinfo.fact

name = zhangsan
age = 18
sex = boy


#创建yaml文件
#并将变量文件文件拷贝过去
# cat userinfo.yaml
- name: user fact
hosts: node1
tasks:
    - name: create dir
      file:
      path: /etc/ansible/facts.d
      state: directory
    - name: copy
      copy:
      src: /mnt/userinfo.fact
      dest: /etc/ansible/facts.d

#引用变量自定义变量文件
#自定义变量文件使用ansible_local
# ansible node1 -m setup -a "filter=ansible_local"
node1 | SUCCESS => {
    "ansible_facts": {
      "ansible_local": {
            "userinfo": {
                "user": {
                  "age": "18",
                  "name": "zhangsan",
                  "sex": "boy"
                }
            }
      },
      "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false
}6:set_fact模块

可以生成一个变量,获取不同主机的版本,然后可以使用shell模块,来将这个变量的值保存到本地的主机上面去
将rhel和版本拼接在一起,然后赋值给另外的一个变量,直接引用这个fact事实变量,然后进行拼接
使用setup模块,来进行拼接,赋值给一个变量,使用debug模块来进行输出
# cat set.yaml
- name: setfact
hosts: node1
tasks:
    - set_fact:
      get_version: "{{ ansible_distribution }}--{{ansible_distribution_version}}"
    - debug:
      var: get_version
ok: => {
    "get_version": "RedHat--9.0"
}  7:lookup变量

都还是就需要set_fact这个模块,来生成一个变量,来使用lookup这个参数,为这个变量赋值
在某些的情况下,需要引用外部的内容来作为内容,可以将主控节点的公钥作为变量,然后传输到被控节上面去
lookup变量能够从文件,命令,变量中作为变量的值
1、从文件中赋值变量
格式: get_passwd:"{{loopup('file','/etc/passwd')}}"  就是将主控节点这个值赋给了get_passwd这个变量
案例:
#将一个/etc/passwd这个文件拷贝到被控节点上面
#这个就是将这个文件里面的内容都拷贝到这个被控节点的文件上面去了
# cat look.yml
- name: look
hosts: node1
tasks:
    - set_fact:
       fff: "{{lookup('file','/etc/passwd')}}"
    - name: copy
      copy:
      content: "{{fff}}"
      dest: /opt/passwd2、从命令中赋值给变量
格式:get_passwd:"{{lookup('pipe','date +%T')}}"
就是将命令的结果作为变量的值
#天剑一个用户,并且密码是redhat
#使用这个来进行一个密码的加密,然后赋值给pd这个变量名,最后使用user模块,来引用这个变量
# cat user.yml
- name: create user
hosts: node1
tasks:
    - name: passwd
      set_fact:
      pd: "{{lookup('pipe','openssl passwd -6 redhat')}}"
    - name: created user
      user:
      name: q1000
      password: "{{pd}}"3、从变量中赋值
就是从环境变量中进行赋值
#直接引用这个环境变量
#最后使用debug模块来进行输出
# cat env.yml
- name: env
hosts: node1
tasks:
    - name: env
      set_fact:
      get_env: "{{lookup('env','HOME')}}"
    - name: debug
      debug:
       msg: "{{get_env}}"


ok: => {
    "msg": "/root"
}8:魔法变量

就是内置的变量, 内置变量有特殊含义的就被称为魔法变量
fact变量只有运行的主机才能够调用,就是只想要所有的被控节点都调用node1的主机的主机名
1、hostvars
获取指定主机的变量信息,可以使用主机名或者主机ip地址(主机清单和- hosts中指定都是ip地址才可以使用ip地址),让所有的主机都获取主机node1的主机名,说到底还是获取的是facts变量的内容,setup模块中的
案例:
#在很多的主机名中指定输出一个主机名即可
# cat magic.yaml
- name: magic
hosts: node1
tasks:
    - debug:
       msg: "{{hostvars['node1'].ansible_default_ipv4.address}}"2、inventory_hostname
列出当前运行的任务的主机(常常和when判断使用)
when是一个判断语句,判断是不是当前的主机名,如果不是则不执行任务,是的话,就执行任务
#当前的主机名是node1的话就执行
- name: magic
hosts: node1
tasks:
    - debug:
       var: ansible_hostname
      when: inventory_hostname == 'node1'

ok: => {
    "ansible_hostname": "node1"
}3、groups
groups列出当前主机清单中的所有的主机组,groups.all列出所有的主机(常用于循环)
groups.web列出web主机组的主机
案例:
#列出主机清单中的所有的主机组和主机
#groups就能列出所有的
- name: magic
hosts: node1
tasks:
    - debug:
       var: groups

#列出所有的主机
- name: magic
hosts: node1
tasks:
    - debug:
       var: groups.all


#列出web中的主机
- name: magic
hosts: node1
tasks:
    - debug:
       var: groups.web  
  
 
 总结:

1、错误的补救的方法
  ignore_errors,忽略这个错误,然后继续执行下一个任务
  handlers这个强制的使用,通过监听的方式
 
2、变量
vars和vars_files,命令模式来定义变量这些都是只能定义一些普通的变量
facts就是收集被控节点的主机的信息
register注册变量就是收集命令的执行结果返回的数据,可以指定结果的输出
set_fact变量:就是可以生成一个变量,
  可以根据fact变量直接调用,然后赋值
  lookup直接赋值,就是直接使用一个值
  自定义赋值
魔法变量的话:也是内置的变量,只不过有特殊含义的变量,可以输出指定的主机名,指定的主机组,以及和when常常使用的能输出当前的是哪一个主机组
 
 
 
 
 
 
 
 
 
 
  
 

来源:https://www.cnblogs.com/qw77/p/18092315
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: RHCE(剧本和变量)