容器学习--进程、内存、容器存储、网络_/proc/net/dev能映射到容器内吗-程序员宅基地

技术标签: 读书笔记  linux  namespace  docker  

  • 来源:https://time.geekbang.org/column/intro/365

容器

  • 镜像:就是一个特殊的文件系统,它提供了容器中程序执行需要的所有文件。
  • 容器所有的进程调度,内存访问,文件的读写都直接跑在宿主机的内核之上
  • 容器是什么:Namespace和Cgroups,它们可以让程序在一个资源可控的独立(隔离)环境中运行,这个就是容器。
  • Namespace:
    • Linux在创建容器的时候,就会创建出一个PID Namespace,PID其实就是进程的编号。这个PID Namespace,就是指每建立出一个Namespace,就会单独对进程进行PID编号,每个Namespace的PID编号从1开始
    • Namespace其实就是一种隔离机制,主要目的是隔离运行在同一个宿主机上的容器,让这些容器之间不能访问彼此的资源。隔离的作用:
      • 第一可以充分利用系统的资源,也就是说在同一台宿主机上可以运行多个用户的容器;
      • 第二保证了安全性,因为不同用户之间不能访问对方的资源
    • 文件系统隔离 Mount Namespace
    • 网络隔离 Network Namespace
  • Cgroups:对指定进程的各种计算机资源的限制,比如限制CPU的使用率,内存使用量,IO设备的流量等等。
    • Cgroups通过不同的子系统限制不同的资源,每个子系统限制一种资源。每个子系统限制资源的方式都是类似的,就是把相关的一组进程分配到一个控制组里,然后通过树结构进行管理,每个控制组都设有自己的资源控制参数。
    • CPU子系统:一个控制组(一组进程,可以理解为一个容器里的所有进程)可使用的最大CPU。
    • memory子系统:一个控制组最大的内存容量
    • pids子系统:限制一个控制组里最多可以运行多少进程。
    • cpuset子系统:限制一个进程组里的进程可以在那几个物理CPU上运行

进程

  • init进程。1号进程,它最基本的功能都是创建出 Linux 系统中其他所有的进程,并且管理这些进程。
# CentOS Linux release 8.2.2004 (Core)
$ ls -la /sbin/init
lrwxrwxrwx 1 root root 22 Jul 21  2020 /sbin/init -> ../lib/systemd/systemd
  • 信号:Linux收到的一个通知.Ctrl+C SIGINT(2)

  • 进程收到信号之后的处理:

    • 忽略(Ignore)。SIGKILL和SIGSTOP这两个信号,进程是不能忽略的。
    • 捕获(Catch)。用户进程可以注册自己针对这个信号的handler。SIGKILL和SIGSTOP这两个信号也不能捕获,只能执行系统的缺省行为。
    • 缺省行为(Default)。
  • 特权信号【SIGKILL和SIGSTOP】就是 Linux 为 kernel 和超级用户去删除任意进程所保留的,不能被忽略也不能被捕获。那么进程一旦收到 SIGKILL,就要退出。

  • 容器里 1 号进程对信号处理的两个要点,这也是这一讲里我想让你记住的两句话:

    • 在容器中,1 号进程永远不会响应 SIGKILL 和 SIGSTOP 这两个特权信号;
    • 对于其他的信号,如果用户自己注册了 handler,1 号进程可以响应。
  • 进程限制:内核进程数限制:/proc/sys/kernel/pid_max

# 容器进程限制
/sys/fs/cgroup/pids/docker/2cfdc5833a8d07f1739978239b6d7d647b67e5b3e6cee1739239f30a210c1aee
echo 1002 > pids.max # 进程数限制
  • 每一个 Linux 进程在退出的时候都会进入一个僵尸状态(EXIT_ZOMBIE);

  • 僵尸进程一定需要父进程调用 wait() 或者 waitpid() 系统调用来清理,这也是容器中 init 进程必须具备的一个功能。

  • Containerd 在停止容器的时候,就会向容器的 init 进程发送一个 SIGTERM 信号。我们会发现,在 init 进程退出之后,容器内的其他进程也都立刻退出了。不过不同的是,init 进程收到的是 SIGTERM 信号,而其他进程收到的是 SIGKILL 信号

  • cpu限制

    • cpu监控含义

    • 每个进程的 CPU Usage 只包含用户态(us 或 ni)和内核态(sy)两部分,其他的系统 CPU 开销并不包含在进程的CPU使用中,而CPU Cgroup只是对进程的 CPU 使用做了限制。

    • cpu.cfs_quota_us(一个调度周期里这个控制组被允许的运行时间)除以 cpu.cfs_period_us(用于设置调度周期)得到的这个值决定了CPU Cgroup每个控制组中 CPU 使用的上限值。

    • cpu.shares 参数,正是这个值决定了CPU Cgroup子系统下控制组可用CPU的相对比例,当系统上 CPU 完全被占满的时候,这个比例才会在各个控制组间起效。

    • Kubernetes中,Limit CPU 就是容器所在Cgroup控制组中的CPU上限值,Request CPU的值就是控制组中的cpu.shares的值。

  • CPU利用率计算

    • Linux里获取CPU使用率的工具,比如top,都是通过读取proc文件系统下的stat文件来得到CPU使用了多少ticks。而这里的ticks,是Linux操作系统里的一个时间单位,可以理解成类似秒,毫秒的概念。由于 /proc/stat 文件是整个节点全局的状态文件,不属于任何一个Namespace,因此在容器中无法通过读取 /proc/stat 文件来获取单个容器的 CPU 使用率。
    • 对于top命令来说,它只能显示整个节点中各项CPU的使用率,不能显示单个容器的各项 CPU 的使用率
    • 单个容器 CPU 使用率 =((utime_2 – utime_1) + (stime_2 – stime_1)) * 100.0 / (HZ * et * 1 )。
      • utime 是表示进程的用户态部分在Linux调度中获得CPU的ticks,stime是表示进程的内核态部分在Linux调度中获得CPU的ticks。
      • ((utime_2 – utime_1) + (stime_2 – stime_1)) 是瞬时进程总的 CPU ticks
      • HZ:Linux固定频率ticks,默认100
      • et 是我们刚才说的那个“瞬时”的时间,也就是得到utime_1和utime_2这两个值的时间间隔。
      • 1 CPU数
      • 也可可简化为进程的CPU使用率 =(进程的ticks/单个CPU总ticks)*100.0
  • load average:Linux进程调度器中可运行队列(Running Queue)的进程平均数和休眠队列(Sleeping Queue)里的一段时间的TASK_UNINTERRUPTIBLE状态下的进程平均数之和。

    • 即Load Average= 可运行队列进程平均数 + 休眠队列中不可打断的进程平均数
    • 如果只考虑CPU的资源,load Average等于单位时间内正在运行的进程加上可运行队列的进程
      • 第一,不论计算机CPU是空闲还是满负载,Load Average都是Linux进程调度器中可运行队列(Running Queue)里的一段时间的平均进程数目。
      • 第二,计算机上的CPU还有空闲的情况下,CPU Usage 可以直接反映到"load average"上,什么是 CPU 还有空闲呢?具体来说就是可运行队列中的进程数目小于CPU个数,这种情况下,单位时间进程 CPU Usage 相加的平均值应该就是"load average"的值。
      • 第三,计算机上的CPU满负载的情况下,计算机上的CPU已经是满负载了,同时还有更多的进程在排队需要CPU资源。这时"load average"就不能和CPU Usage等同了。
    • TASK_UNINTERRUPTIBLE 是Linux进程状态的一种,是进程为等待某个系统资源而进入了睡眠的状态,并且这种睡眠的状态是不能被信号打断的。
    • 当进程处于TASK_UNINTERRUPTIBLE状态时[D]时,此时资源(磁盘I/O、信号量等)处于竞争状态,如果很多进程处于这个等待状态,这会在应用程序的最终性能上体现出来,也就是说用户会发觉应用的性能下降了。
    • Cgroups 更多的是以进程为单位进行隔离,而D状态进程是内核中系统全局资源引入的,所以Cgroups影响不了它。 所以我们可以做的是,在生产环境中监控容器的宿主机节点里D状态的进程数量,然后对D状态进程数目异常的节点进行分析,比如磁盘硬件出现问题引起D状态进程数目增加,这时就需要更换硬盘。
    • 如果打个比方来说明 Load Average 的统计原理。你可以想象每个 CPU 就是一条道路,每个进程都是一辆车,怎么科学统计道路的平均负载呢?就是看单位时间通过的车辆,一条道上的车越多,那么这条道路的负载也就越高。此外,Linux 计算系统负载的时候,还额外做了个补丁把 TASK_UNINTERRUPTIBLE 状态的进程也考虑了,这个就像道路中要把红绿灯情况也考虑进去。一旦有了红灯,汽车就要停下来排队,那么即使道路很空,但是红灯多了,汽车也要排队等待,也开不快。
    • Linux状态:
      • R(TASK_RUNNING):可执行队列中的进程状态,包含正在运行的和准备运行的。其他教科书上所说的READY状态也包含在R状态里。
      • S(TASK_INTERRUPTIBLE):可中断的睡眠状态,可以被信号和wake_up()唤醒的,当信号到来时,进程会被设置为可运行。
      • D(TASK_UNINTERRUPTIBLE):不可中断睡眠状态,只能被wake_up()唤醒。kill对其无效。
    • TASK_UNINTERRUPTIBLE的意义
      • TASK_UNINTERRUPTIBLE存在的意义就在于,内核的某些处理流程是不能被打断的。如果响应异步信号,程序的执行流程中就会被插入一段用于处理异步信号的流程(这个插入的流程可能只存在于内核态,也可能延伸到用户态),于是原有的流程就被中断了。在对某些硬件进行操作时(比如进程调用read系统调用对某个设备文件进行读操作,而read系统调用最终执行到对应设备驱动的代码,并与对应的物理设备进行交互),可能需要TASK_UNINTERRUPTIBLE状态对进程进行保护,以避免进程与设备交互的过程被打断,造成设备陷入不可控的状态。
      • 通常情况下TASK_UNINTERRUPTIBLE状态是非常短暂的,通过ps命令基本上不可能捕捉到。进程又是为什么会被置于 uninterruptible sleep 状态呢?处于 uninterruptible sleep 状态的进程通常是在等待 IO,比如磁盘 IO,网络 IO,其他外设 IO,如果进程正在等待的 IO 在较长的时间内都没有响应,很有可能有 IO 出了问题,可能是外设本身出了故障,也可能是比如挂载的远程文件系统NFS等已经不可访问了,那么就很会不幸地被 ps 看到进程状态位已经变成D。
      • 正是因为得不到 IO 的相应,进程才进入了 uninterruptible sleep 状态,所以要想使进程从 uninterruptible sleep 状态恢复,就得使进程等待的 IO 恢复,比如如果是因为从远程挂载的 NFS 卷不可访问导致进程进入 D状态的,那么可以通过恢复该 NFS 卷的连接来使进程的 IO 请求得到满足,除此之外,要想干掉处在 D 状态进程就只能重启整个 Linux 系统了。如果为了想要杀掉 D 状态的进程,而去杀掉它的父进程(通常是shell,在shell下允许某进程,然后某进程转入D状态),就会出现这样的状态:他们的父进程被杀掉了,但是他们的父进程 PID 都变成了1,也就是 init 进程,D状态的进程会变成僵尸进程。

内存

  • 内存限制 /sys/fs/cgroup/memory
    • memory.limit_in_bytes:控制组里所有进程可使用的内存最大数
    • memory.oom_control:当控制组中的进程内存达到了上限值时,这个参数能够决定会不会触发OOM Killer,默认会触发。
    • memory.usage_in_bytes:当前控制组里所有进程实际使用的内存
  • OOM:用系统总的可用页面数,去乘以OOM校准值oom_score_adj,再加上进程已经使用的物理页面数,计算出来的值越大,那么这个进程被OOM Kill的几率也就越大。
  • 内存类型:比如内核需要分配内存给页表,内核栈,还有slab,也就是内核各种数据结构的Cache Pool;用户态进程里的堆内存和栈的内存,共享库的内存,还有文件读写的Page Cache。
    • RSS:进程真正申请到物理页面的内存大小。对于进程来说,RSS内存包含了进程的代码段内存,栈内存,堆内存,共享库的内存, 这些内存是进程运行所必须的。通过malloc/memset得到的内存,就是属于堆内存。
    • page cache:磁盘上读写到的页面放入内存,这部分内存就是page cache。
  • Memory Cgroup控制组里RSS内存和Page Cache内存的和,正好是memory.usage_in_bytes的值。
  • 容器里 /sys/fs/cgroup/memory/memory.stat rss查看实际使用的内存
  • swap:容器
    • 在linux中,swappiness的取值范围在0到100,值为100的时候系统平等回收匿名内存和Page Cache内存;一般缺省值为60,就是优先回收Page Cache;即使swappiness为0,也不能完全禁止Swap分区的使用,就是说在内存紧张的时候,也会使用Swap来回收匿名内存。
    • 在容器中,当memory.swappiness=0的时候,对匿名页的回收是始终禁止的,也就是始终都不会使用Swap空间
    [1217562.233709] Memory cgroup out of memory: Killed process 3017715 (mem_alloc) 
    total-vm:2060328kB, anon-rss:497240kB, file-rss:1008kB, shmem-rss:0kB, UID:0
    [1217562.296557] oom_reaper: reaped process 3017715 (mem_alloc), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
    
    • 我们还是可以在宿主机上打开swap空间,同时在其他容器对应的Memory Cgroups控制组里,把memory.swappiness设为0,让容器不使用swap,满足memory.limit_in_bytes来限制内存的使用。

容器存储

  • 容器文件系统:减少相同镜像文件在同一个节点上的数据冗余,可以节省磁盘空间,也可以减少镜像文件下载占用的网络资源。
  • 作为容器文件系统,UnionFS通过多个目录挂载的方式工作。OverlayFS就是UnionFS的一种实现,是目前主流Linux发行版中缺省使用的容器文件系统。
  • OverlayFs也是把多个目录合并挂载,被挂载的目录分为两类:lowerdir和upperdir
    • lowerdir允许有多个目录,在被挂载后,这些目录里的文件都是不会被修改或者删除的,也就是只读的
    • upperdir只有一个,不过这个目录是可读写的,挂载点目录中的所有文件修改都会在upperdir中反映出来。
  • 容器的镜像文件中各层正好作为OverlayFS的lowerdir的目录,然后加上一个空的upperdir一起挂载好后,就组成了容器的文件系统。
#!/bin/bash

umount ./merged
rm upper lower merged work -r

mkdir upper lower merged work
echo "I'm from lower!" > lower/in_lower.txt
echo "I'm from upper!" > upper/in_upper.txt
# `in_both` is in both directories
echo "I'm from lower!" > lower/in_both.txt
echo "I'm from upper!" > upper/in_both.txt

sudo mount -t overlay overlay \
 -o lowerdir=./lower,upperdir=./upper,workdir=./work \
 ./merged
  • overlayfs

  • “merged” ,它是挂载点(mount point)目录,也是用户看到的目录,用户的实际文件操作在这里进行。

  • “work/”,这个目录没有在这个图里,它只是一个存放临时文件的目录,OverlayFS中如果有文件修改,就会在中间过程中临时存放文件到这里。

  • upper的in_both.txt会覆盖lower的in_both.txt

  • 在merged/中操作

    • 当创建文件时,这个文件出现在upper
    • 第二种是删除文件,如果我们删除"in_upper.txt",那么这个文件会在upper/目录中消失。如果删除"in_lower.txt", 在 lower/目录里的"in_lower.txt"文件不会有变化,只是在upper/目录中增加了一个特殊文件来告诉OverlayFS,"in_lower.txt’这个文件不能出现在merged/里了,这就表示它已经被删除了。
    • 还有一种操作是修改文件,类似如果修改"in_lower.txt",那么就会在upper/目录中新建一个"in_lower.txt"文件,包含更新的内容,而在lower/中的原来的实际文件"in_lower.txt"不会改变。
  • 磁盘限制容量:采用文件系统提供的quota特性。

    • XFS Quota:可以为Linux系统里的一个用户(user),一个用户组(group)或者一个项目(project)来限制它们使用文件系统的额度(quota),也就是限制它们可以写入文件系统的文件总量。
    • 要使用XFS Quota特性,,必须在文件系统挂载的时候加上对应的 Quota 选项,比如我们目前需要配置 Project Quota,那么这个挂载参数就是"pquota"
    fdisk /dev/sdb # 生成磁盘/dev/sdb1
    mkfs.xfs /dev/sdb1 # 初始化文件系统
    mkdir /mnt/sdb1 # 创建挂载点
    mount -o pquota /dev/sdb1 /mnt/sdb1 # 挂载文件系统 一定要加上pquota标签
    cat /proc/mounts|grep prj
    # /dev/sdb1 /mnt/sdb1 xfs rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,prjquota 0 0
    mkdir /mnt/sdb1/xfs_prjquota # 创建限制容量文件夹
    xfs_quota -x -c 'project -s -p /mnt/sdb1/xfs_prjquota 101' /mnt/sdb1 # 给文件夹打上101的Project ID标记
    xfs_quota -x -c 'limit -p bhard=10m 101' /mnt/sdb1 # 限制容量
    dd if=/dev/zero of=/mnt/sdb1/xfs_prjquota/test.file bs=1024 count=20000 # 写入20M测试,只成功了10M
    # -rw-r--r--. 1 root root 10M Jun 22 09:18 test.file
    
    • docker对宿主机为xfs文件系统限制容量,ext4不行
    docker run --storage-opt size=10M -it centos bash
    
  • 磁盘性能:衡量磁盘性能的两个常见的指标IOPS和吞吐量(Throughput)

    • blkio Cgroup:/sys/fs/cgroup/blkio/
    • Direct I/O:用户写磁盘文件,就会通过Linux内核的文件系统层(fileseytem) -> 块设备层(block layer) -> 磁盘驱动 -> 磁盘硬件,这样一路下去写入磁盘
    • Buffered I/O:用户写磁盘文件,用户进程只需要把文件系统写到内存中(Page Cache)就返回了,而Linux 内核自己有线程会把内存数据再写入到磁盘中。
    • Cgroup v1的blkio控制子系统,只能用来限制Direct I/O的容器的进程读写IOPS和吞吐量,对Buffered I/O无效。这是因为Buffered I/O会把数据先写到内存Page Cache中,然后由内核线程把数据写入磁盘,而Cgroup v1 blkio的子系统独立于memory子系统,无法统计到由Page Cache刷入到磁盘的数据流。
    • Cgroup v2从架构上允许一个控制组里有多个子系统协同运行,这样在一个控制组里只要有io和memory子系统,就可以对Buffered I/O做磁盘读写的限速。
  • dirty page:/proc/sys/vm

    • dirty_writeback_centisecs: 这个参数的值是个时间值,以百分之一秒为单位,缺省值是500,也就是5秒钟。它表示每5秒钟会唤醒内核的flush线程来处理dirty pages。
    • dirty_expire_centisecs:这个参数的值也是一个时间值,以百分之一秒为单位,缺省值是3000,也就是30秒钟。它定义了dirty page在内存中存放的最长时间,如果一个dirty page超过这里定义的时间,那么内核的flush线程也会把这个页面写入磁盘。
    • 当dirty pages数量超过dirty_background_ratio对应的内存量的时候,内核flush线程就会开始把dirty pages写入磁盘 ;
    • 当dirty pages数量超过dirty_ratio对应的内存量,这时候程序写文件的函数调用write()就会被阻塞住,直到这次调用的dirty pages全部写入到磁盘。
    • 根据ftrace的结果,我们发现写数据到Page Cache的时候,需要不断地去释放原有的页面,这个时间开销是最大的。造成容器中Buffered I/O write()不稳定的原因,正是容器在限制内存之后,Page Cache的数量较小并且不断申请释放。

网络

  • Network Namespace可以隔离网络设备
    • 网络设备,指lo eth0等网络设备。ip link
    • IPv4和IPv6协议栈。IP层及以上的TCP和UDP协议栈也是每个Namespace独立工作的。所以IP、TCP、UDP的很多协议,它们的相关参数也是每个Namespace独立的
    • IP路由表。ip route
    • 防火墙规则。iptables
    • 网络状态信息
  • 通过系统调用clone或者unshare()这两个函数来建立新的Network Namespace。
    • clone:在新进程创建的时候,伴随新进程建立,同时也建立新的Network Namespace。通过clone()带上CLONE_NETWORK flag来实现
    • unshare:通过unshare来直接改变当前进程的Network Namespace
  • 容器里Network Namespace的网络参数并不是完全从宿主机Host Namespace里继承来的,也不是完全在新的Network Namespace建立的时候重新开始的。
  • 容器中的/proc/sys/是只读mount的,那么在容器里是不能修改/proc/sys/net的任何参数的。可以在创建容器的时候设置参数
# docker run -d --name net_para --sysctl net.ipv4.tcp_keepalive_time=600 centos:8.1.1911 sleep 3600
7efed88a44d64400ff5a6d38fdcc73f2a74a7bdc3dbc7161060f2f7d0be170d1
# docker exec net_para cat /proc/sys/net/ipv4/tcp_keepalive_time
600
  • 工具
    • ip netns 直接对Network Namespace做相关操作,需要在/var/run/netns/有命名文件指向一个Network Namespace
    • unshare 用来建立一个新的Network Namespace
    • lsns 用于查看当前宿主机上所有的Namespace
    • nsenter 可以进入到任何Namespace中执行命令
  • 容器网络通讯:第一步,让数据包从容器Network Namespace发送到Host Network Namespace上,通过配置一对veth虚拟网络设备的方法实现;第二步,数据包到了Host Network Namespace之后,还需要让它从宿主机eth0发送出去,通过bridge+nat的方式完成。
  • veth
docker run -d --name if-test --network none centos:8.1.1911 sleep 36000
docker exec -it if-test ip addr

pid=$(ps -ef | grep "sleep 36000" | grep -v grep | awk '{print $2}')
echo $pid
# 在"/var/run/netns/"的目录下建立一个符号链接,指向这个容器的 Network Namespace。
# 完成这步操作之后,在后面的"ip netns"操作里,就可以用 pid 的值作为这个容器的 Network Namesapce 的标识了。
mkdir -p /var/run/netns
ln -s /proc/$pid/ns/net /var/run/netns/$pid
 
# Create a pair of veth interfaces
ip link add name veth_host type veth peer name veth_container
# Put one of them in the new net ns
ip link set veth_container netns $pid
 
# In the container, setup veth_container
ip netns exec $pid ip link set veth_container name eth0
ip netns exec $pid ip addr add 172.17.1.2/16 dev eth0
ip netns exec $pid ip link set eth0 up
ip netns exec $pid ip route add default via 172.17.1.1
 
# In the host, set veth_host up
ip link set veth_host up

ip addr add 172.17.1.1/16 dev veth_host
# ip addr delete 172.17.1.1/16 dev veth_host 
ip link set veth_host master docker0
iptables -P FORWARD ACCEPT

# 抓包
# 容器eth0
ip netns exec $pid tcpdump -i eth0 host 39.106.233.176 -nn
# veth_host:
tcpdump -i veth_host host 39.106.233.176 -nn
# docker0
tcpdump -i docker0 host 39.106.233.176 -nn
# host eth0:
tcpdump -i eth0 host 39.106.233.176 -nn
./netperf -H 192.168.0.194 -t TCP_RR 
  • veth实现
    • veth发送数据的函数是veth_xmit(),它里面的主要操作就是找到veth peer设备,然后触发peer设备去接收数据包。veth_container这个接口调用了veth_xmit()来发送数据包,最后就是触发了它的peer设备veth_host去调用netif_rx() 来接收数据包。
    • 而 netif_rx()是一个网络设备驱动里面标准的接收数据包的函数,netif_rx() 里面会为这个数据包raise一个 softirq。
    • 所以,根据veth这个虚拟网络设备的实现方式,我们可以看到它必然会带来额外的开销,这样就会增加数据包的网络延时。
  • macvlan和ipvlan
    • 无论是macvlan还是ipvlan,它们都是在一个物理的网络接口上再配置几个虚拟的网络接口。在这些虚拟的网络接口上,都可以配置独立的IP,并且这些IP可以属于不同的Namespace。
    • 对于macvlan,每个虚拟网络接口都有自己独立的mac地址;而ipvlan的虚拟网络接口是和物理网络接口共享同一个mac地址。
    • 对于延时敏感的应用程序,我们可以考虑使用ipvlan/macvlan网络接口的容器。
    docker run --init --name lat-test-1 --network none -d registry/latency-test:v1 sleep 36000
    
    pid1=$(docker inspect lat-test-1 | grep -i Pid | head -n 1 | awk '{print $2}' | awk -F "," '{print $1}')
    echo $pid1
    ln -s /proc/$pid1/ns/net /var/run/netns/$pid1
    
    ip link add link eth0 ipvt1 type ipvlan mode l2
    ip link set dev ipvt1 netns $pid1
    
    ip netns exec $pid1 ip link set ipvt1 name eth0
    ip netns exec $pid1 ip addr add 172.17.3.2/16 dev eth0
    ip netns exec $pid1 ip link set eth0 up
    
  • 网络重传
    • 快速重传的基本定义是:如果发送端收到3个重复的ACK,那么发送端就可以立刻重新发送ACK对应的下一个数据包,而不用等待发送超时。
    • Linux 系统上还会看到发送端收到一个重复的ACK就快速重传的,这是因为Linux下对SACK做了一个特别的判断之后,就可以立刻重传数据包。
    • RPS和RSS的作用类似,都是把数据包分散到更多的CPU上进行处理,使得系统有更强的网络包处理能力。它们的区别是RSS工作在网卡的硬件层,而RPS工作在Linux内核的软件层。
    • 在把数据包分散到各个CPU时,RPS保证了同一个数据流是在一个CPU上的,这样就可以有效减少包的乱序。那么我们可以把 RPS 的这个特性配置到veth网络接口上,来减少数据包乱序的几率。
    • RSS(Receive Side Scaling),是一项网卡的新特性,俗称多队列。具备多个RSS队列的网卡,可以将不同的网络流分成不同的队列,再分别将这些队列分配到多个CPU核心上进行处理,从而将负荷分散,充分利用多核CPU的能力。

容器安全

  • Privileged的容器也就是允许容器中的进程可以执行所有的特权操作。因为"privileged"包含了所有的Linux capabilities, 这样"privileged"就可以轻易获取宿主机上的所有资源,这会对宿主机的安全产生威胁。所以,我们要根据容器中进程需要的最少特权来赋予 capabilities。
  • Linux capabilities就是把Linux root用户原来所有的特权做了细化,可以更加细粒度地给进程赋予不同权限。对于Linux中的每一个特权操作都有一个对应的capability,对于一个capability,有的对应一个特权操作,有的可以对应很多个特权操作。
  • User Namespace隔离了一台Linux节点上的User ID(uid)和Group ID(gid),它给Namespace中的uid/gid的值与宿主机上的uid/gid值建立了一个映射关系。经过User Namespace的隔离,我们在Namespace中看到的进程的uid/gid,就和宿主机Namespace 中看到的uid和gid不一样了。好处如下:
    • 第一,它把容器中root用户(uid 0)映射成宿主机上的普通用户。
    • 第二,对于用户在容器中自己定义普通用户uid的情况,我们只要为每个容器在节点上分配一个uid范围,就不会出现在宿主机上uid冲突的问题了。
  • 为了减少安全风险,业界都是建议在容器中以非root用户来运行进程。不过在没有User Namespace的情况下,在容器中使用非root用户,对于容器云平台来说,对uid的管理会比较麻烦。

工具使用

  • perf
    • perf stat 计数,用来查看每种event发生的次数;
    • perf record 获取采样数据
    • perf list:perf支持的event
      • Hardware event 来自处理器中的一个 PMU(Performance Monitoring Unit),这些event数目不多,都是底层处理器相关的行为,perf中会命名几个通用的事件,比如 cpu-cycles,执行完成的instructions,Cache相关的cache-misses。
      • Software event 是定义在 Linux 内核代码中的几个特定的事件,比较典型的有进程上下文切换(内核态到用户态的转换)事件context-switches、发生缺页中断的事件 page-faults 等。
      • Tracepoints event 内核中很多关键函数里都有Tracepoints。它的实现方式和Software event类似,都是在内核函数中注册了event。
    • 计数(count):统计某个 event 在一段时间里发生了多少次。
    • 采样(sample):
      • 第一种是按照event的数目(period),比如每发生10000次cycles event就记录一次IP、进程等信息,perf record中的-c参数可以指定每发生多少次,就做一次记录。
      • 第二种是定义一个频率(frequency),perf record中的-F参数就是指定频率的,比如perf record -e cycles -F 99 – sleep 1 ,就是指采样每秒钟做99次。
    • 常规使用
    perf record -a -g -- sleep 60
    perf script > out.perf
    git clone --depth 1 https://github.com/brendangregg/FlameGraph.git
    FlameGraph/stackcollapse-perf.pl out.perf > out.folded
    FlameGraph/flamegraph.pl out.folded > out.sv
    
  • 容器中使用perf
    • Perf通过系统调用perf_event_open()来完成对perf event的计数或者采样。不过Docker使用seccomp(seccomp 是一种技术,它通过控制系统调用的方式来保障 Linux 安全)会默认禁止perf_event_open()。
    • 在用Docker启动容器的时候,我们需要在seccomp的profile里,允许perf_event_open()这个系统调用在容器中使用。在我们的例子中,启动container的命令里,已经加了这个参数允许了,参数是"–security-opt seccomp=unconfined"。
    docker run -itd --name perf-test2 --security-opt seccomp=unconfined centos  bash
    # 宿主机
    echo -1 > /proc/sys/kernel/perf_event_paranoid
    
  • ftrace(function tracer):用来跟踪内核中的函数的。
    • ftrace的操作都可以在tracefs这个虚拟文件系统中完成,对于CentOS,这个tracefs的挂载点在/sys/kernel/debug/tracing下
    # tracefs /sys/kernel/debug/tracing tracefs rw,relatime 0 0
    cat /proc/mounts | grep tracefs 
    
    • 通过对/sys/kernel/debug/tracing下某个文件的echo操作,我们可以向内核的ftrace系统发送命令,然后cat某个文件得到ftrace的返回结果
    • function tracer
    # nop
    cat current_tracer
    
    # hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop
    cat available_tracers
    
    echo function > current_tracer
    # function
    cat current_tracer
    cat trace | more
    
    # set_ftrace_filter 只列出想看到的内核函数,或者通过 set_ftrace_pid 只列出想看到的进程。
    echo nop > current_tracer
    echo do_mount > set_ftrace_filter # 过滤函数
    echo function > current_tracer
    echo 1 > options/func_stack_trace # 展示完整的函数调用栈
    
    # function_graph tracer 查看每个函数的执行时间
    echo '!do_mount ' >> set_ftrace_filter ### 先把之前的do_mount filter给去掉。
    echo kfree_skb > set_graph_function  ### 设置kfree_skb()
    echo nop > current_tracer ### 暂时把current_tracer设置为nop, 这样可以清空trace
    echo function_graph > current_tracer ### 把current_tracer设置为function_graph
    
    • 在ftrace实现过程里,最重要的一个环节是利用gcc编译器的特性,为每个内核函数二进制码中预留了5个字节,这样内核函数就可以调用调试需要的函数,从而实现了ftrace的功能。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_31220203/article/details/118279600

智能推荐

前端开发之vue-grid-layout的使用和实例-程序员宅基地

文章浏览阅读1.1w次,点赞7次,收藏34次。vue-grid-layout的使用、实例、遇到的问题和解决方案_vue-grid-layout

Power Apps-上传附件控件_powerapps点击按钮上传附件-程序员宅基地

文章浏览阅读218次。然后连接一个数据源,就会在下面自动产生一个添加附件的组件。把这个控件复制粘贴到页面里,就可以单独使用来上传了。插入一个“编辑”窗体。_powerapps点击按钮上传附件

C++ 面向对象(Object-Oriented)的特征 & 构造函数& 析构函数_"object(cnofd[\"ofdrender\"])十条"-程序员宅基地

文章浏览阅读264次。(1) Abstraction (抽象)(2) Polymorphism (多态)(3) Inheritance (继承)(4) Encapsulation (封装)_"object(cnofd[\"ofdrender\"])十条"

修改node_modules源码,并保存,使用patch-package打补丁,git提交代码后,所有人可以用到修改后的_修改 node_modules-程序员宅基地

文章浏览阅读133次。删除node_modules,重新npm install看是否成功。在 package.json 文件中的 scripts 中加入。修改你的第三方库的bug等。然后目录会多出一个目录文件。_修改 node_modules

【】kali--password:su的 Authentication failure问题,&sudo passwd root输入密码时Sorry, try again._password: su: authentication failure-程序员宅基地

文章浏览阅读883次。【代码】【】kali--password:su的 Authentication failure问题,&sudo passwd root输入密码时Sorry, try again._password: su: authentication failure

整理5个优秀的微信小程序开源项目_微信小程序开源模板-程序员宅基地

文章浏览阅读1w次,点赞13次,收藏97次。整理5个优秀的微信小程序开源项目。收集了微信小程序开发过程中会使用到的资料、问题以及第三方组件库。_微信小程序开源模板

随便推点

Centos7最简搭建NFS服务器_centos7 搭建nfs server-程序员宅基地

文章浏览阅读128次。Centos7最简搭建NFS服务器_centos7 搭建nfs server

Springboot整合Mybatis-Plus使用总结(mybatis 坑补充)_mybaitis-plus ruledataobjectattributemapper' and '-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏3次。前言mybatis在持久层框架中还是比较火的,一般项目都是基于ssm。虽然mybatis可以直接在xml中通过SQL语句操作数据库,很是灵活。但正其操作都要通过SQL语句进行,就必须写大量的xml文件,很是麻烦。mybatis-plus就很好的解决了这个问题。..._mybaitis-plus ruledataobjectattributemapper' and 'com.picc.rule.management.d

EECE 1080C / Programming for ECESummer 2022 Laboratory 4: Global Functions Practice_eece1080c-程序员宅基地

文章浏览阅读325次。EECE 1080C / Programming for ECESummer 2022Laboratory 4: Global Functions PracticePlagiarism will not be tolerated:Topics covered:function creation and call statements (emphasis on global functions)Objective:To practice program development b_eece1080c

洛谷p4777 【模板】扩展中国剩余定理-程序员宅基地

文章浏览阅读53次。被同机房早就1年前就学过的东西我现在才学,wtcl。设要求的数为\(x\)。设当前处理到第\(k\)个同余式,设\(M = LCM ^ {k - 1} _ {i - 1}\) ,前\(k - 1\)个的通解就是\(x + i * M\)。那么其实第\(k\)个来说,其实就是求一个\(y\)使得\(x + y * M ≡ a_k(mod b_k)\)转化一下就是\(y * M ...

android 退出应用没有走ondestory方法,[Android基础论]为何Activity退出之后,系统没有调用onDestroy方法?...-程序员宅基地

文章浏览阅读1.3k次。首先,问题是如何出现的?晚上复查代码,发现一个activity没有调用自己的ondestroy方法我表示非常的费解,于是我检查了下代码。发现再finish代码之后接了如下代码finish();System.exit(0);//这就是罪魁祸首为什么这样写会出现问题System.exit(0);////看一下函数的原型public static void exit (int code)//Added ..._android 手动杀死app,activity不执行ondestroy

SylixOS快问快答_select函数 导致堆栈溢出 sylixos-程序员宅基地

文章浏览阅读894次。Q: SylixOS 版权是什么形式, 是否分为<开发版税>和<运行时版税>.A: SylixOS 是开源并免费的操作系统, 支持 BSD/GPL 协议(GPL 版本暂未确定). 没有任何的运行时版税. 您可以用她来做任何 您喜欢做的项目. 也可以修改 SylixOS 的源代码, 不需要支付任何费用. 当然笔者希望您可以将使用 SylixOS 开发的项目 (不需要开源)或对 SylixOS 源码的修改及时告知笔者.需要指出: SylixOS 本身仅是笔者用来提升自己水平而开发的_select函数 导致堆栈溢出 sylixos

推荐文章

热门文章

相关标签