0

我正在编写一个 ansible 脚本,我想在每个主机上读取一个文件,并根据该文件中的一些文本为该主机设置一个环境变量。而且我需要该环境变量在该主机上的整个剧本执行期间可用。

我一直在阅读的是,如果我在一个任务下定义 env: ,它仅适用于该任务而不适用于其他后续任务。那是对的吗?

- name: Modify server properties
  hosts: kafka_broker
  vars:
    ansible_ssh_extra_args: "-o StrictHostKeyChecking=no"
    ansible_host_key_checking: false
    contents: "{{ lookup('file', '/etc/kafka/secrets/masterkey.txt') }}"
    extract_key: "{{ contents.split('\n').2.split('|').2|trim }}"

  environment:
    CONFLUENT_KEY: "{{ extract_key }}"

这就是我试图从每个主机获取信息并希望为每个主机设置 env 变量但适用于该主机的整个剧本的方式

- name: Slurp hosts file
  slurp:
    src: /etc/kafka/secrets/masterkey.txt
  register: masterkeyfile

- debug: msg="{{ masterkeyfile['content'] | b64decode }}"

- name: Set masterkeyfilecontent 
  set_fact:
    masterkeyfilecontent: "{{ masterkeyfile['content'] | b64decode }}" 

- name: Set masterkeyval 
  set_fact:
    masterkeyval: "{{ masterkeyfilecontent.split('\n').2.split('|').2|trim }}"

然后我想为每个主机设置环境变量

CONFLUENT_KEY: "{{ masterkeyval }}

- debug:
    var=masterkeyval

可以这样做吗?如何定义我的任务/ ansible 脚本来实现这一目标?

谢谢

4

1 回答 1

0

解决方案 1:本地事实

此解决方案是 IMO 最简单的解决方案,但需要在每个目标服务器上放置一个文件。

假设您将以下可执行文件放入/etc/ansible/facts.d/kafka.fact. 这只是一个虚拟示例,请根据您的确切需求进行调整。我正在使用 jq 输出正确的 json 字符串。echo如果您信任关键内容不会导致问题,您可以直接使用。你也可以使用你喜欢的任何其他可执行文件(python、ruby、perl...),只要你输出一个 json 结构

#!/bin/bash

# replace here with some logic to read the value you need.
# I'll use a static value for this example
PARSED_KEY="I'm a key that should be parsed dynamically"

# echo the json result using jq
echo $(
    jq -n \
    --arg pk "$PARSED_KEY" \
    '{masterkey: $pk}'
)

完成此操作后,您可以看到事实可用于给定主机。我将在这里仅演示 localhost ,但这将适用于具有此本地事实脚本的任何主机:

$ ansible localhost -m setup -a filter=ansible_local
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_local": {
            "kafka": {
                "masterkey": "I'm a key that should be parsed dynamically"
            }
        }
    },
    "changed": false
}

您现在可以在任何需要的地方使用这个事实。请注意,您当然必须收集事实才能使该值可用。在您的情况下,我们可以使用以下剧本进行测试:

---
- hosts: localhost

  environment:
    CONFLUENT_KEY: "{{ ansible_local.kafka.masterkey | default ('empty') }}"

  tasks:
    - name: echo our env var
      command: echo $CONFLUENT_KEY
      register: echo

    - name: show the result
      debug:
        msg: "Our env is: {{ echo.stdout }}"

这使

PLAY [localhost] ***************************************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************************************
ok: [localhost]

TASK [echo our env var] ********************************************************************************************************************************************************
changed: [localhost]

TASK [show the result] *********************************************************************************************************************************************************
ok: [localhost] => {
    "msg": "Our env is: I'm a key that should be parsed dynamically"
}

PLAY RECAP *********************************************************************************************************************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

解决方案 2:自定义事实模块

此解决方案稍微复杂一些,但从长远来看可能具有更好的管理能力,并且不需要将文件放置在目标服务器上。

为了尽可能简单明了,我将演示事实模块放在library与我的剧本相邻的文件夹中。将这样的模块放在集合或角色中对于生产项目来说是可取的,但超出了这个(已经很长的)答案。要获取有关所有这些主题的更多信息,您可以阅读(非详尽的参考文献列表):

创建一个library/kafka_facts.py文件。您将不得不适应您的具体情况。在这种情况下,我决定将密钥放在一行中,/tmp/keyfile.txt并在我的模块中对其进行硬编码。请注意,如果文件不存在,则不会返回事实。我按照上述文档链接中的示例中的建议添加了所有文档字符串。

#!/usr/bin/python

from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

DOCUMENTATION = r'''
---
module: kafka_facts

short_description: This is a test module for example

version_added: "1.0.0"

description: This is a test module for example to answer a question on StackOverflow.

author:
    - Olivier Clavel (@zeitounator)
'''

EXAMPLES = r'''
- name: Return ansible_facts
  kafka_facts:
'''

RETURN = r'''
ansible_facts:
  description: Kafka facts to add to ansible_facts.
  returned: always
  type: dict
  contains:
    kafka_masterkey:
      description: Masterkey present on server.
      type: str
      returned: when /tmp/keyfile.txt exists on server
      sample: 'f18bba7f-caf6-4897-95be-c7d3cc66f98a'
'''

from ansible.module_utils.basic import AnsibleModule


def run_module():
    module_args = dict()

    # Initialize result dict
    result = dict(
        changed=False,
        ansible_facts=dict(),
    )

    module = AnsibleModule(
        argument_spec=module_args,
        supports_check_mode=True
    )

    if module.check_mode:
        module.exit_json(**result)

    keyfile = "/tmp/keyfile.txt"
    try:
        with open(keyfile) as f:
            result['ansible_facts'] = {
                'kafka_masterkey': f.read(),
            }
    except FileNotFoundError:
        pass

    module.exit_json(**result)


def main():
    run_module()


if __name__ == '__main__':
    main()

与我之前的解决方案一样,以下演示将仅在 localhost 上完成,但可以在任何目标服务器上运行。

首先我们在预期的文件中创建一个密钥

echo $(uuidgen) > /tmp/keyfile.txt

我们现在可以使用该模块,如下面的剧本所示:

---
- name: Use custom facts
  hosts: localhost
  gather_facts: false

  environment:
    CONFLUENT_KEY: "{{ ansible_facts.kafka_masterkey | default('N/A') }}"

  tasks:
    - name: echo the env var
      command: echo $CONFLUENT_KEY
      register: echo_before
      changed_when: false

    - name: show our var is empty for now
      debug:
        msg: "CONFLUENT_KEY was returned as: {{ echo_before.stdout }}"

    - name: Gather our custom facts related to kafka
      kafka_facts:

    - name: Echo the env var
      command: echo $CONFLUENT_KEY
      register: echo_after
      changed_when: false

    - name: show our var is empty for now
      debug:
        msg: "CONFLUENT_KEY was returned as: {{ echo_after.stdout }}"

这使:

PLAY [Use custom facts] ****************************************************************************************************************************************************************************************************************

TASK [echo the env var] ****************************************************************************************************************************************************************************************************************
ok: [localhost]

TASK [show our var is empty for now] ***************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": "CONFLUENT_KEY was returned as: N/A"
}

TASK [Gather our custom facts related to kafka] ****************************************************************************************************************************************************************************************
ok: [localhost]

TASK [Echo the env var] ****************************************************************************************************************************************************************************************************************
ok: [localhost]

TASK [show our var now has a value (if the file exists)] *******************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": "CONFLUENT_KEY was returned as: d8fc525e-3fa9-4401-aca3-19ac915e5c0d"
}

PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost                  : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
于 2021-02-28T17:59:33.423 回答