/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
7type 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 | root: /var/lib/docker |
恢复容器
读取/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
2yum -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 | systemctl enable lxc-net.service |
查看是否存在逻辑(虚拟)网桥接口1
brctl show
如果已经存在可以用以下命令删除1
brctl delbr +网桥名
command
1 | TODO... |