运维必备 | ansible 自动化运维工具之变量的定义与调用

2023-10-31 18:17:45 浏览数 (3)

0x01 ansible 变量

前言:在编写ansible脚本中往往会使用变量,它能让我们的工作变得更加灵活,但是在ansible中变量的使用方式有很多种,下面跟着【WeiyiGeek】作者通过一个个简单示例来进行一一讲解,赶快来一起来学习吧。

变量规则及优先级

「1.变量命名规则」

  • 变量名应该由字母、数字、下划线组成
  • 变量名要以字母开头
  • ansible内置的关键字不能作为变量名

「2.变量优先级」描述:下面是优先级从最小到最大的顺序(最后列出的变量赢得优先级):

  • command line values (eg “-u user”)
  • role defaults 1
  • inventory file or script group vars 2
  • inventory group_vars/all 3
  • playbook group_vars/all 3
  • inventory group_vars/* 3
  • playbook group_vars/* 3
  • inventory file or script host vars 2
  • inventory host_vars/* 3
  • playbook host_vars/* 3
  • host facts / cached set_facts 4
  • play vars
  • play vars_prompt
  • play vars_files
  • role vars (defined in role/vars/main.yml)
  • block vars (only for tasks in block)
  • task vars (only for the task)
  • include_vars
  • set_facts / registered vars
  • role (and include_role) params
  • include params
  • extra vars (always win precedence)

变量定义

1.在 playbook 脚本中 vars 配置定义和调用

描述:下面在playbook 脚本中定义变量的几种格式:

代码语言:javascript复制
# 1.可定义单个变量或者多个变量(常规)
vars:
  tvar1: testfile
  tvar2: testfile


# 2.使用YAML的块序列语法也可以定义变量
vars:
  - testvar1: testfile1
  - testvar2: testfile2


# 3.能够以类似"属性"的方式定义变量 (推荐方法)
vars:
  nginx:
    conf80: /etc/nginx/conf.d/80.conf
    conf8080: /etc/nginx/conf.d/8080.conf

下面是Ansible变量引用的几种方式:

代码语言:javascript复制
# 示例1:上面1/2案例变量调用
"{{ tvar1 }}"
"{{ testvar1 }}"


# 示例2.上面3案例变量调用(由于是对象Obj形式调用)
"{{nginx.conf80}}"  #方式1
"{{nginx['conf8080']}}" #方式2

上例中我在引用变量时使用了双引号,而在本文的第一个示例中引用变量时却没有使用双引号,这是因为第一个示例中的变量在被引用时,并没有处于"开头的位",第一个示例中变量被引用时如下:

代码语言:javascript复制
path: /testdir/{{ testvar1 }}   #当 "不处于开头位置" 相当于是拼接路径可以不使用""包含
path: "{{nginx.conf80}}"        #变量被引用时如下,处于"开头的位置"必须使用双引号引起被引用的变量,否则会报语法错误。

不过上述情况也有例外,当在playbook中为模块的参数赋值时,可以使用"冒号"也可以使用"等号",当使用"等号"为模块的参数赋值时,则不用考虑引用变量时是否使用"引号"的问题,

代码语言:javascript复制
file:
  path={{nginx.conf80}}
  path={{nginx['conf8080']}}

偷偷的告诉你哟?极客全栈修炼】微信小程序已开放

可直接在微信里面直接浏览博主文章哟,后续将上线更多有趣的小工具。

2.在 yml 格式的 vars_files 文件中定义变量

描述: 在某些场景中我们还可以在某个文件中定义变量,然后再在playbook中引入对应的文件,引入文件后playbook即可使用文件中定义的变量,即可使用文件中定义的变量

「你可能会问为什么要多此一举呢?」

  • 某些工作场景中需要你想要让别人阅读你的playbook,却不想让别人看到某些值只能看到引入的变量名,但是看不到变量对应的值,这种将变量分离到某个文件中的做法叫做"变量文件分离"
  • "变量文件分离":能够隐藏某些值,将不同类的信息放在不同的文件,变量信息与剧本分离(方便修改)

「"变量文件分离"之变量定义」描述: 建立nginx.yml在文件中定义变量时,不要使用vars关键字,直接定义变量即可,定义变量的语法与在playbook中定义变量的几种语法相同.

代码语言:javascript复制
tee nginx.yml <<'EOF'
#语法一示例:
testvar1: testfile
testvar2: testfile2

#语法二示例:
- testvar1: testfile
- testvar2: testfile2
- 
#语法三示例:
nginx:
  conf80: /etc/nginx/conf.d/80.conf
  conf8080: /etc/nginx/conf.d/8080.conf
EOF

「"变量文件分离"之变量包含引用」

代码语言:javascript复制
# 方式1.单个变量文件引入
vars_files:
  - /testdir/ansible/nginx.yml

# 方式2.也可以引入多个变量文件,每个被引入的文件都需要以"- "开头,示例如下
vars_files:
  - /testdir/ansible/nginx_vars.yml
  - /testdir/ansible/other_vars.yml

# 方式3."vars"关键字和"vars_files"关键字可以同时使用
vars:
  - conf90: /etc/nginx/conf.d/90.conf
vars_files:
  - /testdir/ansible/nginx_vars.yml

「基础实践」示例1.实现变量文件分离引用与命令模块文件模块使用.

代码语言:javascript复制
# var.yml
create:
  directory: Love
  filename: Ansible.sh
  context: "insert file a talk about b t n"  #建议对于字符串一定要添加双引号,可使用转义字符;

# variable.yml
---
- hosts: local
  remote_user: root
  vars: #关键点1-直接在play文件中定义变量
    - dirRoot: /tmp/
  vars_files: #关键点2-包含外部设定的变量文件
    - /root/var.yml
  tasks:
    - name: Create directory
      file: 
        path: "{{dirRoot}}{{create.directory}}"  #关键点3
        state: directory
    - name: touch file
      file:
        path={{dirRoot}}{{create.directory}}/{{create['filename']}}  #关键点4 - 特殊字符可以直接拼接(实际使用时候不建议这样)
        state=touch
    - name: insert context
      lineinfile:
        path="{{dirRoot}}{{create.directory}}/{{create.filename}}"  #关键点5 - 推荐方法采用单双引号包含里面可拼接字符
        line={{create['context']}} 

weiyigeek.top-变量分离案例

3.使用 setup模块/debug模块定义变量

描述:这篇文章所涉及到的内容需要借助两个模块,所以在详细的总结变量的相关使用方法之前会先描述一下这两个模块的用法 setup模块/debug模块

当我们运行一个playbook时自动调用了setup模块从而执行了"[Gathering Facts]"任务,通过这个默认任务收集远程主机的相关信息(例如远程主机的IP地址,主机名,系统版本,硬件配置等信息);

其实这些被收集到的远程主机信息会保存在对应的变量中,当我们要使用这些信息时就可以获取对应的变量;

其实这些远程主机的变量信息不仅仅能够用于输出,我们通常会获取到信息以后对这些信息的值进行判断,判断是否符合我们的要求然后再执行下一步动作,比如先获取到远程主机的系统发行版信息然后判断发行版是centos6还是centos7:

  • 如果是centos6,我们就将准备好的A文件拷贝到远程主机中
  • 如果是centos7,我们就将准备好的B文件拷贝到远程主机中

「实践示例」

代码语言:javascript复制
$ansible local -m setup | more
local | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "10.10.107.222",
            "192.168.1.99"
        ],
        "ansible_all_ipv6_addresses": [
            "fe80::9738:2d26:820f:2d50",
            "fe80::dfc:592e:b4a4:9877"
        ],
        "ansible_apparmor": {
            "status": "disabled"
        },
        "ansible_architecture": "x86_64",
        "ansible_bios_date": "09/21/2015",
        "ansible_bios_version": "6.00",
        "ansible_cmdline": {
            "BOOT_IMAGE": "/vmlinuz-3.10.0-957.12.2.el7.x86_64",
            "LANG": "zh_CN.UTF-8",
            "crashkernel": "auto",
            "quiet": true,
            "rd.lvm.lv": "centos/swap",
            "rhgb": true,
            "ro": true,
            "root": "/dev/mapper/centos-root"
        },

执行上述命令后远程主机local的相关信息将会输出到ansible主机的控制台上,为了方便你阅读返回的信息的格式是json格式

ansible已经将格式化后的json信息返回到了控制台中比如:

  • "ansible_all_ipv4_addresses" 表示远程主机中的所有ipv4地址,从其对应的值可以看出,local主机上一共有3个ipv4地址。
  • "ansible_distribution" 表示远程主机的系统发行版,从其对应的值可以看出local主机的系统发行版为centos
  • "ansible_distribution_version" 表示远程主机的系统版本号,从其对应的值与 "ansible_distribution" 的值可以看出local主机的系统版本为centos7.4
  • "ansible_ens35" 表示远程主机ens35网卡的相关信息,细心如你一定也发现了,我还有两个名为"ens33"和"ens34"的网卡,只不过为了方便示例,这两个网卡的信息被我省略了。
  • "ansible_memory_mb" 表示远程主机的内存配置信息。

我们还可以通过关键字对信息进行过滤还能使用通配符进行相对模糊的过滤:

代码语言:javascript复制
#比如我只是想要查看远程主机的内存配置信息
ansible local -m setup -a 'filter=ansible_memory_mb'  #通过setup模块的filter参数可以指定需要过滤的关键字

#相对模糊的过滤
ansible local -m setup -a "filter=*mb*"

weiyigeek.top-ansible中setup模块的使用

其实除了这些信息以外还能够在远程主机中写入一些自定义的信息,这些自定义信息也可以被setup模块收集到。

「Q: 那么我们应该在哪里定义这些信息呢?该怎样定义这些信息呢?」

❝答:ansible 默认会去目标主机的 /etc/ansible/facts.d 目录下查找主机中的自定义信息,并且规定自定义信息需要写在以".fact"为后缀的文件中,同时这些以".fact"为后缀的文件中的内容需要是INI格式或者是json格式的。 ❞

当setup收集远程主机的"local facts"时,默认会查找远程主机的/etc/ansible/facts.d目录,如果你把"local facts"信息文件放在了其他自定义路径,在使用setup模块时,需要使用"fact_path"参数指定对应的路径;

那么我们来创建一个测试文件路径为local主机的 /etc/ansible/facts.d/testinfo.fact 在文件中写入如下INI格式的信息。

代码语言:javascript复制
$cat testinfo.fact
#INI风格的内容我在"[testmsg]"配置段中配置了两条自定义信息,msg1与msg2。  
[testmsg]
msg1 = This is a demo1
msg2 = This is a demo2


#我们也可以使用json格式进行配置,只是书写格式不同 /tmp/info.fact  ansible_local
{
   "testmsg":{
      "msg1":"This is the first custom test message",
      "msg2":"This is the second custom test message"
   }
}

通过上述方式在目标主机的本地自定义信息被称为"local facts",当我们运行setup模块时,远程主机的"local facts"信息也会被收集,我们可以通过"ansible_local"关键字过滤远程主机的"local facts"信息

代码语言:javascript复制
#获取本地自定义信息 (从默认目录)
ansible local -m setup -a "filter=ansible_local"

#假设我把".fact"文件放在了目标主机的"/tmp"目录下
ansible local -m setup -a "filter=ansible_local fact_path=/tmp"


#执行结果
local | SUCCESS => {
    "ansible_facts": {
        "ansible_local": {
            "testinfo": {
                "testmsg": {
                    "msg1": "This is a demo1",  #关键点
                    "msg2": "This is a demo2"
                }
            }
        },
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false
}

local | SUCCESS => {
    "ansible_facts": {
        "ansible_local": {
            "info": {
                "testmsg": {
                    "msg1": "This is the first custom test message",  #关键点
                    "msg2": "This is the second custom test message"
                }
            }
        },
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false
}

除了使用上面的setup模块,我们还能采用debug模块的作用就是帮助我们进行调试并且把信息输出到ansible控制台上,以便我们能够定位问题;debug模块除了能够使用msg参数输出自定义的信息,还能够通过debug模块直接输出变量信息需要使用var参数。

那么我们先来看一个debug模块使用的小小示例(playbook)如下:

代码语言:javascript复制
---
- hosts: local
  remote_user: root
  vars:
    testvar: value of test variable 1024   #关键1
  tasks:
  - name: touch testfile
    file:
      path: /tmp/testfile
      state: touch
  - name: debug demo
    debug:  #msg中的变量值需要使用引号引起,因为{{testvar}}变量前包含"冒号",如果不使用引号会报语法错误
      msg: "this is debug info , {{testvar}} " #输出自定义debug字符串     #关键2
      #var: testvar  #通过debug的var参数输出了这个变量的内容              #关键3 (与msg不能同时使用)

playbook 解析:我们先在local主机上touch了对应的文件,使用debug的msg参数时也可以引用变量的值并且输出,然后利用debug模块在控制台中输出变量的名称以及变量的值:

代码语言:javascript复制
# 执行结果(1) - msg
TASK [debug demo]
ok: [local] => {
    "msg": "this is debug info , value of test variable 1024 "
}

# 执行结果(2)- var
TASK [debug demo] 
ok: [local] => {
    "testvar": "value of test variable 1024"
}

playbook在运行时默认运行"[Gathering Facts]"任务会收集远程主机的相关信息,这些信息会保存在对应的变量中,我们在playbook中可以使用这些变量,从而利用这些信息.

「Q: 那么我们怎样在playbook获取到这些变量的值呢?」

描述: 在setup模块的示例中我们可以通过"ansible_memory_mb"关键字获取远程主机的内存信息,其实在playbook中也可以直接调用"ansible_memory_mb"变量名;

代码语言:javascript复制
---
- hosts: local
  remote_user: root
  vars:
    test: "Test: "  #关键点
  tasks:
  - name: debug demo
    debug:
      msg: "{{test}} Remote host memory swap information: {{ansible_memory_mb['swap']}} n, ip addrs info: {{ansible_all_ipv4_addresses}}"  #关键点

我们自定义的信息中包含了变量内容,远程主机的内存信息/IP地址信息同时被输出了,执行结果:

代码语言:javascript复制
TASK [debug demo] 
ok: [local] => {
    "msg": "Test: Remote host memory swap information: {u'cached': 0, u'total': 2047, u'free': 2047, u'used': 0} n, ip addrs info: [u'10.10.107.222', u'192.168.1.99']"
}

如上述返回信息所示,"ansible_memory_mb"中其实包含了 "nocache"、"real"、 "swap"三个部分的信息,如果我们只想获得"real"部分的信息,在playbook中引用变量时可以使用如下两种语法。

代码语言:javascript复制
语法一示例:
debug:
     msg: "Remote host memory information : {{ansible_memory_mb.real}}"
语法二示例:
debug:
     msg: "Remote host memory information : {{ansible_memory_mb['real']}}"

「总结」

  • 其实setup模块返回的这些信息都存在了对应的变量中,我们可以通过引用变量从而使用对应的信息;
  • 其实debug模块常常用来调试playbook,输出自定义异常以及setup信息收集的变量输出;
4.使用 register 定义变量

描述:ansible的模块在运行之后其实都会返回一些"返回值",只是默认情况下这些"返回值"并不会显示而已,我们可以把这些返回值写入到某个变量中,然后通过引用对应的变量从而获取到这些返回值,这种将模块的返回值写入到变量中的方法被称为"注册变量";

这些返回值不仅仅能够用于输出通常我们会利用到这些返回值,比如通过模块的返回值决定之后的一些动作,所以注册变量在playbook中还是会被经常用到的;

「基础示例」

代码语言:javascript复制
# 执行命令返回结果利用 debug 查看
---
- hosts: local
  remote_user: root
  vars:
    cmd1: whoami
    cmd2: hostname -I
  tasks:
  - name: execute whoami
    shell: "{{cmd1}}" 
    register: who
  - name: execute hostname
    shell: "{{cmd2}}"
    register: hostname
  - name: execute echo
    shell: "echo test > /tmp/testshellfile"
    register: echovar
  - name: Debug shell module return values
    debug: 
      var: who,hostname,echovar   #可以输出多个变量采用","号进行分割

执行结果:

代码语言:javascript复制
TASK [Debug shell module return values] 
ok: [local] =>
{
  "who,hostname,echovar": "(
  {'stderr_lines': [],
    u'changed': True,    #操作标识!
    u'end': u'2019-07-30 11:31:13.821710',
    'failed': False,     #执行状态
    u'stdout': u'root',  #执行结果
    u'cmd': u'whoami',   #执行命令
    u'rc': 0, 
    u'start': 
    u'2019-07-30 11:31:13.818264',
    u'stderr': u'', 
    u'delta': u'0:00:00.003446', 'stdout_lines': [u'root']},

    {'stderr_lines': [], u'changed': True, u'end': u'2019-07-30 11:31:14.111808', 'failed': False, u'stdout': u'10.10.107.222 192.168.1.99 ', u'cmd': u'hostname -I', u'rc': 0, u'start': u'2019-07-30 11:31:14.108345', u'stderr': u'', u'delta': u'0:00:00.003463', 'stdout_lines': [u'10.10.107.222 192.168.1.99 ']}, 
    
    {'stderr_lines': [], u'changed': True, u'end': u'2019-07-30 11:31:14.367277', 'failed': False, u'stdout': u'', u'cmd': u'echo test > /tmp/testshellfile', u'rc': 0, u'start': u'2019-07-30 11:31:14.364389', u'stderr': u'', u'delta': u'0:00:00.002888', 'stdout_lines': []})"
}

假设,我只是想要获取到上述返回信息中cmd的值则可以使用如下两种语法:

代码语言:javascript复制
#示例的返回信息为shell模块的返回值
#语法一
  - name: shell module return values
    debug:
      msg: "{{testvar.cmd}}"
#语法二
  - name: shell module return values
    debug:
      msg: "{{testvar['cmd']}}"

#输出可采用debug模块的msg进行过滤调用输出,更加的简洁方便;

参考来源:Ansible2.8返回值含义 - https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html

5.使用 vars_prompt 提示输入变量

描述:在某些交换式操作的时候,脚本会提示用户输入一些信息,脚本需要根据用户输入的信息决定下一步的动作

「Q: 那么在playbook中该怎样实现这种交互呢?」

❝答:我们可以这样做提示用户输入信息,然后将用户输入的信息存入到指定的变量中,当我们需要使用这些"输入的信息"时,只要引用对应的变量即可。 ❞

「示例演示:」示例1:使用"vars_prompt"关键字创建了两个变量,两个变量的名称分别为"your_name" 和 "your_age",当用户输入后字符串将被存入变量之中;

代码语言:javascript复制
---
- hosts: local
  remote_user: root
  vars_prompt: 
    - name: "Username"               #变量名称
      prompt: "What is your name"   #交互式输入提示
      private: no                     #是否隐秘:默认Yes(不显示输入)/no(显示输入))
    - name: "Password"
      prompt: "What is your password"
    - name: "Sex"                    #多选项选项
      prompt: "Choose the you Sex n
      m: Mann
      w: Womann
      o: othern"
      private: no
      default: o
    - name: "PayPass"               #加密输入以哈希密码输出 (可以直接创建linux系统用户和设置密码)
      prompt: "Enter Pay PassWord"
      private: no                    #可查看输入
      unsafe: yes                    #支持输入特殊字符 比如: { % 等ansbile含义符号 - 2.8 add
      encrypt: "sha512_crypt"        #关键点-encrypt关键字表示对用户输入的信息进行哈希
      confirm: yes   #关键点-通过"confirm"关键字就能实现需要输入两次完全相同的密码,才能够设置成功
  tasks:
   - name: output prompt vars   #任务名称
     debug:
      msg: "Username = {{Username}} , Password = {{Password}},  Sex = {{Sex}}, Pay Password = {{PayPass}}"
   - name: create user    #创建用户
     user:
       name: "{{Username}}"
       password: "{{PayPass}}"

#未安装passlib支持的 encrypt 加密方式  2.7 版本新增
bcrypt - BCrypt
md5_crypt - MD5 Crypt
sha256_crypt - SHA-256 Crypt
sha512_crypt - SHA-512 Crypt

执行结果:

代码语言:javascript复制
$ansible-playbook prompt.yml
What is your name: Weiyegeek
What is your password:
Choose the you Sex:
 m: Man
 w: Woman
 o: other
 [o]: m
Enter Pay PassWord: Wei123456
confirm Enter Pay PassWord: Wei123456

PLAY [local] 
TASK [Gathering Facts]
ok: [local]

TASK [output prompt vars] 
ok: [local] => {
    "msg": "Username = Weiyegeek , Password = weiyegeek,  Sex = m, Pay Password = $6$WDmSQbaDomZyzbbm$MHXVXALnaZ4oKkptiFi/CYlQJWxrUD4xxHmvSjvZSKqr/4hvmaA/h/JKLIUZL.YZDQXx53EAOp.BgJLPltnxw1"
}

TASK [create user] 
changed: [local]
PLAY RECAP 
local : ok=3  changed=1  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

weiyigeek.top-prompt-createuser

「注意事项」:当使用"encrypt"关键字对字符串进行哈希时,ansible需要依赖Python的passlib库完成哈希操作,所以我们需要使用pip安装对应模块库。

代码语言:javascript复制
python -m pip install passlib  # 或者 pip install passlib

#安装后可使用的加密方式
des_crypt - DES Crypt
bsdi_crypt - BSDi Crypt
bigcrypt - BigCrypt
crypt16 - Crypt16
md5_crypt - MD5 Crypt
bcrypt - BCrypt
sha1_crypt - SHA-1 Crypt
sun_md5_crypt - Sun MD5 Crypt
sha256_crypt - SHA-256 Crypt
sha512_crypt - SHA-512 Crypt
apr_md5_crypt - Apache’s MD5-Crypt variant
phpass - PHPass’ Portable Hash
pbkdf2_digest - Generic PBKDF2 Hashes
cta_pbkdf2_sha1 - Cryptacular’s PBKDF2 hash
dlitz_pbkdf2_sha1 - Dwayne Litzenberger’s PBKDF2 hash
scram - SCRAM Hash
bsd_nthash - FreeBSD’s MCF-compatible nthash encoding

Ansible官方prompt参考文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_prompts.html

6.通过--extra-vars命令行参数传递变量

描述:除了上面定义变量的方法,我们还能够在执行playbook时直接传入需要使用的变量;

比如下面再playbook中并没有定义变量但我们可以在调用playbook时直接从命令行传入变量,果在调用playbook时也没有传入变量则会报错,其实我们也可以先在playbook中定义好变量,然后在执行playbook时以传入变量覆盖playbook中的变量值(前提是命令行中的变量与play中的变量重名):

代码语言:javascript复制
#cat extravars.yml
---
- hosts: local
  remote_user: root
  vars:
    verify: "123456"
  tasks:
  - name: "Passing Variables On The Command Line"
    debug:
      msg: "{{ip}} - {{Port}} - {{service}} , Verify: {{verify}}"  #

执行结果:

代码语言:javascript复制
#三种方式效果相同
ansible-playbook extravars.yml --extra-vars 'ip="192.168.1.1" Port="8080" service="nginx" verify="888888"'
ansible-playbook extravars.yml -e 'ip="192.168.1.1" Port="8080" service="nginx" verify="888888"'
ansible-playbook extravars.yml -e '{"ip":"192.168.1.1","Port":"8080","service":"nginx","verify":"888888"}'

#执行结果:
ok: [local] => {
    "msg": "192.168.1.1 - 8080 - nginx , Verify: 888888"
}

「命令行传入变量文件」描述:命令行不仅能够传入变量还能传入变量文件,变量文件中的变量都会一并被传入,变量文件可以是json格式的/YAML格式的,此处使用YAML格式的变量文件进行示例

代码语言:javascript复制
# cat > filevars.yml <<end
testvar: testvarinfile
countlist:
- one
- two
- three
- four
end

#playbook的内容如下
#cat > filevariable.yml <<END
---
- hosts: local
  remote_user: root
  tasks:
  - name: "Passing Variables On The Command Line"
    debug:
      msg: "{{testvar}} {{countlist[0]}}"
END

「Q: 那么我们怎样从命令行中将变量文件中的变量传入playbook呢?」

代码语言:javascript复制
#使用"@"符号加上变量文件的路径,即可在命令行中传入对应的变量文件,变量文件中的所有变量都可以在playbook中引用
ansible-playbook filevariable.yml -e "@/root/filevars.yml"

#执行结果
TASK [Passing Variables On The Command Line]
ok: [local] => {
    "msg": "testvarinfile one"
}

「总结:」

  • 命令行传入的变量的优先级要高于playbook中的变量,通过这种方法我们就能够更加灵活的指定变量的值了。
  • 命令行传入json字符串来设置变量
代码语言:javascript复制
#通过json格式传入两个变量
ansible-playbook cmdvar.yml -e '{"testvar":"test","testvar1":"test1"}'
ansible-playbook cmdvar.yml -e '{"countlist":["one","two","three","four"]}' #使用两种语法引用变量 {{countlist[0]}} 或者 {{countlist.0}}
  • 命令行可以传入单个或者多个变量也能传入变量文本文件采用-e选项直接搞定;
7.使用 set_fact 模块定义变量

描述:在清单中配置变量,我们知道可以在清单中配置需要被管理的远程主机,也可以将部分远程主机分为一组,其实在配置清单时还可以为主机或主机组设置变量;

「主机变量」描述: 在清单中配置远程主机时,可以同时为主机配置对应的变量,当操作这个主机时即可直接使用对应的变量。

代码语言:javascript复制
#/etc/ansible/hosts
#[ini] 格式
#示例1.只要在定义主机时将变量名和变量值写在主机配置的后面即可,可以为一个主机定义多个主机变量,用空格隔开即可
local ansible_host=10.1.1.70 testhostvar=test70_host_var testvar1=testvar1

#[yaml] 格式
all:
 hosts:
   test70:
     ansible_host: 10.1.1.70
     ansible_port: 22
     testhostvar: test70_host_var
     testhostvar1: test70_host_var1
     testhostvar2:
       thv1: demo1
       thv2: demo2

执行结果:

代码语言:javascript复制
ansible local -m shell -a "echo -e Testhostvar = {{testhostvar}}, n Testhostvar1 = {{testhostvar1}} , testhostvar2.thv1 = {{testhostvar2['thv2']}}"

# local | CHANGED | rc=0 >>
# Testhostvar = testhostvar2048, n Testhostvar1 = testvar1024 , testhostvar2.thv1 = demo2

温馨提示:主机变量的生效范围只限于对应的主机。

「主机组变量」描述: 在清单中我们能将多个主机分为一组方便我们成批的操作远程主机,同样我也可以将变量配置是到组里面;使用vars关键字可以指定组变量,vars关键字位于对应组的下一级

代码语言:javascript复制
#[ini] 格式
[testB]
test70 ansible_host=10.1.1.70
test71 anisble_host=10.1.1.71
 
[testB:vars]
test_group_var1='group var test'
test_group_var2='group var test2'


#[YAML] 格式
all:
 children:
   testB:
     hosts:
       test70:
         ansible_host: 10.1.1.70
         ansible_port: 22
       test71:
         ansible_host: 10.1.1.71
         ansible_port: 22
     vars:
       test_group_var1: 'group var test1'
       test_group_var2: 'group var test2'

执行结果:

代码语言:javascript复制
$ansible ops -m shell -a "echo -e test_group_var1= {{test_group_var1}}, n test_group_var2 = {{test_group_var2}}"
# 10.20.172.179 | CHANGED | rc=0 >>
# test_group_var1=group var test1, test_group_var2 =group var test2

「使用 set_fact 定义变量」描述:set_fact是一个模块,我们可以通过set_fact模块在tasks中定义变量,并且与register的功能很相似,也是将值赋值给变量。它更像shell中变量的赋值方式,可以将某个变量的值赋值给另一个变量,也可以将字符串赋值给变量。

其实通过set_fact模块创建的变量还有一个特殊性,通过set_fact创建的变量就像主机上的facts信息一样可以在之后的play中被引用,而直接在play中采用vars设置变量只能在当前主机

基础示例:

代码语言:javascript复制
# cat > setfact.yml<<end
---
- hosts: local
  remote_user: root
  vars:
    testvar1: tv1    #关键点-只能在当前主机中使用
  tasks:
  - name: "test shell return variable"
    shell: "echo setfact module"
    register: revar
  - name: setfact moudle setting
    set_fact:
      testvar2: tv2  #关键点-可以在随后的play中任意主机调用使用
      testvar3: "{{revar.cmd}}"  #关键点-变量给变量赋值
      cacheable: yes  #示例设置事实,以便它们将保留在事实缓存中
  - debug:
      msg: "Local host : {{testvar1}} - {{testvar2}} -  {{testvar3}} - {{revar.cmd}}"
 
- hosts: local
  remote_user: root
  tasks:
  - name: "other play get testvar2"
    debug:
      msg: "{{testvar2}}"  #有set_fact模块创建可以被第二个play引用
  # - name: "other play get testvar1"   
  #   debug:
  #     msg: "{{testvar1}}"   #不能被第二play引用会报错(所以这里注释一哈)
  - name: "other play get testvar3"
    debug:
      msg: "{{testvar3}}"
  - name: "show execute command"
    debug:
      msg: "{{revar.cmd}}"   #注册的变量也能在第二个play中使用
end

执行结果:

代码语言:javascript复制
$ansible-playbook setfact.yml
# TASK [debug]
#     "msg": "Local host : tv1 - tv2 -  echo setfact module - echo setfact module"

# TASK [other play get testvar2] 
#     "msg": "tv2"

# TASK [other play get testvar3] 
#     "msg": "echo setfact module"

# ok: [local] 
#     "msg": "echo setfact modu

总结:

  • shell模块注册变量在全局play中皆可用,但是不是变量自定义信息;
  • set_fact模块中建立的变量或者引用的变量在全局中即可用
  • 补充参考:https://www.cnblogs.com/f-ck-need-u/p/7571974.html#1
8.使用内置变量

描述: ansible中还有一些内置变量可供我们使用,内置变量的变量名是被ansible保留的,当我们定义变量时不能使用这些变量名。

「内置变量一览表」

  • 1.ansible_version : 获取到ansible的版本号
  • 2.inventory_dir : 获取到ansible主机中清单文件的存放路径,ansible默认的清单文件/etc/ansible/hosts
  • 3.inventory_hostname: 获取到被操作的当前主机的主机名称(对应主机在清单中配置的名称)
  • 4.inventory_hostname_short : 与内置变量inventory_hostname类似但是这个名称更加简短;
  • 5.groups : 获取到清单中"所有分组"的"分组信息"
  • 6.group_names : 获取到当前主机所在分组的组名
  • 7.hostvars : 帮助我们在操作当前主机时获取到其他主机中的信息
  • 8.play_hosts : 获取到当前play所操作的所有主机的主机名列表

「实践案例」

代码语言:javascript复制
# 示例1.直接采用ansible命令行输出内置变量
#-----ansible_version------
ansible local -m debug -a "msg='当前Ansible版本号: {{ansible_version}}'"
# local | SUCCESS => {
#     "msg": "Ansible Version :  {'major': 2, 'full': '2.8.1', 'string': '2.8.1', 'minor': 8, 'revision': 1}"
# }

#-----inventory_dir------
ansible local -m debug -a "msg='Current Hosts Configure: {{inventory_dir}}'"
# local | SUCCESS => {
#     "msg": "Current Hosts Configure: /etc/ansible"
# }

#-----inventory_hostname*------
$ansible local -m debug -a "msg={{inventory_hostname}}"   #主机清单中的长主机名称
# local | SUCCESS => {
#     "msg": "local"
# }
# local | SUCCESS => {
#     "msg": "local"
# }
#无论是IP还是别名,如果清单的主机名称中包含".",inventory_hostname_short都会取得主机名中第一"."之前的字符作为主机的简短名称。
$ansible testA -m debug -a "msg={{inventory_hostname}}" 
# 10.10.107.221 | SUCCESS => {
#     "msg": "10.10.107.221"
# }
$ansible testA -m debug -a "msg={{inventory_hostname_short}}" #主机清单中的短主机名称
# 10.10.107.221 | SUCCESS => {
#     "msg": "10"
# }

#-----groups*------
ansible local -m debug -a "msg={{groups}}"
# local | SUCCESS => {
#     "msg": {
#         "all": [
#             "local",
#             "10.10.107.221",
#             "10.20.172.179"
#         ],
#         "ops": [
#             "10.10.107.221",
#             "10.20.172.179"
#         ],
#         "testA": [
#             "10.10.107.221"
#         ],
#         "testB": [
#             "10.20.172.179"
#         ],
#         "ungrouped": [
#             "local"
#         ]
#     }
# }
ansible local -m debug -a "msg={{groups.testA}}"
# local | SUCCESS => {
#     "msg": [
#         "10.10.107.221"
#     ]
ansible ops -m debug -a "msg={{groups['ungrouped']}}"
# 10.10.107.221 | SUCCESS => {
#     "msg": [
#         "local"
#     ]
# }
# 10.20.172.179 | SUCCESS => {
#     "msg": [
#         "local"
#     ]
# }

#-----group_names------
ansible local -m debug -a "msg={{group_names}}"
# local | SUCCESS => {
#     "msg": [
#         "ungrouped"
#     ]
# }

ansible testA -m debug -a "msg={{group_names}}"
# 10.10.107.221 | SUCCESS => {
#     "msg": [
#         "ops",
#         "testA"
#     ]
# }

「hostvars 内置变量」描述:通过"set_fact"结合"hostvars"的方式,实现了跨play获取其他主机中的变量信息的功能还是很方便的,并且通过gather_facts关键字来控制当前play是否收集对应主机的facts信息yes|no

「实际案例1」:hostvars 与 play_hosts内置变量联合使用

代码语言:javascript复制
# cat > gatherfcats.yml <<end
---
- name: "Play1 : Gather facts of local"
  hosts: local,testB
  remote_user: root
  tasks:
  - name: "show play hosts"
    debug:
      msg: "Play1 -> play include hosts : {{play_hosts}}"  #当前play包含的hosts

- name: "Play2: Gather facts"
  hosts: testA,testB
  remote_user: root
  tasks:
  - debug: 
     msg: "Play2 -> Local IP : {{hostvars['local'].ansible_default_ipv4}}"  #调用Gather中收集local主机中的IP地址信息
  - debug: 
     msg: "Play2 -> Local interface : {{hostvars['local'].ansible_default_ipv4.interface}}"
end

执行结果:

代码语言:javascript复制
#关键点1
TASK [Gathering Facts] 
ok: [10.20.172.179]
ok: [local]

ok: [local] 
    "msg": "Play1 -> play include hosts : [u'local', u'10.20.172.179']"
ok: [10.20.172.179]
    "msg": "Play1 -> play include hosts : [u'local', u'10.20.172.179']"


TASK [Gathering Facts]
ok: [10.20.172.179]
ok: [10.10.107.221]

ok: [10.10.107.221] => {
    "msg": "Play2 -> Local IP : {u'macaddress': u'00:50:56:b3:dc:b4', u'network': u'10.10.107.0', u'mtu': 1500, u'broadcast': u'10.10.107.255', u'alias': u'ens192', u'netmask': u'255.255.255.0', u'address': u'10.10.107.222', u'interface': u'ens192', u'type': u'ether', u'gateway': u'10.10.107.1'}"
}
.....

ok: [10.20.172.179] => {
    "msg": "Play2 -> Local interface : ens192"
}
......

使用"gather_facts"关键字可以控制play是否进行Gathering Facts主机的信息收集;

  • 第一个play中的"gather_facts: no"表示设置当前play不收集对应主机的信息,无法获取到local主机中的facts信息,原因是local的facts信息并未被收集过,所以调用其他主机的facts信息的前提是对应主机的facts信息已经被收集过。
  • 其实除了facts信息,我们还能够利用hostvars内置变量从别的主机中获取到其他类型的一些变量信息,比如其他主机的注册变量、主机变量、组变量等信息,还有就是注册变量并不用像facts信息那样需要事先收集,即可直接通过hostvars跨主机被引用到;

「实际案例2」:hostvars 与 gather_fcats 与 set_fact模块案例:

代码语言:javascript复制
# cat > gatherfcats1.yml <<end
---
- name: "Play 1 - local"
  hosts: local
  remote_user: root
  gather_facts: no  #由于不进行信息收集其他play无法调用该主机的facts信息,执行速度有所提升;
  vars:
    testvar: "通过vars关键字定义的变量方法是无法被跨主机引用的"
  tasks:
  - name: "setfact module settting"
    set_fact:  #但是可以通过set_fact模块与hostvars来进行跨主机引用(重要)(与gather_facts关键字无关)
      var1: "gather_facts settting no"
  - name: "show set_fact"
    shell: "echo {{var1}}"
    register: shellrst #注册变量也能进行hostvars跨主机调用(与gather_facts关键字无关)

- name: "Play 2 - testA "
  hosts: testA
  remote_user: root
  gather_facts: no
  tasks:
  - debug:
      msg: "Play 1 hostvars : {{hostvars.local.var1}} , Play 1 register shell command: {{hostvars.local.shellrst['cmd']}}"  #但是可以采用hostvars来进行跨主机引用set_fact模块设置的变量
      #msg: "play 1 中的 {hostvars['local'].ansible_default_ipv4 不能被调用由于根本没有进行gathering facts主机信息收集"
      #msg: "play 1 中的 testvar 变量不能被引用 {{testvar}} "
end

执行结果:

代码语言:javascript复制
TASK [debug]
ok: [10.10.107.221] => {
    "msg": "Play 1 hostvars : gather_facts settting no , Play 1 register shell command: echo gather_facts settting no"
}

9.使用 include_vars 模块获取动态变量

描述:前面我们学习了var_files也知道了它的应用场景,但是使用var_files有一个缺点,就是当变量文件动态的被添加变量的时候,其后的playbook并不能读取变化增加的变量值;

但是我们可以依靠include_vars模块能够在任务执行过程中,随时的引入变量文件,以便动态的获取到最新的变量文件内容;

代码语言:javascript复制
# 两种语法方式(模块参数介绍请参考Ansible模块介绍)
- include_vars:
    file: /testdir/ansible/testfile
- include_vars: "/testdir/ansible/testfile"

「基础示例」

代码语言:javascript复制
# cat > include_vars_demo.yml<<END
---
- hosts: local
  remote_user: root
  gather_facts: no
  vars_files:
  - /tmp/ansible/demo1/var.yml
  tasks:
  - debug:
      msg: "{{test1}} {{test2}}"  #由于testvar2已经加入到了变量文件中,所有显示OK
  - lineinfile:
      path: /tmp/ansible/demo1/var.yml #修改添加变量文件
      line: "testvar4: add"
  - include_vars: 
      file: /tmp/ansible/demo1/var.yml
      name: trans_var  #关键点将变量文件中变量全部赋值给另外trans_var变量
  #'include_vars'模块重新加载了变量文件
  - debug:
      msg: "include_vars - {{trans_var.testvar4}}"  # 成功调用了trans_var.testvar4变量
  - include_vars: 
      dir: /tmp/ansible/demo2/   #夹杂这个文件中为.yaml / yml / json 
      files_matching: "^var.*" #加载指定目录中以"var_"开头的变量文件
      ignore_files: ["^var_.*",varintest.yaml] #明确指定需要忽略的变量文件名称的列表
      extensions: [yaml,yml,json,varfile] #允许的扩展
      depth: 1 #递归深度
      name: var  #关键点将变量文件中变量全部赋值给另外
    register: returnval
  - debug:
      msg: "{{var}} -> {{returnval.ansible_included_var_files}}"  # 调用var重新赋值的变量;
END

执行结果:

代码语言:javascript复制
#TASK [debug]
ok: [local] => {"msg": "qixi happy"}

#TASK [lineinfile]
changed: [local]
#TASK [include_vars] 
ok: [local]

#TASK [debug]
ok: [local] => {"msg": "include_vars - add"}

#TASK [include_vars] 
ok: [local]
#TASK [debug]
ok: [local] => { "msg": "{u'test3': u'include_varDemo', u'test4': u'WeiyiGeek'} -> [u'/tmp/ansible/demo2/var.yml']"}

#PLAY RECAP 
local   : ok=6    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

本文至此完毕,更多技术文章,尽情等待下篇好文!

原文地址: https://cloud.tencent.com/developer/article/2128543

0 人点赞