Contenu connexe Similaire à マスタリング DEA/NG 第2版 (20) マスタリング DEA/NG 第2版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
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. アプリケーションインスタンスの実行
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
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
29. イメージ
処理AのIO待ち中に処理Bを済ませておきたい
• 処理Aをスタートし、引換券(Promise)をもらう
• IO待ち中に処理Bを開始する
• どうしても処理Aの結果がほしくなったら引換券を
実際の値に交換してもらう(resolve)
• 処理Aは結果が出たら値を記録しておく
(deliver)
2012-09-13 NTT Software Innovation Center 29