暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

​容器技术之namespace和cgroup

万照 2021-08-19
2964

一、概念以及解决的问题

1、Linux namespaces和cgroup是什么?

namespace和cgroup是linux内核中2种隔离的特性。

namespace也就是命名空间,它的存在是为了实现资源在不同的命名空间环境中有相同的名称。调用clone()系统函数,传入参数时指定不同的namespace类型复制出相应隔离的命名空间。

cgroup是控制组,它的存在是为了限制进程对系统各种资源占用的多少,比如cpu,memory,io,net等资源。

2、如何实现一个进程运行环境的隔离?

物理机器上使用fork()一个子进程时,使用命名空间技术,实现子进程与父进程/其他进程之间命名空间的隔离;然后初始化子进程的环境,使用cgroup限制子进程的资源;最后执行用户的命令。这些操作产生的隔离环境,可以称为“容器”

3、一个简单程序使用pid namespace隔离前后的对比

    pid namespace隔离前:
    sh-4.2# echo $$
    3414
    pid namespace隔离后:
    sh-4.2# ps -ef |grep -i sh
    root 14938 14934 0 10:04 pts/2 00:00:00 sh
    ###14938的进程通过CLONE_NEWPID隔离后pid成为1;$$是运行的当前进程ID号
    sh-4.2# echo $$ #输出1


    二、一个隔离network  namespace的方式

      # 物理机器上ifconfig
      docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
      eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
      # 使用CLONE_NEWNET,让每个fork的子进程拥有自己的网络设备
      # 输出null,子进程的网络设备为空,此时它与物理机器的网络设备隔离开来
      sh-4.2# ifconfig #输出null


      三、一个mount namespace实现隔离root文件系统的例子

        # systemctl start docker
        # docker run -itd --name busybox busybox
        # docker exec -it acae3e97bb0f bin/sh
        # docker ps -a
        CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
        acae3e97bb0f busybox "sh" 45 hours ago Exited (137) 16 hours ago busybox
        # mkdir -p rootfs && cd rootfs
        # docker export acae3e97bb0f > rootfs.tar
        # tar xf rootfs.tar && cd ..
        # c语言使用系统clone(),mount(),chdir(),chroot()实现容器镜像,并编译
        gcc -o dc dc.c并运行
        if (mount("proc", "rootfs/proc", "proc", 0, NULL) != 0) {
        perror("proc");
        }
        if (mount("sysfs", "rootfs/sys", "sysfs", 0, NULL)!=0) {
        perror("sys");
        }
        if (mount("none", "rootfs/tmp", "tmpfs", 0, NULL)!=0) {
        perror("tmp");
        }
        if (mount("udev", "rootfs/dev", "devtmpfs", 0, NULL)!=0) {
        perror("dev");
        }
        if (mount("devpts", "rootfs/dev/pts", "devpts", 0, NULL)!=0) {
        perror("dev/pts");
        }
        if (mount("shm", "rootfs/dev/shm", "tmpfs", 0, NULL)!=0) {
        perror("dev/shm");
        }
        if (mount("tmpfs", "rootfs/run", "tmpfs", 0, NULL)!=0) {
        perror("run");
            }
        if ( chdir("./rootfs") || chroot("./") != 0 ){
        }
        # /bin/ps -ef #sh进程的pid是1,当前的mount namespace跟外部是隔离的
        PID USER TIME COMMAND
        1 root 0:00 bin/sh
        2 root 0:00 bin/ps -ef


        四、一个使用cgroup限制进程内存的方式

          #创建并挂载自定义的root cgroup,当前的pid存在于root cgroup
          # mount -t cgroup -o none,name=cgroup123 cgroup123 cgrouptry/
          # echo $$
          1825
          # cat proc/1825/cgroup
          12:name=cgroup123:/
          #在root cgroup下扩展1个cgroup
          # mkdir -p cgroup1
          ├── cgroup1
          │   ├── cgroup.clone_children
          │   ├── cgroup.event_control
          │   ├── cgroup.procs
          │   ├── notify_on_release
          │   └── tasks
          ├── cgroup.clone_children
          ├── cgroup.event_control
          ├── cgroup.procs
          ├── cgroup.sane_behavior
          ├── notify_on_release
          ├── release_agent
          └── tasks
          # cd cgroup1
          # echo $$ >> tasks
          # cat /proc/1825/cgroup #当前的进程pid被加入到/cgroup1控制组
          12:name=cgroup123:/cgroup1


          # 使用系统定义的subsystem来限制进程的资源,memory subsystem被映射到/sys/fs/cgroup/memory
          # mount |grep -i memory
          cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
          # mkdir -p rediscgMemLimit && cd rediscgMemLimit
          # ls rediscgMemLimit #每个配置都是资源限制文件
          cgroup.clone_children memory.kmem.slabinfo memory.memsw.failcnt memory.soft_limit_in_bytes
          cgroup.event_control memory.kmem.tcp.failcnt memory.memsw.limit_in_bytes memory.stat
          cgroup.procs memory.kmem.tcp.limit_in_bytes memory.memsw.max_usage_in_bytes memory.swappiness
          memory.failcnt memory.kmem.tcp.max_usage_in_bytes memory.memsw.usage_in_bytes memory.usage_in_bytes
          memory.force_empty memory.kmem.tcp.usage_in_bytes memory.move_charge_at_immigrate memory.use_hierarchy
          memory.kmem.failcnt memory.kmem.usage_in_bytes memory.numa_stat notify_on_release
          memory.kmem.limit_in_bytes memory.limit_in_bytes memory.oom_control tasks
          memory.kmem.max_usage_in_bytes memory.max_usage_in_bytes memory.pressure_level
          # echo 268435456 > memory.limit_in_bytes #设置进程使用的最大内存为256mb
          # echo 2053 >> tasks #ps aux查看Redis-server进程的信息,并将它加入rediscgMemLimit控制组限制
          USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
          root 2053 0.1 0.2 154000 8152 pts/2 Sl+ 17:00 0:00 /work/soft/redis-5.0.12/src/redis-server *:6379
          # ./redis-benchmark -p 6379 -c 20 -t set -n 300 -d 1000000 -r 10000
          #redis-server写入数据超过262144kB,发生Memory cgroup out of memory: Kill process


          五、实现namespace的原理

          命名空间是分层的,父进程的命名空间可以看到子进程的命名空间,子进程的命名空间被映射到父进程的命名空间,这样在Linux内核中会生成一棵树。


          六、实现cgroup的原理

          cgroup采用虚拟文件系统来管理需要限制的资源信息(subsystem)和被限制的进程列表,先创建root cgroup,再创建子cgroup,root/子cgroup都是文件,然后向资源文件中写入限制进程/进程组的限制信息,最后把被限制的进程号加入到子cgroup中,表示该进程受到子cgroup的资源限制。


          文章转载自万照,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

          评论