

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 安裝配方
<a name="create-custom-setup"></a>

**重要**  
 AWS OpsWorks Stacks 此服務已於 2024 年 5 月 26 日終止，並已針對新客戶和現有客戶停用。我們強烈建議客戶盡快將其工作負載遷移至其他解決方案。如果您對遷移有任何疑問，請透過 [AWS re：Post](https://repost.aws/) 或透過 [AWS Premium Support](https://aws.amazon.com/support) 聯絡 AWS 支援 團隊。

安裝配方會指派給 layer 的安裝[生命週期](workingcookbook-events.md)事件，並會在執行個體啟動後執行。他們會執行像是安裝套件、建立組態檔案和啟動服務等任務。安裝配方完成執行後， OpsWorks Stacks 會執行[部署配方](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` 配方用於指派給 layer 的安裝生命週期事件。

```
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`) 是安裝和設定 Apache 伺服器的 OpsWorks Stacks 內建配方。
+ [tomcat::apache\$1tomcat\$1bind](#create-custom-setup-bind) 配方會設定 Apache 伺服器，做為 Tomcat 伺服器的前端。

**注意**  
您通常可以透過使用內建配方執行一部分的必要任務，省下時間和精力。此配方使用內建的 `apache2::default` 配方安裝 Apache，而非從零開始實作。如需如何使用內建配方的另一個範例，請參閱[部署配方](create-custom-deploy.md)。

下列章節會更詳細的說明 Tomcat 技術指南的安裝配方。如需 `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 和處理 MySQL 伺服器連線的 Java 連接器程式庫。

```
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 [套件資源](https://docs.chef.io/chef/resources.html#id146)使用適用於 Amazon Linux 和 `apt-get` Ubuntu 的適當提供者`yum`來處理安裝。套件提供者會將 OpenJDK 做為 Tomcat 的依存安裝，但 MySQL 連接器程式庫必須明確安裝。

1. 使用 Chef [link 資源](https://docs.chef.io/chef/resources.html#link)，在 Tomcat 伺服器的 lib 目錄中建立連結到 JDK 中 MySQL 連接器程式庫的 symlink。

   使用預設屬性值，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 (自訂 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 run 定義 Tomcat 服務。因此，`tomcat::service` 會包含在數個配方中，確保為每一個 Chef run 定義服務。即使 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` 資料夾則包含僅有一行註解的 dummy 範本，僅會在您嘗試在使用不支援作業系統的執行個體上執行此配方時使用。`tomcat::container_config` 配方不需要指定要使用何種 `tomcat_env_config.erb`。Chef 會自動根據[檔案精確性](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 環境變數，如下所示：
+ 代表 Tomcat 使用者和群組的 `TOMCAT6_USER` 和 `TOMCAT6_GROUP` 都已設為 `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 具有足夠的記憶體資源，此範例為 128MB。
  + `-XX:+UseConcMarkSweepGC` 指定同時標記整理記憶體回收，協助限制記憶體回收造成的暫停。

    如需詳細資訊，請參閱 [Concurrent Mark Sweep Collector Enhancements](http://docs.oracle.com/javase/6/docs/technotes/guides/vm/cms-6.html)。
+ 若 Tomcat 的版本低於 7，範本會取消設定處理 Ubuntu 錯誤的 `LC_ALL`。

**注意**  
若使用預設屬性，部分環境變數便會設為其預設值。但是，明確將環境變數設為屬性表示您可以定義自訂 JSON 屬性覆寫預設屬性，並提供自訂的值。如需覆寫屬性的詳細資訊，請參閱[覆寫屬性](workingcookbook-attributes.md)。

如需完整的範本檔案，請參閱[來源碼](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'] %>" />
```

如需詳細資訊，請參閱[來源碼](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) (ASP)：`proxy_ajp`

  AJP 是一種內部 Tomcat 通訊協定。

兩種配方的 [execute 資源](https://docs.chef.io/chef/resources.html#execute)都會執行 `a2enmod` 命令，透過建立必要的 symlink 啟用指定的模組：
+ 第一個 `execute` 資源會啟用 `proxy` 模組。
+ 第二個 `execute` 資源會啟用通訊協定模組，根據預設設為 `proxy_http`。

  若您要使用 AJP，您可以定義自訂 JSON 覆寫 `apache_tomcat_bind_mod` 屬性，將其設為 `proxy_ajp`。

`apache2::service` 配方是定義 Apache OpsWorks 服務的 Stacks 內建配方。如需詳細資訊，請參閱 Stacks GitHub OpsWorks 儲存庫中的[配方](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 間傳遞流量的連接埠。因為兩個伺服器都位於相同的執行個體上，他們可以使用 localhost URL，並且根據預設都會設為 `http://localhost:8080`。