摘要:本文主要向大家介绍了Linux运维知识之Docker 基础知识之 Namespace, Cgroup,通过具体的内容向大家展现,希望对大家学习Linux运维知识有所帮助。
本文主要向大家介绍了Linux运维知识之Docker 基础知识之 Namespace, Cgroup,通过具体的内容向大家展现,希望对大家学习Linux运维知识有所帮助。
最近工作上需要使用 Docker,在阅读「第一本 Docker 书」后了解了如何成为 Docker 的用户,但对 Docker 中用到技术却不甚了解。都说 Docker 是「新瓶装旧球」,文中笔者将学习到的 Docker 基础技术中的 Namespace,Cgroup 与 AUFS 记录如下。
Linux Namespace 是 Linux 内核提供的一个功能,可以实现系统资源的隔离,如:PID、User ID、Network 等。Linux 中的 chroot 命令可以将当前目录设置为根目录,使得用户的操作被限制在当前目录之下而不影响其他目录。
假设我们成立了一家向外售卖计算资源的公司,用户购买了一个实例在运行自己的应用。如果某些用户能够进入到其他人的实例中,修改或关闭其他实例中应用的状态,那么就会导致不同用户之间相互影响;用户的某些操作可能需要 root 权限,假如我们给每个用户都赋予了 root 权限,那么我们的机器也就没有任何安全性可言了。使用 Namespace,Linux 可以做到 UID 级别的隔离,也就是说,UID 为 n 的用户在自己的 Namespace 中是有 root 权限的,但是在真实的物理机上,他仍然是 UID 为 n 的用户。
目前 Linux 共实现了 6 种不同的 Namespace。
UTS namespaces allow a single system to appear to have different host and domain names to different processes.
UTS(UNIX Timesharing System) Namespace 可以用来隔离 nodename 和 domainname 两个系统标识。在 UTS Namespace 中,每个 Namespace 可以有自己的 hostname。
我们运行下面程序:
func main() { cmd := exec.Command(""zsh"") cmd.SysProcAttr = &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWUTS, } cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Fatal(err) } }
这段代码主要是通过系统调用 clone,并传入 CLONE_NEWUTS 作为参数创建一个新进程,并在新进程内运行 zsh 命令。在 Ubuntu 14.04 上运行这段代码,就可以进入一个交互环境,在环境中运行 ps -af –forest 就可以看到如下的进程树:
验证下父进程和子进程是否在同一个 UTS Namespace 中:
可以看到他们的 UTS Namespace 的编号不同。因为 UTS Namespace 对 hostname 做了隔离,所以在这个环境内修改 hostname 不会影响外部主机。
在目前的 zsh 环境中我们修改 hostname 并打印:
在宿主机上打印 hostname:
可以看到,外部的 hostname 没有被内部的修改所影响。
IPC namespaces isolate processes from SysV style inter-process communication.
IPC(Interprocess Communication) Namespace 用来隔离 System V IPC 和 POSIX message queues。每一个 IPC Namespace 都有自己的 System V IPC 和 POSIX message queue。
我们在上一段代码的基础上增加 CLONE_NEWIPC 标识,表示我们要创建 IPC Namespace。
func main() { cmd := exec.Command(""zsh"") cmd.SysProcAttr = &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC, } cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Fatal(err) } }
在宿主器机查看并创建一个 message queue:
运行代码并查看 message queue:
The PID namespace provides processes with an independent set of process IDs (PIDs) from other namespaces.
PID(Process ID) Namespace 可以用来隔离进程 ID。同一个进程在不同的 PID Namespace 中可以拥有不同的 PID。在 Docker Container 中,使用 ps -ef 可以看到启动容器的进程 PID 为 1,但是在宿主机上,该进程却又有不同的 PID。
继续在代码上添加 CLONE_NEWPID 为子进程创建 PID Namespace。
func main() { cmd := exec.Command(""zsh"") cmd.SysProcAttr = &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID, } cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Fatal(err) } }
运行代码,首先在宿主机上查看进程树:
可以看到 zsh 的 PID 为 11321。在 Namespace 中打印进程 PID:
可以看到,打印出的当前 Namespace 的 PID 为 1,也就是说 11321 的进程被映射到 Namespace 中后 PID 为 1。
Mount namespaces control mount points.
Mount Namespace 用来隔离各个进程看到的挂载点视图。在不同的 Namespace 中,看到的挂载点文件系统层次是不一样的。在 Mount Namespace 中调用 mount 和 unmount 仅仅会影响当前 Namespace 内的文件系统,而对全局文件系统是没有影响的。
在代码中,我们继续加入 CLONE_NEWNS 标识。
func main() { cmd := exec.Command(""zsh"") cmd.SysProcAttr = &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS, } cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Fatal(err) } }
首先运行代码,然后查看 /proc 的文件内容:
可以看到宿主机的 /proc 中文件较多,其中的数字是对应进程的相关信息。下面,将 /proc mount 到 Namespace 中。
可以看到现在以 PID 命名的文件夹明显减少。下面使用 ps -ef 查看系统进程:
可以看到,在当前的 Namespace 中,zsh 是 PID 为 1 的进程。这就说明当前 Namespace 中的 mount 和外部是隔离的,mount 操作没有影响到外部。Docker 的 volumn 正是利用了这个特性。
User namespaces are a feature to provide both privilege isolation and user identification segregation across multiple sets of processes.
User Namespace 主要是隔离用户的用户组 ID。也就是说,一个进程的 User ID 和 Group ID 在 User Namespace 内外可以是不同的。比较常用的是,在宿主机上以一个非 root 用户运行创建一个 User Namespace,然后在 User Namespace 中被映射为了 root 用户。这意味着这个进程在 User Namespace 中有 root 权限,但是在宿主机上却没有 root 权限。
继续修改代码,添加 CLONE_NEWUSER 标识。
func main() { cmd := exec.Command(""zsh"") cmd.SysProcAttr = &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER, } cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Fatal(err) } os.Exit(1) }
首先在宿主机上查看当前用户和用户组:
接下来运行程序,并查看用户组:
可以看到,UID 是不同的,说明 User Namespace 生效了。
Network namespaces virtualize the network stack. On creation a network namespace contains only a loopback interface.
Network Namespace 用来隔离网络设置、IP 地址和端口号等网络栈的 Namespace。Network Namespace 可以让每个容器拥有自己独立的网络设备,而且容器内的应用可以绑定到自己的端口,每个 Namespace 的端口都不会有冲突。在宿主机搭建网桥后,就能很方便地实现容器之间的通信。
我们继续在代码基础上添加 CLONE_NEWNET 标识。
func main() { cmd := exec.Command(""sh"") cmd.SysProcAttr = &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER | syscall.CLONE_NEWNET, } cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Fatal(err) } os.Exit(1) }
首先,在宿主机上查看自己的网络设备:
可以看到在宿主机上有 eth0 和 lo 等网络设备。下面,运行程序,并运行 ifconfig:
我们发现,在 Namespace 中什么网络设备都没有。这可以断定 Namespace 与宿主机之间的网络是处于隔离状态的。
## Cgroups
Linux Namespace 帮助进程隔离出自己的单独空间,而 Cgroups 则可以限制每个空间的大小。Cgroups 提供了对一组进程及将来子进程的资源限制、控制和统计的能力。
Cgroups 有三个组件:
cgroup 负责对进程分组管理,一个 cgroup 包含一组进程并可以设置进程参数
subsystem 是一组资源控制模块,可以关联到 cgroup 上,并对 cgroup 中的进程做出相应限制。
hierarchy 可以把一组 cgroup 串成一个树状结构,这样 cgroup 可以做到继承。
Cgroups 中的 hierarchy 是一种树状结构,Kernel 为了使得对 Cgroups 的配置更加直观,通过一个虚拟的树状文件系统配置 Cgroups 的,通过层级的目录虚拟出 cgroup 树。我们可以在系统上做实验:
1.首先,创建并挂载一个 hierarchy
cgroup.clone_children,cpuset 的 subsystem 会读取这个配置文件,如果这个值是 1,子 cgroup 才会继承父 cgroup 的 cputset 的配置
cgroup.procs 是树中当前节点 cgroup 中的进程组 ID
notify_on_release 和 release_agent 会一起使用。notify_on_release 标识当这个 cgroup 最后一个进程退出的时候是否执行了 release_agent;release_agent 使进程退出后自动清理掉不再使用的 cgroup
tasks 标识该 cgroup 下的进程 ID,将进程 ID 写入 tasks 文件中,便会将相应进程加入到这个 cgroup 中
2.在刚创建好的 hierarchy 上 cgroup 的根节点中拓展出两个子 cgroup
可以看到在 cgroup 目录下创建文件夹的时候,Kernel 会把文件夹标记为子 cgroup,她们继承父 cgroup 的属性。
3.在 cgroup 中添加和移动进程只需要将进程 ID 写到或移动到 cgroup 节点的 tasks 文件中即可
这样,我们就把当前的 3217 进程加入到 cgroup-test:/cgroup-1 中了
4.通过 subsystem 限制 cgroup 中的进程的资源。我们使用系统为每个 subsystem 默认创建的 hierarchy,如 memory 的 hierarchy 来完成实验。
可以看到系统总的内存为 2GB,其中 stess 只能占用到 5% 左右,也就是 100MB。
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注系统运维Linux频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号