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 的简易交互图如下:
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 网络相关的流程,可以得到以下示意图:
3.1、 创建 libcontainer 的 Config 对象
libcontainer.Config 的作用是,定义在一个容器化的环境中执行一个进程所需要的所有配置项。createContainer 函数的存在,使用 Docker Daemon 层创建的 execdriver.Command,创建更下层 libcontainer 所需要的 Config 对象。这个角度来看,execdriver 更像是封装了 libcontainer 对外的接口,实现了将 Docker Daemon 认识的容器启动信息转换为底层 libcontainer 能真正使用的容器启动配置选项。libcontainer.Config 类型与其内部对象的关联图如下:
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 函数为容器进程初始化网络环境。