2.基础技术
2.1 linux namespace
- namespace 方便隔离一系列系统资源,pid,user_id, network等,(chroot 创造目录监狱)
- user_id:因为不同的用户很多时候会需要root权限,不可能都授予他们,namespace可以做user_id级别的隔离,使用户在namespace里面是有root权限的
- pid:每个namespace都有一个init进程(pid=1),是初始化这个namespace的进程
- 其他略
linux 支持的namespace
namespace api的基本系统调用
clone()
创建新进程,根据上图的参数判断创建什么类型(类型掩码),同时子进程会被包含进去unshare()
将进程移出namespacesetns()
将进程加入到namespace
// 不完整的ex,以下为调用后截图为
package main
import (
"log"
"os"
"os/exec"
"syscall"
)
func main() {
cmd := exec.Command("bash")
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
}
//cmd.SysProcAttr.Credential = &syscall.Credential {Uid: uint32(1) , Gid:uint32(1)}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stdout
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
}
复制代码
GO 创建UTS namespace
GO 创建IPC namespace
- 隔离System IPC 和 POSIX mq
- 效果可以通过ipcs比较是否一致
GO 创建PID namespace
- 相关的系统调用对应的c函数有
clone
,unshare
,setns
- 对于
clone
会新建子进程,子进程进入新的namespace- 对于
unshare
和setns
,除了PID namespace,其他namespace,进程会进入新的namespace,而对于pid namespace,该进程创建的子进程才会进入新的pid namespace,因为,很多库文件会认为进程的pid是个常量,变化会引起崩溃,即,一但进程创建,和pid namespace的关系就确定了
GO 创建Mount namespace
- 不同namespace种文件系统层次不一样,
mount()
和unmount()
只会影响当前目录,类似chroot但是更加灵活,是最先实现的NS- 补充在Mount namespace里执行
mount -t proc proc /proc
是因为新的namespace 并没由将proc虚拟文件挂载到/proc,直接访问会访问全局的,挂载了之后会有局部的/proc挂载目录,可能是因为COW,在新的namespace mount不会报已存在,所以和pid namespace一起创建可以看到局部的pid
network namespace
- 一般一个物理网络的设备最多存在于一个network namespace,默认是在root namespace, 并且,如果新创建的namespace 被释放(即所有内部进程终止,并且namespace 文件没有被挂载或者打开),这个namespace的物理网卡会重新回到root namespace 而不是父进程的namespace
- 通常docker用过创建veth对来实现
略
2.2 linux Cgroup
namespace 帮助进程隔离出单独空间,cgroup限制每个空间可用资源
linux Cgroup提供了对一组进程及将来子进程的资源限制,控制和统计能力,这些资源包括CPU,内存,存储和网络等
Cgroup的组件
- cgroup:一个cgroup包含一组进程,可以在cgroup上增加Linux subsystem的各种参数配置,将一组进程和subsystem关联
- subsystem 定义了资源的控制,对cgroup中的进程做相应的限制
lssubsys -a
查看kernel支持哪些- 系统默认为每个subsystem创建了一个hierarchy,通过类似
mount | grep memory
能看到- hierarchy 把一组cgroup串成一个树状结构,使cgroup可以继承
- 可以简单的通过挂载cgroup文件系统来创建
mount -t cgroup -o none,name=cgroup-test cgroup-test
- 一个subsystem只能附加到一个hierarchy上
- 一个hierarchy可以添加多个subsystem
- 一个进程可以在多个不在一个hierarchy的cgroup中
- fork的子进程默认在父进程cgroup中,但可以移出
docker 使用cgroup,docker创建容器时如果指定了 -m ,则会在默认的subsystem(ex:/sys/fs/cgroup/memory/)下创建(./docker/容器id子cgroup)
其他 tip:/proc/self/exe 会一直起一个执行自己的子进程(即shell)
2.3 Union File System
使用branch将不同的文件系统的文件和目录“透明的”覆盖,形成一个单一一致的文件系统,通过COW对文件读写
有意思的是,现有的graphdriver中只有少部分是真正的有写时复制语义的联合文件系统:Overlay的两个版本,从Docker早期就存在的aufs。记住联合文件系统只是一个基于文件的接口,通过把一组目录交错起来来,形成一个单一视图。所以与它不是一个真正的文件系统,如ext4或者xfs,它仅仅是站在一个已有的文件系统上提供了这些功能。在一些场景,对于底层文件系统是有要求的,并且Docker也会同时检查请求的联合文件系统和底层的文件系统,来保证它们是兼容的。
现在的linux上docker 默认使用overlay2而不是AUFS
AUFS 重写了早期的Union FS, 提高可靠性和性能,增加了写分支的负载均衡
AUFS 是Docker选用的第一种存储驱动,可以快速启动,高效利用存储和内存
image layer 和 AUFS
- Docker image 是由一系列read-only layer组成的
- docker history $image 可以查看image的变化
container layer 和 AUFS
- 启动一个container时,docker会为其创建一个read-only的init layer来存储有关内容,也会创建一个read-write的layer执行写操作
- AUFS时,删除一个文件,