

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

# Setup 配方
<a name="create-custom-setup"></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 支持 团队联系。

Setup 配方分配给了该层的 Setup [生命周期](workingcookbook-events.md)事件，这些配方在实例启动后运行。它们执行安装程序包、创建配置文件和启动服务等任务。安装配方完成运行后， OpsWorks Stacks 会运行 [Deploy 配方](create-custom-deploy.md)，将任何应用程序部署到新实例。

**Topics**
+ [tomcat::setup](#create-custom-setup-setup)
+ [tomcat::install](#create-custom-setup-install)
+ [tomcat::service](#create-custom-setup-service)
+ [tomcat::container\$1config](#create-custom-setup-config)
+ [tomcat::apache\$1tomcat\$1bind](#create-custom-setup-bind)

## tomcat::setup
<a name="create-custom-setup-setup"></a>

`tomcat::setup` 配方适合分配给层的 Setup 生命周期事件。

```
include_recipe 'tomcat::install'
include_recipe 'tomcat::service'

service 'tomcat' do
  action :enable
end

# for EBS-backed instances we rely on autofs
bash '(re-)start autofs earlier' do
  user 'root'
  code <<-EOC
    service autofs restart
  EOC
  notifies :restart, resources(:service => 'tomcat')
end

include_recipe 'tomcat::container_config'
include_recipe 'apache2'
include_recipe 'tomcat::apache_tomcat_bind'
```

`tomcat::setup` 配方基本上是一个元配方。它包括一组从属配方，这些配方用于处理安装和配置 Tomcat 及相关程序包的大部分详细信息。`tomcat::setup` 的第一部分运行以下配方，我们将在后面的部分进行介绍：
+ [tomcat::install](#create-custom-setup-install) 配方可安装 Tomcat 服务器包。
+ [tomcat::service](#create-custom-setup-service) 配方可设置 Tomcat 服务。

`tomcat::setup` 的中间部分可启用并启动 Tomcat 服务：
+ Chef [service 资源](https://docs.chef.io/chef/resources.html#service)可在启动时启用 Tomcat 服务。
+ Chef [bash 资源](https://docs.chef.io/chef/resources.html#bash)运行一个 Bash 脚本，以启动 autofs 守护进程，这对于 由Amazon EBS 提供支持的实例来说是必要的。然后，该资源通知 `service` 资源重新启动 Tomcat 服务。

  有关更多信息，请参阅：[autofs](https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Storage_Administration_Guide/s2-nfs-config-autofs.html) (对于 Amazon Linux) 或 [Autofs](https://help.ubuntu.com/community/Autofs) (对于 Ubuntu)。

`tomcat::setup` 的最后一部分可创建配置文件并安装和配置前端 Apache 服务器：
+ [tomcat::container\$1config](#create-custom-setup-config) 配方可创建配置文件。
+ `apache2`配方（简写为`apache2::default`）是 OpsWorks Stacks 的内置配方，用于安装和配置 Apache 服务器。
+ [tomcat::apache\$1tomcat\$1bind](#create-custom-setup-bind) 配方可将 Apache 服务器配置为作为 Tomcat 服务器的前端运行。

**注意**  
您通常可以通过使用内置配方执行某些所需任务来节约时间和精力。此配方使用内置 `apache2::default` 配方来安装 Apache，而不是从头实施 Apache。有关另一个关于如何使用内置配方的示例，请参阅[Deploy 配方](create-custom-deploy.md)。

后面的部分更加详细地介绍了 Tomcat 说明书的 Setup 配方。有关 `apache2` 配方的更多信息，请参阅 [opsworks-cookbooks/apache2](https://github.com/aws/opsworks-cookbooks/tree/release-chef-11.4/apache2)。

## tomcat::install
<a name="create-custom-setup-install"></a>

`tomcat::install `配方可安装 Tomcat 服务器、OpenJDK 以及 Java 连接器库，该连接器库可处理与 MySQL 服务器的连接。

```
tomcat_pkgs = value_for_platform(
  ['debian', 'ubuntu'] => {
    'default' => ["tomcat#{node['tomcat']['base_version']}", 'libtcnative-1', 'libmysql-java']
  },
  ['centos', 'redhat', 'fedora', 'amazon'] => {
    'default' => ["tomcat#{node['tomcat']['base_version']}", 'tomcat-native', 'mysql-connector-java']
  },
  'default' => ["tomcat#{node['tomcat']['base_version']}"]
)

tomcat_pkgs.each do |pkg|
  package pkg do
    action :install
  end
end

link ::File.join(node['tomcat']['lib_dir'], node['tomcat']['mysql_connector_jar']) do
  to ::File.join(node['tomcat']['java_dir'], node['tomcat']['mysql_connector_jar'])
  action :create
end

# remove the ROOT webapp, if it got installed by default
include_recipe 'tomcat::remove_root_webapp'
```

该配方执行以下任务：

1. 创建要安装的程序包的列表，具体取决于实例的操作系统。

1. 安装列表中的每个程序包。

   Chef [package 资源](https://docs.chef.io/chef/resources.html#id146)使用适当的提供程序 (对于 Amazon Linux，使用 `yum`；对于 Ubuntu，使用 `apt-get`) 来处理安装任务。程序包提供程序将 OpenJDK 作为 Tomcat 依赖项进行安装，但必须显式安装 MySQL 连接器库。

1. 使用 Chef [link 资源](https://docs.chef.io/chef/resources.html#link)在 Tomcat 服务器的 lib 目录中创建一个指向 JDK 中的 MySQL 连接器库的符号链接。

   如果使用默认属性值，则 Tomcat lib 目录为 `/usr/share/tomcat6/lib`，并且 MySQL 连接器库 (`mysql-connector-java.jar`) 位于 `/usr/share/java/` 中。

`tomcat::remove_root_webapp` 配方可删除 ROOT Web 应用程序 (默认情况下为 `/var/lib/tomcat6/webapps/ROOT`)，以避免出现一些安全问题。

```
ruby_block 'remove the ROOT webapp' do
  block do
    ::FileUtils.rm_rf(::File.join(node['tomcat']['webapps_base_dir'], 'ROOT'), :secure => true)
  end
  only_if { ::File.exists?(::File.join(node['tomcat']['webapps_base_dir'], 'ROOT')) && !::File.symlink?(::File.join(node['tomcat']['webapps_base_dir'], 'ROOT')) }
end
```

`only_if` 语句可确保配方仅在文件存在时删除文件。

**注意**  
Tomcat 版本由 `['tomcat']['base_version']` 属性指定，该属性在属性文件中被设置为 6。要安装 Tomcat 7，您可以使用自定义 JSON 属性来覆盖该属性。[编辑您的堆栈设置](workingstacks-edit.md)，然后在 **Custom Chef JSON** 框中输入以下 JSON，或者将其添加到任何现有自定义 JSON 即可：  

```
{
  'tomcat' : {
    'base_version' : 7
  }
}
```
自定义 JSON 属性覆盖默认属性，并将 Tomcat 版本设置为 7。有关覆盖属性的更多信息，请参阅[覆盖属性](workingcookbook-attributes.md)。

## tomcat::service
<a name="create-custom-setup-service"></a>

`tomcat::service` 配方创建 Tomcat 服务定义。

```
service 'tomcat' do
  service_name "tomcat#{node['tomcat']['base_version']}"

  case node[:platform]
  when 'centos', 'redhat', 'fedora', 'amazon'
    supports :restart => true, :reload => true, :status => true
  when 'debian', 'ubuntu'
    supports :restart => true, :reload => false, :status => true
  end

  action :nothing
end
```

该配方使用 Chef [service 资源](https://docs.chef.io/chef/resources.html#service)指定 Tomcat 服务名称 (默认情况下为 tomcat6)，并设置 `supports` 属性以定义 Chef 如何管理服务在不同操作系统上的重新启动、重新加载和状态命令。
+ `true` 表示 Chef 可以使用 init 脚本或其他服务提供程序来运行该命令。
+ `false` 表示 Chef 必须尝试以其他方式运行该命令。

请注意，`action` 的设置为 `:nothing`。对于每个生命周期事件， OpsWorks Stacks 都会启动 [Chef 运行](https://docs.chef.io/chef_client_overview.html#the-chef-client-run)以执行相应的配方集。Tomcat 说明书采用常见模式：使配方创建服务定义，但不重新启动服务。Chef 运行中的其他配方处理重新启动，这通常是通过在用于创建配置文件的 `notifies` 资源中添加 `template` 命令来实现的。通知是重新启动服务的一种简便方法，因为它们仅在配置发生改变时重新启动服务。此外，如果 Chef 运行拥有针对一项服务的多个重新启动通知，Chef 最多重新启动服务一次。这种做法可以避免在尝试重新启动运行不正常的服务 (Tomcat 错误的常见起因) 时可能发生的问题。

 必须为使用重新启动通知的任何 Chef 运行定义 Tomcat 服务。因此，多个配方都包含 `tomcat::service`，以确保为每个 Chef 运行都定义了该服务。如果一次 Chef 运行包含 `tomcat::service` 的多个实例，不会造成损失，因为 Chef 确保每一次运行，配方仅执行一次，而无论包括了几次实例。

## tomcat::container\$1config
<a name="create-custom-setup-config"></a>

`tomcat::container_config` 配方基于说明书模板文件创建配置文件。

```
include_recipe 'tomcat::service'

template 'tomcat environment configuration' do
  path ::File.join(node['tomcat']['system_env_dir'], "tomcat#{node['tomcat']['base_version']}")
  source 'tomcat_env_config.erb'
  owner 'root'
  group 'root'
  mode 0644
  backup false
  notifies :restart, resources(:service => 'tomcat')
end

template 'tomcat server configuration' do
  path ::File.join(node['tomcat']['catalina_base_dir'], 'server.xml')
  source 'server.xml.erb'
  owner 'root'
  group 'root'
  mode 0644
  backup false
  notifies :restart, resources(:service => 'tomcat')
end
```

该配方首先调用 `tomcat::service`，以在必要时定义该服务。批量配方包含两个 [template 资源](https://docs.chef.io/chef/resources.html#template)，每个资源都基于一个说明书模板文件创建配置文件，设置文件属性，以及通知 Chef 重新启动服务。

### Tomcat 环境配置文件
<a name="create-custom-setup-config-env"></a>

第一个 `template` 资源使用 `tomcat_env_config.erb` 模板文件创建 Tomcat 环境配置文件，该配置文件用于设置环境变量，如 `JAVA_HOME`。默认文件名是 `template` 资源的参数。`tomcat::container_config` 使用 `path` 属性覆盖默认值，并将配置文件命名为 `/etc/sysconfig/tomcat6` (Amazon Linux) 或 `/etc/default/tomcat6` (Ubuntu)。`template` 资源还指定文件的所有者、组和模式设置，并指示 Chef 不要创建备份文件。

如果您查看源代码，实际上有三个版本的 `tomcat_env_config.erb`，这些版本分别位于 `templates` 目录的不同子目录中。`ubuntu` 和 `amazon` 目录包含适用于它们各自操作系统的模板。`default` 文件夹包含一个仅具有一个注释行的伪模板，仅当您尝试使用不受支持的操作系统在一个实例上运行此配方时，才会使用这个模板。`tomcat::container_config` 配方不需要指定要使用哪个 `tomcat_env_config.erb`。Chef 根据 [File Specificity](http://docs.chef.io/templates.html#file-specificity) 中介绍的规则自动为实例的操作系统选择适当的目录。

此示例中的 `tomcat_env_config.erb` 文件主要包含注释。要设置其他环境变量，取消相应行的注释并提供您的首选值即可。

**注意**  
可能发生更改的任何配置设置都应当定义为一个属性，而不应当在模板中对其进行硬编码。这样，您就无需重写模板来更改设置，只需覆盖该属性即可。

Amazon Linux 模板仅设置一个环境变量，如以下摘录中所示。

```
...
# Use JAVA_OPTS to set java.library.path for libtcnative.so
#JAVA_OPTS="-Djava.library.path=/usr/lib"

JAVA_OPTS="${JAVA_OPTS} <%= node['tomcat']['java_opts'] %>"

# What user should run tomcat
#TOMCAT_USER="tomcat"
...
```

JAVA\$1OPTS 可用于指定 Java 选项，如库路径。如果使用默认的属性值，则模板不会为 Amazon Linux 设置 Java 选项。您可以通过覆盖 `['tomcat']['java_opts']` 属性 (例如，使用自定义 JSON 属性) 来设置您自己的 Java 选项。有关示例，请参阅[创建堆栈](create-custom-stack.md#create-custom-stack-stack)。

Ubuntu 模板设置多个环境变量，如以下模板片段所示。

```
# Run Tomcat as this user ID. Not setting this or leaving it blank will use the
# default of tomcat<%= node['tomcat']['base_version'] %>.
TOMCAT<%= node['tomcat']['base_version'] %>_USER=tomcat<%= node['tomcat']['base_version'] %>
...
# Run Tomcat as this group ID. Not setting this or leaving it blank will use
# the default of tomcat<%= node['tomcat']['base_version'] %>.
TOMCAT<%= node['tomcat']['base_version'] %>_GROUP=tomcat<%= node['tomcat']['base_version'] %>
...
JAVA_OPTS="<%= node['tomcat']['java_opts'] %>"

<% if node['tomcat']['base_version'].to_i < 7 -%>
# Unset LC_ALL to prevent user environment executing the init script from
# influencing servlet behavior.  See Debian bug #645221
unset LC_ALL
<% end -%>
```

在使用默认属性值时，模板会将 Ubuntu 环境变量设置如下：
+ `TOMCAT6_USER` 和 `TOMCAT6_GROUP` (表示 Tomcat 用户和组) 均被设置为 `tomcat6`。

  如果您将 ['tomcat']['base\$1version'] 设置为 `tomcat7`，则变量名称将解析为 `TOMCAT7_USER` 和 `TOMCAT7_GROUP`，并且均被设置为 `tomcat7`。
+ `JAVA_OPTS` 被设置为 `-Djava.awt.headless=true -Xmx128m -XX:+UseConcMarkSweepGC`：
  + 将 `-Djava.awt.headless` 设置为 `true` 可告知图形引擎，实例处于无管模式，没有控制台，这会解决特定图形应用程序的错误行为。
  + `-Xmx128m` 确保 JVM 拥有足够的内存资源，在此示例中为 128 MB。
  + `-XX:+UseConcMarkSweepGC` 指定并发标记清除垃圾收集，这有助于限制垃圾收集引发的暂停。

    有关更多信息，请参阅：[Concurrent Mark Sweep Collector Enhancements](http://docs.oracle.com/javase/6/docs/technotes/guides/vm/cms-6.html)。
+ 如果 Tomcat 版本低于 7，则模板取消 `LC_ALL` 的设置，这可以解决 Ubuntu 错误。

**注意**  
使用默认属性，其中一些环境变量会直接被设置为其默认值。但是，将环境变量显式设置为属性意味着：您可以定义自定义 JSON 属性来覆盖默认属性并提供自定义值。有关覆盖属性的更多信息，请参阅[覆盖属性](workingcookbook-attributes.md)。

要查看完整的模板文件，请参阅 [source code](https://github.com/amazonwebservices/opsworks-example-cookbooks/tree/master/tomcat)。

### Server.xml 配置文件
<a name="create-custom-setup-config-server"></a>

第二个`template`资源`server.xml.erb`用于创建[`system.xml`配置文件](http://tomcat.apache.org/tomcat-7.0-doc/config/)，用于配置 servlet/JSP 容器。 `server.xml.erb`不包含操作系统特定的设置，因此它位于`template`目录的`default`子目录中。

该模板使用标准设置，但它可以为 Tomcat 6 或 Tomcat 7 创建一个 `system.xml` 文件。例如，模板的服务器部分中的以下代码可为指定版本正确配置侦听器。

```
<% if node['tomcat']['base_version'].to_i > 6 -%>
  <!-- Security listener. Documentation at /docs/config/listeners.html
  <Listener className="org.apache.catalina.security.SecurityListener" />
  -->
<% end -%>
  <!--APR library loader. Documentation at /docs/apr.html -->
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
  <Listener className="org.apache.catalina.core.JasperListener" />
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<% if node['tomcat']['base_version'].to_i < 7 -%>
  <!-- JMX Support for the Tomcat server. Documentation at /docs/non-existent.html -->
  <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
<% end -%>
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<% if node['tomcat']['base_version'].to_i > 6 -%>
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<% end -%>
```

该模板使用属性来代替硬编码设置，因此，您可以轻松地通过定义自定义 JSON 属性来更改设置。例如：

```
<Connector port="<%= node['tomcat']['port'] %>" protocol="HTTP/1.1"
           connectionTimeout="20000"
           URIEncoding="<%= node['tomcat']['uri_encoding'] %>"
           redirectPort="<%= node['tomcat']['secure_port'] %>" />
```

有关更多信息，请参阅 [source code](https://github.com/amazonwebservices/opsworks-example-cookbooks/tree/master/tomcat)。

## tomcat::apache\$1tomcat\$1bind
<a name="create-custom-setup-bind"></a>

`tomcat::apache_tomcat_bind` 配方使 Apache 服务器能够充当 Tomcat 的前端，接收传入请求、将其转发到 Tomcat 并将响应返回给客户端。此示例使用 [mod\$1proxy](https://httpd.apache.org/docs/2.2/mod/mod_proxy.html) 作为 Apache 代理/网关。

```
execute 'enable mod_proxy for apache-tomcat binding' do
  command '/usr/sbin/a2enmod proxy'
  not_if do
    ::File.symlink?(::File.join(node['apache']['dir'], 'mods-enabled', 'proxy.load')) || node['tomcat']['apache_tomcat_bind_mod'] !~ /\Aproxy/
  end
end

execute 'enable module for apache-tomcat binding' do
  command "/usr/sbin/a2enmod #{node['tomcat']['apache_tomcat_bind_mod']}"
  not_if {::File.symlink?(::File.join(node['apache']['dir'], 'mods-enabled', "#{node['tomcat']['apache_tomcat_bind_mod']}.load"))}
end

include_recipe 'apache2::service'

template 'tomcat thru apache binding' do
  path ::File.join(node['apache']['dir'], 'conf.d', node['tomcat']['apache_tomcat_bind_config'])
  source 'apache_tomcat_bind.conf.erb'
  owner 'root'
  group 'root'
  mode 0644
  backup false
  notifies :restart, resources(:service => 'apache2')
end
```

要启用 `mod_proxy`，您必须启用 `proxy` 模块和一个基于协议的模块。对于协议模块，您有两个选项：
+ HTTP: `proxy_http`
+ [Apache JServ 协议](http://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html) (AJP)：`proxy_ajp`

  AJP 是一个内部 Tomcat 协议。

配方的这两个 [execute 资源](https://docs.chef.io/chef/resources.html#execute) 都运行 `a2enmod` 命令，该命令通过创建所需的符号链接来启用特定模块：
+ 第一个 `execute` 资源启用 `proxy` 模块。
+ 第二个 `execute` 资源启用协议模块，它在默认情况下被设置为 `proxy_http`。

  如果您希望使用 AJP，您可以定义自定义 JSON 以覆盖 `apache_tomcat_bind_mod` 属性并将其设置为 `proxy_ajp`。

该`apache2::service`配方是 OpsWorks Stacks 的内置配方，用于定义 Apache 服务。有关更多信息，请参阅 OpsWorks Stacks GitHub 存储库中的[配方](https://github.com/aws/opsworks-cookbooks/blob/release-chef-11.4/apache2/recipes/service.rb)。

`template` 资源使用 `apache_tomcat_bind.conf.erb` 创建一个配置文件，默认情况下，该文件被命名为 `tomcat_bind.conf`。它将该配置文件放在 `['apache']['dir']/.conf.d` 目录中。`['apache']['dir']` 属性在内置 `apache2` 属性文件中定义，默认情况下它被设置为 `/etc/httpd` (Amazon Linux) 或 `/etc/apache2` (Ubuntu)。如果 `template` 资源创建或更改配置文件，则 `notifies` 命令将计划重新启动 Apache 服务。

```
<% if node['tomcat']['apache_tomcat_bind_mod'] == 'proxy_ajp' -%>
ProxyPass <%= node['tomcat']['apache_tomcat_bind_path'] %> ajp://localhost:<%= node['tomcat']['ajp_port'] %>/
ProxyPassReverse <%= node['tomcat']['apache_tomcat_bind_path'] %> ajp://localhost:<%= node['tomcat']['ajp_port'] %>/
<% else %>
ProxyPass <%= node['tomcat']['apache_tomcat_bind_path'] %> http://localhost:<%= node['tomcat']['port'] %>/
ProxyPassReverse <%= node['tomcat']['apache_tomcat_bind_path'] %> http://localhost:<%= node['tomcat']['port'] %>/
<% end -%>
```

该模板使用[ProxyPass](https://httpd.apache.org/docs/2.0/mod/mod_proxy.html#proxypass)和[ProxyPassReverse](https://httpd.apache.org/docs/2.0/mod/mod_proxy.html#proxypassreverse)指令来配置用于在 Apache 和 Tomcat 之间传递流量的端口。由于这两个服务器在同一个实例上，所以它们可以使用本地主机 URL，默认情况下，它们均被设置为 `http://localhost:8080`。