关于linux下的capability

Linux的capability深入分析

从编程的角度深入分析了linux下capability的概念,相关工具 libcap

学以致用:

Docker容器往往被剥夺了很多能力,如此在容器启动之后想做一些能力之外的事情该咋办?其实,容器毕竟是容器,和虚拟机是有区别的,我们可以可以限制容器内进程的能力,我们也可以把一个具有超能力的进程放到容器里面执行,这样就可以做一些原本容器没有能力做到的事情,比如: docker exec –privileged … 。

如下图:

结论: 我们不担心容器是在非特权模式下启动的,只要我们想要权限,就可以通过某种方式(如: –privileged)将一个特权进程放到容器里面做特权的事情

etcd 的访问控制

https://coreos.com/etcd/docs/latest/auth_api.html

auth enable 之前必须添加root用户,添加时设置密码:

开启认证:

添加一个非特权账号:(注意,这时候就需要有权限的用户来操作了)

查看有哪些账号:

添加角色:

给角色添加能力:

通过 –help 查看用法:

注意,这里只添加了 /test1 的读写权限,不包含其子目录(文件),如果需要包含,请这么写:

查看有哪些角色了:

查看指定角色的权限:

将用户添加到角色:

查看用户拥有哪些角色:

列出etcd中的所有key:(-p 选项在目录的后面添加 /)

关于用户的更多操作:

 

 

 

 

rpm打包

基础知识

  1. 打包根目录,默认user home目录下的rpmbuild,如下:

     
  2. 打包共需要几个子目录,如下:

    其中:
    SPECS:spec文件目录,其实你完全可以spec文件放在任意你喜欢放置的地方
    SOURCES: 源码目录,spec中Source指定的文件如果是相对路径就来这里找,当然,Source可以是一个url地址; 该目录位置由 _sourcedir 定义,如:

    该目录可以没有,如,通过命令行选项的–define来定义一个指定的路径,如下:(将源码目录定位到当前目录)

    BUILD:%setup指令默认会将源码解压缩到该目录,并在该目录下进行编译
    BUILDROOT:%make_install 宏会将需要安装的文件安装到该目录下,用于生成rpm包
    RPMS:编译好的二进制rpm文件以及debuginfo rpm文件会放到该目录下,如:

    SRPMS:一般来讲,二进制RPM是指定平台下的,在不同的平台下需要重新编译,而SRPMS目录是用于放置带有源码的rpm文件,该rpm文件在安装时会进行及时编译的,如:在对openresty进行rpm打包时有如下srpm文件生成:

    源码的rpm包包含需要的源码和一个spec文件(二进制的rpm包不包含spec文件)
  3. 基于源码编译的rpm包一般(rpmbuild -ba )会产生三个文件,openresty打包产物如下:
  4. 其实你完全可以在spec文件中没有编译操作而只是把预先准备好的二进制文件install到BUILDROOT下,然后生成二进制的rpm文件,但是似乎没有同时产生三个rpm文件显得专业

SPEC文件说明

spec文件中有一大堆的宏和类似宏的东西,搞懂这些对rpm打包极为重要。

%setup

该宏的作用是,将源码安装到BUILD目录下,无参情况下的%setup等价于:

-n选项

如果source解压后的目录名不是spec中定义的 %{name}-%{version}, 那么目录切换就会出错,这时候可以使用-n参数,如: %setup -n phpor ,则上面逻辑中的 %{name}-%{version}将会是-n指定的参数phpor

-c选项

如果源码目录不是在一个指定的目录下,如: 对于软件phpor-1.0,目录结构如下:

如果: tar -zcf phpor-1.0.tar.gz phpor-1.0  则比较正常

如果: cd phpor-1.0 && tar -zcf phpor-1.0 * 则解压出来将没有phpor-1.0这个目录,而是x1 x2 x3 三个目录(或文件),这时候就需要在打rpm包的时候预先创建phpor-1.0这个目录,然后解压到该目录,这就是使用-c的作用了

更多参考: http://www.rpm.org/max-rpm/s1-rpm-inside-macros.html#S3-RPM-INSIDE-SETUP-MULTI-SOURCE

 

%prep

其实该宏很简单:

%install

%makeinstall

%make_install

%files

最好写的细一些,不要直接写 / (根目录)

  1. 配置文件使用%config指定
  2. 如果需要往 /etc/init.d 下安装启动脚本,则更不能直接写 / ,因为这意味着还要安装目录 /etc/init.d ,而该目录已被chkconfig包所使用,会出现冲突,所以可以添加对chkconfig包的依赖(当然不明确声明也没关系,只要/etc/init.d 目录存在即可),然后只安装需要安装的/etc/init.d/下面的脚本

%config

配置文件可以通过该指令指定,配置文件比较特殊,有时候期望卸载软件的时候配置还能保留;rpm是这么处理配置文件的:

  1. 如果配置文件没有修改过,卸载软件时删除
  2. 如果配置文件修改过,卸载软件时备份到其他文件,如:

     

rpmbuild 选项说明

-bp:只解压缩文件,不编译

-bc:编译但不安装

-bi: 编译、安装(到$RPM_BUILD_ROOT目录下),不删除 $RPM_BUILD_ROOT目录,不生成rpm文件,方便查看安装的位置是否正确

-ba:即生成源码rpm包,也生成二进制rpm包
其实源rpm包比较好创建,就是把指定的source和spec两个文件打成一个rpm压缩包;如果是自己已经编译过的目录(不让rpm来编译,就不会有debuginfo rpm包)

-bb: 只生成二进制rpm包

-bs:只生成源码rpm包

-t*:直接build指定的tar包,该tar包中包含了spec文件

rpmbuild debug

rpmbuild总是根据spec文件生成一个shell脚本,该脚本是%_tmppath 下的一个临时文件,具体查看 %_tmppath 的方式:

通过查看生成的shell脚本就能比较清楚地知道rpm是如何工作的,宏扩展后具体是什么

另: spec文件中是可以直接写shell脚本的,如:

 

注释

#标识注释, 注释中的%也需要用%%代替,否则也会执行宏扩展

 

 

关于PHP打rpm包:

  1. 由于扩展的安装没有参考prefix的定义,而是直接安装到系统里面的,所以执行%makeinstall 时,并没有将扩展安装到buildroot下,所以,打包的时候就打不进去,这个需要看一下如何解决

调试技巧

  • rpm打包比较浪费时间的事情是: 源码编译耗时比较长,但是spec文件容易写错,编译出问题的情况比较好解决,因为通常会把编译没有问题的命令贴到spec文件中,问题是安装部分可能会不是太满意,需要多次调试,而rpm打包总是经历编译的步骤,失败后编译使用的临时文件和目录都会删除掉,就是说,每次调试都要经历漫长的编译,然后发现安装部分还是有点儿小问题;解决办法:

办法1: 写spec文件时不删除已编译的文件,下次把%setup %config %build 部分都去掉,直接install,不过这时候需要在install的时候显式的cd到指定的目录

办法2: 直接提供编译好的文件作为源文件,然后spec文件中不写编译语句

办法3: make 时添加选项: %{?_smp_mflags}   即:如果是多处理器的话则并行编译

  • 如何了解rpm打包每个步骤都做了什么?
    rpm的好些阶段都是可以直接写shell命令的,我们不妨添加一些暂停语句使之暂停来查看其生成的中间脚本文件,如:添加 read -p “press enter to continue…” ,示例如下:

     

 

 

rpmbuild中间脚本文件:

实例:

从该脚本来看,里面并没有出现解压缩源码包(%setup)部分的逻辑,即:其中没有包含%prep %setup 部分逻辑,而是从%build部分开始的,%build 逻辑:

其实,%prep (包含%setup逻辑)是一个单独的临时脚本文件,如下:

Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.L3sv7t

%build 部分是一个单独的脚本,一般包含./configure && make,如下:

Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.oaK7vF

%install是单独一个中间shell脚本,如下:

Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.f9kumb

其中包含生成debuginfo,如果不想生成debuginfo,则可以在 ~/.rpmmacros 中添加:

 

 

参考: 制作PHP rpm包

 

Docker 之 修改容器(镜像)存储大小

较早的docker容器默认10G大小的磁盘,现在默认100G大小的磁盘;可能出于某种考虑觉得太小,或者觉得太大,那么如何调整呢?可以不重启docker daemon吗?可以不重启容器吗?如何直接调整镜像的磁盘大小使得以后申请的容器都更大(或更小)?

参考文章: https://jpetazzo.github.io/2014/01/29/docker-device-mapper-resize/

  1. 容器磁盘大小和镜像没有(多少)关系,和dockerd有关系
    1. 如果镜像大小为200G,100G的磁盘大小也不够用,要么dockerd带不动,要么修改dockerd的默认配置
    2. 1.9.1的Base Device Size是107G;容器磁盘大小和所有容器总大小相同(其实可以不同)
    3. 1.11.1的Base Device Size是10G
  2. 动态调整容器磁盘空间:
    脚本:(该脚本不好使)

    1)如果容器未启动,则/dev/mapper/$dev 不存在
    2)如果容器已启动,则/dev/mapper/$dev 显示设备繁忙
    总之,该脚本不好使
  3. 容器的dm文件并不是总是存在的,随着弄起的启动而创建,随着容器的停止而消失:

    注意: 磁盘文件的那一长串字符不同与容器ID
  4. 根本的解决办法

 

PHP编译问题

php 的configure选项中启用模块时,有些是–with-extname ,有些是 –enable-extname ;有何区别?

  1. –enable-extname  用于模块不需要任何依赖,可以直接开启
  2. –with-extname 用于模块有第三方依赖,需要先安装依赖
  3. 其它就没有差别了
  4. 参考: http://stackoverflow.com/questions/1255055/compiling-php-with-modules-vs-using-shared-modules

如何将尽可能多的模块编译为动态扩展?

 

如何编译所有模块:

./configure –enable-all=shared

如何编译除去指定模块外的所有模块:(下面去掉了两个不太熟悉的模块)

./configure –enable-all=shared –with-enchant=no –with-gmp=no

如何在with模块的时候指定依赖的安装路径:

 

编译选项示例:

 

如何将php.ini php-fpm.conf  php.d php-fpm.d 都放到/etc/php 目录下?如下:

编译选项:

–sysconfdir=/etc/php   : 指示 php-fpm.conf   php-fpm.d 安装到 /etc/php 下

–with-config-file-path=/etc/php :指示 php.ini 安装到 /etc/php 下

–with-config-file-scan-dir=/etc/php/php.d : 指示 php.d 安装到 /etc/php 下

 

php.ini 的安装位置和 –sysconfdir没有关系,默认 ${prefix}/lib

php-fpm.conf 的位置和–with-config-file-path 没有关系,默认 ${prefix}/etc 下

 

 

 

Docker 小知识

我们知道,Docker不过是通过资源隔离实现的,通过一定的手段总是可以不进入容器就可以看到(已启动)容器中的所有东西;一般来讲,我们可以通过docker命令提供的exec来进入容器,但是该方法是通过docker daemon完成的,如果docker daemon出现问题无法响应,就不好玩了; 然后,我们就会考虑通过nsenter来进入容器所在的名字空间中;

其实,就算没有nsenter,我们同样也能做一些看似很NB的事情的,如下图,195106是某容器的进程,我们就可以在宿主机上通过如下图的方式进入容器的文件系统,然后可以随意修改文件

shell 小知识点

实例:

脚本功能说明:

不断连接(然后立即断开)本地80端口;如果连接时间超过0.9s则输出连接时长并退出脚本

  1. 通过TIMEFORMAT来设置time命令的输出,time命令的选项不走参数,走环境变量;具体格式man time
  2. nc -z选项的行为是,连接后立即断开,相当于扫描; -v选项输出扫描结果提示,这里面直接扔掉了,所以该选项是多余的;nc似乎没有连接超时的设置
  3. bash不支持浮点数的运算与比较,借助bc实现;对于比较运算,为真输出1,为假输出0; 和bc的返回值无关
  4. bash中支持模运算,上面也用到了
  5. time的输出是输出到标准错误的,一般情况下我们没法直接接收到标准错误的输出,这里就把time放到了一个子shell中执行,然后将子shell的标准错误重定向到标准输出,然后赋值给变量t;所以,需要注意一点的是,time前面的两个圆括号之间是有空格的,少了空格就成了算数运算了(结果必错)

上面脚本执行比较慢,如果是多核的话,可以稍微修改一下:(该脚本未测试,可能不好使)

 

相关参考: http://blog.csdn.net/gengshenghong/article/details/7583580

上面脚本每秒中才能跑300次,怀疑time、nc命令消耗了较多的时间,“改良”脚本如下:

这里省去了time、nc,但是出现了两次date命令;测试结果和上面脚本几乎一样

参考资料: http://www.cnblogs.com/chengmo/archive/2010/10/22/1858302.html

 

如何检测是哪个进程发起了tcp主动请求

一个不能正确工作的脚本 monitor.php:

不能正确工作的原因:

  1. 可能tcpdump没有立即输出抓到的数据包,当输出时连接已经断开许久了
  2. 就算tcpdump能足够快地输出抓到的数据包,执行ss的时候也可能已断开连接(这个至少不至于一个抓不到)
  3. 就算本地主动关闭连接的情况下,连接会一段时间内处于timewait状态,但是timewait状态是不关联pid的哦

验证猜想缓存问题导致的方法:

  1. 启动检测脚本: php monitor.php
  2. nc打开百度80端口连接,然后 ctrl+z
  3. while :; do curl  http://baidu.com ;done
  4. 检测脚本输出结果部分如下:

 

解决办法:

正确的解决办法是使用systemtap: https://sourceware.org/systemtap/SystemTap_Beginners_Guide/useful-systemtap-scripts.html#nettop