前言
我真的真的真的,太几把讨厌不说人话的书籍、文档和博客了,一说docker实现原理,张嘴就是资源、隔离。
什么是资源?
隔离什么?
官方文档就算了,一堆博客和书完全照抄,一堆冗长的文字看下来全是抽象的东西,我是真不知道这些博主是根本不懂,还是揣着明白装糊涂怕被人学会了。
Namespace
容器和事务一样,讲究的是一个隔离,但是又不能彻底隔离。
比如我现在要在一台centos里,实现10个小centos虚拟机,应该考虑什么?
首先正常来说进程的pid是不可重复的,根进程为1,其他依次递增。
那要解决的第一个问题就是怎么让10个容器内,能出现10个一模一样的pid。
两个重复的东西,要想让彼此独立,那么只能给各自加上一个唯一的标记,这个标记就是namespace。
再具体一点,namespace在哪?
一个进程是用PCB描述的,也就是task_struct 结构体,task_struct 中有一个指向namespace结构体的指针。
struct task_struct {
……..
/* namespaces */
struct nsproxy *nsproxy;
…….
}
所以一切都清晰了:每个进程里都有一个nsproxy,描述这个进程属于哪个namespace。
再具体一点,nsproxy里有什么?
struct nsproxy {
atomic_t count;
struct uts_namespace *uts_ns;
struct ipc_namespace *ipc_ns;
struct mnt_namespace *mnt_ns;
struct pid_namespace *pid_ns_for_children;
struct net *net_ns;
};
由于进程和namespace是一对多的关系,所以nsproxy可以共享使用,count是该结构体被引用次数。
-
UTS为Unix Timesharing System,是一个系统最关键的信息,比如hostname、系统版本等等。有了它,可以让每个容器拥有同样的hostname。
-
IPC为Inter Process Communication,是进程通信的方法,比如message queue。有了它,可以让每个容器拥有同样的消息队列。
-
mnt是mount,也就是文件系统,比如ext4、ext3、ext2。有了它,可以让每个容器拥有同样的文件系统。
-
pid是process id。有了它,可以让每个容器拥有同样的pid。比如一个linux里有10个linux容器,那么可以拥有10个pid为1的进程。当然这10个pid为1的根进程的namespace肯定不一样。
-
net是网络设备。有了它,可以让每个容器拥有同样的IP和端口。
再具体一点,怎么看到namespace?
从linux3.8开始,在 /proc/PID/ns
目录下,就可以看到每个进程的namespace了。
这时候可以简单的测一下:
-
host环境下,两个进程的namespace应该大致是一样的
以下为mysql和aliyundun的namespace:
-
在docker中,两个容器的namesapce肯定不一样
以下为两个centos容器的namespace:
CGroup
光实现隔离没用,一台电脑算力就摆在那,怎么分是个问题。
CGroup可以设置进程对CPU、内存和IO的使用限额。
官方文档中docker run的用法:
The
docker run
command must specify an IMAGE to derive the container from. An image developer can define image defaults related to:
- detached or foreground running
- container identification
- network settings
- runtime constraints on CPU and memory
第四个参数就可以设置算力配额。
这个参数是怎么生效的?
在/sys/fs/cgroup/cpu/docker中,Linux为每个容器创建了一个cgroup目录,用容器ID命名。
比如docker run -it --cpu-shares 512 .....
就是直接修改上方cpu.shares文件里的参数。