博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Pause(Infra)容器
阅读量:4147 次
发布时间:2019-05-25

本文共 5176 字,大约阅读时间需要 17 分钟。

Linux 的 namespace

Linux Namespace提供了一种内核级别隔离系统资源的方法,通过将系统的全局资源放在不同的Namespace中,来实现资源隔离的目的。不同Namespace的程序,可以享有一份独立的系统资源。目前Linux中提供了六类系统资源的隔离机制,分别是:

  • Mount: 隔离文件系统挂载点
  • UTS: 隔离主机名和域名信息
  • IPC: 隔离进程间通信
  • PID: 隔离进程的ID
  • Network: 隔离网络资源
  • User: 隔离用户和用户组的ID

namespace API 的使用方法:clone()unshare()setns()系统调用会使用CLONE_NEW*常量来区别六种不同的 namespace 类型。以及还有/proc下的部分文件。

  • CLONE_NEWNS: 用于指定Mount Namespace
  • CLONE_NEWUTS: 用于指定UTS Namespace
  • CLONE_NEWIPC: 用于指定IPC Namespace
  • CLONE_NEWPID: 用于指定PID Namespace
  • CLONE_NEWNET: 用于指定Network Namespace
  • CLONE_NEWUSER: 用于指定User Namespace

注意:可以通过挂载的方式打开文件描述符(只要文件描述符是被打开的,即使当初创建它的进程被销毁,ns会依然存在):

touch ~/mntmount --bind /proc/${pid}}/ns/mnt~/mnt

/proc/${pid}/ns

readlink /proc/${pid}/ns/net

该目录下的每个软链接的内容是一串字符,由 namespace 类型和 inode number 组成:

$ ll /proc/${pid}/ns/cgroup -> cgroup:[4026531835]ipc -> ipc:[4026531839]mnt -> mnt:[4026531840]net -> net:[4026534999]pid -> pid:[4026531836]pid_for_children -> pid:[4026531836]user -> user:[4026531837]uts -> uts:[4026531838]

特点:

  • 若几个进程中对应 namespace 软链接内容一致,则这几个进程同属于同一个 namespace;
  • 即使 namespace 中的进程全部终结了,只要其软链接文件一直处于 open 状态,则 namespace 将一直存在;

network namespace

CLONE_NEWNET指代的是 network namespace

  1. 创建namespace:clone()系统调用
  2. 维持namespace存在:/proc/${pid}/ns
  3. 往namespace里添加进程:setns()系统调用
  4. 帮助进程逃离namespace:unshare()系统调用

docker network

四种模式:docker network ls

  • none 模式(= clone(CLONE_NEWNET) )
  • bridge 桥接模式(= clone(CLONE_NEWNET ) && netlink add)
  • host 模式(=/proc/1/ns/net)
  • container 模式(=/proc/${container_pid}/ns/net)

docker run时使用 –net= 参数指定,默认是 bridge 模式。

  • 父进程通过clone创建子进程时,使用namespace技术,实现子进程与其他进程(包含父进程)的命名空间隔离;
  • 子进程创建完毕之后,使用cgroup技术来处理子进程,实现进程的资源使用限制;
  • 系统在子进程所处namespace内部,创建需要的隔离环境,如隔离的网络栈等;
  • namespace和cgroup两种技术都用上之后,进程所处的“隔离”环境才真正建立,这时“容器”才真正诞生!

Kubernetes 的 Pod 网络

Kubernetes的Pod网络采用的是Docker的container模式网络(pause container --net=none)。两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。

kubernetes中的pause容器主要为每个业务容器提供以下功能(pause clone flags=CLONE_NEW*|):

  • PID命名空间:Pod中的不同应用程序可以看到其他应用程序的进程ID。
  • 网络命名空间:Pod中的多个容器能够访问同一个IP和端口范围。
  • IPC命名空间:Pod中的多个容器能够使用SystemV IPC或POSIX消息队列进行通信。
  • UTS命名空间:Pod中的多个容器共享一个主机名;Volumes(共享存储卷):
  • Pod中的各个容器可以访问在Pod级别定义的Volumes。
docker run -d --name pause_test  --net=none -p 8880:80 ${psimg}  // default is --net=bridgedocker inspect pause_testdocker run -d --name nginx_test --net=container:pause_test --ipc=container:pause_test --pid=container:pause_test nginx# k8s 的 Pod 创建方式,以下是 CNI 的操作简要步骤ip link add dev tt0 type veth peer tt1ip link set tt0 name eth0 netns netip netns exec net ip link set lo upip netns exec net ip link set eth0 up ip netns exec net ip add  add 192.168.0.10/24 dev eth0ip netns exec net ip route add default via 192.168.0.1

pause 就是占坑用的(pid下使用了隔离的ns)。

syscall

fork vfork 区别

  • 直接调用fork()等效于调用clone(2)时仅指定flags为SIGCHLD(共享信号句柄表)。
  • 如果设置了CLONE_VM,则调用进程和子进程在同一内存空间中运行。 特别是,由调用进程或子进程执行的内存写入在另一个进程中也是可见的。
  • 在glibc的 sysdeps/unix/sysv/linux/createthread.c 源码中创建线程的函数 create_thread 中使用了clone(),并指定了相关的flags:
const int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SYSVSEM			   | CLONE_SIGHAND | CLONE_THREAD			   | CLONE_SETTLS | CLONE_PARENT_SETTID			   | CLONE_CHILD_CLEARTID			   | 0);

coding

setns(pidns)

docker ps |grep ${pod_name}docker inspect ${pscid} -f {
{.State.Pid}}ll /proc/{ps_pid}/ns/ // pause 容器和第二个容器共享一个netns,如果是hostnetwork的pod,则跟pid=1共享一个netns

对应的docker 命令

#define _GNU_SOURCE#include 
#include
#include
#include
#include
#include
#include
#include
#include
#define STACK_SIZE (1024 * 1024) int idle(void *args){ printf("I'm child process, and my pid is: %d\n", getpid()); for (;;) { sleep(1); } return 0;} pid_t clone_wrapper(int (*func)(void *), int flag, void *args){ char *stack, *stack_top; stack = (char *)malloc(STACK_SIZE); if (stack == NULL) { printf("alloc stack for child failed!\n"); return -1; } /* 栈空间为什么传尾指针,因为栈是反着的 */ stack_top = stack + STACK_SIZE; /* Assume stack grows downward */ return clone(func, stack_top, flag , args);} char *get_pid_ns(int pid){ char bytes[32]; sprintf(bytes, "/proc/%d/ns/pid", pid); return strdup(bytes);} int main(void){ pid_t childs[2]; char *ns_file; int fd; printf("I'm parent, and my pid is: %d\n", getpid()); childs[0] = clone_wrapper(idle, CLONE_NEWPID, NULL); if (childs[0] == -1) { printf("error: create child thread failed!\n"); return ; } printf("first child's pid is: %d\n", childs[0]); ns_file = get_pid_ns(childs[0]); if (!ns_file) { printf("get child pid ns failed!\n"); return -1; } fd = open(ns_file, O_RDONLY); if (fd == -1) { printf("open child pid ns failed!\n"); return -1; } if (setns(fd, 0) == -1) { printf("set ns failed!\n"); return -1; } printf("I'm parent, and my pid is: %d\n", getpid()); childs[1] = clone_wrapper(idle, 0, NULL); if (childs[1] == -1) { printf("error: create child thread failed!\n"); return -1; } printf("second child's pid is: %d\n", childs[1]); sleep(3); kill(childs[0], SIGTERM); kill(childs[1], SIGTERM); waitpid(childs[0], NULL, 0); waitpid(childs[1], NULL, 0); return 0;}

其中,父进程在创建第一个子进程时指定了 CLONE_NEWPID,然后父进程调用 setns(), 将父进程的 pid namespace 设置为第一个子进程的 pid namespace,接下来父进程又创建了第二个子进程,则此时,第一个进程和第二个处在同一个 pid namespace 中,在父进程的 pid namespace 中,它们的 PID 分别是:18612、18613,在子进程的 pid namespace 中,它们的 PID 分别是:1、2。

转载地址:http://mviti.baihongyu.com/

你可能感兴趣的文章
ubuntu 16.04 下重置 MySQL 5.7 的密码(忘记密码)
查看>>
Ubuntu Navicat for MySQL安装以及破解方案
查看>>
HTTPS那些事 用java实现HTTPS工作原理
查看>>
oracle函数trunc的使用
查看>>
MySQL 存储过程或者函数中传参数实现where id in(1,2,3,...)IN条件拼接
查看>>
java反编译
查看>>
Class.forName( )你搞懂了吗?——转
查看>>
jarFile
查看>>
EJB3.0定时发送jms(发布/定阅)方式
查看>>
EJB与JAVA BEAN_J2EE的异步消息机制
查看>>
数学等于号是=那三个横杠是什么符
查看>>
HTTP协议详解
查看>>
java多线程中的join方法详解
查看>>
ECLIPSE远程调试出现如下问题 ECLIPSE中调试代码提示找不到源
查看>>
java abstract修饰符
查看>>
数组分为两部分,使得其和相差最小
查看>>
java抽象类和接口
查看>>
有趣的排序——百度2017春招
查看>>
二叉树的最近公共祖先LCA
查看>>
数组中累加和为定值K的最长子数组长度
查看>>