Upload
iyudai
View
1.457
Download
2
Embed Size (px)
DESCRIPTION
Cloud Foundryのコンポーネントの1つであるDEAの次期バージョンについて、ソースコードを追いながら簡単な解説を行います。
Citation preview
NTT Software Innovation Center
マスタリング DEA/NG 第2版
岩嵜 雄大
NTT Software Innovation Center
2012-09-13
NTT Software Innovation Center
はじめに
情報はすべて資料作成当時のものです
– 更新が頻繁なため情報がすぐに古くなります
– 必ず最新のソースコードを確認してください
2012-09-13 2
NTT Software Innovation Center
今日の内容
そもそもDEAとは
– 簡単な紹介
DEA_NG起動から、Warden上でのアプリケーション実行までを追う
– エントリーポイントからインスタンス起動まで
Promiseパターンについて
– DEA_NGに頻出するPromiseとは何者か
2012-09-13 3
NTT Software Innovation Center
そもそもDEAとは
2012-09-13 4
NTT Software Innovation Center
DEAとは
ユーザのアプリケーションを実行するためのコンポーネント
– Dropletと呼ばれる形式のアプリケーションを実行する
– DEAを増やすことでCloud Foundryの実行能力はスケールする
2012-09-13 5
App
App
App
App
DEA
App
App
App
App
DEA App
App
App
App
DEA
App
App
App
App
DEA
NTT Software Innovation Center
DEAがやること
Dropletのダウンロード
ランタイム(Java, Ruby, etc)の管理
アプリケーション隔離用コンテナの管理
– コンテナには独自の「Warden」を使用
アプリケーションの管理・死活監視
2012-09-13 6
NTT Software Innovation Center
DEAサーバの起動から アプリケーションの起動まで
2012-09-13 7
NTT Software Innovation Center
全体的な概念図(今日話す範囲)
2012-09-13 8
VM
DEA
NATS
BootStrap Warden
Container
App
Container
App
Container
App
Container
App
Container
App
Instance Instance Instance Instance 1対1対応で生成や起動などを担当 通信はWarden Protocol
CCからの命令 基本的には イベントドリブン
DEAプロセスの セットアップ
生成
※他にもDroplet RegistryやDirectory Serverなどがサブモジュールがある
bin/dea
NTT Software Innovation Center
エントリーポイント
/bin/dea
–コマンドライン引数のパース
–DEAプロセスをキック
2012-09-13 9
bootstrap = Dea::Bootstrap.new(config) EM.run do bootstrap.setup bootstrap.start end
NTT Software Innovation Center
DEA serverの起動
/lib/dea/bootstrap.rb
– プロセスの初期セットアップ (setup_*)
– NATSクライアントの起動
•あとはNATSまかせ
2012-09-13 10
def start start_component start_nats start_directory_server start_finish end … def setup_nats @nats = Dea::Nats.new(self, config) end
NTT Software Innovation Center
NATSに対するディスパッチャーの登録
/lib/dea/nats.rb
– アプリケーションインスタンスの起動イベント
2012-09-13 11
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
NTT Software Innovation Center
Bootstrap側のハンドラ
DEA::Instance オブジェクトを生成
– オブジェクトはレジストリに登録する
2012-09-13 12
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
NTT Software Innovation Center
インスタンスの起動
/lib/dea/instance.rb
– Promiseについては後述
2012-09-13 13
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
ドロップレットをダウンロード
コンテナの準備
インスタンスの起動
NTT Software Innovation Center
コンテナの生成設定
DEAホストのDropletとランタイムをコンテナ内から見えるように追加のマウント情報を設定
2012-09-13 14
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 …次ページに続く
NTT Software Innovation Center
コンテナの生成設定
ライブラリ類もマウントとして追加
2012-09-13 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 …次ページへ
NTT Software Innovation Center
アプリケーションインスタンスの実行
Warden Protocolによりコンテナを起動
コンテナ起動後にメモリとディスクの制限を行う
– promise_setup_network
– promise_limit_disk
– promise_limit_memory
2012-09-13 16
…前ページから
create_request = ::Warden::Protocol::CreateRequest.new create_request.bind_mounts = bind_mounts response =promise_warden_call (connection, create_request).resolve … end end
NTT Software Innovation Center
【寄り道】promise_warden_callで何が起きているのか
connectionは EM::Warden::Client::Connection
2012-09-13 17
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
NTT Software Innovation Center
【寄り道】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 18
NTT Software Innovation Center
【寄り道】Warden Protocol
Wardenに対する各種命令
コマンドラインコマンドとほぼ同等
2012-09-13 19
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>
• ping • create • stop • spawn • link • stream
• run • info • LimitDisk • LimitMemory • net_in • net_out
NTT Software Innovation Center
【寄り道】EM::Warden::FiberAwareClient
/em-warden-client/lib/em/warden/client.rb
– create()メソッドは存在しない
• method_missing()からcall()が呼ばれる
2012-09-13 20
def call(*args, &blk) … f = Fiber.current @connection.call(*args) {|res| f.resume(res) } result = Fiber.yield result.get end def method_missing(method, *args, &blk) call(method, *args, &blk) end
EventMachine::Warden::Client::Connection
NTT Software Innovation Center
【寄り道】EM::Warden::FiberAwareClient
/em-warden-client/lib/em/warden/client.rb
– create()メソッドは存在しない
• method_missing()からcall()が呼ばれる
2012-09-13 21
def call(*args, &blk) … f = Fiber.current @connection.call(*args) {|res| f.resume(res) } result = Fiber.yield result.get end def method_missing(method, *args, &blk) call(method, *args, &blk) end
EventMachine::Warden::Client::Connection
使わなくなりました
NTT Software Innovation Center
【寄り道】EventMachine::Warden::Client::Connection
/em-warden-client/lib/em/warden/client/connection.rb
– EM::Connectionを継承
2012-09-13 22
def call(*args, &blk) if args.first.kind_of?(::Warden::Protocol::BaseRequest) request = args.first else … request = ::Warden::Client::V1.request_from_v1(args.dup) @v1mode = true end @request_queue << { :request => request, :callback => blk } process_queue end
argsに”create”が渡されていれば Warden::Protocol::CreateRequest
Protocolオブジェクトが 渡されている場合
NTT Software Innovation Center
【寄り道】コンテナ生成時におけるマウントの追加
/warden/warden/lib/warden/container/linux.rb
– フックにマウントの命令を追加する
2012-09-13 23
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
NTT Software Innovation Center
Dropletの展開
コンテナ上でtarコマンドを実行
2012-09-13 24
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
NTT Software Innovation Center
スタートアップスクリプトの調整
アプリケーション起動スクリプトのプレースホルダを置換する
– @がデリミタ
2012-09-13 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
NTT Software Innovation Center
スタートアップスクリプトの呼び出し
2012-09-13 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
NTT Software Innovation Center
Promise パターンについて
2012-09-13 27
NTT Software Innovation Center
Promise(future)パターンとは
•非同期プログラミング
– Wardenとの通信が発生する
•ブロックを避けたい
– Wardenの処理待ち中ブロックされたくない
•deferredに似てる
– JavaScriptプログラマーにはおなじみ
DEA::InstanceがPromiseまみれになった
2012-09-13 28
NTT Software Innovation Center
イメージ
•処理Aをスタートし、引換券(Promise)をもらう
• IO待ち中に処理Bを開始する
•どうしても処理Aの結果がほしくなったら引換券を実際の値に交換してもらう(resolve)
•処理Aは結果が出たら値を記録しておく(deliver)
処理AのIO待ち中に処理Bを済ませておきたい
2012-09-13 29