将每个节点的ssh key授权给集群中所有节点的最佳方式

The best way to authorize ssh key of each node to all nodes in the cluster

我想创建一个集群基础结构,每个节点都可以通过 shh 与其他节点进行通信。我想使用 ansible 创建一个 idempotent playbook/role 可以在集群初始化或新节点添加到集群时执行。我想到了 2 个方案来实现这一点。

第一个场景

该场景支持free strategy。无需等待所有主机即可执行任务。但它也要求所有节点都有相关的用户和 public 密钥。因为如果您在同一个剧本中创建用户(由于免费策略),当 task 2 开始时 运行 可能会有未在集群中的其他节点上创建的用户。

虽然我是free strategy的忠实拥护者,但出于效率原因,我没有实现这个场景。它为 n 个节点集群建立了 n^2 个连接。

第二种情况

该场景仅支持linear strategy。由于线性策略,您可以在同一剧本中创建用户,所有用户都将在 task 1 开始之前创建 运行。

我认为这是一个高效的方案。它只为 n 个节点集群建立 2n 个连接。我确实实现了它,并把我写的片段放了进去。

---
- name: create node user
  user:
    name: "{{ node_user }}"
    password: "{{ node_user_pass |password_hash('sha512') }}"
    shell: /bin/bash
    create_home: yes
    generate_ssh_key: yes

- name: fetch all public keys from managed nodes to manager
  fetch: 
    src: "/home/{{ node_user }}/.ssh/id_rsa.pub"
    dest: "tmp/{{ ansible_hostname }}-id_rsa.pub"
    flat: yes

- name: authorize public key for all nodes
  authorized_key:
    user: "{{ node_user }}"
    key: "{{ lookup('file', 'tmp/{{ item }}-id_rsa.pub')}}"
    state: present
  with_items:
    - "{{ groups['cluster_node'] }}"

- name: remove local public key copies
  become: false
  local_action: file dest='tmp/' state=absent
  changed_when: false
  run_once: true

也许我可以使用 lineinfile instead of fetch 但除此之外我不知道它是否正确。当集群规模变大时需要很长时间(因为线性策略)。我可以使用更有效的方法吗?

当 Ansible 循环通过 authorized_key 时,它将(大致)执行以下任务:

  1. 在控制节点
  2. 上创建临时authorized_keypython脚本
  3. 复制新的authorized_keypython脚本到托管节点
  4. 运行 受管节点上的 authorized_key python 脚本具有适当的参数

这随着被管理节点数量的增加而增加n2;有 1000 个盒子,每个盒子执行此任务 1000 次。

我找不到能够正确解释确切幕后情况的特定文档,所以我建议运行一个示例脚本获取感受一下:

- hosts: all
  tasks:
    - name: do thing
      shell: "echo \"hello this is {{item}}\""
      with_items:
        - alice
        - brian
        - charlie

这应该是 运行,带有三重详细标志 (-vvv) 并且输出通过管道传输到 ./ansible.log(例如 ansible-playbook example-loop.yml -i hosts.yml -vvv > example-loop-output.log)。在这些日志中搜索 command.pysftp 将有助于了解您的脚本如何随着 "{{ groups['cluster_node'] }}" 检索的列表的增加而扩展。

对于小型集群,这种低效率是完全可以接受的。但是,它可能会在大型集群上出现问题。

现在,authorized_key 模块本质上只是生成一个 authorized_keys 文件,其中包含 a) authorized_keys 中已经存在的密钥和 b) public集群上每个节点的键。我们可以在控制节点上构建 authorized_keys 文件并将其部署到每个盒子,而不是在每个盒子上单独重复生成 authorized_keys 文件。

authorized_keys 文件本身可以用 assemble; this will take all of the gathered keys and concatenate them into a single file. However, if we just synchronize or copy this file over, we'll wipe out any non-cluster keys added to authorized_keys. To avoid this, we can use blockinfile 生成。 blockinfile 可以管理Ansible添加的集群键。我们将能够添加新密钥,同时删除那些过时的密钥。

- hosts: cluster
  name: create node user and generate keys
  tasks:
    - name: create node user
      user:
        name: "{{ node_user }}"
        password: "{{ node_user_pass |password_hash('sha512') }}"
        shell: /bin/bash
        create_home: yes
        generate_ssh_key: yes

    - name: fetch all public keys from managed nodes to manager
      fetch:
        src: "/home/{{ node_user }}/.ssh/id_rsa.pub"
        dest: "/tmp/keys/{{ ansible_host }}-id_rsa.pub"
        flat: yes
  become: yes

- hosts: localhost
  name: generate authorized_keys file
  tasks:
    - name: Assemble authorized_keys from a directory
      assemble:
        src: "/tmp/keys"
        dest: "/tmp/authorized_keys"

- hosts: cluster
  name: update authorized_keys file
  tasks:
   - name: insert/update configuration using a local file
     blockinfile:
       block: "{{ lookup('file', '/tmp/authorized_keys') }}"
       dest: "/home/{{ node_user }}/.ssh/authorized_keys"
       backup: yes
       create: yes
       owner: "{{ node_user }}"
       group: "{{ node_group }}"
       mode: 0600
  become: yes

按原样,此解决方案不容易与角色兼容;角色旨在仅处理主机的单个值(主机、组、一组组等),上述解决方案需要在组和本地主机之间切换。

我们可以用 delegate_to 来解决这个问题,尽管对于大型集群来说它可能有点低效,因为集群中的每个节点都会尝试组装 authorized_keys。根据 ansible 项目的整体结构(以及团队的规模),这可能是理想的,也可能不是理想的;当浏览带有 delegate_to 的大型脚本时,很容易错过正在本地执行的某些操作。

 - hosts: cluster
      name: create node user and generate keys
      tasks:
        - name: create node user
          user:
            name: "{{ node_user }}"
            password: "{{ node_user_pass |password_hash('sha512') }}"
            shell: /bin/bash
            create_home: yes
            generate_ssh_key: yes

        - name: fetch all public keys from managed nodes to manager
          fetch:
            src: "/home/{{ node_user }}/.ssh/id_rsa.pub"
            dest: "/tmp/keys/{{ ansible_host }}-id_rsa.pub"
            flat: yes

        - name: Assemble authorized_keys from a directory
          assemble:
            src: "/tmp/keys"
            dest: "/tmp/authorized_keys"
          delegate_to: localhost

        - name: insert/update configuration using a local file
          blockinfile:
            block: "{{ lookup('file', '/tmp/authorized_keys') }}"
            dest: "/home/{{ node_user }}/.ssh/authorized_keys"
            backup: yes
            create: yes
            owner: "{{ node_user }}"
            group: "{{ node_group }}"
            mode: 0600
      become: yes