Cloud Foundry中DEA启动应用实例时环境变量的使用
在Cloud Foundry v2中,当应用用户须要启动应用的实例时。用户通过cf CLI向cloud controller发送请求,而cloud controller通过NATS向DEA转发启动请求。真正运行启动事宜的是DEA,DEA主要做的工作为启动一个warden container, 并将droplet等内容拷贝进入container内部。最后配置完指定的环境变量,在这些环境变量下启动应用的启动脚本。
本文将从阐述Cloud Foundry中DEA怎样为应用实例的启动配置环境变量。
DEA接收应用启动请求及其运行流程
在这部分,通过代码的形式来说明DEA对于应用启动请求的运行流程。
- 首先DEA订阅对应主题的消息,主题为“dea.#{bootstrap.uuid}.start”,含义为“自身DEA的应用启动消息”:
subscribe("dea.#{bootstrap.uuid}.start") do |message|
bootstrap.handle_dea_directed_start(message)
end - 当收到订阅主题之后,运行bootstrap.handle_dea_directed_start(message),含义为“通过bootstrap类实例来处理应用的启动请求”:
def handle_dea_directed_start(message)
start_app(message.data)
end - 能够觉得处理的入口。即为以上代码中的start_app方法:
def start_app(data)
instance = instance_manager.create_instance(data)
return unless instance instance.start
end - 在start_app方法中。首先通过instance_manager类实例来创建一个instance对象。通过运行instance实例的类方法start,能够看到自始至终。传递的參数的原始来源都是通过NATS消息传递来的message。也就是1中的message:
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 - 当中真正关于应用启动的运行在promise_start方法中实现:
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方法实现了应用的启动。
DEA环境变量的配置
在以上步骤的第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
可见。实际是创建了一个RunningEnv类的实例。參数主要为cloud controller发送来的信息。
在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
主要是通过指定路径,然后从路径中提取出变量。以下以一个ruby的应用为例,其staging_info.yml文件的内容为:
---
detected_buildpack: Ruby/Rack
start_command: bundle exec rackup config.ru -p $PORT
因此,终于@staged_info的内容如上。在instance.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
能够看到,DEA通过StartupScriptGenerator类来创建start_script,当中參数为三个。第一个为刚才涉及到的command,后两个均须要通过env变量来生成。
如今看exported_user_environment_variables方法的实现:
def exported_user_environment_variables
to_export(translate_env(message.env))
end
该方法实现从message中提取中属性为env的信息,作为用户的环境变量。
进入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
可见在生成系统的环境变量的时候,首先创建一个env数组变量,当中有信息VCAP_APPLICATION, VCAP_SERVICES, MEMORY_LIMIT三种。当中VCAP_APPLICATION的信息例如以下:
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
这部分信息中包括了应用的name,uris,users,version等一系列内容。当中须要特别注意的是代码hash = strategy_env.vcap_application,该代码的作用为调用了RunningEnv类中vcap_application方法,例如以下:
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
这部分内容主要从message中寻找是否存在和WHITELIST_SERVICES_KEYS中值作为键的值,若存在的话,增加services_hash该变量。
随后,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
能够看到,这里主要包括执行信息的环境变量,比方说HOME文件夹,TMP暂时文件夹,应用实例执行的host地址,应用实例执行的port号。当中最为重要的就是应用实例执行的port号。在我的还有一篇博文Cloud Foundry中DEA与warden通信完毕应用port监听 中。有涉及到怎样通过warden server来开辟port,终于为DEA所用,并通过环境变量的形式传递给应用实例的启动脚本。当中VCAP_APP_PORT和PORT都是warden
container开启那个port号。
分析完了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
以上的代码即为创建启动脚本的过程。当中@system_envs为之前分析的env.exported_system_environment_variables, @user_envs为env.exported_user_environment_variables。
还有两个脚本是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代码例如以下:
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
以上即创建完了start_script,回到instance.rb中的promise_start方法中,即运行
response = container.spawn(start_script,
container.resource_limits(self.file_descriptor_limit, NPROC_LIMIT))
即完毕应用实例的启动工作。详情能够进入container类中的spawn方法实现。
Cloud Foundry中DEA启动应用实例时环境变量的使用的更多相关文章
- Cloud Foundry中DEA与warden通信完毕应用port监听
在Cloud Foundry v2版本号中,DEA为一个用户应用执行的控制模块,而应用的真正执行都是依附于warden. 更详细的来说,是DEA接收到Cloud Controller的请求:DEA发送 ...
- Cloud Foundry中 JasperReports service集成
Cloud Foundry作为业界第一个开源的PaaS解决方案,正越来越多的被业界接受和认可.随着PaaS的发展,Cloud Foundry顺应潮流,充分发挥开源项目的特点,到目前为止,已经支持了大批 ...
- Cloud Foundry中warden的网络设计实现——iptable规则配置
在Cloud Foundry v2版本号中,该平台使用warden技术来实现用户应用实例执行的资源控制与隔离. 简要的介绍下warden,就是dea_ng假设须要执行用户应用实例(本文暂不考虑ward ...
- Cloud Foundry中通用service的集成
目前,CloudFoundry已经集成了很多第三方的中间件服务,并且提供了用户添加自定义服务的接口.随着Cloud Foundry的发展,开发者势必会将更多的服务集成进Cloud Foundry,以供 ...
- Cloud Foundry中vmc tunnel与caldecott原理
在Cloud Foundry中,用户可以vmc create-service创建一个service instance,但是常规情况下,用户不能手动地进一步对service instance进行设计.以 ...
- Cloud Foundry中gorouter对StickySession的支持
Cloud Foundry作为业界出众的PaaS平台,在应用的可扩展性方面做得很优秀. 详细来讲,在一个应用须要横向伸展的时候,Cloud Foundry能够轻松地帮助用户做好伸展工作,也就是创建出一 ...
- vue中利用.env文件存储全局环境变量,以及配置vue启动和打包命令
目录 1,前言 2,.env文件的作用 3,配置.env文件 4,配置启动命令 5,获取.env中的全局变量 5,实际用处 1,前言 分享一下vue项目中利用.env文件存储全局环境变量,以及利于项目 ...
- 配置JDK时环境变量path和JAVA_HOME的作用
1.PATH环境变量.作用是指定命令搜索路径,在i命令行下面执行命令如javac编译java程序时,它会到PATH变量所指定的路径中查找看是否能找到相应的命令程序.需要把jdk安装目录下的bin目录增 ...
- jenkins中如何实现执行脚本时的变量共享
1.主要是利用EnvInject Plugin插件,所以要首先安装插件,安装好后如下图: 2.然后在“增加构建步骤”中,插入一个“Execute Python script” 代码我用的python3 ...
随机推荐
- 重启虚拟目录或站点,不重启iis
更改站点或虚拟目录的.net Framework 版本会导致重启整个iis服务,所以需要单独重启某个站点或虚拟目录 首先右键点击网站-所有任务-将配置保存到一个文件,从文件中查找到 Location ...
- 【译】EntityFramework6与EntityFrameworkCore的区别
EntityFramework6 EF6 是一个久经考验的数据库访问技术,发展多年,拥有许多特性,并且成熟稳定.2008年EF作为 .Net 3.5 Sp1 和Visual Studio 2008 S ...
- 特性(C# 和 Visual Basic)
特性提供功能强大的方法,用以将元数据或声明信息与代码(程序集.类型.方法.属性等)相关联. 特性与程序实体关联后,即可在运行时使用名为“反射”的技术查询特性. 有关更多信息,请参见 反射(C# 和 V ...
- Java中抽象类概述
抽象类 1.抽象类的定义 抽象类是为子类提供一个规范,这就必须联系到继承: 抽象类的制定就是让子类继承的: public abstract 类名{ //类体 //抽象方法 修饰符 abstract ...
- 使用BigDecimal转换较长小数时候出现科学计数法的问题
public static String divToString(double v1, double v2, int scale){ if (scale < 0) { throw new Ill ...
- 和为k的最长子数组及其延伸
问题1: /** * 问题描述: * 给定一个无序数组arr,其中元素可正.可负.可0, * 求arr所有的子数组中正数与负数个数相等的最长子数组长度 * * 解题思路:对数组进行处理,正数为1,负数 ...
- 【Git使用详解】Egit的常用操作详解
常用操作 操作 说明 Fetch 从远程获取最新版本到本地,不会自动merge Merge 可以把一个分支标签或某个commit的修改合并现在的分支上 Pull 从远程获取最新版本并merge到本地相 ...
- 【OOB】MSHTML!CPasteCommand::ConvertBitmaptoPng heap-based buffer overflow学习
IE 11 MSHTML!CPasteCommand::ConvertBitmaptoPng heap-based buffer overflow学习 MS14-056, CVE-2014-41 ...
- [转] javascript组件开发方式
作为一名前端工程师,写组件的能力至关重要.虽然JavaScript经常被人嘲笑是个小玩具,但是在一代代大牛的前仆后继的努力下,渐渐的也摸索了一套组件的编写方式. 下面我们来谈谈,在现有的知识体系下,如 ...
- ajax和jsonp
ajax和jsonp 前言:ajax和jsonp可以与后台通信,获取数据和信息,但是又不用刷新整个页面,实现页面的局部刷新. 一.ajax 定义:一种发送http请求与后台进行异步通讯的技术. 原理: ...