In Cloud Foundry v2, when an app user needs to launch an instance of the app. The user sends a request to the cloud controller via the CF CLI, and the cloud controller forwards the request to the DEA via Nats. The real start-up is Dea,dea's main job is to start a Warden container, and copy the droplet and other content into the container interior. Finally, the specified environment variables are configured to start the app's startup script under these environment variables.
This article will explain how the DEA in cloud Foundry configures environment variables for the launch of an application instance.
DEA receive application start request and its running flow
In this section, the code is used to illustrate the flow of the DEA to the application launch request.
- First, the DEA subscribes to the message of the subject, "Dea.#{bootstrap.uuid}.start", meaning "self-DEA Application initiation message":
Subscribe ("Dea.#{bootstrap.uuid}.start") do |message| Bootstrap.handle_dea_directed_start (message) end
- After you receive the subscription topic, run Bootstrap.handle_dea_directed_start (message), meaning "to process the application's start request through the Bootstrap class instance":
def handle_dea_directed_start (message) Start_app (message.data) end
- Able to feel the handling of the entrance. That is, the Start_app method in the above code:
def start_app (data) instance = instance_manager.create_instance (data) return unless instance Instance.start End
- In the Start_app method. First, a instance object is created through the Instance_manager class instance. By running the class method of the instance instance start, you can see from beginning to finish. The original source of the passed reference is a message that is delivered through NATS messages. The message in 1:
def start (&callback) p = promise.new do ... [ Promise_droplet, promise_container ].each (&:run). each (&:resolve) [ promise_ Extract_droplet, promise_exec_hook_script (' Before_start '), promise_start ].each (&:resolve) ... P.deliver End
- The real implementation of the application launch is implemented in the Promise_start method:
def Promise_start Promise.new do |p| env = Env.new (Startmessage.new (@raw_attributes), self) if staged_info command = Start_command | | staged_info[' start_command ' unless command P.fail (missingstartcommand.new) Next E nd start_script = dea::startupscriptgenerator.new (Command, Env.exported_use R_environment_variables, env.exported_system_environment_variables). Generate Else Start_script = Env.exported_environment_variables + "./startup;\nexit" End response = Container.spawn (start _script, Container.resource_limits (Self.file_descriptor_limit, nproc_limit)) attr ibutes[' warden_job_id ' = response.job_id container.update_path_and_ip bootstrap.snapshot.save P.deli Ver End End
Can see in the 5th step. DEA involves the application of env environment variables and other information. Finally, the application start-up is realized by Container.spawn method.
Configuration of DEA environment variables
In step 5th of the above steps, you first created the environment variable ENV = env.new (Startmessage.new (@raw_attributes), self). The initialization method for the Env class is as follows:
DEF initialize (message, instance_or_staging_task=nil) @strategy_env = if message.is_a? Stagingmessage stagingenv.new (message, instance_or_staging_task) else runningenv.new (message, Instance_or_staging_task) End End
Visible. An instance of the Runningenv class is actually created. The main parameters are the information sent by the cloud controller.
In the Promise_start method, after the env variable is created, the build of the Start_script variable is selected by inferring the Staged_info.
Now analyze the Staged_info code implementation:
def staged_info @staged_info | | = Begin Dir.mktmpdir do |destination_dir| Staging_file_name = ' staging_info.yml ' copied_file_name = ' #{destination_dir}/#{staging_file_name} ' Copy_ Out_request ("/home/vcap/#{staging_file_name}", Destination_dir) yaml.load_file (copied_file_name) if File.exists? (copied_file_name) End End End
This is done primarily by specifying the path and then extracting the variable from the path. The following is an example of a ruby application where the contents of the Staging_info.yml file are:
---detected_buildpack:ruby/rackstart_command:bundle exec rackup config.ru-p $PORT
So, finally @staged_info the content as above. In the Instance.start method, command is the bundle EXEC rackup config.ru-p $PORT.
With the command variable, then build the Start_script variable:
Start_script = dea::startupscriptgenerator.new ( command, env.exported_user_environment_variables, env.exported_system_environment_variables ). Generate
It can be seen that the DEA creates the Start_script through the Startupscriptgenerator class, with a number of three. The first one for the command that was just involved, the latter two need to be generated by the ENV variable.
Now look at the implementation of the Exported_user_environment_variables method:
def exported_user_environment_variables To_export (translate_env (message.env)) end
The method implements the information extracted from the message with the property as Env, as the user's environment variable.
The method of entering Env.exported_system_environment_variables is implemented:
def exported_system_environment_variables env = [ ["Vcap_application", Yajl::encoder.encode (vcap_ application)], ["Vcap_services", Yajl::encoder.encode (vcap_services)], ["Memory_limit", "#{ Message.mem_limit}m "] env << [" Database_url ", Databaseurigenerator.new (message.services). DATABASE _uri] if Message.services.any? To_export (env + strategy_env.exported_system_environment_variables) end
When generating the environment variables of the system, we first create an env array variable, which has information vcap_application, vcap_services, memory_limit three kinds. Among the vcap_application information, such as the following:
def vcap_application @vcap_application | | = begin hash = strategy_env.vcap_application hash["limits"] = message.limits hash["Application_ Version "] = message.version hash[" application_name "] = message.name hash[" application_uris "] = Message.uris # Translate keys for backwards compatibility hash["version"] = hash["Application_version"] hash["name"] = hash["Application_name"] hash["URIs"] = hash["Application_uris"] hash["users"] = hash["Application_ Users "] hash end End
This part of the information includes the application of Name,uris,users,version and a series of content. It is important to note that the code hash = strategy_env.vcap_application, which is used to invoke the Vcap_application method in the Runningenv class, such as the following:
def vcap_application hash = {} hash["instance_id"] = instance.attributes["instance_id"] hash["Instance_ Index "] = Message.index hash[" host "] = Hoststartupscriptgenerator hash[" port "] = Instance.instance_container _port started_at = time.at (instance.state_starting_timestamp) hash["started_at"] = started_at hash[" Started_at_timestamp "] = started_at.to_i hash[" start "] = hash[" Started_at "] hash[" state_timestamp "] = hash[ "Started_at_timestamp"] hash end
Visible in the above code, vcap_application information is recorded in a lot of information about the application instance, including INSTANCE_ID, Instance_index, host, Port, Started_at, Started_at_ Timestamp, start, state_timestamp and so on.
Vcap_services information such as the following:
Whitelist_service_keys =%w[name Label Tags plan plan_option credentials syslog_drain_url].freeze def vcap_services @vcap_services | | = begin Services_hash = hash.new {|h, k| h[k] = []} Message.services.each do |service| Service_hash = {} whitelist_service_keys.each do |key| Service_hash[key] = Service[key] If Service[key] end services_hash[service["label"] "<< Service_hash End Services_hash end End
This part of the content is mainly from the message to find whether there is a value in the Whitelist_services_keys value as the key, if present, increase services_hash the variable.
Subsequently, the DEA executes code in env << ["Database_url", Databaseurigenerator.new (message.services). Database_uri] If Message.services.any, the role of this part of the code mainly to understand the meaning of databaseurigenerator, this part of the author is still not very clear.
And then later. DEA Run code to_export (env + strategy_env.exported_system_environment_variables), this part of the content is important, mainly to enter the Strategy_ View the Exported_system_environment_variables method in the class where the Env object is located:
def exported_system_environment_variables env = [ ["HOME", "$PWD/app"], ["TMPDIR", "$PWD/tmp"], [" Vcap_app_host ", HOST], [" Vcap_app_port ", Instance.instance_container_port], ] env << [" PORT "," $ Vcap_app_port "] env End
Can see, here mainly includes the execution information environment variables, such as home folder, tmp temporary folder, the application instance executes the host address, the application instance executes the port number. The most important of these is the port number that the application instance executes. There's one more blog in mine. In the Cloud Foundry, the DEA and Warden communication has been applied in port monitoring. There is a startup script that involves how to open a port through Warden server, which is finally used by the DEA and passed to the application instance in the form of an environment variable. Both Vcap_app_port and port are Warden container open the port number.
After analyzing the three parameters of startupscriptgenerator, you need to enter the Generate method of the Startupscriptgenerator class:
def generate script = [] script << "umask 077" script << @system_envs script << EXPORT _buildpack_env_variables_script Script << @user_envs script << "ENV > Logs/env.log" Script << start_script% @start_command script.join ("\ n") end
The code above is the process of creating a startup script. Among them, @system_envs is the env.exported_system_environment_variables of the previous analysis, @user_envs is Env.exported_user_environment_ Variables
There are also two scripts that are Export_buildpack_env_variables_script and start_script. The code for the Export_buildpack_env_variables_script script, such as the following, is intended to run all SH scripts under a path:
Export_buildpack_env_variables_script = Strip_heredoc (<<-bash). Freeze unset Gem_path If [-D app/. PROFILE.D]; Then for i in app/.profile.d/*.sh, do if [-R $i]; $i fi done unset i fi BASH
The Start_script code, for example, is:
Start_script = Strip_heredoc (<<-bash). Freeze droplet_base_dir= $PWD CD app (%s) > > (Tee $ Droplet_base_dir/logs/stdout.log) 2> > (Tee $DROPLET _base_dir/logs/stderr.log >&2) & started=$! echo "$STARTED" >> $DROPLET _base_dir/run.pid wait $STARTED BASH
Above that is created Start_script, back to instance.rb in the Promise_start method, that is, run
Response = Container.spawn (Start_script, container.resource_limits (Self.file_descriptor_limit, NPROC_LIMIT))
The start of the application instance is complete. Details can be entered into the container class in the Spawn method implementation.
About
Liang Daocloud software Project Master. In the past two years, we have studied the knowledge and technology of PAAs in the field of cloud computing. Believing in the technology of lightweight virtualization containers will have a deep impact on the PAAs landscape and even determine the future of PAAs technology.
Reprint please indicate the source.
Many of the other things in my own understanding, there must be deficiencies and errors in some places. I hope this article can be of some help to the people who contact the DEA environment variables, if you are interested in this area, and have better ideas and suggestions, please contact me.
my e-mail: [Email protected]Sina Micro-blog:@ Lotus seeds and clear
The use of environment variables when the DEA launches application instance in Cloud Foundry