Source analysis of Stager components in Foundry Cloud

Label Foundry CloudStagerSource codeDroplet
1425 people read comments(0) Collection Report

Foundry Cloud in a component, called Stager, it is mainly responsible for the work of the user is deployed into the Foundry Cloud source code packaged into a DEA can extract the implementation of droplet.

On the production of Cloud, Foundry V1 droplet in a complete process for the:

  1. Users will be uploaded to the application source code Controller Cloud;
  2. Controller NATS to send the request to Stager through the Cloud, the requirements of the production of dropet;
  3. Stager from Controller Cloud download the application of compressed source code, and extract;
  4. Stager will be extracted after the application source code, add the operation of the container as well as the start of the termination of the script;
  5. Compressed into droplet form to Foundry Cloud.

Stager production droplet total process to achieve

Now from the production of a droplet process will be analyzed in the Foundry Stager Cloud source.

Received from the Stager request, then began to produce droplet. And Stager can receive the Controller Cloud sent to the stage request, is due to the Stager subscription to the theme of the "staging" message, /stager/lib/vcap/server.rb code as follows:

Setup_subscriptions def
@config[: do |q| queues].each
The @nats_conn.subscribe (@sids Q,: queue = > q) do |msg, reply_to|
@thread_pool.enqueue {execute_request (MSG, reply_to)} ("request #{msg} Enqueued")
End ("to #{q} Subscribed")
From the above code, we can know, subscribe to the @config[: queues] in the theme, after the release of the theme of the message, was received by the Stager, Stager implementation of the execute_request method to perform the received message msg. Here the implementation, also with the @thread_pool, the variable is to create a thread pool, the results will be added to join the thread pool.

The following entry into execute_request's /stager/lib/vcap/server.rb method:

Execute_request def (encoded_request, reply_to)
Request = Yajl:: Parser.parse (encoded_request)
Rescue = > e
Task = VCAP:: Stager:: (request, @task_config)
Result = nil
Encoded_result = Yajl:: Encoder.encode (result)
EM.next_tick {@nats_conn.publish (reply_to, encoded_result)}
In this method, we first parse the request request, get the request object, and then generate a task object through the request object and Task, the @task_config class is VCAP:: Stager:: Task. Among them, there are attributes in @task_config: ruby_path,: ruby_plugin_path, secure_user_manager. After creating the task object, the execution of the task.perform. About perform method, is the most important part of the whole Stager implementation flow, the following will enter the perform /stager/lib/vcap/stager/task.rb method:

Perform def
Workspace = VCAP:: Stager:: Workspace.create
App_path = File.join (workspace.root_dir, "")
Download_app (app_path)

Unpack_app (app_path, workspace.unstaged_dir)

Stage_app (workspace.unstaged_dir, workspace.staged_dir, @task_logger)

Droplet_path = File.join (workspace.root_dir, "droplet.tgz")
Create_droplet (workspace.staged_dir, droplet_path)

Upload_droplet (droplet_path)
If workspace workspace.destroy
The process is very clear in the method, the order is download_app, upack_app, stage_app, create_droplet, upload_app.

First enter the download_app method:

Download_app def (app_path)
Cfg_file = ("curl_dl_config")
Write_curl_config (@request["download_uri"], cfg_file.path, output = app_path)
RES = @亚军。run_logged(“环境-你http_proxy U https_proxy卷曲的S F K # { cfg_file。路径}”)
如果cfg_file cfg_file.unlink
该方法中较为重要的部分就是如何write_curl_config,以及如何执行脚本命令。在write_curl_config中有一个参数@请求[“download_uri”],该参数的意义是用户上传的应用源码存在云控制器处的位置,也是老手要去下载的应用源码的未知。该参数的产生的流程为:云控制器中的app_controller。Rb中,调用stage_app方法,该方法中调用了download_uri方法,并将其作为请求的一部分,通过NAT发给了老手。关于write_curl_config主要实现的是将卷曲的配置条件写入指定的路径,以便在执行卷曲命令的时候,可以通过配置文件的读入来方便实现,实现代码为:RES =”亚军。run_logged(“环境-你http_proxy U https_proxy卷曲的S F K # { cfg_file。路径}”)。

当将应用源码下载至指定路径之后,第二步需要做的是将其解压,unpack_app方法则实现了这一点,实现代码为:RES = @亚军。run_logged(“解压- Q # { packed_app_path } - D # { dst_dir }”)。


DEF stage_app(src_dir,dst_dir,task_logger)
plugin_config = {
“source_dir”= > src_dir,
“dest_dir”= > dst_dir,
“环境”=“请求[属性] ]
plugin_config_file = tempfile新(“plugin_config”)

cmd = [ @ ruby_path,@ run_plugin_path,
“请求[“属性”] [“framework_info”] [“name”],
RES = @亚军。run_logged(CMD,:max_staging_duration = > @ max_st。/斌/ runaging_duration)
如果plugin_config_file plugin_config_file.unlink

在该方法中,首先创建plugin_config这个哈希对象,随后创建plugin_config_file,又创建了CMD对象,最后通过RES = @亚军。run_logged(CMD,:max_staging_duration = > @ max_staging_duration)实现了执行了对象现在分析CMD CMD:

cmd = [ @ ruby_path,@ run_plugin_path,
“请求[“属性”] [“framework_info”] [“name”],
该对象中@ ruby_path为老手组件所在节点出红宝石的可执行文件路径;@ ruby_plugin_path为运行插件可执行文件的路径,具体为:/歌手/斌/ run_plugin;@请求[属性] [“framework_info”] [“name”]为所需要执行阶段操作的应用源码的框架名称,比如,java_web,弹簧,玩,为Rails3等框架;而plugin_config_file路径时所需配置文件的路径则是做插件。



DEF create_droplet(staged_dir,droplet_path)
cmd = [“CD”,staged_dir,“&”、“copyfile_disable =真”、“焦油”、“CZF”,droplet_path,“*”]加入(“”)。
When you create a droplet after the compression package, Stager will be uploaded to the Cloud Controller dropet of a path, so that after the DEA in the start of this application, you can download from the Controller Cloud file system to droplet, and extract the boot. The following is the implementation of upload_app code:

Upload_droplet def (droplet_path)
Cfg_file = ("curl_ul_config")
Write_curl_config (@request["upload_uri"], cfg_file.path, "form" = "upload[droplet]=@#{droplet_path}")
Res = @runner.run_logged ("-u http_proxy env https_proxy -u curl -s -S -f -K #{cfg_file.path}")
If cfg_file cfg_file.unlink
At this point, Stager do the work of the general process, has been fully completed, but at the same time, the use of the detailed technology, this paper does not have one one about.


Although the Stager implementation process has been described, but the most important module on the stage_app Stager method of the specific implementation, this paper has not been described in this article will be explained in detail in this part of the content, and the Java_web and standalone these two different frameworks for the case analysis.

As well as mentioned above, the code to implement the function of stage is:

CMD = @run_plugin_path, [@ruby_path,
@request["properties", "framework_info", "name"],
./bin/ ("") run plugin_config_file.path].join

Res = @runner.run_logged (cmd,
Max_staging_duration = > @max_staging_duration)
About CMD in the various parameters, the above has been analyzed, and now more in-depth understanding of @run_plugin_path, the object point to the /stager/bin/run_plugin executable file, and now into the file:

ENV['BUNDLE_GEMFILE'] ||= File.expand_path ('../../Gemfile', __FILE__)
$LOAD_PATH.unshift (File.expand_path ('../../lib', __FILE__))
Unless ARGV.length = = 2
Usage: "run_staging_plugin [plugin name] [plugin config file] puts"
Exit 1
Config_path, plugin_name = ARGV
Klass = StagingPlugin.load_plugin_for (plugin_name)
Plugin = klass.from_file (config_path)
This part of the source code is mainly achieved, extracted from the CMD command in the two parameters, one for the plugin_name, the other for the config_name, and through the two parameters to achieve the load plugin and the real stage.

First into the load_plugin_for method, the source location is /vcap_staging/lib/vcap/staging/plugin/common.rb:

Self.load_plugin_for./bin/ (framework) run def
Framework = framework.to_s
Plugin_path = File.join (staging_root, framework,'plugin.rb')
Plugin_path require
Object.const_get ("#{camelize (framework)}Plugin")

Java_web framework for the application of the stage process

First Java_web as an example in the load_plugin_for method, first extracted from the application of the framework set by the source code, and through the framework to create the plugin_path object, and then to achieve the require framework directory plugin.rb files, Java_web applications need to require file: /vcap-staging/lib/vcap/staging/plugin/java_web/plugin.rb.

By require after the sub class of plugin.rb, when the implementation of plugin.stage_application, the direct access to the stage_application /vcap-staging/lib/vcap/staging/plugin/java_web/plugin.rb method, the method of source code:

Stage_application def
Dir.chdir (destination_directory) do
Webapp_root = Tomcat.prepare (destination_directory)
Copy_source_files (webapp_root)
Web_config_file = File.join (webapp_root,'WEB-INF/web.xml')
File.exist web_config_file? Unless
Web "application staging failed: web.xml not found raise"
Services = services]: if environment environment[
Copy_service_drivers (File.join (webapp_root,'../../lib'), services)
Destination_directory Tomcat.prepare_insight, environment, if Tomcat.insight_bound insight_agent? Services
Configure_webapp (webapp_root, self.autostaging_template, environment) self.skip_staging unless (webapp_root) ()
In stage_application, the implementation steps are: 1 to create the corresponding directory; 2 copy of the source code and the corresponding required files; 3 to set the configuration file; 4 to add the start and termination of the script.

Create the appropriate directory, including create_app_directory, in the application of the target directory to create the log folder as well as the TMP folder.

In the implementation code webapp_root = Tomcat.prepare (destination_directory), the prepare into the /vcap-staging/lib/vcap/staging/plugin/java_web/tomcat.rb method:

Self.prepare def (DIR)
FileUtils.cp_r (resource_dir, DIR)
Output = #{dir} unzip; -q resources/]%x[cd
Raise "Could not unpack Tomcat: #{output} unless $= = 0?
Webapp_path = File.join (DIR, "Tomcat", "webapps", "ROOT")
Server_xml = File.join (DIR, "Tomcat", "conf", "server.xml")
FileUtils.rm_f (server_xml)
FileUtils.rm (File.join (DIR, "resources", "") (File.join (DIR, "resources", "droplet.yaml"), dir ()
FileUtils.mkdir_p (webapp_path)
In this method, the first copy from the resource folder to the dir directory, then the resource folder in the file decompression, and then in the dir directory for a series of file operations. Finally returns the web_app path.

Returns to the stage_application method, copy_source_fiiles from some of the source files are all copied to webapp_path, code for: copy_source_files (webapp_root); then check the WEB-INF/web.xml configuration file exists, if not exist, throw the exception, instead of the code"

File.exist web_config_file? Unless
Web "application staging failed: web.xml not found raise"

Then apply the required number of service driven copy to the specified lib directory, code: copy_service_drivers (File.join (webapp_root,'../../lib'), services, and then to achieve the configuration of the Tomcat, the main agent configuration: destination_directory environment, insight_agent, if Tomcat.insight_bound services Tomcat.prepare_insight, and then on the webapp road wood configuration, the code is: configure_webapp (webapp_root), Self.autostaging_template, (environment) self.skip_staging unless (webapp_root); finally to the realization of the startup script and the termination of the script generation, code for:

First look at the Java_web framework for the application of the startup script to create the create_startup_script method in /vcap-staging/lib/vcap/staging/plugin/common.rb:

Create_startup_script def
Path = File.join (destination_directory,'startup') (path,'wb') |f| do
Startup_script f.puts
FileUtils.chmod (0500, path)
In the method, the first in the target file to add a startup file, and then open the file, and startup_script generated by the contents of the startup file is written, the final startup file permissions configuration. Now enter the startup_script method, the location is /vcap-staging/lib/vcap/staging/plugin/java_web/plugin.rb:

Startup_script def
Vars = {}
Vars['CATALINA_OPTS'] = configure_catalina_opts
Generate_startup_script (vars) do
CATALINA_OPTS= $CATALINA_OPTS "`ruby resources/set_environm./bin/ run./bin/ run./bin/ runent` export"
Env > Run > env.log./bin/
Getopts while ": opt" p:; do
$opt in case
If [-lt $PORT 0]; then
Missing "or invalid port echo (-p)"
Exit 1
Resources/generate_server_xml $PORT Ruby
In generate startup script method Huitian clamp start command, located in the /vcap-staging/lib/vcap/staging/plugin/common.rb and scripts to run achieved to add:

<%=% > change_directory_for_start
<%=../logs/stdout.log 2>../logs/stderr.log & start_command% > >
Where change_directory_for_start is: Tomcat CD; and start_command is: run./bin/

At this point, the start of the script to add is completed, the termination of the script to add, but also roughly the same, mainly to obtain the application process PID, and then forced to kill the process, this paper is no longer specifically explain the stop script generation.

To explain this, a Java_web framework for app applications as well as complete stage completed, the follow-up will be packaged into a droplet and upload. Stage completed, the nature is the need to use the DEA, and DEA is to get droplet, extract to the corresponding directory, and finally through the DEA node to provide the running environment, the implementation of the compressed startup in the droplet script, and ultimately in DEA to start the application.

Standalone framework for the application of the stage process

In Foundry standalone, the Cloud application is considered to be unable to be identified by Foundry Cloud all types of the framework of the application. In this case, Foundry stager Cloud or will be packaged.

In the production of standalone application of droplet, the total production process and Java_web and other can be identified by the Foundry Cloud framework, there is no difference, but in stage, it will be a big difference. Because for the application of Cloud, Foundry stager standalone does not provide all of the source code for all run dependent, so the implementation of the application depends on the user must be uploaded before uploading and application source code bundled in the upload.

After identifying the need to package the source framework is defined as standalone, Stager uses sub class StandalonePlugin sub class StagingPlugin to achieve the stage process. Process and its type of framework of the same application, but there are some differences in the specific operation, first of all to read the stage_application method:

Stage_application def
Dir.chdir (destination_directory) do
Everything executable perms as, start command may #Give be a script
FileUtils.chmod_R (0744, File.join (destination_directory,'app'))
The main difference between the runtime_specific_staging and the following methods. The main function of the runtime_specific_staging method is to judge whether the application's running environment needs ruby. If not, staging_application will continue to be executed; if necessary, prepare the corresponding gem package and gem package for the application.

Then in the create_startup_script method, the first application of the runtime type, if it is ruby, Java, python, then the generation of the corresponding start script, if it is other types of words, then directly into the generate_startup_script method. Now run Java as an example, analysis of the implementation of the process, the code is as follows:

Java_startup_script def
Vars = {}
Java_sys_props = "$PWD/tmp"
Vars['JAVA_OPTS'] = "-Xms#{application_memory}m -Xmx#{application_memory}m #{java_sys_props} $JAVA_OPTS"
Generate_startup_script (vars)
The realization of the method, after the creation of the vars object, by passing the vars parameters to the method generate_startup_script, to achieve the start script generation, the code is as follows:

Def generate_startup_script (env_vars = {})
After_env_before_script = block_given?? yield: "\n"
Template = <<-SCRIPT
# /bin/bash!
<%= environment_statements_for (env_vars)% >
<%=% > after_env_before_script
<%=% > change_directory_for_start
<%=../logs/stdout.log 2>../logs/stderr.log & start_command% > >
<%=% > get_launched_process_pid
$STARTED "echo"../
<%=% > wait_for_launched_process
TODO ERB is pretty # - irritating when it comes to blank lines, such as when'after_env_before_script'is nil.
Is probably a better # There way that doesn't involve making the above Heredoc horrible. (template).Result (binding).Lines.reject {|l| L = ~ /^\s*$/}.join
Here, it is the stage process to start the script generation, the termination of the generation of the script, the process is also consistent, mainly to obtain the application process of PID, and then through the PID kill to achieve the termination of the application.

Above is the author of Foundry Stager in the Cloud component of the simple source analysis.

About the author:

Sun Hongliang,DAOCLOUDSoftware engineer. In the past two years, the main research areas of PaaS related knowledge and technology in the field of cloud computing. Firmly believe that the technology of lightweight virtual container, will bring the depth of the impact of the PaaS field, and even determine the future direction of PaaS technology.

Reproduced clearly indicate the source.

This document is more out of my own understanding, certainly in some places there are shortcomings and errors. I hope this article will be able to contact Foundry Stager in the Cloud component of the people some help, if you are interested in this area, and have better ideas and suggestions, please contact me.

My email address:
Sina weibo:Fu Ruqing lotus seed

Guess you're looking for
View comments
* the above user comments only represent their personal views, does not represent the views or position of the CSDN website
    personal data
    • Visit81266 times
    • Integral:One thousand three hundred and twenty-one
    • Grade
    • Rank:18689th name
    • Original47
    • Reproduced:0
    • Translation:1
    • Comments:49
    Blog column
    Contact information
    Latest comments