分享

CLOUD FOUNDRY启动流程及为其添加系统服务

本帖最后由 xioaxu790 于 2014-6-20 08:49 编辑
问题导读:

1、如何启动通过dev_setup安装的Cloud Foundry?
2、如何添加系统服务?
3、怎样查看echo服务是否成功?




前提
已经正确使用dev_setup安装Cloud Foundry
large_iyOc_6a5e000029d1118e1.jpg
dev_setup安装完毕时提示的部署成功的信息


启动流程
流程分析
vcap_dev源码


vcap源码
通过dev_setup安装的Cloud Foundry可以通过以下代码启动
  1. ~/cloudfoundry/vcap/dev_setup/bin/vcap_dev start
复制代码

vcap_dev会从以下代码处获得Cloud Foundry的各个组件和部署信息
  1. vcap_components = JSON.parse(File.read(Deployment.get_vcap_config_file(deployment_config_path)))
  2. deployment_info = JSON.parse(File.read(Deployment.get_deployment_info_file(deployment_config_path)))
复制代码

具体的文件路径可以从dev_setup/lib下的vcap_defs.rb下查看,其中
vcap_components默认的获取文件路径是
  1. ~/cloudfoundry/.deployment/devbox/config/vcap_components.json
复制代码

deployment_info默认的获取文件路径是
  1. ~/cloudfoundry/.deployment/devbox/config/deployment_info.json
复制代码

经过各种处理之后,将信息送入vcap进行处理
  1. 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)
  1. exec("#{component_start_path}")
复制代码

例如:
启动vblob_gateway时,执行
  1. /root/cloudfoundry/vcap/bin/../services/vblob/bin/vblob_gateway -c /root/cloudfoundry/.deployments/devbox/config/vblob_gateway.yml
复制代码
  1. #!/usr/bin/env ruby
  2. require 'rubygems'
  3. require 'erb'
  4. require 'json'
  5. require 'fileutils'
  6. require 'optparse'
  7. require File.expand_path(File.join("..", "lib", "vcap_defs"), File.dirname(__FILE__))
  8. script_dir = File.expand_path(File.dirname(__FILE__))
  9. # TODO: Once we integrate better with bin/vcap, this option parsing should
  10. # become more robust
  11. options = {}
  12. opts = OptionParser.new do |opts|
  13.   opts.banner = "Usage: #{$0} [--name deployment_name] [--dir cloudfoundry_home_dir] [start|stop|restart|tail|status]"
  14.   opts.on("-n", "--name deployment_name", "Name of the deployment") do |n|
  15.     options["name"] = n
  16.   end
  17.   opts.on("-d", "--dir cloud_foundry_home_dir", "Cloud foundry home directory") do |n|
  18.     options["home"] = n
  19.   end
  20. end
  21. opts.order!(ARGV)
  22. options["name"], options["home"] = Deployment.get_deployment_target if options.empty?
  23. if options["name"]
  24.   puts "Targeting deployment "#{options["name"]}" with cloudfoundry home "#{options["home"]}""
  25. else
  26.   options["name"] = DEPLOYMENT_DEFAULT_NAME
  27.   puts "Targeting default deployment "#{options["name"]}""
  28. end
  29. command = ARGV[0] ? ARGV.shift.downcase : nil
  30. if command.nil? || !%w[start stop restart tail status].include?(command)
  31.   STDERR.puts "Usage: #{$0} [-n deployment_name] [-d cloudfoundry_home_dir] [start|stop|restart|tail|status] [COMPONENT]"
  32.     exit 1
  33. end
  34. deployment_config_path = Deployment.get_config_path(options["name"], options["home"])
  35. begin
  36.   vcap_components = JSON.parse(File.read(Deployment.get_vcap_config_file(deployment_config_path)))
  37.   deployment_info = JSON.parse(File.read(Deployment.get_deployment_info_file(deployment_config_path)))
  38. rescue => e
  39.   STDERR.puts "#{e.inspect}. Could not parse deployment config files . Please check your deployment."
  40.   exit 1
  41. end
  42. # select only those components that are on the command line (if any)
  43. if !ARGV.empty? and (vcap_components["components"] & ARGV).empty?
  44.   STDERR.puts "The component(s) specified on the command line are unknown.\nPlease specify one or more of (#{vcap_components['components'].join(' ')})"
  45.   exit 1
  46. end
  47. vcap_components["components"] &= ARGV unless ARGV.empty?
  48. unless File.exists?(File.join(deployment_info["cloudfoundry_path"], "bin", "vcap"))
  49.   puts "Cannot find vcap repo. Please specify the deployment name or the deployment home directory where to find the config file"
  50.   exit 1
  51. end
  52. ruby_bin_dir = deployment_info["ruby_bin_dir"]
  53. maven_bin_dir = deployment_info["maven_bin_dir"]
  54. gemdir = `#{File.join(ruby_bin_dir, "gem")} environment gemdir`.split("\n")[0]
  55. ENV["PATH"] = "#{ruby_bin_dir}:#{File.join(gemdir, "bin")}:#{maven_bin_dir}:#{ENV["PATH"]}"
  56. if vcap_components["components"].include?("cloud_controller")
  57.   puts "Setting up cloud controller environment"
  58.   ENV["CLOUD_CONTROLLER_CONFIG"]=File.join(deployment_config_path, "cloud_controller.yml")
  59.   ENV["RAILS_ENV"]="production"
  60.   # Start the cloud controller component first as other components like the
  61.   # health manager depend on it. Specifically, the health manager uses the same
  62.   # database as the one used by the cloud controller.
  63.   vcap_components["components"].unshift(vcap_components["components"].delete("cloud_controller"))
  64. end
  65. if vcap_components["components"].include?("uaa")
  66.   puts "Setting up the uaa environment"
  67.   ENV["CLOUD_CONTROLLER_CONFIG_PATH"]=deployment_config_path
  68.   ENV["UAA_TOMCAT"]=File.join(deployment_config_path, "../deploy/uaa-tomcat/")
  69. end
  70. # Set both http_proxy and HTTP_PROXY
  71. %w(http_proxy https_proxy no_proxy).each do |var|
  72.   ENV[var] = ENV[var] || ENV[var.upcase] unless ENV[var.upcase].nil?
  73.   ENV[var.upcase] = ENV[var.upcase] || ENV[var] unless ENV[var].nil?
  74. end
  75. def exec_cmd(cmd)
  76.   id = fork {
  77.     puts "Executing #{cmd}"
  78.     exec(cmd)
  79.   }
  80.   pid, status = Process.waitpid2(id)
  81.   status.exitstatus
  82. end
  83. ruby_binary = File.join(ruby_bin_dir, "ruby")
  84. vcap_path = File.join(deployment_info["cloudfoundry_path"], "bin")
  85. vcap_launch = File.join(script_dir, "vcap")
  86. puts "Using cloudfoundry config from #{deployment_config_path}"
  87. exec_cmd("#{ruby_binary} #{vcap_launch} #{command} #{vcap_components["components"].join(" ")} -c #{deployment_config_path} -v #{vcap_path} -l #{deployment_info["deployment_log_path"]}")
复制代码
  1. #!/usr/bin/env ruby
  2. # Copyright (c) 2009-2011 VMware, Inc.
  3. #
  4. # Usage: bin/vcap_system [start|stop|restart|tail|status] [component ...]
  5. #
  6. require 'yaml'
  7. require 'fileutils'
  8. require 'optparse'
  9. require 'rubygems'
  10. require 'eventmachine'
  11. require 'nats/client'
  12. require File.expand_path(File.join("..", "lib", "vcap_components"), File.dirname(__FILE__))
  13. # This is used to bring up and down the NATS messaging server.
  14. class NatsServer
  15.   attr_reader :was_running
  16.   alias :was_running? :was_running
  17.   class << self
  18.     def kill_autostart_server
  19.       pid ||= File.read(NATS::AUTOSTART_PID_FILE).chomp.to_i
  20.       %x[kill -9 #{pid}] if pid
  21.       %x[rm #{NATS::AUTOSTART_PID_FILE}]
  22.       %x[rm #{NATS::AUTOSTART_LOG_FILE}]
  23.     end
  24.   end
  25.   def initialize(uri=NATS::DEFAULT_URI, pid_file=NATS::AUTOSTART_PID_FILE)
  26.     @uri = URI.parse(uri)
  27.     @pid_file = pid_file
  28.   end
  29.   def server_pid
  30.     @pid ||= File.read(@pid_file).chomp.to_i
  31.   end
  32.   def start_server
  33.     if NATS.server_running? @uri
  34.       @was_running = true
  35.       return
  36.     end
  37.     nats_server = `which nats-server`
  38.     unless $? == 0 && nats_server
  39.       STDERR.puts "Could not locate a nats-server, exiting.".red
  40.       exit 1
  41.     end
  42.     nats_server.chomp!
  43.     `#{nats_server} -p #{@uri.port} -P #{@pid_file} -d 2> /dev/null`
  44.     NATS.wait_for_server(@uri, 5)
  45.   end
  46.   def is_running?
  47.     NATS.server_running? @uri
  48.   end
  49.   def kill_server
  50.     if File.exists? @pid_file
  51.       %x[kill -9 #{server_pid} 2> /dev/null]
  52.       %x[rm #{@pid_file} 2> /dev/null]
  53.       %x[rm #{NATS::AUTOSTART_LOG_FILE} 2> /dev/null]
  54.     end
  55.   end
  56. end
  57. module Run
  58.   def self.start_init
  59.     nats_server = NatsServer.new
  60.     nats_server.start_server
  61.   end
  62.   def self.start(args)
  63.     self.start_init
  64.     components(args).each(&:start)
  65.   end
  66.   def self.stop_cleanup(args)
  67.     # Only process this if no one else running..
  68.     running_components = components(args).select {|c| c.running?}.map{|c| c.name }
  69.     return unless running_components.empty?
  70.     nats_server = NatsServer.new
  71.     return unless nats_server.is_running?
  72.     nats_server.kill_server
  73.   end
  74.   def self.stop(args)
  75.     components(args).each(&:stop)
  76.     self.stop_cleanup(args)
  77.   end
  78.   def self.restart(args)
  79.     stop(args)
  80.     start(args)
  81.   end
  82.   def self.reset(args)
  83.     unless $noprompt
  84.       exit unless ask_to_continue "#{'CAUTION'.red}: All data will be lost. Continue?"
  85.     end
  86.     # Try not to shoot ourselves in the foot..
  87.     running_components = components([]).select {|c| c.running?}.map{|c| c.name }
  88.     unless running_components.empty?
  89.       puts "It looks like the following components are already running: #{running_components.join(', ')}"
  90.       puts "Resetting VCAP now may produce unintended results."
  91.       exit unless ask_to_continue
  92.       puts
  93.     end
  94.     cc_dir = File.expand_path(File.join(DIR, '..', 'cloud_controller'))
  95.     run_command("Resetting the CloudController database", "cd #{cc_dir} 2>&1 && bundle exec rake db:drop 2>&1")
  96.     puts
  97.     cc_log_dir = File.join(cc_dir, 'log')
  98.     run_command("Clearing the CloudController logs", "cd #{cc_log_dir} 2>&1 && rm -f *.log 2>&1")
  99.     puts
  100.     # TODO - Need to connect to mysql and unprovision any orphaned DBs
  101.     kill_service_instances('redis', /redis-server/)
  102.     puts
  103.     kill_service_instances('mongodb', /mongod/)
  104.     puts
  105.     kill_service_instances('neo4j', /neo4j/ )
  106.     puts
  107.     puts "Cleaning shared directories:"
  108.     dirs = %w[/var/vcap/services /var/vcap/shared /var/vcap/sys /var/vcap.local/* /tmp/vcap-run]
  109.     max_len = dirs.reduce(0) {|cur_max, dir| dir.length > cur_max ? dir.length : cur_max}
  110.     dirs.each do |dir|
  111.         run_command("  %-#{max_len}s" % [dir], "rm -rf #{dir}")
  112.     end
  113.   end
  114.   # Make tail exits fast and clean
  115.   def self.exit_tails
  116.     unless @tpids.empty?
  117.       kill = "kill -9 #{@tpids.join(' ')}"
  118.       `#{kill} 2> /dev/null`
  119.     end
  120.     exit!
  121.   end
  122.   def self.tail(args)
  123.     tail = 'tail'
  124.     # Annoying when CTRL-C takes forever
  125.     trap('TERM') { Run.exit_tails }
  126.     trap('INT')  { Run.exit_tails }
  127.     if RUBY_PLATFORM =~ /linux/i
  128.       # tail in coreutils 7.5 uses inotify
  129.       tail_version = `tail --version | grep coreutils | head -n1`.slice(/\d+.\d+/)
  130.       if tail_version < '7.5'
  131.         path = `which inotail`
  132.         tail = path.strip if $?.success?
  133.       end
  134.     end
  135.     @tpids = []
  136.     EM.run do
  137.       components(args).map do |component|
  138.         next unless component.running?
  139.         args = [tail, '-f', component.log_file]
  140.         tpid = EM.get_subprocess_pid( EM.popen(args.join(' '), Tail, component).signature )
  141.         @tpids << tpid
  142.       end
  143.       if @tpids.empty?
  144.         puts 'No tailable components currently running.'
  145.         EM.stop
  146.       end
  147.     end
  148.   end
  149.   module Tail
  150.     include EM::Protocols::LineText2
  151.     def initialize(component)
  152.       @component = component
  153.     end
  154.     def prefix
  155.       "%s --> " % @component.name.rjust(15)
  156.     end
  157.     def receive_line(line)
  158.       puts prefix + line
  159.       if line.start_with?('F') # FATAL
  160.         puts prefix + "fatal error, closing tail"
  161.         close_connection_after_writing
  162.       end
  163.     end
  164.   end
  165.   def self.status(args)
  166.     components(args).each(&:status)
  167.   end
  168.   private
  169.   # type: all, core, service, service_tool, service_auxilary, and name of registered components, e.g. router, mysql_node ...
  170.   def self.component_collection(type)
  171.     return Component.getNamedComponents().keys if type == "all"
  172.     return [type] if Component.getNamedComponents().include?(type)
  173.     collection = []
  174.     check_type_method_sym = "#{type}?".to_sym
  175.     Component.getNamedComponents().each do |k, v|
  176.       if v.method_defined?(check_type_method_sym) && v.new('foo').send(check_type_method_sym)
  177.         collection << k
  178.       end
  179.     end
  180.     collection
  181.   end
  182.   def self.alias_args(args)
  183.     aliased = []
  184.     args.each do |arg|
  185.       case arg
  186.       when 'mongo'
  187.         aliased << 'mongodb'
  188.       else
  189.         collection = Run.component_collection(arg)
  190.         if collection.empty?
  191.           aliased << arg
  192.         else
  193.           aliased.concat collection
  194.         end
  195.       end
  196.     end
  197.     aliased
  198.   end
  199.   def self.expand_args(args)
  200.     args = Run.alias_args(args)
  201.     new_args = []
  202.     args.each do |arg|
  203.       if Component.getNamedComponents().keys.include? arg
  204.         new_args << arg
  205.       else # This is a service, expand in place here..
  206.         new_args << "#{arg}_gateway"
  207.         new_args << "#{arg}_node"
  208.       end
  209.     end
  210.     new_args
  211.   end
  212.   def self.components(args)
  213.     args = Component.getNamedComponents().keys if args.empty?
  214.     args = Run.expand_args(args)
  215.     components = args.map do |arg|
  216.       component = Component.create(arg)
  217.       if component.nil?
  218.         STDOUT.puts "Skipping invalid component: #{arg}"
  219.       else
  220.         STDOUT.puts "Skipping excluded component: #{component.name}" if component.is_excluded?
  221.       end
  222.       component if (component && component.exists? && !component.is_excluded?)
  223.     end.compact
  224.     STDERR.puts "Don't know how to process '#{args.inspect}' \?\?" if components.empty?
  225.     components
  226.   end
  227.   def self.pad_left(str, padlen=2)
  228.     pad_str = " " * padlen
  229.     str.split("\n")                   \
  230.        .map {|line| pad_str + line }  \
  231.        .join("\n")
  232.   end
  233.   def self.run_command(desc, command)
  234.     print desc
  235.     output = `#{command}`
  236.     if $? == 0
  237.       puts " Ok".green
  238.     else
  239.       puts " Failed".red
  240.       puts "  '#{command}' exited with status '#{$?}', output:"
  241.       puts pad_left(output, 2)
  242.     end
  243.     [$?, output]
  244.   end
  245.   def self.ask_to_continue(prompt="Would you like to continue?")
  246.     should_continue = nil
  247.     while should_continue == nil
  248.       print prompt + " (y/N) "
  249.       choice = STDIN.gets.chomp
  250.       if choice.empty? || /^n/i.match(choice)
  251.         should_continue = false
  252.       elsif /^y/i.match(choice)
  253.         should_continue = true
  254.       end
  255.     end
  256.     should_continue
  257.   end
  258.   def self.find_processes(cmd_re)
  259.     output = `ps ax -o pid= -o command=`
  260.     ret = []
  261.     output.split("\n").each do |line|
  262.       pid, cmd = line.split(' ', 2)
  263.       ret << {:pid => pid, :command => cmd} if cmd_re.match(cmd)
  264.     end
  265.     ret
  266.   end
  267.   def self.kill_service_instances(name, cmd_re)
  268.     print "Checking for stray #{name} instances"
  269.     instances = find_processes(cmd_re)
  270.     puts " Ok".green
  271.     return if instances.empty?
  272.     puts "The following #{name} instances are running:"
  273.     puts "  PID    COMMAND"
  274.     instances.each do |instance|
  275.       puts "  %-6d %s" %  [instance[:pid], instance[:command]]
  276.     end
  277.     if ask_to_continue("Would you like to kill them?")
  278.       run_command("Killing instances", "kill -9 #{instances.map{|i| i[:pid]}.join(' ')}")
  279.     end
  280.   end
  281. end
  282. $config_dir ||= ENV['CLOUD_FOUNDRY_CONFIG_PATH']
  283. args = ARGV.dup
  284. opts_parser = OptionParser.new do |opts|
  285.   opts.on('--port PORT')                           { |port| $port = port.to_i }
  286.   opts.on('--configdir CONFIGDIR', '-c CONFIGDIR') { |dir| $config_dir = File.expand_path(dir.to_s) }
  287.   opts.on('--config CONFIGDIR')                    { |dir| $config_dir = File.expand_path(dir.to_s) }
  288.   opts.on('--vcapdir VCAP_DIR', '-v VCAP_DIR')     { |dir| $vcap_dir = File.expand_path(dir.to_s) }
  289.   opts.on('--logdir LOG_DIR', '-l LOG_DIR')        { |dir| $log_dir = File.expand_path(dir.to_s) }
  290.   opts.on('--no-color', '--nocolor', '--nc')       { $nocolor = true }
  291.   opts.on('--noprompt', '-n')                      { $noprompt = true }
  292. end
  293. excluded ||= ENV['CLOUD_FOUNDRY_EXCLUDED_COMPONENT'] || Component.getExcludedComponents().join('|')
  294. puts "- Excluded components: #{excluded}.\n  See dev_setup/README for details" if !excluded.empty?
  295. args = opts_parser.parse!(args)
  296. ENV['CLOUD_FOUNDRY_CONFIG_PATH'] = $config_dir
  297. DIR = $vcap_dir || File.expand_path("../../../bin", __FILE__)
  298. SERVICE_DIR = File.join(DIR, '/services')
  299. unless $log_dir
  300.   $log_dir = "/tmp/vcap-run"
  301.   FileUtils.mkdir_p($log_dir)
  302. end
  303. $nocolor = true unless STDOUT.tty?
  304. if args.empty?
  305.   STDERR.puts "Usage: #{$0} [start|stop|restart|tail|status] [COMPONENT] [--no-color] [--config CONFIGDIR]"
  306. else
  307.   command = args.shift.downcase
  308.   if Run.respond_to?(command)
  309.     Run.send(command, args)
  310.   else
  311.     STDERR.puts "Don't know what to do with #{command.inspect}"
  312.   end
  313. end
复制代码


添加系统服务
添加系统服务
  1. vcap_components.rb
复制代码

在最近安装的Cloud Foundry中,移除了vcap/bin/services目录(可能是service的借口发生了变化,我不清楚)
原先的文档(参考链接)中所提及的方法已经不可使用(还有值得借鉴的地方)
说先将echo文件夹添加到vcap/services/目录下,并添加执行权限。
根据上文描述,我们可以发现vcap_dev启动时是从下述文件获得的component信息
  1. ~/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”
  1. def self.components(args)
  2.     args = Component.getNamedComponents().keys if args.empty?
  3.     args = Run.expand_args(args)
  4.     components = args.map do |arg|
  5.       component = Component.create(arg)
  6.       if component.nil?
  7.         STDOUT.puts "Skipping invalid component: #{arg}"
  8.       else
  9.         STDOUT.puts "Skipping excluded component: #{component.name}" if component.is_excluded?
  10.       end
  11.       component if (component && component.exists? && !component.is_excluded?)
  12.     end.compact
  13.     STDERR.puts "Don't know how to process '#{args.inspect}' \?\?" if components.empty?
  14.     components
  15.   end
复制代码

component变量是由Component.create返回的,再看Component.create函数
  1. def self.create(name, configuration_file=nil)
  2.     sub_class = @@named_components[name]
  3.     if sub_class
  4.       sub_class.new(name, configuration_file)
  5.     else
  6.       nil
  7.     end
  8.   end
复制代码


我们可以发现sub_class是由@@name_components[name]得出的,通过阅读代码可以发现@@name_components是在Component.register函数中定义的
  1. def self.register(name, excluded=nil)
  2.     @@named_components[name] = self
  3.     default_excluded=/#{DEFAULT_CLOUD_FOUNDRY_EXCLUDED_COMPONENT}/
  4.     if excluded == true || (excluded.nil? && name =~ default_excluded)
  5.       @@excluded << name
  6.     end
  7.   end
复制代码

当vcap中require了vcap_components.rb时,会执行该文件最下端的代码段
  1. # register valid named components
  2. ## core
  3. %w(router cloud_controller dea health_manager uaa acm).each do |core|
  4.    CoreComponent.register(core)
  5. end
  6. ## services: gateways & nodes
  7. %w(redis mysql mongodb rabbitmq postgresql vblob neo4j memcached couchdb elasticsearch filesystem).each do |service|
  8.   ServiceComponent.register("#{service}_gateway")
  9. end
  10. %w(redis mysql mongodb rabbitmq postgresql vblob neo4j memcached couchdb elasticsearch).each do |service|
  11. ServiceComponent.register("#{service}_node")
  12. end
  13. ## service auxiliary
  14. %w(service_broker).each do |auxiliary|
  15. ServiceAuxiliaryComponent.register(auxiliary)
  16. end
  17. ## service tools
  18. %w(backup_manager snapshot_manager).each do |tool|
  19.   ServiceToolComponent.register(tool)
  20. end
复制代码

所以当我们只在vcap_component.json中添加echo_node和echo_gateway时,vcap_dev可以识别出来,但是传入vcap时,会被认为是没有注册的component而不会处理,所以添加系统服务时同样需要修改vcap_components.rb
11.jpg
在vcap_components.rb中注册echo_node和echo_gateway服务


根据之前的文档,还需要向cloudcontroller中添加token配置,并向/cloudfoundry/vcap/services/tools/misc/bin/nuke_service.rb中添加配置文件路径(让cloud foundry知道系统提供了echo服务),最后安装echo的依赖库,具体参考之前的文档。
这次再次运行cloud foundry
22.jpg
再次运行cloud foundry


终于运行成功了,使用VMC工具看看cloud foundry是否显示了echo服务
33.jpg
已经在services中显示echo服务了
至此,echo服务算是添加成功了。

已有(1)人评论

跳转到指定楼层
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条