部署应用前,必须对即将部署的动作进行recipe配置。例如,官网例子,recipe依赖单一的MongoDB服务实例和Tomcat服务实例;由于例子已做好配置,该recipe可以不做任何修改也可以进行下一步操作。当然,也可以对一些可选项修改,比如,recipe中配置的服务实例数量。
Anatomy(剖析) of a Recipe
Recipes是在不修改应用代码和架构的同时,对应用程序栈安装、启动、协调、监控的执行计划。部署文件和文件夹位于应用的recipe文件夹中,例如:<cloudifyRoot>/recipes/apps/petclinic-simple. 下面是例子PetClinic应用的recipe文件和文件夹目录列表:
- 应用描述文件:petclinic-mongo-application.groovy
- MongoDB recipe 文件夹
- Tomcat recipe 文件夹
运行MongoDB和Tomcat所需要的文件,可以在对应的mongod和tomcat子文件夹中找到。下图是PetClinic application的recipe文件和文件夹目录结构:
The Application Recipe
每一份recipe必须包括一个描述文件,用于描述它所需要的的服务(层)及其依赖关系。
例子PetClinic application包含两种services, 每项service包含了单一的service实例:
- Tomcat (将该服务称之为:tomcat)
- MongoDB (将该服务称之为:mongod)
以下为PetClinic application的应用描述文件的内容:
application
{ name="petclinic"
service {
name = "mongod"
}
service {
name = "tomcat"
dependsOn = ["mongod"]
}
}
从该文件中可发现以下信息:
- 运行该应用所需要的服务名称。
- tomcatservice 依赖于mongodservice; 因此tomcatservice 在mongodservice成功启动前不会启动。
The Service Recipe
每一项服务受制于其自己的service recipe,该recipe通常基于一个recipe 结构。每一个service文件夹包含服务需要的所有信息。以下为service recipe的文件和文件夹:
- 一个服务描述文件(<service name>-service.groovy).
- 可选的properties文件(<service name>.properties),该文件包含recipe可能用到的配置参数。
- 所有指定的生命周期脚本文件, e.g.<service name>_install.groovy, <service name>_start.groovy, etc.
例如,<service name>_install.groovy脚本通常将在服务安装过程中运行, 当然这需要在服务描述符文件lifecyclesection中的指定。 注:你可以为生命周期处理程序指定任何名称,只要你在对应的服务文件指定他们的确切名称。 - 一个可选文件夹usmlib,用于放置自定义probes所需的所有jar文件。
Cloudify是松散的,你可以按需配置Scripted Probes。同样,你也可以开发自己的Plugin Probes ,并将其加入该文件夹中的recipe。例如,PetClinic application 使用了Cloudify Mongo Plugin Probe cloudify-mongo-plugin.jar ,用于提取mongodservice细节信息, 同时监测其端口活性。
The Lifecycle Scripts(生命周期脚本)
在tomcat recipe文件夹的Groovy脚本中,你可以发现它们基本上都是是可以由任何的groovy或java代码编写的普通脚本。唯一不同的是Cloudify API((ServiceContext API)的使用。). API暴露了service运行的细节,因此一个服务可以定位并使用同一个应用中的其它服务的细节。例如,PetClinic application通过API将mongodservice 实例的端口暴露给应用的其它所用服务。
Getting the Service Binaries(获取服务程序)
服务的二进制文件(执行程序文件),例子中的Tomcat和MongoDB发布版上传到为服务实例提供的machines中是相当大的。因此,当提供machines时,recipes通过一个指定的repository(可能是任一的Http、Ftp或本地文件系统)获取,并不从recipe中上传。那样可提高Cloudify环境的性能和反应。在Groovy中内置了支持Apache Ant的AntBuilder,通过Antgettask获取程序二进制文件。
The MongoDB Recipe
MongoDB (mongod) Recipe 执行下步骤:
- 基于它的实例ID和属性文件计算其端口,然后通知serviceContext API,以便其他服务可以与它通信
- 在提供的machine中下载、解压、安装 Mongo binaries
- 启动 MongoDB Server process
- 使用MongoLivenessDetector plugin 检测该项服务是否启动成功。
- 收集度量标准(Active Read Clients, Active Write Client, etc.) 显示在Cloudify web管理控制台
Themongodservice 的描述在<cloudify root>/recipes/apps/petclinic/mongod/mongod-service.groovy.
The Tomcat Recipe
Tomcat Recipe执行下步骤:
- 在提供的machine中下载、解压、安装Tomcat binaries
- 配置Tomcat HTTP Port并开启remote JMX
- 启动 Tomcat process (using the bin/catalina script)
- 通过使用PortLivnessDetector plugin检查8080端口来检测server 是否启动成功
- 通过JMX Monitoring plugin监测Tomcat的性能
The tomcat service 描述文件是<cloudify root>/recipes/apps/petclinic/tomcat/tomcat-service.groovy.
Configuringtomcatwith themongodport(mongod被Tomcat可用)
在例子中,我们使用ServiceContext API 和其 k/v 存储机制,用于分享mongod服务实例 (host address and port) 细节给tomcat服务实例。
首先, MongoDB recipe 暴露mongod服务实例细节如下:
// In <cloudify root>/recipes/apps/petclinic/mongod/mongod_install.groovy
import com.gigaspaces.cloudify.dsl.context.ServiceContextFactory
// ...
// Getting the mongod service instance ID
instanceID = serviceContext.getInstanceId()
// Calculating the mongod port and storing it in the serviceContext for later use
// (by the service and by other services).
serviceContext.attributes.thisInstance["port"] = config.basePort+instanceID
// ...
然后,Tomcat recipe 检索这些细节如下:
import com.gigaspaces.cloudify.dsl.context.ServiceContextFactory
// ...
// Waiting for the mongos service for 20 seconds
mongoService = serviceContext.waitForService(config.mongoService, 20, TimeUnit.SECONDS)
// Waiting for the mongod service instance for 60 seconds (In this case,
mongoInstances = mongoService.waitForInstances(mongoService.numberOfPlannedInstances, 60, TimeUnit.SECONDS)
// Retrieving the mongod host address.
// Note that the mongoInstances array is zero based.
def mongoServiceHost = mongoInstances[0].hostAddress
// Retrieving the mongod port
// Note that the mongoServiceInstances array is one based.
def mongoServiceInstances=serviceContext.attributes.mongod.instances
mongoServicePort=mongoServiceInstances[1].port
这个工作完成后,tomcat服务实例知道了mongod服务实例的主机地址和端口,即可以与之通信了。