

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 为 Chef 11.10 堆栈实施配方
<a name="workingcookbook-chef11-10"></a>

**重要**  
该 AWS OpsWorks Stacks 服务于 2024 年 5 月 26 日终止，新客户和现有客户均已禁用。我们强烈建议客户尽快将其工作负载迁移到其他解决方案。如果您对迁移有疑问，请通过 re [AWS : Post 或通过 Pre](https://repost.aws/) mium Su [AWS pp](https://aws.amazon.com/support) ort 与 AWS 支持 团队联系。

相对于 Chef 11.4 堆栈，Chef 11.10 堆栈提供以下优势：
+ Chef 运行使用 Ruby 2.0.0，所以您的配方可以使用新的 Ruby 语法。
+ 配方可以使用 Chef 搜索和数据包。

  Chef 11.10 堆栈可以使用许多社区说明书，而无需修改。
+ 您可以使用 Berkshelf 管理说明书。

  Berkshelf 提供了一个更灵活的方法来管理您的自定义说明书，以及在堆栈中使用社区说明书。
+ 说明书必须在 `metadata.rb` 中宣布依赖项。

  如果您的说明书依赖另一个说明书，则您必须在您说明书的 `metadata.rb` 文件中包括该依赖项。例如，如果您的说明书包括含有类似于 `include_recipe anothercookbook::somerecipe` 的语句的配方，则您说明书的 `metadata.rb` 文件必须包括以下行：`depends "anothercookbook"`。
+ OpsWorks 只有当堆栈包含 MySQL 层时，堆栈才会在堆栈的实例上安装 MySQL 客户端。
+ OpsWorks 只有当堆栈包含 Ganglia 层时，Stacks 才会在堆栈的实例上安装 Ganglia 客户端。
+ 如果部署运行 `bundle install`，并且安装失败，则部署也会失败。

**重要**  
请勿为自定义或社区说明书重复使用内置说明书名称。与内置说明书具有相同名称的自定义说明书可能会失败。[有关 Chef 11.10、11.4 和 0.9 堆栈中可用的内置食谱的完整列表，请参阅 opsworks-cookbooks 存储库。 GitHub](https://github.com/aws/opsworks-cookbooks)  
包含非 ASCII 字符的说明书在 Chef 0.9 和 11.4 堆栈上可以成功运行，而在 Chef 11.10 堆栈上可能会失败。原因在于，Chef 11.10 堆栈为 Chef 运行使用 Ruby 2.0.0，这在编码上要比 Ruby 1.8.7 严格得多。为确保这些说明书在 Chef 11.10 堆栈上成功运行，使用非 ASCII 字符的每个文件都应当在顶部包含一个注释，以提供关于编码的提示。例如，对于 UTF-8 编码，注释将为 `# encoding: UTF-8`。有关 Ruby 2.0.0 编码的更多信息，请参阅 [Encoding](http://www.ruby-doc.org/core-2.0.0/Encoding.html)。

**Topics**
+ [说明书安装和优先顺序](#workingcookbook-chef11-10-override)
+ [使用 Chef 搜索](#workingcookbook-chef11-10-search)
+ [使用数据包](#workingcookbook-chef11-10-databag)
+ [使用 Berkshelf](#workingcookbook-chef11-10-berkshelf)

## 说明书安装和优先顺序
<a name="workingcookbook-chef11-10-override"></a>

Chef 11.10 OpsWorks 堆栈的安装 Stacks 食谱的过程与早期的 Chef 版本略有不同。对于 Chef 11.10 堆栈，在 OpsWorks Stacks 安装内置、自定义和 Berkshelf 食谱后，它会按以下顺序将它们合并到公共目录中：

1. 内置说明书。

1. Berkshelf 说明书 (如果有)。

1. 自定义说明书 (如果有)。

当 OpsWorks Stacks 执行此合并时，它会复制目录的全部内容，包括配方。如果有重复项，则将依照以下规则处理：
+ Berkshelf 说明书的内容优先于内置说明书。
+ 自定义说明书的内容优先于 Berkshelf 说明书。

为了说明此过程的原理，请想一想以下场景，其中所有这三个说明书目标都包括名为 `mycookbook` 的说明书：
+ 内置说明书：`mycookbook` 包括一个名为 `someattributes.rb` 的属性文件、一个名为 `sometemplate.erb` 的模板文件以及一个名为 `somerecipe.rb` 的配方。
+ Berkshelf 说明书：`mycookbook` 包括 `sometemplate.erb` 和 `somerecipe.rb`。
+ 自定义说明书：`mycookbook` 包括 `somerecipe.rb`。

合并的说明书包含以下内容：
+ 内置说明书中的 `someattributes.rb`。
+ Berkshelf 说明书中的 `sometemplate.erb`。
+ 自定义说明书的 `somerecipe.rb`。

**重要**  
您不应当通过将整个内置说明书复制到您的存储库，然后修改该说明书的部分内容来自定义您的 Chef 11.10 堆栈。这样做会覆盖整个内置说明书，包括配方。如果 OpsWorks Stacks 更新了该食谱，则除非您手动更新私有副本，否则您的堆栈将无法从这些更新中受益。有关如何自定义堆栈的更多信息，请参阅[自定义堆栈 OpsWorks](customizing.md)。

## 使用 Chef 搜索
<a name="workingcookbook-chef11-10-search"></a>

您可以在您的配方中使用 Chef [`search` 方法](http://docs.chef.io/dsl_recipe.html#search)来查询堆栈数据。你使用与 Chef 服务器相同的语法，但是 OpsWorks Stacks 从本地节点对象获取数据，而不是查询 Chef 服务器。这些数据包括：
+ 实例的[堆栈配置和部署属性](workingstacks-json.md)。
+ 实例的内置和自定义说明书的属性文件中的属性。
+ Ohai 收集的系统数据。

堆栈配置和部署属性包含配方通常通过搜索获得的大部分信息，包括堆栈中每个在线实例的主机名和 IP 地址等数据。 OpsWorks 堆栈会为每个[生命周期事件](workingcookbook-events.md)更新这些属性，从而确保它们准确反映当前堆栈状态。这意味着，您可以经常在您的堆栈中使用与搜索相关的社区配方，而无需修改。搜索方法仍返回适当的数据；但这些数据来自堆栈配置和部署属性，而不是服务器。

 OpsWorks Stacks 搜索的主要局限性在于，它只能处理本地节点对象中的数据，尤其是堆栈配置和部署属性。因此，可能无法通过搜索获得以下类型的数据：
+ 其他实例上本地定义的属性。

  如果配方在本地定义了属性，则该信息不会报告给 OpsWorks Stacks 服务，因此您无法使用搜索从其他实例访问该数据。
+ 自定义 `deploy` 属性。

  您可以在[部署应用程序](workingapps-deploying.md)时指定自定义 JSON，然后便会在堆栈针对该部署的实例上安装相应的属性。但是，如果您仅部署到所选的实例上，则将仅在这些实例上安装属性。针对这些自定义 JSON 属性的查询在所有其他实例上都将失败。此外，自定义属性仅包含在针对该特定部署的堆栈配置和部署 JSON 中。只有当下一个生命周期事件安装了一组新的堆栈配置和部署属性后，才能访问这些属性。请注意，如果您[为堆栈指定自定义 JSON](workingstacks-json.md)，则会在每个生命周期事件的每个实例上安装这些属性，并且这些属性在整个搜索期间始终可以访问。
+ 其他实例中的 Ohai 数据。

  Chef 的 [Ohai 工具](http://docs.chef.io/resource_ohai.html)获取实例上的各种系统数据，然后将这些数据添加到节点对象中。这些数据存储在本地，并且不会反馈给 OpsWorks Stacks 服务，因此搜索无法从其他实例中访问 Ohai 数据。但是，堆栈配置和部署属性中可能包含其中一些数据。
+ 离线实例。

  堆栈配置和部署属性仅包含联机实例的数据。

以下配方摘录显示了如何使用搜索获取 PHP 层的实例的私有 IP 地址。

```
appserver = search(:node, "role:php-app").first
Chef::Log.info("The private IP is '#{appserver[:private_ip]}'")
```

**注意**  
当 OpsWorks Stacks 向节点对象添加堆栈配置和部署属性时，它实际上会创建两组图层属性，每组属性都具有相同的数据。一个集合位于`layers`命名空间中，这就是 OpsWorks Stacks 存储数据的方式。另一组属性位于 `role` 命名空间中，用于定义 Chef 服务器存储等效数据的方式。`role`命名空间的目的是允许为 Chef 服务器实现的搜索代码在 OpsWorks Stacks 实例上运行。如果您正在专门为 OpsWorks Stacks 编写代码，则可以在前面的示例`role:php-app`中使用`layers:php-app`或并`search`返回相同的结果。

## 使用数据包
<a name="workingcookbook-chef11-10-databag"></a>

您可以在您的配方中使用 Chef [`data_bag_item` 方法](http://docs.chef.io/dsl_recipe.html#data-bag-item)来查询数据包中的信息。您使用与将为 Chef 服务器使用的语法相同的语法，但 OpsWorks Stacks 从实例的堆栈配置和部署属性中获取数据。但是， OpsWorks Stacks 目前不支持 Chef 环境，因此请`node.chef_environment`务必返回`_default`。

您可以通过使用自定义 JSON 向 `[:opsworks][:data_bags]` 属性添加一个或多个属性，来创建数据包。以下示例显示了在自定义 JSON 中创建数据包的一般格式。

**注意**  
您不能通过将数据包添加到您的说明书存储库中来创建数据包。您必须使用自定义 JSON。

```
{
  "opsworks": {
    "data_bags": {
      "bag_name1": {
        "item_name1: {
          "key1" : “value1”,
          "key2" : “value2”,
          ...
        }
      },
      "bag_name2": {
        "item_name1": {
          "key1" : “value1”,
          "key2" : “value2”,
          ...
        }
      },
      ...
    }
  }
}
```

您通常[为堆栈指定自定义 JSON](workingstacks-json.md)，这会在后继的每个生命周期事件的每个实例上安装自定义属性。您还可以在部署应用程序时指定自定义 JSON，但仅为该部署安装这些属性，并且这些属性可能将仅安装到一组所选的实例中。有关更多信息，请参阅 [部署应用程序](workingapps-deploying.md)。

以下自定义 JSON 示例创建名为 `myapp` 的数据包。它包含一个项目 `mysql` 以及两个密钥值对。

```
{ "opsworks": {
    "data_bags": {
      "myapp": {
        "mysql": { 
          "username": "default-user",
          "password": "default-pass"
        }
      }
    }
  }
}
```

要使用您的配方中的数据，您可以调用 `data_bag_item` 并向其传递数据包和值名称，如以下摘录所示。

```
mything = data_bag_item("myapp", "mysql")
Chef::Log.info("The username is '#{mything['username']}' ")
```

要修改数据包中的数据，仅修改自定义 JSON 即可，然后自定义 JSON 将安装在下一个生命周期事件的堆栈的实例上。

## 使用 Berkshelf
<a name="workingcookbook-chef11-10-berkshelf"></a>

对于 Chef 0.9 和 Chef 11.4 堆栈，您仅可以安装一个自定义说明书存储库。对于 Chef 11.10 堆栈，您可以使用 [Berkshelf](http://berkshelf.com/) 管理您的说明书及其依赖项，这使您可以从多个存储库中安装说明书。（有关更多信息，请参阅 [本地打包说明书依赖项](best-practices-packaging-cookbooks-locally.md)。） 特别是，使用 Berkshelf，您可以直接从其存储库安装与 OpsWorks Stacks 兼容的社区食谱，而不必将它们复制到您的自定义食谱存储库中。哪个版本的 Berkshelf 受支持，取决于操作系统。有关更多信息，请参阅 [OpsWorks 堆栈操作系统](workinginstances-os.md)。

要使用 Berkshelf，您必须明确启用它，如[安装自定义说明书](workingcookbook-installingcustom-enable.md)中所述。然后，将一个 `Berksfile` 文件放入您的说明书存储库的根目录 (指明要安装哪些说明书)。

要指定 Berksfile 中的外部说明书源，可在该文件的顶部添加一个源属性以指定默认存储库 URL。 URLs 除非您明确指定存储库，否则 Berkshelf 将在源代码中查找食谱。然后为您希望安装的每个说明书添加一行，格式如下：

```
cookbook 'cookbook_name', ['>= cookbook_version'], [cookbook_options]
```

`cookbook` 后面的字段指定特定的说明书。
+ *cookbook\$1name*—（必需）指定食谱的名称。

  如果您不包含任何其他字段，Berkshelf 会安装来自指定来源的食谱。 URLs
+ *cookbook\$1version*—（可选）指定一个或多个食谱版本。

  您可以使用诸如 `=` 或 `>=` 等前缀指定特定版本或一系列可接受的版本。如果您不指定版本，则 Berkshelf 将安装最新的版本。
+ *cookbook\$1options*—（可选）最后一个字段是一个哈希，其中包含一个或多个键值对，用于指定存储库位置等选项。

  例如，您可以添加一个 `git` 密钥来指定特定的 Git 存储库，添加一个 `tag` 密钥来指定特定的存储库分支。指定存储库分支通常是确保安装您的首选说明书的最佳方式。

**重要**  
切勿通过以下方式宣布说明书：在您的 Berksfile 中添加 `metadata` 行，并在 `metadata.rb` 中宣布说明书依赖项。为了能够正确执行这一操作，这两个文件必须位于同一目录中。对于 OpsWorks Stacks，Berksfile 必须位于存储库的根目录中，但`metadata.rb`文件必须位于各自的食谱目录中。您应当在 Berksfile 中明确宣布外部说明书。

下面是 Berksfile 的一个示例，介绍了指定说明书的不同方法。有关如何创建 Berksfile 的更多信息，请参阅 [Berkshelf](http://berkshelf.com/)。

```
source "https://supermarket.chef.io"

cookbook 'apt'
cookbook 'bluepill', '>= 2.3.1'
cookbook 'ark', git: 'git://github.com/opscode-cookbooks/ark.git'
cookbook 'build-essential', '>= 1.4.2', git: 'git://github.com/opscode-cookbooks/build-essential.git', tag: 'v1.4.2'
```

此文件安装以下说明书：
+ 社区说明书存储库中最新版本的 `apt`。
+ 社区说明书中最新版本的 `bluepill`，但前提是它是 2.3.1 版本或更高版本。
+ 指定存储库中最新版本的 `ark`。

  此示例的 URL 适用于上的公共社区食谱存储库 GitHub，但您可以安装来自其他存储库（包括私有存储库）的食谱。有关更多信息，请参阅 [Berkshelf](http://berkshelf.com/)。
+ 指定存储库的 v1.4.2 分支中的 `build-essential` 说明书。

除了 Berksfile，自定义说明书存储库还可以包含自定义说明书。在这种情况下， OpsWorks Stacks 会安装两套食谱，这意味着一个实例最多可以有三个食谱存储库。
+ 内置说明书安装到 `/opt/aws/opsworks/current/cookbooks` 中。
+ 如果您的自定义说明书存储库包含说明书，则它们将安装到 `/opt/aws/opsworks/current/site-cookbooks` 中。
+ 如果您已启用了 Berkshelf，并且您的自定义说明书存储库包含 Berksfile，则指定说明书将安装到 `/opt/aws/opsworks/current/berkshelf-cookbooks` 中。

在安装过程中，内置食谱和您的自定义食谱会安装在每个实例上，除非您手动运行 “更新[**自定义食谱” 堆栈命令，否则不会随后进行更新**](workingstacks-commands.md)。 OpsWorks Stacks 在`berks install`每次 Chef 运行时都会运行，因此您的 Berkshelf 食谱会根据以下规则针对每个[生命周期事件](workingcookbook-events.md)进行更新：
+ 如果存储库中有新的说明书版本，则此操作将从存储库更新说明书。
+ 否则，此操作将从本地缓存更新 Berkshelf 说明书。

**注意**  
此操作将覆盖 Berkshelf 说明书，因此，如果您修改了任何说明书的本地副本，则所做的更改将被覆盖。有关更多信息，请参阅 [Berkshelf](http://berkshelf.com/)。

您还可以通过以下方法更新您的 Berkshelf 说明书：运行 **Update Custom Cookbooks** 堆栈命令，这会同时更新 Berkshelf 说明书和您的自定义说明书。