Dynamic writeback throttling

二月 18, 2016
filesystem

https://lwn.net/Articles/405076/

Dynamic writeback throttling最主要的核心思想就是IO带宽估算。

the bandwidth estimation allows the kernel to scale dirty limits and I/O sizes to make the best use of all of the devices in the system, regardless of any specific device's performance characteristics.
传统writeback机制的做法是,当进程脏页超过一定比例时,调用balance_dirty_pages()函数进入同步写dirty pages过程,直到dirty pages的比例下降到一定比例,之后才允许该进程返回。

该机制存在三个问题:

  1. 进程脏页比率多少才合适?
  2. 内存压力太大时,多个后台进程同时writeback,会产生大量的随机IO,设备吞吐量下降
  3. 如何更准确的估算设备的真实带宽?

Dynamic writeback throttling的基本做法是:

  1. 启发式的计算设备的真实带宽
  2. 用户进程不再主动同步写,而是通过wait方式等待后台进程刷脏页

Read the rest of this entry »

浅析 Linux 初始化 init 系统

二月 15, 2016
Kernel

  1. 浅析 Linux 初始化 init 系统,第 1 部分: sysvinit
  2. 浅析 Linux 初始化 init 系统,第 2 部分: UpStart
  3. 浅析 Linux 初始化 init 系统,第 3 部分: Systemd

其中关于systemd对系统启动速度的优化很有趣,另外systemd引入的资源隔离也是一个很值得思考的问题。

基于veth的网络虚拟化

关于Network Namespace的原理不再详解,请直接移步:Namespaces in operation, part 7: Network namespaces 

但是需要注意的,这个文章里network namespace操作所使用的是最新内核&操作系统提供的非常便利的ip netns工具,不过这些工具在低版本的操作系统上都是不提供的。如果真的需要使用network namespace,最好通过netlink编程的方式来实现,直接基于操作系统调用来完成所有设备的虚拟化工作
我们知道在clone进程的时候使用CLONE_NEWNET参数可以创建一个新的独立的network namespace,但是光有这个还是远远不够的,所有网络设备都没有初始化、没启动,这个时候的容器就是一个完全的离线的容器,不在任何网络里,也访问不了任何网络。 

为了让容器独立能够与外网接通,我们需要创建并初始化一些设备,让容器内的网络和外网互通,veth是一种比较简单的方案

Read the rest of this entry »

Namespaces系列3:network namespace

一月 18, 2016
namespaces

As the name would imply, network namespaces partition the use of the network—devices, addresses, ports, routes, firewall rules, etc.—into separate boxes, essentially virtualizing the network within a single running kernel instance. Network namespaces entered the kernel in 2.6.24

1. 简介

简单点来说,network namespace主要实现了一套独立的协议栈,为不同的应用程序实现完整的网络隔离

由于有了独立的网络协议栈,应用程序可以灵活自主的组建适合自己的网络架构。network namespace技术本身其实不复杂,复杂的是有了network namespace之后网络的构建,就像给你一堆服务器,交换机,路由器,你需要用网线把这些设备连接起来,并设置路由规则,防火墙规则,网络地址等等,才能实现网络访问

network namespace能解决很多问题,常见的如端口

在多个业务混部在同一个机器上的时候,端口协调是一件非常困难的事情,kubernetes在介绍自身的网络模型中也提到了这一点:

Coordinating ports across multiple developers is very difficult to do at scale and exposes users to cluster-level issues outside of their control. Dynamic port allocation brings a lot of complications to the system - every application has to take ports as flags, the API servers have to know how to insert dynamic port numbers into configuration blocks, services have to know how to find each other, etc.

https://kubernetes.io/docs/admin/networking/

但是,如果有了network namespace,用户就不需要关心这个问题,不同的network namespace有各自的网络地址,端口不再受限制

另外,还有虚拟化,安全性,自定义网络等等

2. 基本用法

2.1. 创建

创建network namespace有两种方式:

  1. clone系统调用并指定CLONE_NEWNET,子进程会在一个全新的network namespace里,这种方法适合老系统老内核
  2. 新的操作系统,用户可以简单的通过 ip netns add ns1 来创建一个名为ns1的network namespace

第一种方法,clone的用法是:

 child_pid = clone(childFunc, child_stack + STACK_SIZE, /* Points to start of
          downwardly growing stack */ CLONE_NEWNET | SIGCHLD, argv[1]);

network namespace创建完之后,默认情况下,除了一个lo回环设备,是没有任何其他网络设备的,通过/sbin/ifconfig和ip link我们可以看得出来

2.2. 加入

加入一个network namespace,通过setns系统调用,具体可参考: http://man7.org/linux/man-pages/man2/setns.2.html

2.3. 销毁

和其他namespace一样,当network namespace中的最后一个进程退出时,内核会自动销毁它所引用的network namespace

Namespaces系列2:mnt namespace

一月 16, 2016
namespaces

mnt namespaces是实现容器文件系统最核心的基础技术之一,mnt namespaces可以为容器提供一个独立的文件系统视图

这里我除了介绍mnt namespace相关技术之外,还介绍shared subtrees技术,which allows mount and unmount events to be propagated between mount namespaces in an automatic, controlled fashion.

1. 简介

mnt namespaces是linux最早引入的namespace,appearing in 2002 in Linux 2.4.19. 主要是为了隔离不同进程组可以看到的挂载点,meaning that processes in different namespaces see and are able to manipulate different views of the single directory hierarchy.

这里面的隔离有几个意思:

  1. 同一个mnt namespace下的所有进程看到的mount points一定是一样的
  2. 不同的mnt namespace下的进程看到的mount points不一定是一样的
  3. 不同的mnt namespace下所做的mount和umount操作,都是互相不可见的

这个不一定的原因是因为mnt namespace刚创建出来的时候,默认会继承父亲namespace的所有信息,但是如上面第三点说的,之后所做的任何mount & umount操作,都与父namespace无关,也和任何其他的namespace无关

操作系统启动的时候,内核会为系统初始化一个根mnt namespace,也叫"initial namespace". 后面的mnt namespace都是通过clone系统调用 + CLONE_NEWNS参数创建出来的,When a new mount namespace is created, it receives a copy of the mount point list replicated from the namespace of the caller of clone()

新的mnt namespace创建之后,任何mount & umount操作,都是只在该nmt namespace下生效,并且这些mount points变更,只有在这个mnt namespace里的进程才能看到,其他mnt namespaces下的进程是看不到

有了mnt namespace,我们能做的事情是非常多的,例如:

  1. 出于某种目的,为每个xx提供一个独立的文件系统视图,xx既可以是用户,也可以是“容器”等等
  2. 结合pid namespace + 重载proc文件系统实现pid隔离
  3. 等等

Read the rest of this entry »

Namespaces系列1:PID namespace

一月 15, 2016
namespaces

1. 简介

The global resource isolated by PID namespaces is the process ID number space. This means that processes in different PID namespaces can have the same process ID.

As with processes on a traditional Linux (or UNIX) system, the process IDs within a PID namespace are unique, and are assigned sequentially starting with PID 1. Likewise, as on a traditional Linux system, PID 1—the init process—is special: it is the first process created within the namespace, and it performs certain management tasks within the namespace.

PID隔离最主要的两个目的是:

  1. 为容器提供安全的进程操作
  2. 支持容器热迁移:PID namespaces are used to implement containers that can be migrated
    between host systems while keeping the same process IDs for the
    processes inside the container.

对于第一点,加入容器之间没有pid namespace,所有容器都在同一个pid namespace下,那么任何一个容器内的pid在其他容器内都是可见的,这样会导致一个很严重的问题,就是容器之间的进程操作(例如ps,pstree,kill,killall命令等等)会相互影响,尤其是在混部的场景下,这种问题是很致命的,而且难以追查

Read the rest of this entry »

Intel Cache Allocation Technology: CPU LLC Isolate

一月 13, 2016
cgroup, Kernel

LLC是指Last Level cache,目前来说通常也就是L3 Cache,CAT是指Intel架构提供的一组工具&技术,用以支持LLC隔离。隔离的目的是针对Latency sensitive服务程序,提高QoS。可以考虑到两个LS敏感的程序跑在同一个机器上相互影响的程度还是比较大。

相关的一些资料如下:

  1. x86: Intel Cache Allocation Technology support
  2. Intel® 64 and IA-32 Architectures Software Developer Manuals 其中的 17.16 PLATFORM SHARED RESOURCE CONTROL: CACHE ALLOCATION TECHNOLOGY

Read the rest of this entry »

linux内核调度:cfs(Completely Fair Scheduler) 的简单原理

十一月 10, 2015
Kernel

cfs为完全公平调度提供了一种统一的抽象:虚拟时钟vruntime

不关心进程是否sleep,不关心是否为交互式进程,它只基于虚拟时钟,通过一棵RB树维护所有的进程。RB树的key就是进程的虚拟运行时间。

cfs提供了一种可扩展的进程调度架构,实现了机制和策略的分离

因为真实的硬件上同一时刻只能允许一个任务在运行,所以为了模拟并行,需要引入一个虚拟时钟的概念。虚拟时钟将进程睡眠补偿,优先级,权限等各种参数映射到同一维度。

Read the rest of this entry »

memory.force_empty清空cgroup内存

十一月 5, 2015
cgroup

linux-2.6.32.68

memory.force_empty这个参数的最主要作用就是触发cgroup内存回收。但是有两种方式:

  1. root用户通过echo 1 > memory.force_empty 强制回收内存
  2. memory cgroup被删除时,会将当前计数charge到parent cgroup。(可能会触发parent cgroup的内存回收)

对应内核的函数是

/*
 * make mem_cgroup's charge to be 0 if there is no task.
 * This enables deleting this mem_cgroup.
 */
static int mem_cgroup_force_empty(struct mem_cgroup *mem, bool free_all)

这个函数的使用有两个基本的前提条件:

  1. 当前cgroup里不允许有任何进程
  2. 不存在子cgroup

因为mem_cgroup_force_empty()函数的实现里多处使用如下判断:

    if (cgroup_task_count(cgrp) || !list_empty(&cgrp->children))
        goto out;

dentry缓存

十一月 1, 2015
filesystem

最近追查某线上服务发现一个问题:频繁的创建&删除目录或者文件,会消耗大量的物理内存。

原因是因为,目录或者文件删除后,dentry并没有立即释放,而是保存在了superblock上的lru链表上。通常只有内核认为内存紧张的时候才会回收此部分内存。

不过由于dentry结构非常小,这种case下内存增长是很慢。

 Active / Total Objects (% used)    : 740781 / 764463 (96.9%)
 Active / Total Slabs (% used)      : 31522 / 31522 (100.0%)
 Active / Total Caches (% used)     : 74 / 101 (73.3%)
 Active / Total Size (% used)       : 144439.65K / 148432.28K (97.3%)
 Minimum / Average / Maximum Object : 0.01K / 0.19K / 8.00K

  OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME                   
460278 460278 100%    0.19K  21918       21     87672K dentry                 
 91416  81993  89%    0.10K   2344       39      9376K buffer_head            
 30660  29625  96%    0.19K   1460       21      5840K kmalloc-192            
 30080  23012  76%    0.06K    470       64      1880K kmalloc-64             

Read the rest of this entry »