首页 > 解决方案 > 将每个节点的 ssh key 授权给集群中所有节点的最佳方式

问题描述

我想创建一个集群基础设施,每个节点都通过 shh 与其他节点通信。我想使用 ansible 创建一个幂等剧本/角色,可以在集群初始化或新节点添加到集群时执行。我能够想到 2 个场景来实现这一目标。

 第一个场景

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

第一个场景

尽管我是自由策略的忠实拥护者,但由于效率原因,我没有实施这种方案。它n^2n节点集群建立连接。

 第二个场景

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

第二个场景

我认为这是一个有效的场景。它只2nn节点集群建立连接。我确实实现了它,我把我写的片段。

---
- 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而不是fetch但除此之外我不知道这是否是正确的方法。当集群大小变大时需要很长时间(因为线性策略)。有没有更有效的方法可以使用?

标签: linuxsshansiblessh-keysansible-role

解决方案


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

  1. 在控制节点上创建一个临时的authorized_key python脚本
  2. 将新的 authorized_key python 脚本复制到被管节点
  3. 使用适当的参数在受管节点上运行 authorized_key python 脚本

随着受管节点数量的增加,这增加了 n 2 ;有 1000 个盒子,这个任务每个盒子执行 1000 次。

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

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

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

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

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

可以使用assemble生成 authorized_keys 文件本身;这将获取所有收集的密钥并将它们连接到一个文件中。但是,如果我们只是synchronizecopy这个文件结束,我们将清除任何添加到授权密钥的非集群密钥。为了避免这种情况,我们可以使用blockinfileblockinfile可以管理 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

推荐阅读