docker-v0.1.0源码分析

/sbin/init

检查docker可执行文件的绝对路径是否在/sbin/init目录下已经存在
如果在,则设置docker容器启动之前的环境:

设置环境变量

设置网络

根据参数中的网关IP添加网关路由

设置uid/gid

根据参数中的username或者uid,调用系统调用设置uid和gid,切换用户

exec执行docker程序

如果不存在则根据参入的命令行参数选择启动docker deamon还是执行docker cli的命令调用

-d daemon

主要是创建一个server对象, 然后通过这个server创建tcp服务端,创建server实质就是创建runtime对象,runtime对象中封装了所有docker daemon运行时所需要的所有的信息,在创建runtime时,首先会在 /var/lib/docker目录下创建对应的文件:containers,graph文件夹,然后创建对应的镜像tag存储对象,通过名为lxcbr0的卡的网络创建网络管理,最后创建dockerhub的认证对象AuthConfig。

创建runtime

只支持amd64,在/var/lib/docker创建runtime

containers

创建目录,权限0700

graph

创建目录,权限0700

repositories

创建或读入文件
关键数据结构:

1
2
3
4
5
6
7
type TagStore struct {
path string
graph *Graph
Repositories map[string]Repository
}

type Repository map[string]string

如果存在/var/lib/docker/repositories文件(json格式),则读入文件内容到TagStore中,否则将TagStore内容写入到/var/lib/docker/repositories文件,权限为0600。

从lxcbr0网桥接口创建网络管理器

通过指定名为lxcbr0的网络接口来实现网络管理,一个网络管理的实例包括:网桥名字,ip网络,ip分配器,端口分配器,端口映射器。实例化一个网络管理时,要将这些属性全部赋值,ip分配器是一个ip地址的channel,里面的ip地址是通过lxcbr0接口的ip 和对应的网关mask计算得到的子网ip。端口分配器是一个存放了指定范围49153~65535个int的channel,端口映射器是设置和清除iptable的方法集合。

根据网络接口获取IPv4地址(net.IPNet)
根据IPv4地址创建IP分配器(利用了net.IPNet),通过网卡lxcbr0的第一个ip和网关mask得到这个网卡下的所有子网ip,并且封装成一个ip分配器

1
2
3
根据IPv4计算网络地址范围(首IP-末IP),取首IP
根据掩码计算可用hosts个数
阻塞写入所有可用IP到分配器IP队列(排除network IP、broadcast IP、gateway IP)

创建port分配器,端口范围地址为[49153,65535]

1
阻塞写入所有port到分配器port队列

创建PortMapper,端口映射器通过设置iptables规则来处理将外部端口映射到容器。它跟踪所有映射,并能够随意取消映射。

1
2
3
4
5
6
7
8
9
删除docker规则链
清空nat表docker规则链的PREROUTING规则
清空nat表docker规则链的OUTPUT规则
清空nat表docker规则链
删除nat表docker规则链
创建docker规则链
创建nat表docker规则链
创建nat表docker规则链的PREROUTING规则
创建nat表docker规则链的OUTPUT规则

读取认证文件

仓库地址为 https://registry.docker.io ,文件路径为/var/lib/docker/.dockercfg,包括两个字段:auth = basic auth值、email = email值

创建runtime数据结构

1
2
3
4
5
6
7
root:           /var/lib/docker
repository: /var/lib/docker/containers
containers: container/list(list.New())双向链表
networkManager: NetworkManager结构
graph: Graph结构
repositories: TagStore结构
authConfig: AuthConfig结构

恢复容器

读取/var/lib/docker/containers目录,实际就是所有之前运行过的容器的目录,目录名为对应容器的id,对于每一个文件夹:

1
2
3
4
5
6
7
8
9
10
11
12
每个文件夹都有一个config.json文件,将其读入到Container数据结构中,实例化一个container对象,包括容器id
检查config.json的id和所加载的容器id是否一样,以此判断容器信息是否被更改过
将加载的容器注册到runtime中的容器list链表中
检查runtime的容器链表是否已包含当前容器id,存在则提示错误
检查容器id是否为空 如果为空则说明容器是错误的,则退出返回错误
指定container的runtime为当前runtime
创建container的互斥锁和条件变量
创建container的stdout和stderr结构,均为container/list双向链表
创建container的stdin和stdinPipe结构,分别为io.ReadCloser和io.WriteCloser类型,对应为io.Pipe的管道文件描述符
在容器目录下创建stdout日志文件,权限为0600,格式为"{id}-stdout.log",将其write接口添加到stdout双向链表中
在容器目录下创建stderr日志文件,权限为0600,格式为"{id}-stderr.log",将其write接口添加到stderr双向链表中
将容器数据结构添加到runtime的容器链表的最后

创建tcp服务端

在127.0.0.1:4242上启动tcp监听,接受tcp的请求,为每个请求开启一个单独的协程处理请求,如果有请求到来则进行处理。处理过程为:获取请求中的参数然后调用call,call根据参数是否有值来执行不同方法,如果没有参数,则执行runtime的help方法;如果有参数,进行参数的处理,处理逻辑:获取第二个参数,作为docker后的命令,然后获取命令之后的所有参数,整条命令进行日志打印输出,之后再通过cmd命令和反射技术找到对应的cmd所对应的方法,最后将参数传入方法,执行cmd对应的方法,结果返回connect中。
connect在此作为io.Writer类型参数,命令结果将写入到其中。

关于lxcbr0

安装lxc之后,就会有lxcbr0网络接口。
几个参考链接:
github lxc
lxc官网
LXC-Linux Containers介绍
Linux 容器的建立和简单管理
Enable LXC neworking in Debian Jessie, Fedora 21 and others

lxc安装

Centos7在Base repo上没有可用的lxc,因此需要先安装epel仓库。

1
2
yum -y install epel-release
yum install lxc lxc-templates lxc-extras dnsmasq bridge-utils iptables-services

利用brctl工具来创建网桥lxcbr0,利用dnsmasq工具提供dhcp服务。

1
vim /etc/systemd/system/lxc-net.service

添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Unit]
Description=Bridge interface for LXC Containers

[Service]
Type=oneshot

# Bring up bridge interface
ExecStart=/sbin/brctl addbr lxcbr0
ExecStart=/sbin/ip address add 10.0.3.1/24 dev lxcbr0
ExecStart=/sbin/ip link set lxcbr0 up

RemainAfterExit=yes

# Bring bridge interface down
ExecStop=/sbin/ip link set lxcbr0 down
ExecStop=/sbin/brctl delbr lxcbr0

1
vim /etc/systemd/system/lxc-dhcp.service

添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Unit]
Requires=lxc-net.service

[Service]
ExecStart=/sbin/dnsmasq \
--dhcp-leasefile=/var/run/lxc-dnsmasq.leases \
--user=nobody \
--group=nobody \
--keep-in-foreground \
--listen-address=10.0.3.1 \
--except-interface=lo \
--bind-interfaces \
--dhcp-range=10.0.3.2,10.0.3.254

[Install]
WantedBy=default.target

1
2
3
4
systemctl enable lxc-net.service
systemctl enable lxc-dhcp.service
systemctl start lxc-net.service
systemctl start lxc-dhcp service

查看是否存在逻辑(虚拟)网桥接口

1
brctl show

如果已经存在可以用以下命令删除

1
brctl delbr +网桥名

command

1
2
3
4
5
6
7
8
9
10
11
12
13
TODO...

tcp call
conn
stdout
stdin

local call
new docker server
解析参数
方法名命名格式
通过反射查找方法
调用方法
显示 Gitment 评论