标签:
在Cloud Foundry v2中,当应用用户须要启动应用的实例时。用户通过cf CLI向cloud controller发送请求,而cloud controller通过NATS向DEA转发启动请求。真正运行启动事宜的是DEA,DEA主要做的工作为启动一个warden container, 并将droplet等内容拷贝进入container内部。最后配置完指定的环境变量,在这些环境变量下启动应用的启动脚本。
本文将从阐述Cloud Foundry中DEA怎样为应用实例的启动配置环境变量。
在这部分,通过代码的形式来说明DEA对于应用启动请求的运行流程。
subscribe("dea.#{bootstrap.uuid}.start") do |message|
bootstrap.handle_dea_directed_start(message)
end def handle_dea_directed_start(message)
start_app(message.data)
end def start_app(data)
instance = instance_manager.create_instance(data)
return unless instance
instance.start
end 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 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
end
start_script =
Dea::StartupScriptGenerator.new(
command,
env.exported_user_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))
attributes['warden_job_id'] = response.job_id
container.update_path_and_ip
bootstrap.snapshot.save
p.deliver
end
end能够看到在第5步中。DEA涉及到了应用的ENV环境变量等信息。最后通过container.spawn方法实现了应用的启动。
在以上步骤的第5步,首先创建了环境变量env = Env.new(StartMessage.new(@raw_attributes), self)。Env类的初始化方法例如以下:
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
在promise_start方法中,创建env变量之后,通过推断staged_info来选择start_script变量的构建。
如今分析staged_info的代码实现:
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--- detected_buildpack: Ruby/Rack start_command: bundle exec rackup config.ru -p $PORT
有了command变量之后,接着是构建start_script变量:
start_script =
Dea::StartupScriptGenerator.new(
command,
env.exported_user_environment_variables,
env.exported_system_environment_variables
).generate
如今看exported_user_environment_variables方法的实现:
def exported_user_environment_variables
to_export(translate_env(message.env))
end
进入env.exported_system_environment_variables的方法实现:
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 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 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
可见在以上代码中,vcap_application信息中记录了非常多关于应用实例的信息,包含instance_id, instance_index, host, port, started_at, started_at_timestamp, start, state_timestamp等。
VCAP_SERVICES的信息例如以下:
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随后,DEA在执行代码env << ["DATABASE_URL", DatabaseUriGenerator.new(message.services).database_uri] if message.services.any?,该部分代码的作用主要要理解DatabaseUriGenerator的含义,这部分笔者仍不是非常清楚。
再后来。DEA运行代码to_export(env + strategy_env.exported_system_environment_variables),这部分的内容很重要,主要要进入strategy_env对象所在的类中查看exported_system_environment_variables方法:
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
分析完了StartupScriptGenerator的三个參数,须要进入StartupScriptGenerator类的generate方法:
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还有两个脚本是EXPORT_BUILDPACK_ENV_VARIABLES_SCRIPT和START_SCRIPT。EXPORT_BUILDPACK_ENV_VARIABLES_SCRIPT脚本的代码例如以下,其意为运行某路径下的全部sh脚本:
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 ]; then
. $i
fi
done
unset i
fi
BASH 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
BASHresponse = container.spawn(start_script,
container.resource_limits(self.file_descriptor_limit, NPROC_LIMIT))
关于作者:
孙宏亮。DAOCLOUD软件project师。两年来在云计算方面主要研究PaaS领域的相关知识与技术。坚信轻量级虚拟化容器的技术,会给PaaS领域带来深度影响,甚至决定未来PaaS技术的走向。
转载请注明出处。
本文很多其它出于我本人的理解,肯定在一些地方存在不足和错误。希望本文可以对接触DEA环境变量的人有些帮助,假设你对这方面感兴趣,并有更好的想法和建议,也请联系我。
Cloud Foundry中DEA启动应用实例时环境变量的使用
标签:
原文地址:http://www.cnblogs.com/bhlsheji/p/5407339.html