docker swarmkit 源码阅读: Allocator 部分

Allocator 负责分配网络资源(swarm 底层是基于 overlay vxlan 模型),文档里这么描述:

The allocator dispenses resources, such as volumes and networks to tasks, as required.

 

allocator 大致运行流程是这样:

1. 先进行初始化,包括:

1). 判断集群中是否已经存在 ingressNetwork,如果不存在就创建,ingressNetwork 的网段是 10.255.0.0/16。如果 ingressNetwork 没有分配资源,进行分配,包括地址池和 Driver;

2). 获取集群中的 network 列表,对每一个 network 判断是否分配资源,如果未分配则进行分配,分配的逻辑和 ingressNetwork 一样(所以 ingressNetwork 算是一个预定义的 network);

3). 获取集群中的 node 列表,由于「node 必须被 attach 到 ingressNetwork」,所以 node.Attachment.Network 会被修改成 ingressNetwork,然后检查是否已经分配,没有分配同样进行分配,分配的 IP 会放到 Attachment.Addresses ;

4). 获取 service 列表,对每个 service 如果没分配则进行分配。分配之前如果 Spec.Endpoint 不为空(用户自定义),把 Spec.Endpoint 赋给 Endpoint,而且如果 Spec.Endpoint.Ports 不为空,表示需要暴露端口给外部,所以要把 ingressNetwork 加入到 Endpoint.VirtualIPs。

先分配端口,就是 PublishedPort,如果 Spec.Endpoint 为空,不用分配,否则分配, Endpoint.Ports 保存着运行时信息,Spec.Endpoint.Ports 保存配置信息,如果两者长度不同,以 Spec.Endpoint.Ports 为准,但是如果两者的 Name、TargetPort、Protocol 相同而且 Spec 中指定的 PublishedPort 为 0,就以  Endpoint.Ports 为准,这是为了保持 sticky;

接着,Endpoint.Spec 被赋成 Spec.Endpoint,为了保持对齐。

然后如果 Spec.Endpoint 不为空而且 Spec.Endpoint.Mode 是 ResolutionModeDNSRoundRobin 方式,就不必要用 VIP 了,把 Endpoint.VirtualIPs 释放掉。

否则遍历 Endpoint.VirtualIPs,分配 VIP。然后继续对 Spec.Task.Networks (或者 Spec.Networks,优先级比 Spec.Task.Networks 低) 遍历分配,当然如果 Spec.Task.Networks 中的网络和 Endpoint.VirtualIPs 里的网络有一样的,表示已经分配过了,忽略,否则分配,分配之后会被加入到 Endpoint.VirtualIPs。

如果 Spec.Endpoint 为空或者 Spec.Endpoint.Ports 为空而且 Endpoint 不为空,表示不需要 VIP 了,会把 Endpoint.VirtualIPs 中属于 ingressNetwork 的 VIP 释放掉。

5). 获取集群中的 task 列表,对每一个 task,先基于 task 所属 service 生成 task 的 NetworkAttachment,以便给 task 分配。

如果 service 的 Spec.Endpoint.Ports 不为空,说明 service 要暴露端口给外部,需要把 task 加入到 ingressNetwork 中,所以 task 的 Networks (元素类型 NetworkAttachment) 要加上 ingressNetwork。

对 task 的 Spec.Networks (从 service 中获取)遍历,获取每个元素(NetworkAttachmentConfig 结构)的 network、aliases 和 addresses 组合成 NetworkAttachment 结构加入到 task 的 Networks 中。

接着判断是否可以 Vote,如果 task 没有网络或者 task 已经分配好而且所属 service 也已分配好,则可以 Vote,Vote 通过后把 task 状态修改成 allocated,表示资源已经分配完毕。如果不满足 Vote 条件,则进行分配。

分配的时候如果 service 还没分配,退出,如果 task Networks 中有 network 还没分配,退出。然后执行分配,对 task Networks 中的每一个 network 分配一个 IP,分配是基于NetworkAttachment 的网络信息和 NetworkAttachment.Addresses([]string 结构,保存数据如 10.0.10.1/24) 中的子网信息,由于一个网络可能有多个子网,所以要指定子网,但事实上只要成功分配一个子网中的 IP,就不再继续分配。另外分配的 IP,会放在 NetworkAttachment.Addresses 中,覆盖掉对应的子网。

至于为什么要 Vote,这里有一段 doc,到目前我还没搞懂究竟是什么意思,看代码浅显的理解,没觉得 Vote 有什么用,每个 task 分配好之后 Vote 总会通过。

// Package allocator aims to manage allocation of different
// cluster-wide resources on behalf of the manager. In particular, it
// manages a set of independent allocator processes which can mostly
// execute concurrently with only a minimal need for coordination.
//
// One of the instances where it needs coordination is when to move a
// task to ALLOCATED state. Since a task can move to ALLOCATED state
// only when all task allocators have completed their service of
// allocation, they all have to agree on that. The way this achieved
// in `allocator` is by creating a `taskBallot` to which all task
// allocators register themselves as mandatory voters. For each task
// that needs allocation, each allocator independently votes to indicate
// the completion of their allocation. Once all registered voters have
// voted then the task is moved to ALLOCATED state.
//
// Other than the coordination needed for task ALLOCATED state, all
// the allocators function fairly independently.

2. 上面的部分是初始化,初始化之后会监控 Event,对 Event 做处理。

监控的 Event 有:

EventCreateNetwork
EventDeleteNetwork
EventCreateService
EventUpdateService
EventDeleteService
EventCreateTask
EventUpdateTask
EventDeleteTask
EventCreateNode
EventUpdateNode
EventDeleteNode
EventCommit

针对不同的 Event 做不同的分配操作以保持集群状态最新,其中收到最后一个 EventCommit,会对之前分配失败的操作进行重试,包括 network、service 和 task。

 

总结一下我觉得重要的 point:

1. node 都会被加入到 ingressNetwork,ingressNetwork 的网段是10.255.0.0/16;

2. 如果一个 service 需要对外保留端口,那么它的所有 task 都会加入 ingressNetwork(创建ingressNetwork IP);

3. 对于 service,可以指定一个 Network,并绑定多个 SubNet,这样系统会自动对 task 从多个 SubNet 创建 IP,只要从一个 SubNet 创建成功就不再从其他 SubNet 创建。

所以可以规划一个 /16 的 Network 和 很多的 /24 的 SubNet 赋给 service,task 就不用担心 IP 不够用的问题了;

4. 对于 service 的 VIP,无论是否 service 是否暴露端口,都会把 Spec.Task.Networks(如果为空,使用 Spec.Networks)中指定的 Network 分配一个 VIP 放入 Endpoint.VirtualIPs(如果已经在 Endpoint.VirtualIPs 中则不分配),而且,这个 VIP 只要是 Network 中的任何一个 SubNet 就可以。

所以,系统会为 service 指定的所有 Network 都申请一个 VIP,这个 VIP 就是内部 service VIP。

5. 所以如果一个 service 对外暴露端口,而且 Network 和 ingressNetwork 网段不同,那么 VIP 会有两个,一个是 ingressNetwork IP,一个是 Network 的 IP;对于此 service 的 task,也有两个 IP,同样是  ingressNetwork IP 和 Network 的 IP。

 

事实上如果对 Docker 的网络模型不够了解(目前我了解的还不够全面),这部分代码看起来会很痛苦,特别是各个相关数据结构的关联让人不好理解。

这里把相关的数据结构截图保存,以供参考。

s114ef3094-2dd9-47f1-af1b-12636a4c5856s222e9237094-b1af-4bd1-a769-6516d09a5d1f s3352103a01-f49b-4e2d-a3be-edb2ea62cfe1s9fa7018e-8738-4a25-8748-fc0ca11e7fabs4447c171656-b038-4f57-9887-c98184e02a4a s555720d8776-7725-4bf6-8a14-32ded04ed159 s6666e799f46-4895-4691-ade9-d602cc7bd691 s7774d344a4a-9c27-4937-abea-aec20f15346d

 

参考:

https://github.com/docker/libnetwork/blob/master/docs/design.md 

发表评论

电子邮件地址不会被公开。 必填项已用*标注