29
NTT Software Innovation Center マスタリング DEA/NG 第2版 岩嵜 雄大 NTT Software Innovation Center 2012-09-13

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

  • Upload
    iyudai

  • View
    1.457

  • Download
    2

Embed Size (px)

DESCRIPTION

Cloud Foundryのコンポーネントの1つであるDEAの次期バージョンについて、ソースコードを追いながら簡単な解説を行います。

Citation preview

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

NTT Software Innovation Center

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

岩嵜 雄大

NTT Software Innovation Center

2012-09-13

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

NTT Software Innovation Center

はじめに

情報はすべて資料作成当時のものです

– 更新が頻繁なため情報がすぐに古くなります

– 必ず最新のソースコードを確認してください

2012-09-13 2

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

NTT Software Innovation Center

今日の内容

そもそもDEAとは

– 簡単な紹介

DEA_NG起動から、Warden上でのアプリケーション実行までを追う

– エントリーポイントからインスタンス起動まで

Promiseパターンについて

– DEA_NGに頻出するPromiseとは何者か

2012-09-13 3

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

NTT Software Innovation Center

そもそもDEAとは

2012-09-13 4

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

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

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

NTT Software Innovation Center

DEAがやること

Dropletのダウンロード

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

アプリケーション隔離用コンテナの管理

– コンテナには独自の「Warden」を使用

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

2012-09-13 6

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

NTT Software Innovation Center

DEAサーバの起動から アプリケーションの起動まで

2012-09-13 7

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

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

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

NTT Software Innovation Center

エントリーポイント

/bin/dea

–コマンドライン引数のパース

–DEAプロセスをキック

2012-09-13 9

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

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

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

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

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

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

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

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

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

ドロップレットをダウンロード

コンテナの準備

インスタンスの起動

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

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 …次ページに続く

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

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 …次ページへ

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

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

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

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

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

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

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

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

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

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

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

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

使わなくなりました

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

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オブジェクトが 渡されている場合

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

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

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

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

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

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

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

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

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

NTT Software Innovation Center

Promise パターンについて

2012-09-13 27

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

NTT Software Innovation Center

Promise(future)パターンとは

•非同期プログラミング

– Wardenとの通信が発生する

•ブロックを避けたい

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

•deferredに似てる

– JavaScriptプログラマーにはおなじみ

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

2012-09-13 28

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

NTT Software Innovation Center

イメージ

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

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

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

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

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

2012-09-13 29