将每个节点的ssh key授权给集群中所有节点的最佳方式
The best way to authorize ssh key of each node to all nodes in the cluster
我想创建一个集群基础结构,每个节点都可以通过 shh 与其他节点进行通信。我想使用 ansible 创建一个 idempotent playbook/role 可以在集群初始化或新节点添加到集群时执行。我想到了 2 个方案来实现这一点。
第一个场景
task 1
从节点获取 ssh 密钥(可能将其分配给变量或写入文件)。
- 然后执行locally的
task 2
循环遍历其他节点,并使用获取的密钥授权第一个节点。
该场景支持free strategy。无需等待所有主机即可执行任务。但它也要求所有节点都有相关的用户和 public 密钥。因为如果您在同一个剧本中创建用户(由于免费策略),当 task 2
开始时 运行 可能会有未在集群中的其他节点上创建的用户。
虽然我是free strategy的忠实拥护者,但出于效率原因,我没有实现这个场景。它为 个节点集群建立了 个连接。
第二种情况
task 1
按顺序从所有节点获取 ssh 密钥。然后将每个写入一个文件,该文件的名称根据 ansible_hostname
. 设置
- 然后执行locally的
task 2
循环遍历其他节点并授权所有密钥。
该场景仅支持linear strategy。由于线性策略,您可以在同一剧本中创建用户,所有用户都将在 task 1
开始之前创建 运行。
我认为这是一个高效的方案。它只为 个节点集群建立 个连接。我确实实现了它,并把我写的片段放了进去。
---
- 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 时,它将(大致)执行以下任务:
- 在控制节点
上创建临时authorized_keypython脚本
- 复制新的authorized_keypython脚本到托管节点
- 运行 受管节点上的 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.py
和 sftp
将有助于了解您的脚本如何随着 "{{ 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
我想创建一个集群基础结构,每个节点都可以通过 shh 与其他节点进行通信。我想使用 ansible 创建一个 idempotent playbook/role 可以在集群初始化或新节点添加到集群时执行。我想到了 2 个方案来实现这一点。
第一个场景
task 1
从节点获取 ssh 密钥(可能将其分配给变量或写入文件)。- 然后执行locally的
task 2
循环遍历其他节点,并使用获取的密钥授权第一个节点。
该场景支持free strategy。无需等待所有主机即可执行任务。但它也要求所有节点都有相关的用户和 public 密钥。因为如果您在同一个剧本中创建用户(由于免费策略),当 task 2
开始时 运行 可能会有未在集群中的其他节点上创建的用户。
虽然我是free strategy的忠实拥护者,但出于效率原因,我没有实现这个场景。它为 个节点集群建立了 个连接。
第二种情况
task 1
按顺序从所有节点获取 ssh 密钥。然后将每个写入一个文件,该文件的名称根据ansible_hostname
. 设置
- 然后执行locally的
task 2
循环遍历其他节点并授权所有密钥。
该场景仅支持linear strategy。由于线性策略,您可以在同一剧本中创建用户,所有用户都将在 task 1
开始之前创建 运行。
我认为这是一个高效的方案。它只为 个节点集群建立 个连接。我确实实现了它,并把我写的片段放了进去。
---
- 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 时,它将(大致)执行以下任务:
- 在控制节点 上创建临时authorized_keypython脚本
- 复制新的authorized_keypython脚本到托管节点
- 运行 受管节点上的 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.py
和 sftp
将有助于了解您的脚本如何随着 "{{ 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