发布于 2015-12-05 11:08:31 | 495 次阅读 | 评论: 0 | 来源: 网络整理
外部容器化管理器 会在 命令行中 执行 外部容器化工具,以命令形式传递参数给外部容器化工具,额外的数据则通过 stdin 和 stdout 交换。
若所有命令都可执行,外部容器化工具将不会退出。如果退出,则表示可能出错了。下文将会列出把所有 外部容器化工具需要实现的功能的概况,及其机制示意图。
外部容器化工具 使用stderr来获得状态和调试信息,这些信息被记录在文件里,详见 Enviroment: Sandbox.
外部容器化工具 通过命令行,实现下面所述的功能。 很多 外部容器化工具 会通过 stdin 传输一个 protobuf 信息。 有些 外部容器化工具 还会通过 stdout 传回一个结果 protobuf 信息。 所有的 protobuf 信息都会把其原有长度放在前缀里——这叫 "Record-IO"-格式。 详见Record-IO De/Serializing示例.
命令 | 传入INPUT-PROTO | 结果RESULT-PROTO |
---|---|---|
launch |
containerizer::Launch |
无 |
update |
containerizer::Update |
无 |
usage |
containerizer::Usage |
mesos::ResourceStatistics |
wait |
containerizer::Wait |
containerizer::Termination |
destroy |
containerizer::Destroy |
无 |
containers |
无 | containerizer::Containers |
recover |
无 | 无 |
命令的执行顺序一般不用特别在意,但有一个例外:当载入一个任务时,外部容器管理器将在特定容器上,先接收到launch
,并返回后,容器才可以运行其他的命令。
launch
操作。
launch
操作不会等待执行结果,而是直接返回 - 外部容器化工具里通过fork-exec实现。wait
操作。
仔细观察 从已经载入的容器启动,直到其结束整个状态。
容器载入后,slave节点将不再认为其仍然处于命令行环境中,外部容器化工具里的容器,其整个生命周期里都将被下面的命令操控。注:命令顺序不重要。
当容器是活动状态时,可通过 外部容器化工具 的分隔机制 设置资源限额(比如 内存限额)。
recover
,执行还原操作。 - 操作的结果将不返回任何 protobuf 的信息。recover
命令返回后,外部容器化管理器将调用外部容器化工具里的containers
命令。Containers
命令返回的容器列表,对于标记为孤机的容器,slave节点将在调用等待 wait
命令之后,执行destroy
销毁之。wait
命令。在生命周期所有其他命令执行完毕后, wait
才退出。当容器处于活动状态时,slave节点故障。
外部容器化管理器标记的孤机,处于虽然活动但无法被slave节点恢复的状态,最终将被销毁。
执行器载入任务 的所有必须的信息都通过该命令获取。 此命令不会等待执行器或命令返回信息。 wait
才会等待程序执行完成返回信息后才退出。
launch < containerizer::Launch
此操作会通过stdin 收到 containerizer::Launch protobuf 如下:
/**
* Encodes the launch command sent to the external containerizer
* program.
*/
message Launch {
required ContainerID container_id = 1;
optional TaskInfo task_info = 2;
optional ExecutorInfo executor_info = 3;
optional string directory = 4;
optional string user = 5;
optional SlaveID slave_id = 6;
optional string slave_pid = 7;
optional bool checkpoint = 8;
}
此操作不会通过stdout返回任何数据。
用于中止执行器或命令,此命令会阻塞直至执行器或命令被中止。
wait < containerizer::Wait > containerizer::Termination
此操作会通过stdin 收到 containerizer::Wait protobuf 如下:
/**
* Encodes the wait command sent to the external containerizer
* program.
*/
message Wait {
required ContainerID container_id = 1;
}
此操作会通过stdout 返回 containerizer::Termination protobuf 如下:
/**
* Information about a container termination, returned by the
* containerizer to the slave.
*/
message Termination {
// A container may be killed if it exceeds its resources; this will
// be indicated by killed=true and described by the message string.
required bool killed = 1;
required string message = 2;
// Exit status of the process.
optional int32 status = 3;
}
只有当 容器化工具或者 底层隔离器通过终止任务进程来强制使用限制时(例如:任务使用内存超过内存使用限额),终止参数 killed
才会被设置。
用于给指定的容器发送新的资源配额。新的资源配额会改变容器内任务的处理速度。
update < containerizer::Update
此操作会通过stdin 收到 containerizer::Update protobuf 如下:
/**
* Encodes the update command sent to the external containerizer
* program.
*/
message Update {
required ContainerID container_id = 1;
repeated Resource resources = 2;
}
此操作不会通过stdout返回任何数据。
用于获知指定容器当前资源使用情况。
usage < containerizer::Usage > mesos::ResourceStatistics
此操作会通过stdin 收到 containerizer::Usage protobuf 如下:
/**
* Encodes the usage command sent to the external containerizer
* program.
*/
message Usage {
required ContainerID container_id = 1;
}
此操作会通过stdout 收到 containerizer::ResourceStatistics protobuf 如下:
/*
* A snapshot of resource usage statistics.
*/
message ResourceStatistics {
required double timestamp = 1; // Snapshot time, in seconds since the Epoch.
// CPU Usage Information:
// Total CPU time spent in user mode, and kernel mode.
optional double cpus_user_time_secs = 2;
optional double cpus_system_time_secs = 3;
// Number of CPUs allocated.
optional double cpus_limit = 4;
// cpu.stat on process throttling (for contention issues).
optional uint32 cpus_nr_periods = 7;
optional uint32 cpus_nr_throttled = 8;
optional double cpus_throttled_time_secs = 9;
// Memory Usage Information:
optional uint64 mem_rss_bytes = 5; // Resident Set Size.
// Amount of memory resources allocated.
optional uint64 mem_limit_bytes = 6;
// Broken out memory usage information (files, anonymous, and mmaped files)
optional uint64 mem_file_bytes = 10;
optional uint64 mem_anon_bytes = 11;
optional uint64 mem_mapped_file_bytes = 12;
}
比较少用,有点像被控关机,不过是在slave节点出现故障切换的时候。详见Slave Recovery。
destroy < containerizer::Destroy
此操作会通过stdin 收到 containerizer::Destroy protobuf 如下:
/**
* Encodes the destroy command sent to the external containerizer
* program.
*/
message Destroy {
required ContainerID container_id = 1;
}
此操作不会通过stdout返回任何数据。
返回所有活动状态的容器识别号。
containers > containerizer::Containers
此操作不会通过stdin接收任何额外数据。
此操作会通过stdout 传回 containerizer::Containers protobuf 如下:
/**
* Information on all active containers returned by the containerizer
* to the slave.
*/
message Containers {
repeated ContainerID containers = 1;
}
可以让外部容器化工具对其本身进行状态恢复。 若 外部容器化工具通过 文件系统来使用状态检查锚点,那就可以方便的将该状态信息进行反序列化。详见slave节点还原概况
recover
此操作不会通过stdin收到任何数据。 此操作不会通过stdout返回任何数据。
上面所提及的protobuf和消息可通过下面方式查看详细信息:
containerizer::XXX
是在
include/mesos/containerizer/containerizer.proto
中定义的。
mesos::XXX
是在 include/mesos/mesos.proto
中定义的。沙箱环境,由两部分构成:
执行器会通过 cd
进入工作目录,同时 stderr 重定向进执行器的"stderr"日志文件。
Note 不是所有的 容器化调用 都有完整的沙箱环境。
在外部容器化工具里,有一些额外的环境变量可供设置:
MESOS_LIBEXEC_DIRECTORY = mesos-executor, mesos-usage, ...等的路径。 This information is always present.
Note 这两项设置对于将一批容器绑定于一个slave节点中十分有用,且可以在需要的时候正确的还原。
default_container_image
提供的镜像。这变量只有当调用加载命令launch
时可用。在mesos启动slave节点时,在其命令前面添加 GLOG
级别,以便显示更多 外部容器管理工具的状态信息。级别可设置为大于或等于2。
GLOG_v=2 ./bin/mesos-slave --master=[...]
外部容器工具将从执行器的stderr
日志文件获得其本身 stderr
日志,可通过增强细化日志 展开具体位置。
日志输出示例:
I0603 02:12:34.165662 174215168 external_containerizer.cpp:1083] Invoking external containerizer for method 'launch'
I0603 02:12:34.165675 174215168 external_containerizer.cpp:1100] calling: [/Users/till/Development/mesos-till/build/src/test-containerizer launch]
I0603 02:12:34.165678 175824896 slave.cpp:497] Successfully attached file '/tmp/ExternalContainerizerTest_Launch_lP22ci/slaves/20140603-021232-16777343-51377-7591-0/frameworks/20140603-021232-16777343-51377-7591-0000/executors/1/runs/558e0a69-70da-4d71-b4c4-c2820b1d6345'
I0603 02:12:34.165686 174215168 external_containerizer.cpp:1101] directory: /tmp/ExternalContainerizerTest_Launch_lP22ci/slaves/20140603-021232-16777343-51377-7591-0/frameworks/20140603-021232-16777343-51377-7591-0000/executors/1/runs/558e0a69-70da-4d71-b4c4-c2820b1d6345
容器工具stderr输出的目录可在 stderr 文件里找到(文件目录参见上方最后一行日志)。
cat /tmp/ExternalContainerizerTest_Launch_lP22ci/slaves/20140603-021232-16777343-51377-7591-0/frameworks/20140603-021232-16777343-51377-7591-0000/executors/1/runs/558e0a69-70da-4d71-b4c4-c2820b1d6345/stderr
record-io格式化的protobuf形式如下:
name: offset
length: 00 - 03 = record length in byte
Example length: 00000240h = 576 byte total protobuf size
十六进制dump示例:
00000000: 4002 0000 0a26 0a24 3433 3532 3533 6162 2d64 3234 362d 3437 :@....&.$435253ab-d246-47
00000018: 6265 2d61 3335 302d 3335 3432 3034 3635 6438 3638 1a81 020a :be-a350-35420465d868....
00000030: 030a 0131 2a16 0a04 6370 7573 1000 1a09 0900 0000 0000 0000 :...1*...cpus............
00000048: 4032 012a 2a15 0a03 6d65 6d10 001a 0909 0000 0000 0000 9040 :@2.**...mem............@
00000060: 3201 2a2a 160a 0464 6973 6b10 001a 0909 0000 0000 0000 9040 :2.**...disk............@
00000078: 3201 2a2a 180a 0570 6f72 7473 1001 220a 0a08 0898 f201 1080 :2.**...ports..".........
00000090: fa01 3201 2a3a 2a1a 2865 6368 6f20 274e 6f20 7375 6368 2066 :..2.*:*.(echo 'No such f
000000a8: 696c 6520 6f72 2064 6972 6563 746f 7279 273b 2065 7869 7420 :ile or directory'; exit
000000c0: 3142 2b0a 2932 3031 3430 3532 362d 3031 3530 3036 2d31 3637 :1B+.)20140526-015006-167
000000d8: 3737 3334 332d 3535 3430 332d 3632 3536 372d 3030 3030 4a3d :77343-55403-62567-0000J=
000000f0: 436f 6d6d 616e 6420 4578 6563 7574 6f72 2028 5461 736b 3a20 :Command Executor (Task:
00000108: 3129 2028 436f 6d6d 616e 643a 2073 6820 2d63 2027 7768 696c :1) (Command: sh -c 'whil
00000120: 6520 7472 7565 203b 2e2e 2e27 2952 0131 22c5 012f 746d 702f :e true ;...')R.1"../tmp/
00000138: 4578 7465 726e 616c 436f 6e74 6169 6e65 7269 7a65 7254 6573 :ExternalContainerizerTes
00000150: 745f 4c61 756e 6368 5f6c 5855 6839 662f 736c 6176 6573 2f32 :t_Launch_lXUh9f/slaves/2
00000168: 3031 3430 3532 362d 3031 3530 3036 2d31 3637 3737 3334 332d :0140526-015006-16777343-
00000180: 3535 3430 332d 3632 3536 372d 302f 6672 616d 6577 6f72 6b73 :55403-62567-0/frameworks
00000198: 2f32 3031 3430 3532 362d 3031 3530 3036 2d31 3637 3737 3334 :/20140526-015006-1677734
000001b0: 332d 3535 3430 332d 3632 3536 372d 3030 3030 2f65 7865 6375 :3-55403-62567-0000/execu
000001c8: 746f 7273 2f31 2f72 756e 732f 3433 3532 3533 6162 2d64 3234 :tors/1/runs/435253ab-d24
000001e0: 362d 3437 6265 2d61 3335 302d 3335 3432 3034 3635 6438 3638 :6-47be-a350-35420465d868
000001f8: 2a04 7469 6c6c 3228 0a26 3230 3134 3035 3236 2d30 3135 3030 :*.till2(.&20140526-01500
00000210: 362d 3136 3737 3733 3433 2d35 3534 3033 2d36 3235 3637 2d30 :6-16777343-55403-62567-0
00000228: 3a18 736c 6176 6528 3129 4031 3237 2e30 2e30 2e31 3a35 3534 ::.slave(1)@127.0.0.1:554
00000240: 3033 4000
用Python来发送和接收 record-io 格式化信息
# Read a data chunk prefixed by its total size from stdin.
def receive():
# Read size (uint32 => 4 bytes).
size = struct.unpack('I', sys.stdin.read(4))
if size[0] <= 0:
print >> sys.stderr, "Expected protobuf size over stdin. " \
"Received 0 bytes."
return ""
# Read payload.
data = sys.stdin.read(size[0])
if len(data) != size[0]:
print >> sys.stderr, "Expected %d bytes protobuf over stdin. " \
"Received %d bytes." % (size[0], len(data))
return ""
return data
# Write a protobuf message prefixed by its total size (aka recordio)
# to stdout.
def send(data):
# Write size (uint32 => 4 bytes).
sys.stdout.write(struct.pack('I', len(data)))
# Write payload.
sys.stdout.write(data)