マスタリング DEA/NG 第2版

岩嵜 雄大
NTT Software Innovation Center
2012-09-13


                 NTT Software Innovation Center
はじめに




              情報はすべて資料作成当時のものです
              – 更新が頻繁なため情報がすぐに古くなります
              – 必ず最新のソースコードを確認してください




2012-09-13           NTT Software Innovation Center   2
今日の内容

              そもそもDEAとは
              – 簡単な紹介


              DEA_NG起動から、Warden上でのアプリ
               ケーション実行までを追う
              – エントリーポイントからインスタンス起動まで


              Promiseパターンについて
              – DEA_NGに頻出するPromiseとは何者か



2012-09-13              NTT Software Innovation Center   3
そもそもDEAとは




2012-09-13    NTT Software Innovation Center   4
DEAとは
  ユーザのアプリケーションを実行するためのコン
   ポーネント
      – Dropletと呼ばれる形式のアプリケーションを実行する
      – DEAを増やすことでCloud Foundryの実行能力はスケー
        ルする
                   App
                                                     App
                   App
                                                     App
                   App
                                                     App
             App   App
                                              App    App
             App
                   DEA
             App                               App   DEA
             App                               App
                                              App
             DEA
                                              DEA

2012-09-13         NTT Software Innovation Center          5
DEAがやること


              Dropletのダウンロード

              ランタイム(Java, Ruby, etc)の管理

              アプリケーション隔離用コンテナの管理
              – コンテナには独自の「Warden」を使用

              アプリケーションの管理・死活監視




2012-09-13             NTT Software Innovation Center   6
DEAサーバの起動から
             アプリケーションの起動まで




2012-09-13      NTT Software Innovation Center   7
全体的な概念図(今日話す範囲)

                       DEA
                  Instance
                     Instance
                        Instance
                           Instance
                                                     1対1対応で生成や起動などを担当
                                                     通信はWarden Protocol
                     生成
CCからの命令
                       NATS
基本的には
イベントドリブン                                               App App App App App
                     DEAプロセスの
                     セットアップ                       ContainerContainerContainer
                                                      ContainerContainer

        bin/dea       BootStrap                             Warden

                                     VM
       ※他にもDroplet RegistryやDirectory Serverなどがサブモジュールがある
2012-09-13                 NTT Software Innovation Center                       8
エントリーポイント

              /bin/dea

               bootstrap = Dea::Bootstrap.new(config)
                EM.run do
                  bootstrap.setup
                  bootstrap.start
                end


              – コマンドライン引数のパース
              – DEAプロセスをキック


2012-09-13                 NTT Software Innovation Center   9
DEA serverの起動
  /lib/dea/bootstrap.rb
      – プロセスの初期セットアップ (setup_*)
      – NATSクライアントの起動
         • あとはNATSまかせ
             def start
               start_component
               start_nats
               start_directory_server
               start_finish
             end
             …
             def setup_nats
               @nats = Dea::Nats.new(self, config)
             end
2012-09-13               NTT Software Innovation Center   10
NATSに対するディスパッチャーの登録

  /lib/dea/nats.rb
      – アプリケーションインスタンスの起動イベント

   def start
   …
     subscribe("dea.#{bootstrap.uuid}.start") do |message|
       bootstrap.handle_dea_directed_start(message)
     end
   …
   end

   def subscribe(subject)
     sid = client.subscribe(subject) do |raw_data, respond_to|
       message = Message.decode(self, subject, raw_data, respond_to)
       logger.debug "Received on #{subject.inspect}: #{message.data.inspect}"
       yield message
     end

     @sids[subject] = sid
   end

2012-09-13                    NTT Software Innovation Center                    11
Bootstrap側のハンドラ


  DEA::Instance オブジェクトを生成
      – オブジェクトはレジストリに登録する

 def handle_dea_directed_start(message)
   instance = create_instance(message.data)
 …
   instance.start
 end
 …
 def create_instance(attributes)
   instance = Instance.new(self, Instance.translate_attributes(attributes))

   instance.on(Instance::Transition.new(:born, :starting)) do
     instance_registry.register(instance)
   end
 … (他のインスタンス状態変化トリガ)
   instance
 end

2012-09-13                   NTT Software Innovation Center                   12
インスタンスの起動
  /lib/dea/instance.rb
        – Promiseについては後述
  def start(&callback)
  …
        if !droplet.droplet_exist?
          logger.info("Starting droplet download")

  …
          promise_droplet_download.resolve
                                                                      ドロップレット
        end
                                                                      をダウンロード
        promise_container = Promise.new do |p|
          promise_create_container.resolve
          promise_setup_network.resolve
          promise_limit_disk.resolve
          promise_limit_memory.resolve                              コンテナの準備
          p.deliver
        end
  …
        promise_extract_droplet.resolve
        promise_prepare_start_script.resolve
                                                                    インスタンスの
  …
        promise_start.resolve
                                                                       起動
  end

2012-09-13                         NTT Software Innovation Center             13
コンテナの生成設定

  DEAホストのDropletとランタイムをコンテナ内
   から見えるように追加のマウント情報を設定

def promise_create_container
…
    connection = promise_warden_connection(:app).resolve

     # Droplet and runtime
     bind_mounts = [droplet.droplet_dirname, runtime.dirname].map do |path|
       bind_mount = ::Warden::Protocol::CreateRequest::BindMount.new
       bind_mount.src_path = path
       bind_mount.dst_path = path
       bind_mount.mode = ::Warden::Protocol::CreateRequest::BindMount::Mode::RO
       bind_mount
     end

…次ページに続く



2012-09-13                    NTT Software Innovation Center                  14
コンテナの生成設定


  ライブラリ類もマウントとして追加


 …前ページから
    # Extra mounts (these typically include libs like pq, mysql, etc)
    bootstrap.config["bind_mounts"].each do |bm|
      bind_mount = ::Warden::Protocol::CreateRequest::BindMount.new

             bind_mount.src_path = bm["src_path"]
             bind_mount.dst_path = bm["dst_path"] || bm["src_path"]

             mode = bm["mode"] || "ro"
             bind_mount.mode = BIND_MOUNT_MODE_MAP[mode]

      bind_mounts << bind_mount
    end
 …次ページへ


2012-09-13                         NTT Software Innovation Center       15
アプリケーションインスタンスの実行
  Warden Protocolによりコンテナを起動
 …前ページから
    create_request = ::Warden::Protocol::CreateRequest.new
    create_request.bind_mounts = bind_mounts

        response =promise_warden_call
                         (connection, create_request).resolve
 …
   end
 end

  コンテナ起動後にメモリとディスクの制限を行う
      – promise_setup_network
      – promise_limit_disk
      – promise_limit_memory

2012-09-13                NTT Software Innovation Center        16
【寄り道】      promise_warden_callで何が起きているのか

  connectionは EM::Warden::Client::Connection
  def promise_warden_call(connection, request)
       Promise.new do |p|
         logger.debug2(request.inspect)
         connection.call(request) do |result|
           logger.debug2(result.inspect)
            …
           if error
              logger.warn "Request failed: #{request.inspect}"
              logger.log_exception(error)

              p.fail(error)
            else
              p.deliver(response)
            end
          end
         …
   end

2012-09-13                 NTT Software Innovation Center        17
【寄り道】      Warden Clientの話

  Warden本体については「すごく分かるWarden」
      – http://www.slideshare.net/i_yudai/warden


  3つのライブラリ
      – warden-client
         • Unix Socketによる(簡易な)実装
         • ライブラリ的なものも含む
      – em-warden-client
         • EventMachineを使用した実装
         • warden-clientに依存
      – warden-protocol
         • サーバクライアント間の通信に使うクラスのセット
2012-09-13             NTT Software Innovation Center   18
【寄り道】      Warden Protocol
  Wardenに対する各種命令
        •    ping              •   run
        •    create            •   info
        •    stop              •   LimitDisk
        •    spawn             •   LimitMemory
        •    link              •   net_in
        •    stream            •   net_out

  コマンドラインコマンドとほぼ同等
ping                          - ping warden
create [OPTION OPTION ...]    - create container, optionally pass options.
destroy <handle>              - shutdown container <handle>
stop <handle>                 - stop all processes in <handle>
spawn <handle> cmd            - spawns cmd inside container <handle>, returns #jobid
link <handle> #jobid          - do blocking read on results from #jobid
stream <handle> #jobid        - do blocking stream on results from #jobid
run <handle> cmd              - short hand for stream(spawn(cmd)) i.e. spawns cmd, streams the result
list                          - list containers
info <handle>                 - show metadata for container <handle>
limit <handle> mem [<value>] - set or get the memory limit for the container (in bytes)
net <handle> #in              - forward port #in on external interface to container <handle>
net <handle> #out <address[/mask][:port]> - allow traffic from the container <handle> to address
<address>

2012-09-13                             NTT Software Innovation Center                                   19
【寄り道】      EM::Warden::FiberAwareClient
  /em-warden-client/lib/em/warden/client.rb
      – create()メソッドは存在しない
         • method_missing()からcall()が呼ばれる

  def call(*args, &blk)
    …
    f = Fiber.current
    @connection.call(*args) {|res| f.resume(res) }
    result = Fiber.yield

    result.get     EventMachine::Warden::Client::Connection
  end

  def method_missing(method, *args, &blk)
    call(method, *args, &blk)
  end

2012-09-13               NTT Software Innovation Center   20
【寄り道】      EM::Warden::FiberAwareClient
  /em-warden-client/lib/em/warden/client.rb
      – create()メソッドは存在しない
         • method_missing()からcall()が呼ばれる

  def call(*args, &blk)
    …
                使わなくなりました
    f = Fiber.current
    @connection.call(*args) {|res| f.resume(res) }
    result = Fiber.yield

    result.get     EventMachine::Warden::Client::Connection
  end

  def method_missing(method, *args, &blk)
    call(method, *args, &blk)
  end

2012-09-13               NTT Software Innovation Center   21
【寄り道】      EventMachine::Warden::Client::Connection
  /em-warden-
   client/lib/em/warden/client/connection.rb
      – EM::Connectionを継承

def call(*args, &blk)
  if args.first.kind_of?(::Warden::Protocol::BaseRequest)
    request = args.first              Protocolオブジェクトが
  else                                   渡されている場合
    …
    request = ::Warden::Client::V1.request_from_v1(args.dup)
    @v1mode = true
                               argsに”create”が渡されていれば
  end
                             Warden::Protocol::CreateRequest
   @request_queue << { :request => request, :callback => blk }

  process_queue
end
2012-09-13               NTT Software Innovation Center          22
【寄り道】      コンテナ生成時におけるマウントの追加
  /warden/warden/lib/warden/container/linux.rb
      – フックにマウントの命令を追加する
   def write_bind_mount_commands(request)
     return if request.bind_mounts.nil? || request.bind_mounts.empty?

     File.open(File.join(container_path, "hook-parent-before-clone.sh"), "a") do |file|
       …
       request.bind_mounts.each do |bind_mount|
         src_path = bind_mount.src_path
         dst_path = bind_mount.dst_path

         # Fix up destination path to be an absolute path inside the union
         dst_path = File.join(container_path, "union", dst_path[1..-1])

         mode = case bind_mount.mode
                when Protocol::CreateRequest::BindMount::Mode::RO
                  "ro"
                when Protocol::CreateRequest::BindMount::Mode::RW
                  "rw"
                else
                  raise "Unknown mode"
                end

         file.puts "mkdir -p #{dst_path}" % [dst_path]
         file.puts "mount -n --bind #{src_path} #{dst_path}"
         file.puts "mount -n --bind -o remount,#{mode} #{src_path} #{dst_path}"
       end
     end
   end


2012-09-13                                NTT Software Innovation Center                  23
Dropletの展開
  コンテナ上でtarコマンドを実行


   def promise_extract_droplet
     Promise.new do |p|
       connection = promise_warden_connection(:app).resolve
       script = "tar zxf #{droplet.droplet_path}"

         promise_warden_run(connection, script).resolve

       p.deliver
     end
   end




2012-09-13                   NTT Software Innovation Center   24
スタートアップスクリプトの調整
  アプリケーション起動スクリプトのプレースホル
   ダを置換する
      – @がデリミタ

   def promise_prepare_start_script
     Promise.new do |p|
       connection = promise_warden_connection(:app).resolve
       script = "sed -i
   's@%VCAP_LOCAL_RUNTIME%@#{runtime.executable}@g' startup"

         promise_warden_run(connection, script).resolve

       p.deliver
     end
   end




2012-09-13                   NTT Software Innovation Center    25
スタートアップスクリプトの呼び出し
   def promise_start
     Promise.new do |p|
       script = []
       script << "renice 0 $$"
       script << "ulimit -n %d" % self.file_descriptor_limit
       script << "ulimit -u %d" % 512
       script << "umask 077"

       env = Env.new(self)
       env.env.each do |(key, value)|
         script << "export %s=%s" % [key, value]
       end

       startup = "./startup"

       # Pass port to `startup` if we have one
       if self.instance_host_port
         startup << " -p %d" % self.instance_host_port
       end

       script << startup
       script << "exit"

       connection = promise_warden_connection(:app).resolve

       request = ::Warden::Protocol::SpawnRequest.new
       request.handle = attributes["warden_handle"]
       request.script = script.join("¥n")
       response = promise_warden_call(connection, request).resolve

       attributes["warden_job_id"] = response.job_id

       p.deliver
     end
   end

2012-09-13                                   NTT Software Innovation Center   26
Promise パターンについて




2012-09-13       NTT Software Innovation Center   27
Promise(future)パターンとは

 DEA::InstanceがPromiseまみれになった

             • 非同期プログラミング
              – Wardenとの通信が発生する


             • ブロックを避けたい
              – Wardenの処理待ち中ブロックされたくない


             • deferredに似てる
              – JavaScriptプログラマーにはおなじみ



2012-09-13              NTT Software Innovation Center   28
イメージ

 処理AのIO待ち中に処理Bを済ませておきたい

             • 処理Aをスタートし、引換券(Promise)をもらう

             • IO待ち中に処理Bを開始する

             • どうしても処理Aの結果がほしくなったら引換券を
               実際の値に交換してもらう(resolve)

             • 処理Aは結果が出たら値を記録しておく
               (deliver)



2012-09-13             NTT Software Innovation Center   29

マスタリング DEA/NG 第2版

  • 1.
    マスタリング DEA/NG 第2版 岩嵜雄大 NTT Software Innovation Center 2012-09-13 NTT Software Innovation Center
  • 2.
    はじめに  情報はすべて資料作成当時のものです – 更新が頻繁なため情報がすぐに古くなります – 必ず最新のソースコードを確認してください 2012-09-13 NTT Software Innovation Center 2
  • 3.
    今日の内容  そもそもDEAとは – 簡単な紹介  DEA_NG起動から、Warden上でのアプリ ケーション実行までを追う – エントリーポイントからインスタンス起動まで  Promiseパターンについて – DEA_NGに頻出するPromiseとは何者か 2012-09-13 NTT Software Innovation Center 3
  • 4.
    そもそもDEAとは 2012-09-13 NTT Software Innovation Center 4
  • 5.
    DEAとは  ユーザのアプリケーションを実行するためのコン ポーネント – Dropletと呼ばれる形式のアプリケーションを実行する – DEAを増やすことでCloud Foundryの実行能力はスケー ルする App App App App App App App App App App App DEA App App DEA App App App DEA DEA 2012-09-13 NTT Software Innovation Center 5
  • 6.
    DEAがやること  Dropletのダウンロード  ランタイム(Java, Ruby, etc)の管理  アプリケーション隔離用コンテナの管理 – コンテナには独自の「Warden」を使用  アプリケーションの管理・死活監視 2012-09-13 NTT Software Innovation Center 6
  • 7.
    DEAサーバの起動から アプリケーションの起動まで 2012-09-13 NTT Software Innovation Center 7
  • 8.
    全体的な概念図(今日話す範囲) DEA Instance Instance Instance Instance 1対1対応で生成や起動などを担当 通信はWarden Protocol 生成 CCからの命令 NATS 基本的には イベントドリブン App App App App App DEAプロセスの セットアップ ContainerContainerContainer ContainerContainer bin/dea BootStrap Warden VM ※他にもDroplet RegistryやDirectory Serverなどがサブモジュールがある 2012-09-13 NTT Software Innovation Center 8
  • 9.
    エントリーポイント  /bin/dea bootstrap = Dea::Bootstrap.new(config) EM.run do bootstrap.setup bootstrap.start end – コマンドライン引数のパース – DEAプロセスをキック 2012-09-13 NTT Software Innovation Center 9
  • 10.
    DEA serverの起動 /lib/dea/bootstrap.rb – プロセスの初期セットアップ (setup_*) – NATSクライアントの起動 • あとはNATSまかせ def start start_component start_nats start_directory_server start_finish end … def setup_nats @nats = Dea::Nats.new(self, config) end 2012-09-13 NTT Software Innovation Center 10
  • 11.
    NATSに対するディスパッチャーの登録  /lib/dea/nats.rb – アプリケーションインスタンスの起動イベント def start … subscribe("dea.#{bootstrap.uuid}.start") do |message| bootstrap.handle_dea_directed_start(message) end … end def subscribe(subject) sid = client.subscribe(subject) do |raw_data, respond_to| message = Message.decode(self, subject, raw_data, respond_to) logger.debug "Received on #{subject.inspect}: #{message.data.inspect}" yield message end @sids[subject] = sid end 2012-09-13 NTT Software Innovation Center 11
  • 12.
    Bootstrap側のハンドラ  DEA::Instanceオブジェクトを生成 – オブジェクトはレジストリに登録する def handle_dea_directed_start(message) instance = create_instance(message.data) … instance.start end … def create_instance(attributes) instance = Instance.new(self, Instance.translate_attributes(attributes)) instance.on(Instance::Transition.new(:born, :starting)) do instance_registry.register(instance) end … (他のインスタンス状態変化トリガ) instance end 2012-09-13 NTT Software Innovation Center 12
  • 13.
    インスタンスの起動  /lib/dea/instance.rb – Promiseについては後述 def start(&callback) … if !droplet.droplet_exist? logger.info("Starting droplet download") … promise_droplet_download.resolve ドロップレット end をダウンロード promise_container = Promise.new do |p| promise_create_container.resolve promise_setup_network.resolve promise_limit_disk.resolve promise_limit_memory.resolve コンテナの準備 p.deliver end … promise_extract_droplet.resolve promise_prepare_start_script.resolve インスタンスの … promise_start.resolve 起動 end 2012-09-13 NTT Software Innovation Center 13
  • 14.
    コンテナの生成設定  DEAホストのDropletとランタイムをコンテナ内 から見えるように追加のマウント情報を設定 def promise_create_container … connection = promise_warden_connection(:app).resolve # Droplet and runtime bind_mounts = [droplet.droplet_dirname, runtime.dirname].map do |path| bind_mount = ::Warden::Protocol::CreateRequest::BindMount.new bind_mount.src_path = path bind_mount.dst_path = path bind_mount.mode = ::Warden::Protocol::CreateRequest::BindMount::Mode::RO bind_mount end …次ページに続く 2012-09-13 NTT Software Innovation Center 14
  • 15.
    コンテナの生成設定  ライブラリ類もマウントとして追加 …前ページから # Extra mounts (these typically include libs like pq, mysql, etc) bootstrap.config["bind_mounts"].each do |bm| bind_mount = ::Warden::Protocol::CreateRequest::BindMount.new bind_mount.src_path = bm["src_path"] bind_mount.dst_path = bm["dst_path"] || bm["src_path"] mode = bm["mode"] || "ro" bind_mount.mode = BIND_MOUNT_MODE_MAP[mode] bind_mounts << bind_mount end …次ページへ 2012-09-13 NTT Software Innovation Center 15
  • 16.
    アプリケーションインスタンスの実行  WardenProtocolによりコンテナを起動 …前ページから create_request = ::Warden::Protocol::CreateRequest.new create_request.bind_mounts = bind_mounts response =promise_warden_call (connection, create_request).resolve … end end  コンテナ起動後にメモリとディスクの制限を行う – promise_setup_network – promise_limit_disk – promise_limit_memory 2012-09-13 NTT Software Innovation Center 16
  • 17.
    【寄り道】 promise_warden_callで何が起きているのか  connectionは EM::Warden::Client::Connection def promise_warden_call(connection, request) Promise.new do |p| logger.debug2(request.inspect) connection.call(request) do |result| logger.debug2(result.inspect) … if error logger.warn "Request failed: #{request.inspect}" logger.log_exception(error) p.fail(error) else p.deliver(response) end end … end 2012-09-13 NTT Software Innovation Center 17
  • 18.
    【寄り道】 Warden Clientの話  Warden本体については「すごく分かるWarden」 – http://www.slideshare.net/i_yudai/warden  3つのライブラリ – warden-client • Unix Socketによる(簡易な)実装 • ライブラリ的なものも含む – em-warden-client • EventMachineを使用した実装 • warden-clientに依存 – warden-protocol • サーバクライアント間の通信に使うクラスのセット 2012-09-13 NTT Software Innovation Center 18
  • 19.
    【寄り道】 Warden Protocol  Wardenに対する各種命令 • ping • run • create • info • stop • LimitDisk • spawn • LimitMemory • link • net_in • stream • net_out  コマンドラインコマンドとほぼ同等 ping - ping warden create [OPTION OPTION ...] - create container, optionally pass options. destroy <handle> - shutdown container <handle> stop <handle> - stop all processes in <handle> spawn <handle> cmd - spawns cmd inside container <handle>, returns #jobid link <handle> #jobid - do blocking read on results from #jobid stream <handle> #jobid - do blocking stream on results from #jobid run <handle> cmd - short hand for stream(spawn(cmd)) i.e. spawns cmd, streams the result list - list containers info <handle> - show metadata for container <handle> limit <handle> mem [<value>] - set or get the memory limit for the container (in bytes) net <handle> #in - forward port #in on external interface to container <handle> net <handle> #out <address[/mask][:port]> - allow traffic from the container <handle> to address <address> 2012-09-13 NTT Software Innovation Center 19
  • 20.
    【寄り道】 EM::Warden::FiberAwareClient  /em-warden-client/lib/em/warden/client.rb – create()メソッドは存在しない • method_missing()からcall()が呼ばれる def call(*args, &blk) … f = Fiber.current @connection.call(*args) {|res| f.resume(res) } result = Fiber.yield result.get EventMachine::Warden::Client::Connection end def method_missing(method, *args, &blk) call(method, *args, &blk) end 2012-09-13 NTT Software Innovation Center 20
  • 21.
    【寄り道】 EM::Warden::FiberAwareClient  /em-warden-client/lib/em/warden/client.rb – create()メソッドは存在しない • method_missing()からcall()が呼ばれる def call(*args, &blk) … 使わなくなりました f = Fiber.current @connection.call(*args) {|res| f.resume(res) } result = Fiber.yield result.get EventMachine::Warden::Client::Connection end def method_missing(method, *args, &blk) call(method, *args, &blk) end 2012-09-13 NTT Software Innovation Center 21
  • 22.
    【寄り道】 EventMachine::Warden::Client::Connection  /em-warden- client/lib/em/warden/client/connection.rb – EM::Connectionを継承 def call(*args, &blk) if args.first.kind_of?(::Warden::Protocol::BaseRequest) request = args.first Protocolオブジェクトが else 渡されている場合 … request = ::Warden::Client::V1.request_from_v1(args.dup) @v1mode = true argsに”create”が渡されていれば end Warden::Protocol::CreateRequest @request_queue << { :request => request, :callback => blk } process_queue end 2012-09-13 NTT Software Innovation Center 22
  • 23.
    【寄り道】 コンテナ生成時におけるマウントの追加  /warden/warden/lib/warden/container/linux.rb – フックにマウントの命令を追加する def write_bind_mount_commands(request) return if request.bind_mounts.nil? || request.bind_mounts.empty? File.open(File.join(container_path, "hook-parent-before-clone.sh"), "a") do |file| … request.bind_mounts.each do |bind_mount| src_path = bind_mount.src_path dst_path = bind_mount.dst_path # Fix up destination path to be an absolute path inside the union dst_path = File.join(container_path, "union", dst_path[1..-1]) mode = case bind_mount.mode when Protocol::CreateRequest::BindMount::Mode::RO "ro" when Protocol::CreateRequest::BindMount::Mode::RW "rw" else raise "Unknown mode" end file.puts "mkdir -p #{dst_path}" % [dst_path] file.puts "mount -n --bind #{src_path} #{dst_path}" file.puts "mount -n --bind -o remount,#{mode} #{src_path} #{dst_path}" end end end 2012-09-13 NTT Software Innovation Center 23
  • 24.
    Dropletの展開  コンテナ上でtarコマンドを実行 def promise_extract_droplet Promise.new do |p| connection = promise_warden_connection(:app).resolve script = "tar zxf #{droplet.droplet_path}" promise_warden_run(connection, script).resolve p.deliver end end 2012-09-13 NTT Software Innovation Center 24
  • 25.
    スタートアップスクリプトの調整  アプリケーション起動スクリプトのプレースホル ダを置換する – @がデリミタ def promise_prepare_start_script Promise.new do |p| connection = promise_warden_connection(:app).resolve script = "sed -i 's@%VCAP_LOCAL_RUNTIME%@#{runtime.executable}@g' startup" promise_warden_run(connection, script).resolve p.deliver end end 2012-09-13 NTT Software Innovation Center 25
  • 26.
    スタートアップスクリプトの呼び出し def promise_start Promise.new do |p| script = [] script << "renice 0 $$" script << "ulimit -n %d" % self.file_descriptor_limit script << "ulimit -u %d" % 512 script << "umask 077" env = Env.new(self) env.env.each do |(key, value)| script << "export %s=%s" % [key, value] end startup = "./startup" # Pass port to `startup` if we have one if self.instance_host_port startup << " -p %d" % self.instance_host_port end script << startup script << "exit" connection = promise_warden_connection(:app).resolve request = ::Warden::Protocol::SpawnRequest.new request.handle = attributes["warden_handle"] request.script = script.join("¥n") response = promise_warden_call(connection, request).resolve attributes["warden_job_id"] = response.job_id p.deliver end end 2012-09-13 NTT Software Innovation Center 26
  • 27.
    Promise パターンについて 2012-09-13 NTT Software Innovation Center 27
  • 28.
    Promise(future)パターンとは DEA::InstanceがPromiseまみれになった • 非同期プログラミング – Wardenとの通信が発生する • ブロックを避けたい – Wardenの処理待ち中ブロックされたくない • deferredに似てる – JavaScriptプログラマーにはおなじみ 2012-09-13 NTT Software Innovation Center 28
  • 29.
    イメージ 処理AのIO待ち中に処理Bを済ませておきたい • 処理Aをスタートし、引換券(Promise)をもらう • IO待ち中に処理Bを開始する • どうしても処理Aの結果がほしくなったら引換券を 実際の値に交換してもらう(resolve) • 処理Aは結果が出たら値を記録しておく (deliver) 2012-09-13 NTT Software Innovation Center 29