distcc和dmucs分布式编译环境的负载均衡


使用distcc分布式编译的特点与潜在问题

作为经典的分布式编译工具,distcc 在日常工作中常为我们使用来解决大型项目在单一工作站上编译较慢的问题。
其主要用于对 C, Object C 以及 C++ 代码进行并行编译,将可以并行的编译任务分布于编译集群中的各个工作站,有效利用各机器资源,达到整体编译性能的成倍提升。
在类 Unix 系统上,distcc 使用 sendfile 系统调用在不同工作节点之间传送文件,尽管这种网络文件传输会占用一定的时间,他们对工作机的 CPU 资源占用却很小,
而且这种分发任务的方式能够简化构建环境的配置,distcc 在这方面同早期的一些基于共享文件系统的分布编译环境 (dmake, pvmmake 等等 ) 相比几乎是 0 配置。
distcc 对各个编译节点的本地系统库及头文件基本没有要求,即使在不同的节点上这些组件的版本不同也不会影响到最终编译结果的正确性,
实际情况是 distcc 会在本地 (client机) 完成存在版本依赖的编译任务。
这个在本地做过预处理的 ASCII 源文件及其他命令行选项即可唯一确定一个目标文件,而与此任务在哪台机器上运行无关,通过分发这种任务到各个节点,即可消除对头文件的依赖。
同理 distcc 通过在任务的分发节点做链接来消除对库文件的依赖。
然而,distcc 的缺点在于其负载均衡算法过于简单,distcc 的代理进程对各个工作机当前的负荷没有感知,分发预处理文件的唯一依据是主机出现在DISTCC_HOST环境变量中的次序,
主机名越靠前,就会得到更多的编译任务,然而当编译场中某些机器性能过差,整体编译性能会显著下降,当阻塞 Make 运行的编译任务运行在这些机器上的时候,这种性能变化尤为明显。


一种实现负载均衡的解决方案:使用 DMUCS

毫无疑问,在上述编译集群中,有必要采用负载均衡来使编译系能得到最大的优化。这就需要在编译集群中增加监控各工作机工作量的监控程序,动态检测和平衡编译机的负载。
一个有效的方案是使用 DMUCS(Distributed Multi-User Compilation System)应用。DMUCS 是一个实现于 distcc 之上的动态平衡和任务分布程序。它可以:


  支持多用户同时编译,扩展性好,可以很好处理新增的负载。
  支持多种操作系统所组成的编译集群。
  可以使用具有多处理器(多核)编译主机的所有处理资源。
  可以充分使用具有不同处理速度的编译主机,使整体编译性能达到最优。
  可以保证参与编译的主机不会由于编译任务而产生超负载的情况。
  考虑到了编译主机上由非编译任务所引起的负载情况。
  支持从编译集群中动态的增加或者移除编译主机。


DMUCS 以下四个部分程序组成。

dmucs 主服务程序

DMUCS 解决方案的核心服务程序。每个编译集群仅运行一个 dmucs 主服务程序,其运行于哪一台主机上没有限制。
该程序从一个配置文件读出编译场里处理器数目和每个可能主机的“潜能”。
然后从网络接收每个编译主机的平均负载信息,编译任务数量和监控程序得到的编译请求信息。
dmucs 管理一个编译场里主机的数据库,并调度主机去编译任务,当有编译请求时给出可用的最快的主机。


Loadavg 监控程序

编译场的每个参与编译的主机上均需要启动这个程序。loadavg 定期发送编译主机的平均负载到 dmucs 服务器。
这样当某个主机的平均负载太高时,dmucs 服务器会将不会优先给该主机分配编译任务。


gethost 编译命令

gethost 是具体进行编译的命令,其运行于distcc之上。
该命令从运行dmucs主服务程序的主机获取编译集群中的机器信息来获得放进 DISTCC_HOSTS 环境变量里的主机,然后调用所分配的编译机进行编译。
在编译结束后,gethost 释放所分配的主机到 dmucs 主服务器。用户使用“make CXX=gethost distcc”来启动编译。


Monitor 管理程序(非必须)

编译集群的管理员可以使用这个程序监控编译场的繁忙情况。


distcc

distcc 会安装如下可执行文件:

distcc

整个编译任务通常由一台机器发起,在 distcc 编译环境下,这台机器被称为 client,client 必须使用 distcc 来替代原有的 GNU 编译器命令,
由于 distcc 的后台编辑程序仍然是 GNU 编译器,distcc 与 gcc, g++, cc, c++ 等程序的编译参数兼容。
distcc 必须与 Make 命令的 -j 参数协同使用,client 机通过指定此参数来定义并发编译的任务数。在默认情况下,一台编译机的 distcc 允许的并发任务的数量是 CPU 数量 +2。


distccd

distccd 是运行在编译场内各个节点上的 distcc 代理程序,distccd 的常用参数如下:
  -j: 指定可以在本节点上运行的最大任务数;
  -N: 如果编译节点上运行有其他重要任务,可以通过指定 -N 参数来调整编译进程的运行优先级;
  -a: 指定 distccd 可以接受来自哪些节点的连接请求,-a 参数的值可以是一个网段,也可以是所有编辑节点主机名 (IP 地址 ) 的列表。


distccmon-text

可以通过运行 distccmon-text 来通过一个字符界面监控整个编译任务,此命令唯一的参数是监控任务的刷新间隔 ( 秒 )。


distccmon-gnome

一个图形化的监控前端,下图是此程序的一个运行实例。其中,任务进度指示条颜色的意义分别为:
绿色:compiling;紫色:preprocessing;蓝色:receiving;橙色:connecting;白色:idle;


Distcc配置

Distcc client 通过配置环境变量 DISTCC_HOSTS 来指定编译场中的各个节点,具体命令如:
export DISTCC_HOSTS=”192.168.1.1, 192.168.1.2”


还有配置文件/etc/default/distcc


dmucs配置

在/etc/init.d/distcc中添加
ps aux | sed ‘/grep/d’ | grep -q loadavg || loadavg -s A-desktop &
DEAMON_ARGS=”-j4 –pid-file……”


loadavg -s 是为了向主机A-desktop发送编译负载信息。
最后在DEAMON_ARGS参数中加入-jN,N表示当前机器上将运行几个编译进程。


配置dmucs
sudo vi /etc/default/dmucs
SERVER=yes
dmucs 是用于接收loadavg发送过来的编译负载信息,以及编译数据,用于在server端建立一个TCP的监听端口。
默认监听端口为9714,client端loadavg会与server端的dumucs建立TCP连接,发送和接收数据
可以用lsof | grep 9714 查看dmucs,当然先要启动dmucs。
最后再server上启动dmucs,和 distcc 守护进程
sudo /etc/init.d/dmucs start
sudo /etc/init.d/distcc start
在distcc中运行loadavg的意思是,server也参与编译工作。自己做自己的client。


client端设置,以B为例,C以此类推
设置/etc/default/distcc
sudo vi /etc/default/distcc
STARTDISTCC=”true”
ALLOWEDNETS=”127.0.0.1 192.168.0.1/16”
LISTENER=”192.168.0.2”
其他参数不变。
一般来说,LISTENER设置为本机Ip就可以了。


所以我们可以用如下命令代替,它可以自动搜索到本机ip,所以即使client重启ip变了,也不用担心
LISTENER=`ifconfig | grep ‘192.168’ | cut -d: -f2 | awk ‘{ print $1}’ | head -n1`


启动distcc 守护进程,client端不用启动dmucs,
sudo /etc/init.d/distcc start


最后在client端设置编译参数:
sudo vi ~/.bashrc
加入:
export ARCH=arm
export CROSS_COMPILE=”gethost –server A-desktop distcc arm-XXX-eabi-”
以同样的方式配置C完成以后就可以开始编译了,


选取一个linux kernel编译
make arm_defconfig
make -j20
console 会显示distcc的编译相关信息,
同时可以在当前编译的机器上,另外开一个console上输入distccmon-text来查看distcc分派编译包的信息


从官网的说明

60-second instructions


  1. For each machine, download distcc, unpack, and do
    ./configure && make && sudo make install
  2. On each of the servers, run distccd –daemon, with –allow options to restrict access.
  3. Put the names of the servers in your environment:
    export DISTCC_POTENTIAL_HOSTS=’localhost red green blue’
  4. Build!
    cd ~/work/myproject; pump make -j8 CC=distcc



pump mode

注意dictcc3版本以后添加了pump模式。
In “pump” mode, distcc sends source file with their included header files to the compilation servers, which now carry out both preprocessing and compilation. As a result, distcc-pump can distribute files up to 10 times faster to compilation servers than distcc.


distcc-pump is easily deployed through a wrapper script around an existing build command, such as ‘make’. Pump mode uses the system header files from the compilation servers, so it works best if all of your compilation servers are configured identically, or if you use cross-compilers that come with their own system header files.


编译器如果是绝对路径

If the compiler name is an absolute path, it is passed verbatim to the server and the compiler is run from that directory. For example:
distcc /usr/local/bin/gcc-3.1415 -c hello.c



联合使用ccache和distcc

网上说:
联合使用distcc和ccache的效果就和仅使用 distcc一样。