如何自动将新Droplet添加到您的配置管理系统

来自菜鸟教程
跳转至:导航、​搜索

介绍

使用 DigitalOcean 元数据服务,管理员可以提供允许新服务器自动配置自身的说明。 虽然这很有用,但许多组织喜欢在 Chef 或 Puppet 等配置管理工具中处理其所有基础架构配置。

在本指南中,我们将演示如何使用元数据服务和 CloudInit 引导 DigitalOcean 服务器以连接到现有的配置管理部署。 然后可以由配置管理服务处理服务器的实际配置。 我们将演示如何引导 Chef 和 Puppet 节点。

先决条件

为了完成本指南,您必须熟悉 DigitalOcean 元数据服务。 您可以在 本指南 中找到有关如何在元数据服务中输入信息和从元数据服务中检索信息的更多信息。

本指南将利用一种名为 cloud-config 的脚本,该脚本在您的 Droplet 上的 CloudInit 服务首次启动时使用,以执行首次运行配置。 您应该对 cloud-config 脚本、它们的语法和行为有一些基本的了解,以便更好地理解如何修改本指南中介绍的脚本。 您可以在此处 找到有关云配置脚本 的介绍。 有关更实际的示例(以及有关格式限制的一些讨论),您可以在此处 阅读我们关于使用 cloud-config 执行一些基本任务的指南。

使用 Cloud-Config 脚本引导 Chef 节点

使用 DigitalOcean 元数据服务,您可以使用 cloud-config 脚本轻松地将新服务器连接到现有 Chef 控制的基础架构中。

要将您的新服务器添加到此系统,您必须已经配置了一个 Chef 服务器,您的新服务器可以联系该服务器以接收配置说明。 如果您在部署 Chef 服务器和管理工作站时需要帮助,可以按照 本指南 开始。

总体规划

当新服务器上线时,必须将其置于 Chef 服务器的控制之下。 通常,这可以通过使用 knife 管理命令并使用 bootstrap 子命令连接到新服务器来完成。 这将连接到新服务器,安装 Chef 客户端和允许新节点连接到 Chef 服务器的验证凭据。 之后,Chef 客户端连接到服务器,验证自身,接收新的客户端凭据,从服务器中提取其配置,并执行任何必要的操作以使自身进入所需状态。

在本指南中,我们将使用 cloud-config 脚本来替换手动引导步骤,允许新节点自动连接到 Chef 服务器、验证自身、接收客户端凭据并执行初始 Chef 客户端运行。 服务器将在首次启动时自动执行此操作,无需管理员的任何手动帮助。

从 Knife 配置文件中收集必要的数据

为了使我们的 cloud-config 脚本成功引导,它需要访问通常可用于 knife 命令的凭据。 具体来说,我们需要以下信息:

  • Chef 验证名称
  • 验证密钥
  • 可以访问 Chef 服务器的 URL

所有这些信息都以正确的格式在工作站上用于管理 Chef 基础架构的 knife 配置文件中可用。 在 Chef 存储库中,应该有一个名为 .chef 的隐藏目录,其中包含此文件。

假设您的 Chef 存储库位于工作站的主目录中并且名为 chef-repo,您可以通过键入以下内容输出文件的内容:

cat ~/chef-repo/.chef/knife.rb

您需要的信息在下面突出显示:

current_dir = File.dirname(__FILE__)
log_level                :info
log_location             STDOUT
node_name                "jellingwood"
client_key               "#{current_dir}/jellingwood.pem"
validation_client_name   "digitalocean-validator"
validation_key           "#{current_dir}/digitalocean-validator.pem"
chef_server_url          "https://your_server.com/organizations/digitalocean"
syntax_check_cache_path  "#{ENV['HOME']}/.chef/syntaxcache"
cookbook_path            ["#{current_dir}/../cookbooks"]

验证名称和 Chef 服务器 URL 可以直接从文件中获取。 复制这些值,以便您可以在 cloud-config 文件中使用它们。

validation_key 指向保存实际密钥的位置。 在上面的例子中,这表示它与knife.rb文件位于同一目录下,名为digitalocean-validator.pem。 对于您的配置,这可能会有所不同。

我们需要这个文件的内容,所以再次使用 cat 命令。 修改命令以将其指向为您的验证器密钥指定的位置:

cat ~/chef-repo/.chef/digitalocean-validator.pem

您将看到一个 RSA 私钥:

-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA3O60HT5pwEo6xUwcZ8WtExBUhoL3bTjlsvHVXg1JVmBUES+f
V9jLu2N00uSZEDZneCIQyHLBXnqD/UNvWEPNvPzt1ecXzmw2BytB7lPDW4/F/8tJ
vAVrKqC7B04VFGmcFY2zC8gf8BWmX8CNRDQooM7UO5OWe/H6GDGPPRIITerO3GrU

. . .

sWyRAoGBAKNc/ZUM8ljRV0UJxQ9nbdozXRZjtUaNgXMNiw+oP2HYYdHrlkKnGHYJ
Js63rvjpq8pocjE8YI+2H0v4/4uWqW8GEBfrWbLMzGsYPnRyiHR5+hgjCUU50RB3
eFoNbURwLYcq2Z/IAQZpDpJWpofz3OVMpMXtei1cIflrAAd2wtWO
-----END RSA PRIVATE KEY-----

复制整个验证密钥,以便您可以暂时在 cloud-config 脚本中使用它。

基本 Cloud-Config Chef 客户端安装

获得上述数据后,您可以构建脚本。 Chef 配置可以通过一个名为 chef 的专用 cloud-config 模块来完成。 cloud-config 必须包含有效的 YAML,并且必须将 #cloud-config 作为脚本的第一行。

首先,您的脚本将如下所示:

#cloud-config
chef:

cloud-config 文档声称能够从 Ruby gem、包或使用传统的“omnibus”安装方法安装 Chef 客户端。 然而,在实践中,gem 和 package 方法都容易失败,所以我们将使用“omnibus”方法。 虽然通常没有必要,但我们也会明确列出综合安装程序的位置。

我们将 force_install 设置为“假”。 这样,如果由于某种原因已经在映像上安装了 Chef 客户端(例如,如果您从快照进行部署),则不会重新安装客户端。 到目前为止,我们的脚本如下所示:

#cloud-config
chef:
  install_type: "omnibus"
  omnibus_url: "https://www.opscode.com/chef/install.sh"
  force_install: false

接下来,我们可以选择使用 node_name 指令为 Chef 基础架构中的新服务器选择一个名称。 如果您不设置此项,Chef 将使用服务器的主机名,因此这是可选的。 但是,这在您的 Chef 环境中必须是唯一的。

之后,我们可以添加从 Chef 工作站获取的所有连接信息。 我们将 server_url 选项设置为 Chef 服务器的位置,与 knife.rb 文件中的位置完全相同。 validation_name 选项也是如此。

对于验证密钥,我们将使用 YAML 管道符号 (|) 输入我们在工作站上找到的整个验证密钥:

#cloud-config
chef:
  install_type: "omnibus"
  omnibus_url: "https://www.opscode.com/chef/install.sh"
  force_install: false
  node_name: "new_node"
  server_url: "https://your_server.com/organizations/digitalocean"
  validation_name: "digitalocean-validator"
  validation_key: |
    -----BEGIN RSA PRIVATE KEY-----
    MIIEowIBAAKCAQEA3O60HT5pwEo6xUwcZ8WtExBUhoL3bTjlsvHVXg1JVmBUES+f
    V9jLu2N00uSZEDZneCIQyHLBXnqD/UNvWEPNvPzt1ecXzmw2BytB7lPDW4/F/8tJ
    vAVrKqC7B04VFGmcFY2zC8gf8BWmX8CNRDQooM7UO5OWe/H6GDGPPRIITerO3GrU

    . . .

    sWyRAoGBAKNc/ZUM8ljRV0UJxQ9nbdozXRZjtUaNgXMNiw+oP2HYYdHrlkKnGHYJ
    Js63rvjpq8pocjE8YI+2H0v4/4uWqW8GEBfrWbLMzGsYPnRyiHR5+hgjCUU50RB3
    eFoNbURwLYcq2Z/IAQZpDpJWpofz3OVMpMXtei1cIflrAAd2wtWO
    -----END RSA PRIVATE KEY-----

此时,您的脚本具有连接到 Chef 服务器和创建客户端凭据所需的所有身份验证。

配置 Chef 环境、run_list 和属性

虽然上述详细信息为客户端连接到 Chef 服务器提供了足够的信息,但我们没有向节点提供有关如何实际配置自身的任何信息。 我们也可以在 cloud-config 脚本中提供这些信息。

要指定新节点应放置的环境,请使用 environment 选项。 如果未设置,则将设置 _default 环境,这是尚未赋予其他环境的 Chef 节点的通用默认值。

chef:
  environment: "staging"

我们的 run_list 可以指定为客户应按顺序应用的项目的简单列表。 这些可以是配方或角色。

chef:
  run_list:
    - "recipe[lamp]"
    - "role[backend-web]"

您可以使用 initial_attributes 层次结构指定新节点的初始属性。 这将设置影响 run_list 应用方式的初始属性:

chef:
  initial_attributes:
    lamp:
      apache:
        port: 80
      mysql:
        username: webclient
        pass: $#fjeaiop34S

当连接到之前的 cloud-config 脚本时,它可能看起来像这样:

#cloud-config
chef:
  install_type: "omnibus"
  omnibus_url: "https://www.opscode.com/chef/install.sh"
  force_install: false
  node_name: "new_node"
  server_url: "https://your_server.com/organizations/digitalocean"
  validation_name: "digitalocean-validator"
  validation_key: |
    -----BEGIN RSA PRIVATE KEY-----
    MIIEowIBAAKCAQEA3O60HT5pwEo6xUwcZ8WtExBUhoL3bTjlsvHVXg1JVmBUES+f
    V9jLu2N00uSZEDZneCIQyHLBXnqD/UNvWEPNvPzt1ecXzmw2BytB7lPDW4/F/8tJ
    vAVrKqC7B04VFGmcFY2zC8gf8BWmX8CNRDQooM7UO5OWe/H6GDGPPRIITerO3GrU

    . . .

    sWyRAoGBAKNc/ZUM8ljRV0UJxQ9nbdozXRZjtUaNgXMNiw+oP2HYYdHrlkKnGHYJ
    Js63rvjpq8pocjE8YI+2H0v4/4uWqW8GEBfrWbLMzGsYPnRyiHR5+hgjCUU50RB3
    eFoNbURwLYcq2Z/IAQZpDpJWpofz3OVMpMXtei1cIflrAAd2wtWO
    -----END RSA PRIVATE KEY-----
  environment: "staging"
  run_list:
    - "recipe[lamp]"
    - "role[backend-web]"
  initial_attributes:
    lamp:
      apache:
        port: 80
      mysql:
        username: webclient
        pass: $#fjeaiop34S

重定向输出和配置 Chef 客户端运行

上面的脚本包含 chef: 部分下所需的所有信息。 但是,我们应该使用其他一些 cloud-config 模块做一些其他事情。

首先,我们应该指定我们希望将每个命令和子命令的输出重定向到 CloudInit 进程的输出日志中。 默认情况下位于 /var/log/cloud-init-output.log。 我们可以像这样使用 output 模块来做到这一点:

output: {all: '| tee -a /var/log/cloud-init-output.log'}

我们要做的另一件事是将 Chef 客户端设置为在安装和配置后实际运行。 在撰写本文时,综合安装方法不会自动执行此操作。

我们可以通过等到 chef-client 可执行文件安装在服务器上后再调用命令来强制执行此行为。 使用简单的 bash 循环,我们将每五秒检查一次该文件是否存在。 找到后,我们将运行 chef-client 以实现我们指定的初始配置。

runcmd 模块可用于发出任意命令。 这是我们的 bash 循环的理想位置:

runcmd:
  - while [ ! -e /usr/bin/chef-client ]; do sleep 5; done; chef-client

此外,您还可以选择添加另一个 cloud-config 指令,以在首次启动后对元数据端点进行空路由。 这很有用,因为我们将私钥放入用户数据中。 如果不对元数据端点进行空路由,服务器上的任何用户都可以访问它。 通过添加实现这一点:

disable_ec2_metadata: true

将这些与我们迄今为止构建的脚本相结合,我们可以获得引导节点并将其连接到我们的 Chef 基础设施所需的完整脚本:

#cloud-config
chef:
  install_type: "omnibus"
  omnibus_url: "https://www.opscode.com/chef/install.sh"
  force_install: false
  node_name: "new_node"
  server_url: "https://your_server.com/organizations/digitalocean"
  validation_name: "digitalocean-validator"
  validation_key: |
    -----BEGIN RSA PRIVATE KEY-----
    MIIEowIBAAKCAQEA3O60HT5pwEo6xUwcZ8WtExBUhoL3bTjlsvHVXg1JVmBUES+f
    V9jLu2N00uSZEDZneCIQyHLBXnqD/UNvWEPNvPzt1ecXzmw2BytB7lPDW4/F/8tJ
    vAVrKqC7B04VFGmcFY2zC8gf8BWmX8CNRDQooM7UO5OWe/H6GDGPPRIITerO3GrU

    . . .

    sWyRAoGBAKNc/ZUM8ljRV0UJxQ9nbdozXRZjtUaNgXMNiw+oP2HYYdHrlkKnGHYJ
    Js63rvjpq8pocjE8YI+2H0v4/4uWqW8GEBfrWbLMzGsYPnRyiHR5+hgjCUU50RB3
    eFoNbURwLYcq2Z/IAQZpDpJWpofz3OVMpMXtei1cIflrAAd2wtWO
    -----END RSA PRIVATE KEY-----
  environment: "staging"
  run_list:
    - "recipe[lamp]"
    - "role[backend-web]"
  initial_attributes:
    lamp:
      apache:
        port: 80
      mysql:
        username: webclient
        pass: $#fjeaiop34S
output: {all: '| tee -a /var/log/cloud-init-output.log'}
runcmd:
  - while [ ! -e /usr/bin/chef-client ]; do sleep 5; done; chef-client
disable_ec2_metadata: true

可以根据需要为基础架构中的每个新服务器调整上述脚本。

使用 Cloud-Config 脚本引导 Puppet 节点

如果您的基础架构依赖 Puppet 进行配置管理,您可以改用 puppet 模块。 与 Chef 示例一样,引导 Puppet 节点涉及使用 cloud-config 将新服务器附加到现有配置管理基础架构。

在开始之前,您应该为您的基础架构配置一个 Puppet 主服务器。 如果您在启动和运行 Puppet 服务器时需要帮助,请查看 本指南

总体规划

当一个新的 Puppet 服务器上线时,会安装一个 Puppet 代理,以便它可以与 Puppet 主服务器通信。 该代理负责接收和应用指示节点所需状态的信息。 为此,代理与主节点连接,上传有关自身的数据,下拉描述其所需状态的当前目录,并执行达到该状态所需的操作。

不过,在此之前,代理必须在第一次运行时将自己注册到主服务器。 它创建一个证书签名请求并将其发送给主节点进行签名。 通常,代理会定期重新连接到主服务器,直到证书被签名,但如果适合您的环境,您可以将 Puppet 配置为自动签署具有某些特征的传入请求(我们将在稍后介绍)。

使用我们的 cloud-config 脚本,我们将为我们的新服务器配置首次连接到主服务器所需的信息。 此时,它可以以目录的形式从 Puppet 主服务器检索配置详细信息。

从 Puppet Master 那里收集必要的数据

在构建我们的 cloud-config 文件之前,我们需要做的第一件事是从我们需要连接的 Puppet 主服务器收集数据。 我们只需要几条信息。

首先,您需要获取 Puppet 主服务器的完全限定域名 (FQDN)。 您可以通过键入以下内容来执行此操作:

hostname -f

在大多数情况下,它应该返回如下内容:

puppet.example.com

您还可以检查您的 Puppet 主配置文件以查看是否设置了 dns_alt_names 选项:

cat /etc/puppet/puppet.conf
. . .

dns_alt_names = puppet,puppet.example.com

. . .

如果您的 Puppet Master 的 SSL 证书是在设置这些选项后生成的,那么它们也可能可用。

我们需要收集的另一个项目是 Puppet Master 的证书颁发机构证书。 这可以在 /var/lib/puppet/ssl/certs/ca.pem/var/lib/puppet/ssl/ca/ca_crt.pem 中找到:

sudo cat /var/lib/puppet/ssl/certs/ca.pem

结果将如下所示:

-----BEGIN CERTIFICATE-----
MIIFXjCCA0agAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFQdXBw
ZXQgQ0E6IHB1cHBldDAeFw8xNTAyMTkxOTA0MzVaFw0yMDAyMTkxOTA0MzVaMBwx
GjAYBgNVBAMMEVB1cHBldCBDQTogcHVwcGV0MIICIjANBgkqhkiG9w0BAQEFAAOC

. . .

arsjZT5/CtIhtP33Jl3mCp7U2F6bsk4/GDGRaAsFXjJHvBbL93NzgpkZ7elf0zUP
rOcSGrDrUuzuJk8lEAtrZr/IfAgfKKXPqbyYF95V1qN3OMY+aTcrK20XTydKVWSe
l5UfYGY3S9UJFrSn9aBsZzN+10HXPkaFKo7HxpztlYyJNI8UVSatcRF4aYYqt9KR
UClnR+2WxK5v7ix0CVd4/KpYH/6YivvyTwxrhjF2AksZKg==
-----END CERTIFICATE-----

完整复制证书。 我们将把它包含在我们的 cloud-config 文件中,以便我们的新服务器可以验证它们是否连接到正确的 Puppet Master。

获得这些信息后,您可以开始构建 cloud-config 文件,以便新服务器可以将自己插入现有的 Puppet 基础架构。

基本 Cloud-Config Puppet 节点安装

新 Puppet 节点的 cloud-config 配置相当简单。 所有 Puppet 特定的配置都位于文件的 puppet: 部分。 与每个 cloud-config 文件一样,第一行必须单独包含 #cloud-config

#cloud-config
puppet:

在此之下,只有两个小节。 第一个是 ca_cert 键。 这将使用管道字符开始一个 YAML 文本块,以便 CA 证书可以作为一个缩进块完整地给出:

#cloud-config
puppet:
  ca_cert: |
    -----BEGIN CERTIFICATE-----
    MIIFXjCCA0agAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFQdXBw
    ZXQgQ0E6IHB1cHBldDAeFw8xNTAyMTkxOTA0MzVaFw0yMDAyMTkxOTA0MzVaMBwx
    GjAYBgNVBAMMEVB1cHBldCBDQTogcHVwcGV0MIICIjANBgkqhkiG9w0BAQEFAAOC
    
    . . .
    
    arsjZT5/CtIhtP33Jl3mCp7U2F6bsk4/GDGRaAsFXjJHvBbL93NzgpkZ7elf0zUP
    rOcSGrDrUuzuJk8lEAtrZr/IfAgfKKXPqbyYF95V1qN3OMY+aTcrK20XTydKVWSe
    l5UfYGY3S9UJFrSn9aBsZzN+10HXPkaFKo7HxpztlYyJNI8UVSatcRF4aYYqt9KR
    UClnR+2WxK5v7ix0CVd4/KpYH/6YivvyTwxrhjF2AksZKg==
    -----END CERTIFICATE-----

确保包括整个证书以及开始和结束标记并适当缩进。

puppet: 下的第二个部分是 conf: 部分。 这用于指定将附加到通用 puppet.conf 文件的键值对。 键值对应该放在节标题下,就像它们在 puppet.conf 文件中一样。

例如,至少,新服务器需要知道 Puppet 主服务器的地址。 在 puppet.conf 文件中,它位于 [agent] 部分下,如下所示:

. . .

[agent]
server = puppet.example.com

. . .

要在 cloud-config 语法中指定这一点,您可以将其添加到我们目前所拥有的内容中:

#cloud-config
puppet:
  ca_cert: |
    -----BEGIN CERTIFICATE-----
    MIIFXjCCA0agAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFQdXBw
    ZXQgQ0E6IHB1cHBldDAeFw8xNTAyMTkxOTA0MzVaFw0yMDAyMTkxOTA0MzVaMBwx
    GjAYBgNVBAMMEVB1cHBldCBDQTogcHVwcGV0MIICIjANBgkqhkiG9w0BAQEFAAOC
    
    . . .
    
    arsjZT5/CtIhtP33Jl3mCp7U2F6bsk4/GDGRaAsFXjJHvBbL93NzgpkZ7elf0zUP
    rOcSGrDrUuzuJk8lEAtrZr/IfAgfKKXPqbyYF95V1qN3OMY+aTcrK20XTydKVWSe
    l5UfYGY3S9UJFrSn9aBsZzN+10HXPkaFKo7HxpztlYyJNI8UVSatcRF4aYYqt9KR
    UClnR+2WxK5v7ix0CVd4/KpYH/6YivvyTwxrhjF2AksZKg==
    -----END CERTIFICATE-----
  conf:
    agent:
      server: "puppet.example.com"

请注意,conf: 部分与 ca_cert 部分是内联的,而不是子元素。 这是连接到 Puppet master 所需的最低要求。 在 puppet.conf 中找到的任何其他配置项都可以以类似的方式添加,方法是首先为部分名称创建一个级别,然后定义键值对。

在此之后,我们应该将所有未来的输出重定向到 cloud-init-output.log 文件,并添加一个 runcmd 行,与我们为 Chef 配置添加的行相当。 这将等到安装 Puppet 代理,然后启用并重新启动它。 我们还可以在第一次运行后对元数据端点进行空路由,就像我们在 Chef 部分中所做的那样。 这些行 cloud-config 指令应放在任何其他模块部分之外:

. . .

  conf:
    agent:
      server: "puppet.example.com"
output: {all: '| tee -a /var/log/cloud-init-output.log'}
runcmd:
  - while [ ! -e /usr/bin/puppet ]; do sleep 5; done; puppet agent --enable; service puppet restart
disable_ec2_metadata: true

有了这些信息,新服务器可以连接到 Puppet 主服务器,然后生成客户端证书签名请求以传输到主服务器。 默认情况下,客户端证书必须在 Puppet Master 上手动签名。 完成此操作后,在下一个 Puppet 代理更新间隔(默认每 30 分钟),节点将从 Puppet Master 中下拉其配置。 稍后我们将演示如何实现一个相对安全的自动签名机制来避免这种延迟。

定义节点的证书名称

可以放入新服务器的 puppet.conf 文件中的值之一是一种特殊情况。 在 cloud-config 文件中,如果给定了某些变量,则 certname 选项可以替换环境中的值。 识别以下变量:

  • %i:服务器的实例ID。 这将在创建服务器时取自 http://169.254.169.254/metadata/v1/id。 它对应于用于唯一标识 Droplet 的 Droplet ID。
  • %f:服务器的 FQDN。

考虑到这一点,常见的 certname 设置如下所示:

#cloud-config
puppet:

. . .

  conf:
    agent:
      server: "puppet.example.com"
      certname: "%i.%f"

这将产生一个 certname ,其模式类似于:

   |-Droplet ID
   |
   |            |-Fully Qualified Domain Name
   |            |
|-----||-------------------|
123456.testnode.example.com

将 Droplet ID 作为 certname 的一部分对于配置安全 Puppet 自动签名很有用,我们将在下一节中看到。

实施 Puppet 证书自动签名

如果您希望实施证书自动签名系统以避免需要管理员干预,有几个选项。 您必须首先在您的 Puppet 主服务器上进行设置。

在 Puppet Master 服务器上的 puppet.conf 文件中,您可以在文件的 [master] 部分下设置 autosign 选项。 这可以采用几个不同的值:

  • true:这将告诉 Puppet Master 服务器签署每个进来的证书请求,而不做任何检查。 这在真实环境中是极其危险的,因为任何主机都可以获得 CSR 签名并进入您的基础架构。
  • ':第二个选项是指定一个文件,该文件将用作主机白名单或主机正则表达式。 Puppet master 将根据此列表检查证书签名请求,以查看是否应该对证书进行签名。 再次不建议这样做,因为证书名称很容易被欺骗。
  • ':第三个选项是指定一个可以运行的脚本或可执行文件来确定是否应该对证书签名请求进行签名。 Puppet 将把 certname 作为参数传入,并通过标准输入传入整个 CSR。 如果返回退出状态 0,则证书已签名。 如果给出另一个状态,则证书将 而不是 被签名。

基于策略的自动签名是实现自动密钥签名的最安全方式,因为它允许您在区分合法和非合法请求方面任意复杂。

为了演示基于策略的自动签名,您可以将 certname 变量添加到包含 %i 实例 ID 变量的 cloud-config 中。 我们将使用 %i.%f 以便它还包括选择的主机名:

#cloud-config
puppet:
  conf:
    agent:
      server: "puppet.example.com"
      certname: "%i.%f"
  ca_cert: |

   . . .

您的完整 cloud-config 现在可能看起来像这样:

#cloud-config
puppet:
  conf:
    agent:
      server: "puppet.example.com"
      certname: "%i.%f"
  ca_cert: |
    -----BEGIN CERTIFICATE-----
    MIIFXjCCA0agAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFQdXBw
    ZXQgQ0E6IHB1cHBldDAeFw8xNTAyMTkxOTA0MzVaFw0yMDAyMTkxOTA0MzVaMBwx
    GjAYBgNVBAMMEVB1cHBldCBDQTogcHVwcGV0MIICIjANBgkqhkiG9w0BAQEFAAOC
    
    . . .
    
    arsjZT5/CtIhtP33Jl3mCp7U2F6bsk4/GDGRaAsFXjJHvBbL93NzgpkZ7elf0zUP
    rOcSGrDrUuzuJk8lEAtrZr/IfAgfKKXPqbyYF95V1qN3OMY+aTcrK20XTydKVWSe
    l5UfYGY3S9UJFrSn9aBsZzN+10HXPkaFKo7HxpztlYyJNI8UVSatcRF4aYYqt9KR
    UClnR+2WxK5v7ix0CVd4/KpYH/6YivvyTwxrhjF2AksZKg==
    -----END CERTIFICATE-----
output: {all: '| tee -a /var/log/cloud-init-output.log'}
runcmd:
  - while [ ! -e /usr/bin/puppet ]; do sleep 5; done; puppet agent --enable; service puppet restart
disable_ec2_metadata: true

在 Puppet 主服务器上,我们必须设置一个验证脚本。 由于 Puppet 已经安装了 Ruby,我们可以制作一个简单的 Ruby 脚本。

因为我们使用 certname%i.%f 格式,我们可以检查 certname 的第一部分(第一个点之前的部分)是否对应于有效的 Droplet ID为我们的帐户。 这只是一个简单的检查,实际上它并没有比白名单文件做更多的事情。 但是,如果您愿意,可以将此想法调整为更复杂。

为此,我们需要来自 DigitalOcean 控制面板的“应用程序和 API”部分的个人访问令牌。 您还需要安装其中一个 DigitalOcean Ruby 库。 下面,我们将向您展示一些使用 BargeDropletKit DigitalOcean Ruby 客户端的简化脚本。

如果您想使用 Barge 客户端,请在您的 Puppet master 上安装 gem:

sudo gem install barge

以下脚本可用于检查证书签名请求中 certname 的第一部分是否与有效的 Droplet ID 对应:

#!/usr/bin/env ruby

require 'barge'

TOKEN = 'YOUR_DIGITALOCEAN_API_TOKEN'

droplet_ids = []
certname = ARGV[0]
id_string = certname.slice(0...(certname.index('.')))
id_to_check = id_string.to_i

client = Barge::Client.new(access_token: TOKEN)
droplets = client.droplet.all

droplets.droplets.each do |droplet|
        droplet_ids << droplet.id
end

Kernel.exit(droplet_ids.include?(id_to_check))

如果您希望使用官方 DigitalOcean Ruby 客户端 DropletKit,您可以通过键入以下命令安装 gem:

sudo gem install droplet_kit

请注意,DropletKit gem 仅对 Ruby 2.0 及更高版本有效,因此在使用 Puppet 附带的 Ruby 版本时可能不会出现这种情况。

DropletKit 的脚本可以这样修改:

#!/usr/bin/env ruby

require 'droplet_kit'

TOKEN = 'YOUR_DIGITALOCEAN_API_TOKEN'

droplet_ids = []
certname = ARGV[0]
id_string = certname.slice(0...(certname.index('.')))
id_to_check = id_string.to_i

client = DropletKit::Client.new(access_token: TOKEN)
droplets = client.droplets.all

droplets.each do |droplet|
        droplet_ids << droplet.id
end

Kernel.exit(droplet_ids.include?(id_to_check))

您可以将与您安装的 gem 对应的脚本放在名为 /etc/puppet/validate.rb 的文件中,并通过键入以下命令将其标记为可执行文件:

sudo chmod +x /etc/puppet/validate.rb

然后,您可以将以下内容添加到您的 puppet.conf 文件(如果使用 Open Source Puppet,则位于 /etc/puppet/puppet.conf):

. . .

[master]
autosign = /etc/puppet/validate.rb

. . .

重新启动 Apache 服务以实施新的签名策略:

sudo service apache2 restart

现在,当您的 Puppet Master 收到证书签名请求时,它将检查证书名称的第一部分是否与您帐户中的有效 Droplet 名称相对应。 这是一个粗略的示例,说明如何使用可执行文件验证请求。

结论

通过利用 cloud-config 脚本,您可以轻松地引导您的新服务器并将它们交给您现有的配置管理系统。 这允许您在进行管理解决方案范围之外的重要更改之前,立即通过现有工具控制您的基础架构。