Fork me on GitHub

开发运维中遇到的一些小但很坑的问题

系统迁移过程中发现的一些坑(CentOS 6 到 CentOS 7),并不一定有普适性,记录在此:

TCP_NODELAY

socket 交互过程,即使一个简单的小数据包,发现网络传输上的耗时也有将近40ms(或者38ms,39ms的样子),同样的代码,在原系统上毫无问题,新的系统上就死活表现不正常。最终发现是TCP_NODELAY未开启,简单的说,就是数据包要凑够一定大小才发送,否则就等一个固定的时间间隔才发送,主要是对于以前带宽小,网络条件不好时候的特定优化,对如今耗时敏感的场景并不合适。相关介绍,请参见:TCP启用和禁用 TCP_NODELAY 有何影响?

gettimeofday

gettimeofday,在不同系统上,表现不同,最坏情况下,性能开销比较大,如果是需要计算时间间隔,可以实测与 clock_gettime 的对比(建议简单写个循环,测试一下耗时,在我们的环境上,gettimeofday 进入了系统调用,开销差了10倍)。相关讨论见:faster equivalent of gettimeofdaygettimeofday() should never be used to measure time

CentOS 7 更换内核

centos 7上更换内核的文章很多,除了 rpm 或者 yum 安装新内核的包外,就是更改 grub 的配置了,绝大多数文章也会说到先查看新内核 menuentry 的序号,然后修改/etc/default/grub 中 GRUB_DEFAULT 为对应的序号即可,或者将GRUB_DEFAULT 改为 saved 然后使用 grub2-set-default 设置新内核的序号或者 title 即可,然而……我遇到的情况则是以上操作重启后均未生效,百般实验,最终实践证明,grub2-mkconfig -o /boot/grub2/grub.cfg 使之前的操作生效,这一步应该是在重启前,修改 grub 配置之后,最终证明靠谱的操作顺序是:

# 安装对应的内核rpm 包
rpm -ivh --oldpackaged --replacefiles  [kernel-headers.x86_64  kernel-devel.x86_64 kernel-tools-libs.x86_64 kernel-tools.x86_64 kernel.x86_64]

# 查看新内核 menutry 启动顺序
cat /boot/grub2/grub.cfg |grep menuentry

# 设置内核启动顺序
sed -i "s/GRUB_DEFAULT=0/GRUB_DEFAULT=1/g" /etc/default/grub

# 设置生效
grub2-mkconfig -o /boot/grub2/grub.cfg

GPU 显存变小

事情是这样子的,有一批P4的线上 GPU 机器是分批到位的,cuda 驱动也由厂商安装好,安装线上服务时发现第一批机器显存比其它机器要少接近500M,导致不能与其他机器一样同构部署。具体表现是使用 nvidia-smi 命令查询时,显示显存只有 7606 MiB,而正常的机器则有 8115 MiB。这个问题困扰了好几天,各种更换内核,重装驱动未果,正准备让厂商替换时,突然发现其 ECC 并没有关闭,使用 nvidia-smi -e 0 关闭重启之后,发现显存正常,恍然大悟。ECC memory 是一种修正错误内存技术,需要占用一定存储空间,关闭之后不进行检查,性能也能有10%~15%的提升。果然各种奇奇怪怪的问题最终的原因都很简单

其它

...

caffe源码编译安装要点

  • 编译ImageMagick:

    ./configure --prefix=/home/***/*** --with-quantum-depth=8 --without-x --without-xml --without-tiff --without-openexr --without-fontconfig --without-freetype --with-jpeg=yes --disable-openmp --disable-docs LDFLAGS="-L/home/***/***/lib" CPPFLAGS="-I/home/***/***/include"
  • openblas编译:

    修改 CC = ~/.jumbo/opt/gcc46/bin/gcc,FC = ~/.jumbo/opt/gcc46/bin/gfortran,COMMON_OPT = -O2
    注释COMMON_PROF = -pg
  • 编译protobuf:

    修改src/google/protobuf/io中的kDefaultTotalBytesLimit和kDefaultTotalBytesWarningThreshold,去除message=64M的限制,重新编译安装
    CXXFLAGS="-O3 -fPIC" ./configure --prefix=/home/tcheng/protobuf/ --enable-shared --enable-static
    这样编译出来可移植的静态库
  • boost编译安装:(最好安装在系统目录下)

    ./bootstrap.sh --prefix=path/to/installation/prefix
    ./b2 install
    静态库增加-fpic选项的方法: 
    http://blog.csdn.net/lzshlzsh/article/details/50110771
  • Opencv的安装:

    mkdir build && cd build && ccmake .. 
    a. 安装的时候最好去除各种不必要的选项,避免最终编译连接时,需要引入各种静态库
    b. 动态库和静态库需要分别编译,动态库用于caffe编译,静态库用于服务编译
    c. Png和Jpeg库最好从Opencv带的3rdparty源码中编译出来,有版本限制,最好将系统自带库升级到相应版本以上,注意升级原有libpng相关的代码
    d. 注意opencv 编译的时候,需要检查依赖库的路径,是否指向的是期望的版本,存在多个版本的时候会出现编译使用的不是期望版本的问题。
    e. cuda 8.0 需要 opencv 2.4.13 的版本,老版本编译有错
  • caffe安装:

    修改Makefile.config,make all, make test, make runtest
    caffe需要修改CUDA_CHECK宏,避免程序结束资源释放出错的问题
    CUDA 8.0+CUDNN 5的时候需要修改源代码,根据出错信息调整函数名称或者参数

并行执行任务的 shell 脚本

工作中有时会需要跑一些任务处理的脚本,其处理的任务相对单一,但数据量大,导致单次运行脚本的时间比较长,如果任务是互相独立并且是可以分片的,那么,并行处理无疑是更快的方式。

在 shell 中,可以使用管道,生产者-消费者模型来实现任务的并行处理。下面提供一个 shell 脚本,自定义其中的 run 函数,可以实现指定并发度的并行:

#!/bin/sh

# 定义同时执行的任务数量,相当于并发度
SUB_PROCESS_NUM=20

# 参数解析,-s: 设置SUB_PROCESS_NUM,即并发度; -h: 输出帮助信息
while getopts :s:h opt
do
    case $opt in
        s) let SUB_PROCESS_NUM=OPTARG+0 ;;
        h) echo -e "Usage\n"
           echo -e "\t$0\t-s\tCocurrnet subprocess num\n"
           echo -e "\t\t\t-h\tPrint this help info\n"
           echo -e "\tAny questions pls feel free to contact  frostmourn716@gmail.com\n"
           exit 0;;
        *) echo "unknown option: $opt" ;;
    esac
done

#准备命名管道
tmpfile="$$.fifo"
mkfifo $tmpfile
exec 6<>$tmpfile
rm -rf $tmpfile

#producer写入任务标记
for ((i=1; i<=$SUB_PROCESS_NUM; i++))
do
    echo "sub job"
done >&6

# 需要自定义的任务函数,比如这里是发送请求
run(){
    ts=`date +%y%m%d%H%I%S`
    echo -e "$ts\tprocess $1"
    curl -s -X POST --data-urlencode "content@$1" --data-urlencode "id=$RANDOM" "http://*.*.*.*:8080/test.php" >/dev/null 2>&1
}

# 具体分配执行任务的代码,以处理图片为例,遍历图片,以指定的并发度处理任务
# 每个任务执行结束之后会将标记写入管道,当管道中存在任务标记时,代码会读出并且启动下一个任务
for image in `ls ./images/*`
do
    read line
    (run "${image}" >> ./log 2>&1; echo "sub job") >&6 &
done <&6

一些 shell 片段

记录一些 shell 片段,日常运维开发工作中方便使用:

  1. IP 列表排序

    sort -t'.' -k1,1n -k2,2n -k3,3n -k4,4n
  2. shell 命令获取唯一 id 的一种方法

    openssl rand -hex 12
  3. 获取使用了特定文件的进程 ID

    /sbin/fuser -v [文件路径]
  4. 添加帐号与删除帐号

    #!/bin/bash
    USERNAME=$1
    PASSWD=$2
    echo "adding user ${USERNAME}..."
    useradd -d /data/${USERNAME} -m ${USERNAME} && echo ${PASSWD} | passwd --stdin ${USERNAME} && chmod 755 /data/${USERNAME} && echo "succeed"
    userdel -r ${USERNAME}
  5. 转置文件(每行列数相同)

    #!/bin/bash
    awk 'BEGIN{ORS=""} {
        for(i=1;i<=NF;++i){
            contents[i,NR]=$i;}
        } 
        END{for(x=1;x<=NF;++x){
                for(y=1;y<=FNR;++y){
                    print contents[x,y];
                    if(y<FNR){print " ";
                }
            };
            if(x<NF){print "\n";}
    }}' file.txt

一些有用的 Github 小项目

记录一些有用的 Github 工具类型的项目,经常需要的时候找不到,现找又很费时间,记录在此:

  • 代理列表:https://github.com/fate0/proxylist,包含国内外的透明、匿名、高匿的 http 和 https 代理,7分钟更新一次。顺道提供一段脚本,自动解析属地在中国的高匿 https 代理,如下:

    #!/bin/sh
    curl -s 'https://raw.githubusercontent.com/fate0/proxylist/master/proxy.list#' | grep 'high_anonymous' | grep '"CN"' | grep '"https"' | awk 'BEGIN{OFS=":"} {if(match($0, /\"host\": \"([0-9\.]+)\".*\"port\": ([0-9]+)/, a)){print a[1], a[2]}; if(match($0, /\"port\": ([0-9]+),.*\"host\": \"([0-9\.]+)\"/, a)){print a[2], a[1]};}'
  • 超级速查表:https://github.com/skywind3000/awesome-cheatsheets,常用命令小卡片,主要包括 Bash,GDB,VIM 等
  • 待更新...