本帖最后由 xioaxu790 于 2014-6-20 08:49 编辑
问题导读:
1、如何启动通过dev_setup安装的Cloud Foundry?
2、如何添加系统服务?
3、怎样查看echo服务是否成功?
前提
已经正确使用dev_setup安装Cloud Foundry
dev_setup安装完毕时提示的部署成功的信息
启动流程
流程分析
vcap_dev源码
vcap源码
通过dev_setup安装的Cloud Foundry可以通过以下代码启动
- ~/cloudfoundry/vcap/dev_setup/bin/vcap_dev start
复制代码
vcap_dev会从以下代码处获得Cloud Foundry的各个组件和部署信息
- vcap_components = JSON.parse(File.read(Deployment.get_vcap_config_file(deployment_config_path)))
- deployment_info = JSON.parse(File.read(Deployment.get_deployment_info_file(deployment_config_path)))
复制代码
具体的文件路径可以从dev_setup/lib下的vcap_defs.rb下查看,其中
vcap_components默认的获取文件路径是
- ~/cloudfoundry/.deployment/devbox/config/vcap_components.json
复制代码
deployment_info默认的获取文件路径是
- ~/cloudfoundry/.deployment/devbox/config/deployment_info.json
复制代码
经过各种处理之后,将信息送入vcap进行处理
- exec_cmd("#{ruby_binary} #{vcap_launch} #{command} #{vcap_components["components"].join(" ")} -c #{deployment_config_path} -v #{vcap_path} -l #{deployment_info["deployment_log_path"]}")
复制代码
vcap在解析上上述输入的信息后将各个component送入Component类中进行启动(dev_setup/lib/vcap_components.rb)
- exec("#{component_start_path}")
复制代码
例如:
启动vblob_gateway时,执行
- /root/cloudfoundry/vcap/bin/../services/vblob/bin/vblob_gateway -c /root/cloudfoundry/.deployments/devbox/config/vblob_gateway.yml
复制代码
- #!/usr/bin/env ruby
-
- require 'rubygems'
- require 'erb'
- require 'json'
- require 'fileutils'
- require 'optparse'
- require File.expand_path(File.join("..", "lib", "vcap_defs"), File.dirname(__FILE__))
-
- script_dir = File.expand_path(File.dirname(__FILE__))
-
- # TODO: Once we integrate better with bin/vcap, this option parsing should
- # become more robust
- options = {}
- opts = OptionParser.new do |opts|
- opts.banner = "Usage: #{$0} [--name deployment_name] [--dir cloudfoundry_home_dir] [start|stop|restart|tail|status]"
- opts.on("-n", "--name deployment_name", "Name of the deployment") do |n|
- options["name"] = n
- end
- opts.on("-d", "--dir cloud_foundry_home_dir", "Cloud foundry home directory") do |n|
- options["home"] = n
- end
- end
- opts.order!(ARGV)
-
- options["name"], options["home"] = Deployment.get_deployment_target if options.empty?
-
- if options["name"]
- puts "Targeting deployment "#{options["name"]}" with cloudfoundry home "#{options["home"]}""
- else
- options["name"] = DEPLOYMENT_DEFAULT_NAME
- puts "Targeting default deployment "#{options["name"]}""
- end
-
- command = ARGV[0] ? ARGV.shift.downcase : nil
-
- if command.nil? || !%w[start stop restart tail status].include?(command)
- STDERR.puts "Usage: #{$0} [-n deployment_name] [-d cloudfoundry_home_dir] [start|stop|restart|tail|status] [COMPONENT]"
- exit 1
- end
-
- deployment_config_path = Deployment.get_config_path(options["name"], options["home"])
-
- begin
- vcap_components = JSON.parse(File.read(Deployment.get_vcap_config_file(deployment_config_path)))
- deployment_info = JSON.parse(File.read(Deployment.get_deployment_info_file(deployment_config_path)))
- rescue => e
- STDERR.puts "#{e.inspect}. Could not parse deployment config files . Please check your deployment."
- exit 1
- end
-
- # select only those components that are on the command line (if any)
- if !ARGV.empty? and (vcap_components["components"] & ARGV).empty?
- STDERR.puts "The component(s) specified on the command line are unknown.\nPlease specify one or more of (#{vcap_components['components'].join(' ')})"
- exit 1
- end
- vcap_components["components"] &= ARGV unless ARGV.empty?
-
- unless File.exists?(File.join(deployment_info["cloudfoundry_path"], "bin", "vcap"))
- puts "Cannot find vcap repo. Please specify the deployment name or the deployment home directory where to find the config file"
- exit 1
- end
-
- ruby_bin_dir = deployment_info["ruby_bin_dir"]
- maven_bin_dir = deployment_info["maven_bin_dir"]
- gemdir = `#{File.join(ruby_bin_dir, "gem")} environment gemdir`.split("\n")[0]
- ENV["PATH"] = "#{ruby_bin_dir}:#{File.join(gemdir, "bin")}:#{maven_bin_dir}:#{ENV["PATH"]}"
-
- if vcap_components["components"].include?("cloud_controller")
- puts "Setting up cloud controller environment"
- ENV["CLOUD_CONTROLLER_CONFIG"]=File.join(deployment_config_path, "cloud_controller.yml")
- ENV["RAILS_ENV"]="production"
-
- # Start the cloud controller component first as other components like the
- # health manager depend on it. Specifically, the health manager uses the same
- # database as the one used by the cloud controller.
- vcap_components["components"].unshift(vcap_components["components"].delete("cloud_controller"))
- end
-
- if vcap_components["components"].include?("uaa")
- puts "Setting up the uaa environment"
- ENV["CLOUD_CONTROLLER_CONFIG_PATH"]=deployment_config_path
- ENV["UAA_TOMCAT"]=File.join(deployment_config_path, "../deploy/uaa-tomcat/")
- end
-
- # Set both http_proxy and HTTP_PROXY
- %w(http_proxy https_proxy no_proxy).each do |var|
- ENV[var] = ENV[var] || ENV[var.upcase] unless ENV[var.upcase].nil?
- ENV[var.upcase] = ENV[var.upcase] || ENV[var] unless ENV[var].nil?
- end
-
- def exec_cmd(cmd)
- id = fork {
- puts "Executing #{cmd}"
- exec(cmd)
- }
- pid, status = Process.waitpid2(id)
- status.exitstatus
- end
-
- ruby_binary = File.join(ruby_bin_dir, "ruby")
- vcap_path = File.join(deployment_info["cloudfoundry_path"], "bin")
- vcap_launch = File.join(script_dir, "vcap")
-
- puts "Using cloudfoundry config from #{deployment_config_path}"
- exec_cmd("#{ruby_binary} #{vcap_launch} #{command} #{vcap_components["components"].join(" ")} -c #{deployment_config_path} -v #{vcap_path} -l #{deployment_info["deployment_log_path"]}")
复制代码
- #!/usr/bin/env ruby
- # Copyright (c) 2009-2011 VMware, Inc.
- #
- # Usage: bin/vcap_system [start|stop|restart|tail|status] [component ...]
- #
-
- require 'yaml'
- require 'fileutils'
- require 'optparse'
-
- require 'rubygems'
- require 'eventmachine'
- require 'nats/client'
-
- require File.expand_path(File.join("..", "lib", "vcap_components"), File.dirname(__FILE__))
-
- # This is used to bring up and down the NATS messaging server.
- class NatsServer
-
- attr_reader :was_running
- alias :was_running? :was_running
-
- class << self
- def kill_autostart_server
- pid ||= File.read(NATS::AUTOSTART_PID_FILE).chomp.to_i
- %x[kill -9 #{pid}] if pid
- %x[rm #{NATS::AUTOSTART_PID_FILE}]
- %x[rm #{NATS::AUTOSTART_LOG_FILE}]
- end
- end
-
- def initialize(uri=NATS::DEFAULT_URI, pid_file=NATS::AUTOSTART_PID_FILE)
- @uri = URI.parse(uri)
- @pid_file = pid_file
- end
-
- def server_pid
- @pid ||= File.read(@pid_file).chomp.to_i
- end
-
- def start_server
- if NATS.server_running? @uri
- @was_running = true
- return
- end
-
- nats_server = `which nats-server`
- unless $? == 0 && nats_server
- STDERR.puts "Could not locate a nats-server, exiting.".red
- exit 1
- end
- nats_server.chomp!
- `#{nats_server} -p #{@uri.port} -P #{@pid_file} -d 2> /dev/null`
- NATS.wait_for_server(@uri, 5)
- end
-
- def is_running?
- NATS.server_running? @uri
- end
-
- def kill_server
- if File.exists? @pid_file
- %x[kill -9 #{server_pid} 2> /dev/null]
- %x[rm #{@pid_file} 2> /dev/null]
- %x[rm #{NATS::AUTOSTART_LOG_FILE} 2> /dev/null]
- end
- end
- end
-
- module Run
- def self.start_init
- nats_server = NatsServer.new
- nats_server.start_server
- end
-
- def self.start(args)
- self.start_init
- components(args).each(&:start)
- end
-
- def self.stop_cleanup(args)
- # Only process this if no one else running..
- running_components = components(args).select {|c| c.running?}.map{|c| c.name }
- return unless running_components.empty?
- nats_server = NatsServer.new
- return unless nats_server.is_running?
- nats_server.kill_server
- end
-
- def self.stop(args)
- components(args).each(&:stop)
- self.stop_cleanup(args)
- end
-
- def self.restart(args)
- stop(args)
- start(args)
- end
-
- def self.reset(args)
- unless $noprompt
- exit unless ask_to_continue "#{'CAUTION'.red}: All data will be lost. Continue?"
- end
-
- # Try not to shoot ourselves in the foot..
- running_components = components([]).select {|c| c.running?}.map{|c| c.name }
- unless running_components.empty?
- puts "It looks like the following components are already running: #{running_components.join(', ')}"
- puts "Resetting VCAP now may produce unintended results."
- exit unless ask_to_continue
- puts
- end
-
- cc_dir = File.expand_path(File.join(DIR, '..', 'cloud_controller'))
- run_command("Resetting the CloudController database", "cd #{cc_dir} 2>&1 && bundle exec rake db:drop 2>&1")
- puts
-
- cc_log_dir = File.join(cc_dir, 'log')
- run_command("Clearing the CloudController logs", "cd #{cc_log_dir} 2>&1 && rm -f *.log 2>&1")
- puts
-
- # TODO - Need to connect to mysql and unprovision any orphaned DBs
- kill_service_instances('redis', /redis-server/)
- puts
- kill_service_instances('mongodb', /mongod/)
- puts
- kill_service_instances('neo4j', /neo4j/ )
- puts
-
- puts "Cleaning shared directories:"
- dirs = %w[/var/vcap/services /var/vcap/shared /var/vcap/sys /var/vcap.local/* /tmp/vcap-run]
- max_len = dirs.reduce(0) {|cur_max, dir| dir.length > cur_max ? dir.length : cur_max}
- dirs.each do |dir|
- run_command(" %-#{max_len}s" % [dir], "rm -rf #{dir}")
- end
- end
-
- # Make tail exits fast and clean
- def self.exit_tails
- unless @tpids.empty?
- kill = "kill -9 #{@tpids.join(' ')}"
- `#{kill} 2> /dev/null`
- end
- exit!
- end
-
- def self.tail(args)
- tail = 'tail'
-
- # Annoying when CTRL-C takes forever
- trap('TERM') { Run.exit_tails }
- trap('INT') { Run.exit_tails }
-
- if RUBY_PLATFORM =~ /linux/i
- # tail in coreutils 7.5 uses inotify
- tail_version = `tail --version | grep coreutils | head -n1`.slice(/\d+.\d+/)
- if tail_version < '7.5'
- path = `which inotail`
- tail = path.strip if $?.success?
- end
- end
-
- @tpids = []
- EM.run do
- components(args).map do |component|
- next unless component.running?
- args = [tail, '-f', component.log_file]
- tpid = EM.get_subprocess_pid( EM.popen(args.join(' '), Tail, component).signature )
- @tpids << tpid
- end
- if @tpids.empty?
- puts 'No tailable components currently running.'
- EM.stop
- end
- end
- end
-
- module Tail
- include EM::Protocols::LineText2
-
- def initialize(component)
- @component = component
- end
-
- def prefix
- "%s --> " % @component.name.rjust(15)
- end
-
- def receive_line(line)
- puts prefix + line
- if line.start_with?('F') # FATAL
- puts prefix + "fatal error, closing tail"
- close_connection_after_writing
- end
- end
-
- end
-
- def self.status(args)
- components(args).each(&:status)
- end
-
- private
-
- # type: all, core, service, service_tool, service_auxilary, and name of registered components, e.g. router, mysql_node ...
- def self.component_collection(type)
- return Component.getNamedComponents().keys if type == "all"
- return [type] if Component.getNamedComponents().include?(type)
- collection = []
- check_type_method_sym = "#{type}?".to_sym
- Component.getNamedComponents().each do |k, v|
- if v.method_defined?(check_type_method_sym) && v.new('foo').send(check_type_method_sym)
- collection << k
- end
- end
- collection
- end
-
- def self.alias_args(args)
- aliased = []
- args.each do |arg|
- case arg
- when 'mongo'
- aliased << 'mongodb'
- else
- collection = Run.component_collection(arg)
- if collection.empty?
- aliased << arg
- else
- aliased.concat collection
- end
- end
- end
- aliased
- end
-
- def self.expand_args(args)
- args = Run.alias_args(args)
- new_args = []
- args.each do |arg|
- if Component.getNamedComponents().keys.include? arg
- new_args << arg
- else # This is a service, expand in place here..
- new_args << "#{arg}_gateway"
- new_args << "#{arg}_node"
- end
- end
- new_args
- end
-
- def self.components(args)
- args = Component.getNamedComponents().keys if args.empty?
- args = Run.expand_args(args)
- components = args.map do |arg|
- component = Component.create(arg)
- if component.nil?
- STDOUT.puts "Skipping invalid component: #{arg}"
- else
- STDOUT.puts "Skipping excluded component: #{component.name}" if component.is_excluded?
- end
- component if (component && component.exists? && !component.is_excluded?)
- end.compact
- STDERR.puts "Don't know how to process '#{args.inspect}' \?\?" if components.empty?
- components
- end
-
- def self.pad_left(str, padlen=2)
- pad_str = " " * padlen
- str.split("\n") \
- .map {|line| pad_str + line } \
- .join("\n")
- end
-
- def self.run_command(desc, command)
- print desc
- output = `#{command}`
- if $? == 0
- puts " Ok".green
- else
- puts " Failed".red
- puts " '#{command}' exited with status '#{$?}', output:"
- puts pad_left(output, 2)
- end
- [$?, output]
- end
-
- def self.ask_to_continue(prompt="Would you like to continue?")
- should_continue = nil
- while should_continue == nil
- print prompt + " (y/N) "
- choice = STDIN.gets.chomp
- if choice.empty? || /^n/i.match(choice)
- should_continue = false
- elsif /^y/i.match(choice)
- should_continue = true
- end
- end
- should_continue
- end
-
- def self.find_processes(cmd_re)
- output = `ps ax -o pid= -o command=`
- ret = []
- output.split("\n").each do |line|
- pid, cmd = line.split(' ', 2)
- ret << {:pid => pid, :command => cmd} if cmd_re.match(cmd)
- end
- ret
- end
-
- def self.kill_service_instances(name, cmd_re)
- print "Checking for stray #{name} instances"
- instances = find_processes(cmd_re)
- puts " Ok".green
-
- return if instances.empty?
-
- puts "The following #{name} instances are running:"
- puts " PID COMMAND"
- instances.each do |instance|
- puts " %-6d %s" % [instance[:pid], instance[:command]]
- end
-
- if ask_to_continue("Would you like to kill them?")
- run_command("Killing instances", "kill -9 #{instances.map{|i| i[:pid]}.join(' ')}")
- end
- end
-
- end
-
- $config_dir ||= ENV['CLOUD_FOUNDRY_CONFIG_PATH']
- args = ARGV.dup
- opts_parser = OptionParser.new do |opts|
- opts.on('--port PORT') { |port| $port = port.to_i }
- opts.on('--configdir CONFIGDIR', '-c CONFIGDIR') { |dir| $config_dir = File.expand_path(dir.to_s) }
- opts.on('--config CONFIGDIR') { |dir| $config_dir = File.expand_path(dir.to_s) }
- opts.on('--vcapdir VCAP_DIR', '-v VCAP_DIR') { |dir| $vcap_dir = File.expand_path(dir.to_s) }
- opts.on('--logdir LOG_DIR', '-l LOG_DIR') { |dir| $log_dir = File.expand_path(dir.to_s) }
- opts.on('--no-color', '--nocolor', '--nc') { $nocolor = true }
- opts.on('--noprompt', '-n') { $noprompt = true }
- end
-
- excluded ||= ENV['CLOUD_FOUNDRY_EXCLUDED_COMPONENT'] || Component.getExcludedComponents().join('|')
- puts "- Excluded components: #{excluded}.\n See dev_setup/README for details" if !excluded.empty?
-
- args = opts_parser.parse!(args)
- ENV['CLOUD_FOUNDRY_CONFIG_PATH'] = $config_dir
-
- DIR = $vcap_dir || File.expand_path("../../../bin", __FILE__)
-
- SERVICE_DIR = File.join(DIR, '/services')
- unless $log_dir
- $log_dir = "/tmp/vcap-run"
- FileUtils.mkdir_p($log_dir)
- end
-
- $nocolor = true unless STDOUT.tty?
-
- if args.empty?
- STDERR.puts "Usage: #{$0} [start|stop|restart|tail|status] [COMPONENT] [--no-color] [--config CONFIGDIR]"
- else
- command = args.shift.downcase
- if Run.respond_to?(command)
- Run.send(command, args)
- else
- STDERR.puts "Don't know what to do with #{command.inspect}"
- end
- end
复制代码
添加系统服务
添加系统服务
复制代码
在最近安装的Cloud Foundry中,移除了vcap/bin/services目录(可能是service的借口发生了变化,我不清楚)
原先的文档(参考链接)中所提及的方法已经不可使用(还有值得借鉴的地方)
说先将echo文件夹添加到vcap/services/目录下,并添加执行权限。
根据上文描述,我们可以发现vcap_dev启动时是从下述文件获得的component信息
- ~/cloudfoundry/.deployment/devbox/config/vcap_components.json
复制代码
则仍然通过这里添加echo_node,echo_gateway服务,并在config目录下添加配置文件。
在vcap_component.json中添加echo_node和echo_gateway服务
现在启动一下cloud foundry
启动cloud foundry
这里会发现Skipping invalid component: echo_XXXXX
再回头看一下vcap的源码,从以下代码发现”Skipping invalid component”
- def self.components(args)
- args = Component.getNamedComponents().keys if args.empty?
- args = Run.expand_args(args)
- components = args.map do |arg|
- component = Component.create(arg)
- if component.nil?
- STDOUT.puts "Skipping invalid component: #{arg}"
- else
- STDOUT.puts "Skipping excluded component: #{component.name}" if component.is_excluded?
- end
- component if (component && component.exists? && !component.is_excluded?)
- end.compact
- STDERR.puts "Don't know how to process '#{args.inspect}' \?\?" if components.empty?
- components
- end
复制代码
component变量是由Component.create返回的,再看Component.create函数
- def self.create(name, configuration_file=nil)
- sub_class = @@named_components[name]
- if sub_class
- sub_class.new(name, configuration_file)
- else
- nil
- end
- end
复制代码
我们可以发现sub_class是由@@name_components[name]得出的,通过阅读代码可以发现@@name_components是在Component.register函数中定义的
- def self.register(name, excluded=nil)
- @@named_components[name] = self
- default_excluded=/#{DEFAULT_CLOUD_FOUNDRY_EXCLUDED_COMPONENT}/
- if excluded == true || (excluded.nil? && name =~ default_excluded)
- @@excluded << name
- end
- end
复制代码
当vcap中require了vcap_components.rb时,会执行该文件最下端的代码段
- # register valid named components
-
- ## core
- %w(router cloud_controller dea health_manager uaa acm).each do |core|
- CoreComponent.register(core)
- end
-
- ## services: gateways & nodes
- %w(redis mysql mongodb rabbitmq postgresql vblob neo4j memcached couchdb elasticsearch filesystem).each do |service|
- ServiceComponent.register("#{service}_gateway")
- end
-
- %w(redis mysql mongodb rabbitmq postgresql vblob neo4j memcached couchdb elasticsearch).each do |service|
- ServiceComponent.register("#{service}_node")
- end
-
- ## service auxiliary
- %w(service_broker).each do |auxiliary|
- ServiceAuxiliaryComponent.register(auxiliary)
- end
-
- ## service tools
- %w(backup_manager snapshot_manager).each do |tool|
- ServiceToolComponent.register(tool)
- end
复制代码
所以当我们只在vcap_component.json中添加echo_node和echo_gateway时,vcap_dev可以识别出来,但是传入vcap时,会被认为是没有注册的component而不会处理,所以添加系统服务时同样需要修改vcap_components.rb
在vcap_components.rb中注册echo_node和echo_gateway服务
根据之前的文档,还需要向cloudcontroller中添加token配置,并向/cloudfoundry/vcap/services/tools/misc/bin/nuke_service.rb中添加配置文件路径(让cloud foundry知道系统提供了echo服务),最后安装echo的依赖库,具体参考之前的文档。
这次再次运行cloud foundry
再次运行cloud foundry
终于运行成功了,使用VMC工具看看cloud foundry是否显示了echo服务
已经在services中显示echo服务了
至此,echo服务算是添加成功了。
|