八、Docker Container 网络 下(摘自《Docker源码分析》)

1、Docker Client 配置容器网络模式

Docker 目前支持 4 种网络模式,分别是 bridge、host、container、none

配置 Docker Container 网络环境与创建 Docker Container 网络环境有一些区别
  • 配置网络环境指用户通过向 Docker Client 传递网络参数,实现 Docker Container 网络环境参数的配置,这部分配置由 Docker Client 传递至 Docker Daemon,并由 Docker Daemon 保存
  • 创建网络环境指,用户通过 Docker Client 向 Docker Daemon 发送容器启动命令之后,Docker Daemon 根据之前保存的网络参数,实现 Docker Container 的启动,并在启动过程中完成 Docker Container 网络环境的创建。

1.1、Docker Client 使用

Docker 架构中,用户可以通过 Docker Client 来配置 Docker Container 的网络模式。配置过程主要通过 docker run 命令来完成,实现配置的方式是在 docker run 命令中添加网络参数。

Docker Client 解析出 run 命令之后,立即调用相应的处理函数 CmdRun 进行处理关于 run 请求的具体内容。CmdRun 的作用主要可以归纳为三点:

*解析 Docker Client 传入的参数,解析出 config、hostconfig 和 cmd 对象等;
*发送请求至 Docker Daemon,创建一个 container 对象,完成 Docker Container 启动前的准备工作;
*发送请求至 Docker Daemon,启动相应的 Docker Container(包含创建 Docker Container 网络环境创建)。

1.2、runconfig 包解析

config 的类型为 Config 结构体,hostConfig 的类型为 HostConfig 结构体,两种类型的定义均位于 runconfig 包。Config 与 HostConfig 类型同用以描述 Docker Container 的配置信息,然而两者之间又有着本质的区别,最大的区别在于两者各自的作用范畴:
*Config 结构体:描述 Docker Container 独立的配置信息。独立的含义是:Config 这部分信息描述的是容器本身,而不会与容器所在 host 宿主机相关;
*HostConfig 结构体:描述 Docker Container 与宿主机相关的配置信息。

1.2.1、Config 结构体

结构体的定义如下:

type Config struct {
   Hostname        string  //容器主机名
   Domainname      string //域名名称
   User            string  //用户名
   Memory          int64  // Memory limit (in bytes)容器内存使用上限(单位:字节)
   MemorySwap      int64  // Total memory usage (memory + swap); set `-1' to disable swap 容器所有的内存使用上限(物理内存 + 交互区),关闭交互区支持置为 -1
   CpuShares       int64  // CPU shares (relative weight vs. other containers)容器 CPU 使用 share 值,其他容器的相对值
   Cpuset          string // Cpuset 0-2, 0,1CPU 核的使用集合
   AttachStdin     bool  //是否附加标准输入
   AttachStdout    bool  //是否附加标准输出
   AttachStderr    bool  //是否附加标准错误输出
   PortSpecs       []string // Deprecated - Can be in the format of 8080/tcp 目前已被遗弃
   ExposedPorts    map[nat.Port]struct{}  //容器内部暴露的端口号
   Tty             bool // Attach standard streams to a tty, including stdin if it is not closed.是否分配一个伪终端 tty
   OpenStdin       bool // Open stdin 在没有附加标准输入时,是否依然打开标准输入
   StdinOnce       bool // If true, close stdin after the 1 attached client disconnects.若为真,表示第一个客户关闭标准输入后关闭标准输入功能
   Env             []string //容器进程运行的环境变量
   Cmd             []string  //容器内通过 ENTRYPOINT 运行的指令
   Image           string // Name of the image as it was passed by  容器 rootfs 所依赖的镜像名称
the operator (eg. could be symbolic)
   Volumes         map[string]struct{}  //容器需要从 host 宿主机上挂载的目录
   WorkingDir      string  //容器内部的指定工作目录
   Entrypoint      []string //覆盖镜像属性中默认的 ENTRYPOINT
   NetworkDisabled bool  //是否关闭容器网络功能
   OnBuild         []string
}
1.2.2、HostConfig 结构体

结构体的定义如下:

type HostConfig struct {
   Binds           []string //从宿主机上绑定到容器的 volumes
   ContainerIDFile string  //文件名,文件用以写入容器的 ID
   LxcConf         []utils.KeyValuePair  //添加自定义的 lxc 选项
   Privileged      bool  //是否赋予该容器扩展权限
   PortBindings    nat.PortMap  //容器绑定到 host 宿主机的端口
   Links           []string  //添加其他容器的链接到该容器
   PublishAllPorts bool  //是否向宿主机暴露所有端口信息
   Dns             []string  //自定义的 DNS 服务器地址
   DnsSearch       []string  //自定义的 DNS 查找服务器地址
   VolumesFrom     []string  //从指定的容器中挂载到该容器的 volumes
   Devices         []DeviceMapping  //为容器添加一个或多个宿主机设备
   NetworkMode     NetworkMode //为容器设置的网络模式
   CapAdd          []string  //为容器用户添加一个或多个 Linux Capabilities
   CapDrop         []string  //为容器用户禁用一个或多个 Linux Capabilities
   RestartPolicy   RestartPolicy  //当一个容器异常退出时采取的重启策略
}
1.2.3、runconfig 解析网络模式

runconfig 包中的 ParseSubcommand 函数调用 parseRun 函数完成命令请求的分析,该函数完成了四方面的工作:
*定义与容器配置信息相关的 flag 参数;
*解析 docker run 命令后紧跟的请求内容,将请求内容全部保存至 flag 参数中,余下的内容一个为镜像 image 名,另一个为需要在容器内执行的 cmd 命令;
*通过 flag 参数验证参数的有效性,并处理得到 Config 结构体与 HostConfig 结构体需要的属性值;
*创建并初始化 Config 类型实例 config、HostConfig 类型实例 hostConfig,最总返回 config、hostConfig 与 cmd。

1.3、CmdRun 执行

CmdRun 后续主要工作是:利用 Docker Daemon 暴露的 RESTful API 接口,将 docker run 的请求发送至 Docker Daemon。

CmdRun 执行过程中 Docker Client 与 Docker Daemon 的简易交互图如下:
CmdRun 中Docker Client 与Docker Daemon 交互图.jpg

2、Docker Daemon 创建容器网络流程

Docker Daemon 接收到 Docker Client 的请求大致可以分为两次,第一次为 create container,第二次为 start container

2.1、创建容器并配置网络参数

Docker Daemon 首先接收并处理 create container 请求。需要注意的是:create container 并非创建了一个运行的容器,而是完成了以下三个主要的工作:
*通过 runconfig 包解析出 create container 请求中与 Docker Container 息息相关的 config 对象;
*在 Docker Daemon 内部创建了与 Docker Container 对应的 container 对象;
*完成 Docker Container 启动前的准备化工作,如准备所需镜像、创建 rootfs 等。

与 Docker Container 网络模式配置相关的内容主要位于创建 container 对象中
container := &Container{
   ID:              id,
   Created:         time.Now().UTC(),
   Path:            entrypoint,
   Args:            args, //FIXME: de-duplicate from config
   Config:          config,
   hostConfig:      &runconfig.HostConfig{},
   Image:           img.ID, // Always use the resolved image id
   NetworkSettings: &NetworkSettings{},
   Name:            name,
   Driver:          daemon.driver.String(),
   ExecDriver:      daemon.execDriver.Name(),
   State:           NewState(), 
}
其中 hostConfig 对象将在 start container 请求执行过程中被赋值,NetworkSettings 类型的作用是描述容器的网络具体信息
type NetworkSettings struct {
    IPAddress   string  //IP 网络地址
    IPPrefixLen int  //网络标识位长度
    Gateway     string  //网关地址
    Bridge      string  //网桥地址
    PortMapping map[string]PortMapping // Deprecated 端口映射
    Ports       nat.PortMap  //端口号
}

2.2、启动容器之网络配置

启动容器过程中,Docker Daemon 首先通过 runconfig 包中 ContainerHostConfigFromJob 函数,解析出请求中的 hostConfig 对象
Start 函数执行过程中,与 Docker Container 网络模式相关的部分主要有三部分:
*initializeNetwork(),初始化 container 对象中与网络相关的属性;
*populateCommand,填充 Docker Container 内部需要执行的命令,Command 中含有进程启动命令,还含有容器环境的配置信息,也包括网络配置;
*container.waitForStart(),实现 Docker Container 内部进程的启动,进程启动之后,为进程创建网络环境等。

3、execdriver 网络执行流程

在 Docker Daemon 启动容器的最后一步,即调用了 execdriver 的 Run 函数来执行。通过分析 Run 函数的具体实现,关于 Docker Container 的网络执行流程主要包括两个环节:
(1) 创建 libcontainer 的 Config 对象
(2) 通过 libcontainer 中的 namespaces 包执行启动容器

Docker Container 网络相关的流程,可以得到以下示意图:
execdriver.Run 执行流程图.jpg
3.1、 创建 libcontainer 的 Config 对象

libcontainer.Config 的作用是,定义在一个容器化的环境中执行一个进程所需要的所有配置项。createContainer 函数的存在,使用 Docker Daemon 层创建的 execdriver.Command,创建更下层 libcontainer 所需要的 Config 对象。这个角度来看,execdriver 更像是封装了 libcontainer 对外的接口,实现了将 Docker Daemon 认识的容器启动信息转换为底层 libcontainer 能真正使用的容器启动配置选项。libcontainer.Config 类型与其内部对象的关联图如下:

libcontainer.Config 类型关系图.jpg

3.2、 调用 libcontainer 的 namespaces 启动容器

创建完 libcontainer.Config 实例 container,经过一系列其他方面的处理之后,最终 execdriver 执行 namespaces.Exec 函数实现启动容器,container 对象依然是 namespace.Exec 函数中一个非常重要的参数

4、libcontainer 实现内核态网络配置

当 execdriver 调用 libcontainer 中 namespaces 包的 Exec 函数时,libcontainer 开始发挥其实现容器功能的作用。Docker Container 的网络创建,因此从这个角度来看Exec 的实现可以分为三个步骤:
(1) 通过 createCommand 创建一个 Golang 语言内的 exec.Cmd 对象;

(2) 启动命令 exec.Cmd,执行容器内第一个进程;

(3) 通过 InitializeNetworking 函数为容器进程初始化网络环境。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容