相关文章推荐
细心的毛巾  ·  html转pdf ...·  1 年前    · 

例如:host_port、HOST_PORT、var5是符合命名规范的,foo-port、 foo port、foo.port 、12都不符合命名规范。

变量的定义通常是YAML形式,在inventory host文件中也可以使用INI形式。

ansible变量不仅可以支持简单的key=value格式,而且也支持更复杂数据结构,例如字典类型等。

二 变量的作用域

变量的作用域可以分为四种:

  • 作用于全局的变量
  • 作用于play的变量
  • 作用于task的变量
  • 作用于host的变量
  • 接下来我们根据变量的作用域,详细分析下ansible变量的定义、使用和调用顺序。

    三 作用于全局的变量

    3.1 配置文件变量

    ansible配置文件会定义一些变量信息,主要是对执行环境、连接信息变量的定义。

    例如inventory目录、library目录、与目的主机连接方式、越权信息、连接超时时间等等。

    3.2 系统环境变量

    在ansible连接到目的主机时,会以non-login shell登陆到目的主机,此时目的主机的/etc/bashrc和~/.basrc的环境变量会被加载,所以这两个文件中设置的环境变量会作用于playbook全局。

    3.3 命令行变量

    我们可以在执行playbook的命令行指定变量,需要注意的是, 命令行指定的变量在所有其他变量中优先级是最高的。 也就是说如果命令行指定的变量和其他地方指定的变量有冲突时,那么ansible最终会采用命令行定义的变量。

    命令行指定变量示例如下:

    ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo"

    四 作用于play的变量

    4.1 playbook中的变量

    vars语句定义全局变量

    我们可以在playbook中使用「vars」语句定义变量,该变量作用于整个play。

    # inventory/playbooks/ test.yaml - hosts: node1 vars: http_port: 80

    上面示例中中「http_port」是一个作用于整个play的变量,对这个play里的tasks、roles、import、include等等之下定义的task均生效。

    引用变量文件

    除了将变量写在playbook中,我们也可以将变量放在一个单独的YAML文件中,通过「vars_files」语句来导入。

    「vars_files」变量只能作用于play全局,不能在某个task中单独被引用。「vars_files」参数可以使用系统绝对路径或playbook文件的相对路径。

    举个🌰:

    我们在playbooks目录下创建一个vars-files.yaml文件:

    # playbooks/vars- files.yaml age: 100

    在playbook中使用vars_files语句引用该变量文件:

    # playbooks/ test.yaml - hosts: node1 vars_files: . /vars- files.yaml tasks: - debug: msg: " My age is {{ age }} "

    4.2 roles中的变量

    4.2.1 default变量

    default变量位于roles/defaults/main.yml文件中,该变量作用于role里的所有play,通常作为模版或模块里的默认参数。

    default变量与ansible filter变量 「{{ some_variable | default("some_value") }}」具有同样的作用,在所有ansible变量中优先级最低。

    4.2.2 dependencies变量

    dependencies变量位于roles/meta/main.yaml文件中,该变量与「role」语句同级缩进,作用于本身的role和dependencies role。

    举个🌰:

    role_A 和 role_B定义了相同的task,debug出「age」变量:

    # tasks file for role_B - debug: var: age # tasks file for role_B - debug: var: age

    role_A/meta/main.yaml定义role_A依赖role_B,并指定「age」变量等于26:

    dependencies: - role: role_B vars: age: 27

    写playbook,引用role_A:

    # playbooks/ test.yaml - hosts: node1 roles: - role: role_A

    执行结果如下:

    ➜  lab-ansible ansible-playbook playbooks/test.yaml
    PLAY [node1] ********************************************************************************************
    TASK [Gathering Facts] **********************************************************************************
    ok: [node1]
    TASK [role_B : debug] ***********************************************************************************
    ok: [node1] => {
        "age": 27
    TASK [role_A : debug] ***********************************************************************************
    ok: [node1] => {
        "age": 27
    PLAY RECAP **********************************************************************************************
    node1                      : ok=3    changed=0    unreachable=0    failed=0

    输出结果显示,dependencise变量「age」在role_A和role_B均生效。

    4.2.3 vars变量

    vars变量位于roles/vars/main.yml,该变量作用于role里的所有模块。通常将除了默认变量的其他的变量放在这个文件内。

    4.3 register变量

    register方法能够将一个task的执行结果注册为一个变量。书写格式要与模块名称对齐,该变量作用于整个play。

    通常register变量和when语句联合使用,以达到满足某些条件才运行task的目的。

    五 作用于task的变量

    5.1 playbook中的变量

    with modules

    我们可以为某个模块定义变量,该变量作用于这个task。

    举个🌰,示例中为「debug」模块定义了「name」和「age」变量并在「msg」参数后使用了这两个变量:

    # playbooks/ test.yaml - hosts: node1 tasks: - debug: msg: " My name is {{ name }} and I'm {{ age }} years old " vars: name: Maurice age: 27

    with import*/include*

    在使用import_playbook、import_tasks、include_tasks、import_role、include_role时可以在import*/include*的同级位置指定变量,该变量作用于导入的所有play。

    使用import_role举个🌰:

    playbook导入role_A,并定义变量「age」,这样role_A内的play就可以使用「age」变量了:

    # playbooks/ test.yaml - hosts: node1 tasks: - import_role: name: role_A vars: age: 1000

    其他import*/include*的语句使用方法类似,只要记住 缩进与import*/include*语句保持一致 即可。

    with roles

    在playbook中使用roles语句来导入role时也可以定义变量,该变量作用于role包含的所有play。

    举个🌰:

    playbook使用roles语句导入role_A,并定义变量「age」:

    # playbooks/ test.yaml - hosts: node1 roles: - role: role_A vars: age: 1000

    通过上面两个示例我们发现,roles和import_role语句定义变量写法上很相似,其实import_role和include_role是新版本的语法,功能上完全可以代替roles语句。如果你使用的ansible版本>=2.4,建议 使用include_role和import_role 语句。

    5.2 roles中的变量

    指的是在tasks/main.yaml或handlers/main.yaml内书写task时指定的变量,该变量作用于某个task,这个变量类型和上述章节中「5.1playbook中的变量—with modules」类似,这里就不再举例说明。

    六 作用于host的变量

    6.1 系统变量Facts

    6.1.1 facts变量

    ansible中有个特殊的变量,这些变量不是开发者定义的,而是ansible根据目的主机环境信息自动收集的,称之为fact变量。

    fact变量很实用,和「when」语句配合使用会让你的代码更加健壮。

    举个🌰,如果当前的操作系统为「RedHat」类型,则通过yum安装需要的软件包:

    # playbooks/ test.yaml - hosts: node1 tasks: - yum : name: firewalld state: present when: ansible_os_family == ' RedHat '

    6.1.2 facts缓存

    在执行playbook时,我们发现在「Gathering Facts」步骤时总会卡住一会,如果定义的play多了,会非常耗时。其实这步就是ansible在收集目的主机的facts信息。

    如果我们定义的playbook中并没有使用到fact变量,那么我们可以选择将其关闭,只需添加「gather_facts: false」即可。

    如果必须要使用facts信息,我们可以将fact信息缓存到redis服务或本地json文件中,这样当我们第二次执行playbook时,ansible就会读取缓存信息,从而加快运行速度。

    假设本地redis服务正常运行,我们只需更改ansible配置文件即可达到缓存fact的目的。

    redis缓存:

    [defaults]
    gathering = smart
    fact_caching = redis
    fact_caching_timeout = 86400

    json文件缓存:

    [defaults]
    gathering = smart
    fact_caching = jsonfile
    fact_caching_connection = /path/to/cachedir
    fact_caching_timeout = 86400

    6.2 inventory中的变量

    6.2.1 主机变量

    关于主机和主机组变量,我们在「 ansible基础-安装与配置 :3.1 主机与主机组」中有介绍,当时介绍了组和主机变量的定义方法、变量的分离、优先级等知识点。其实主机变量的知识点不复杂,这里做下总结。主机变量是指作用在某一台主机上的变量。位置可以与主机定义写在一起也可以写在inventory/host_vars/a_host_name.yaml文件里。通常前者使用使用INI格式,后者使用YAML格式。这里要注意一下YAML的语法,在「:」后面要留有一个空格。如果组变量和主机变量都对同一个主机定义了相同的变量,那么 ansible最终会采用主机变量而放弃组变量 。主机变量示例:INI格式:

    # inventory/hosts
    [nodes]
    node1
    node2
    node3
    [nodes:vars]
    http_port=80
    database_port=3306

    转换为YAML格式:

    # inventory/ hosts nodes: hosts: node1: node2: node3: vars: http_port: \ ' 80\' database_port: \ ' 3306\'

    不含节点定义的主机变量定义:

    # inventory/host_vars/ node1.yaml http_port: \ ' 80\' database_port: \ ' 3306\'

    6.2.2 组变量

    和主机变量类似,组变量作用于主机组,即多个主机。位置可以与主机组定义写在一起也可以写在inventory/group_vars/a_group_name.yaml文件里。通常前者使用使用INI格式,后者使用YAML格式。

    INI格式:

    # inventory/hosts
    [nodes]
    node1
    node2
    node3
    [nodes:vars]
    http_port=80
    database_port=3306

    转换为YAML格式:

    # inventory/ hosts nodes: hosts: node1: node2: node3: vars: http_port: \ ' 80\' database_port: \ ' 3306\'

    定义一个字典变量,位于inventory/group_vars/nodes.yaml:

    # 一位职工记录 name: Maurice job: Developer skill: Develop program employed: True foods: - Apple - Orange languages: shell: Elite python: Elite c ++: Lame

    将上面示例转换为一行:

    # 一位职工记录 {name: Maurice,job: Developer,skill: Develop program,employed: True,foods: [\ ' Apple\',\'Orange\'],languages: {shell: Elite,python: Elite,c++: Lame}}

    很显然,YANL格式分行来写会更加直观和美观。

    七 变量的调用顺序

    通过上面描述,我们发现ansible能够定义变量的地方真的是太多太多了,我个人觉得ansible变量这块的设计有点复杂和冗余。

    在生产中,我们要读懂别人的代码或者让自己的代码更加健壮,就必须清楚的知道ansible最终会使用哪个变量。这里我总结下ansible的调用变量的顺序,当小伙伴迷茫时可以回来看下这个列表(越靠后变量优先级越高,越会被ansible采用)

  • 命令行参数(非-e指定的参数,eg: "-u user -b yes")
  • roles defaults目录下的变量
  • 组变量:inventory 文件
  • 组变量:inventory/group_vars/all
  • 组变量:playbook/group_vars/all
  • 组变量:inventory/group_vars/*
  • 组变量:playbook/group_vars/*
  • 主机变量:inventory 文件
  • 主机变量:inventory/group_vars/*
  • 主机变量:playbook/group_vars/*
  • facts变量
  • play变量:vars定义的
  • play变量:vars_prompt定义的
  • play变量:vars_files导入的
  • roles vars目录下的变量
  • block中task定义的变量
  • playbook中task定义的变量
  • include_vars导入的变量
  • set_facts/register注册的变量
  • 使用roles/include_role/import_role语句时定义的变量
  • 使用include语句(ansible旧版本)时定义的变量
  • 命令行-e参数指定的额外变量(优先级最高)
  • 八 变量的使用

    8.1 模块使用变量

    一个变量被定义后,在它的作用域内的play可以直接调用,例如:

    我们定义了整个play作用域的变量「name」和「age」,那么在之后的两个debug模块内可以直接调用。

    # playbooks/ test.yaml - hosts: node1 vars: name: Maurice age: 27 tasks: - debug: msg: " My name is {{ name }} and I'm {{ age }} years old " - debug: msg: " Hello Maurice " when: name == ' Maurice '

    输出结果展示:

    ➜  lab-ansible ansible-playbook playbooks/test.yaml
    PLAY [node1] ********************************************************************************************
    TASK [Gathering Facts] **********************************************************************************
    ok: [node1]
    TASK [debug] ********************************************************************************************
    ok: [node1] => {
        "msg": "My name is Maurice and I'm 27 years old"
    TASK [debug] ********************************************************************************************
    ok: [node1] => {
        "msg": "Hello Maurice"
    PLAY RECAP **********************************************************************************************
    node1                      : ok=3    changed=0    unreachable=0    failed=0

    8.2 模版使用变量

    变量被频繁使用的还有roles里的模版,位于roles/template/xxx.j2,该模版使用python的Jinja2模版语法。

    roles模版多被用于生成服务的配置文件,所以会调用很多的变量。

    示例如下:

      "customerMonthInfo": "{{ cmp_server_customerMonthInfo }}",
      "type_black_list": [{% for type_black in cmp_server_type_black_list %}"{{ type_black }}"{{ '' if loop.last else ',' }}{% endfor %}],

    上述示例中「customerMonthInfo」的参数比较简单,就是变量「cmp_server_customerMonthInfo」的值

    「type_black_list」参数从列表变量「cmp_server_type_black_list」中获取,执行结果是个字符串,字符串由该列表的元素以逗号为间隔组成,最后一个参数后没有逗号。

    九 本节应该掌握的技能

  • 掌握变量的命名规范
  • 掌握变量定义的方法
  • 掌握变量的作用域及调用顺序
  • 会在模块和模版里使用变量
  • 熟悉Jinja2模版的语法规则
  • 十 参考链接

  • https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#passing-variables-on-the-command-line
  • https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html
  • https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
  • 红帽DO407 Automation with Ansible 教材
  • 欢迎大家关注我的公众号: