月度归档:2014年10月

[MySQL FAQ]系列 — 修改my.cnf配置不生效

问题
修改了 my.cnf 配置文件后,却不生效,这是怎么回事?
原因
我们注意到,这里只说了修改 my.cnf,并没有说清楚其绝对路径是哪个文件。也就是说,有可能修改的不是正确路径下的my.cnf文件。

 

在MySQL中,是允许存在多个 my.cnf 配置文件的,有的能对整个系统环境产生影响,例如:/etc/my.cnf。有的则只能影响个别用户,例如:~/.my.cnf。

MySQL读取各个my.cnf配置文件的先后顺序是:

  • /etc/my.cnf
  • /etc/mysql/my.cnf
  • /usr/local/mysql/etc/my.cnf
  • ~/.my.cnf
  • 其他自定义路径下的my.cnf,例如:/data/mysql/yejr_3306/my.cnf

不管是mysqld服务器端程序,还是mysql客户端程序,都可以采用下面两个参数来自行指定要读取的配置文件路径:

  • –defaults-file=#, 只读取指定的文件(不再读取其他配置文件)
  • –defaults-extra-file=#, 从其他优先级更高的配置文件中读取全局配置后,再读取指定的配置文件(有些选项可以覆盖掉全局配置从的设定值)

因此,可以看到,如果你修改的是非“著名”目录下的 my.cnf,有可能看起来是不生效的,需要自行指定,或者统一放在 /etc/my.cnf 下,采用多实例的方式来管理即可。

福建技术嘉年华:20141018技术沙龙活动PPT

本次活动的PPT已经上传到百度云盘上,请点击这里 查看!
本次活动的视频已经上传到百度云盘上,请点击这里 查看!

下次活动预告:
时间:2015年1月
地点:待定(预计在软件园,可容纳100多人)
主要内容:系统运维、数据库、运维开发、大数据、移动开发、测试、安全等领域,我们也非常欢迎其他同行可以主动报名提供主题分享!
敬请关注!

Docker基础与高级

  1. Docker安装
  2. devicemapper
  3. 玩转net namespace
  4. port map
  5. 直接使用docker默认分配的IP对外提供服务(测试中)
  6. Docker COMMAND
  7. 搭建私有Registry注册中心
  8. docker with HTTPS
  9. Docker Web-UI(shipyard)
  10. 镜像制作
  11. 内置bridge(nat)和自建网络桥接使用区别
  12. Docker Event事件监听
  13. 神器
  14. FAQ

tag: cloud, virtual, docker, lxc

1. Docker安装

  • 按照官方说明:红帽6、centos均通过epel源,执行yum install docker-io进行docker安装,启动服务是service docker start
  • 经测试,红帽6、centos也可以通过在官网上下载编译好的二进制文件到本地也可以使用,但需要提前手动执行service cgconfig start来挂载cgroup,然后./docker-latest -d来启动服务。
    下载地址:https://get.docker.io/builds/Linux/x86_64/docker-latest
    https://get.docker.io/builds/Linux/x86_64/docker-1.0.1
    

但是官方提示需要内核大于3.8版本,否则可能会有问题。el>3.8

2. devicemapper

  • 扩容存储池大小、扩容容器文件系统大小
    https://www.dockboard.org/resizing-docker-containers-with-the-device-mapper-plugin/
    
    实验成功,但是一旦容器关闭再启动,就会报错,还得根据文档再做一次dmsetup load; dmsetup resume才能成功启动容器(但是如果不先启动容器,就无法使用dmsetup命令来resume),因此能否用于生产环境有待继续探索
    

3. 玩转net namespace

  • 首先要支持ip netns指令。而红帽6及epel均不支持,解决方法:
    yum install -y http://rdo.fedorapeople.org/rdo-release.rpm
    yum update -y iproute
    
  • ip netns
    直接执行这个命令(或ip netns list)读取的是/var/run/netns下的文件名,因此若不存在/var/run/netns,需要mkdir -p /var/run/netns
    
  • 配置像LXC一样的网络
    I. 宿主配置
      1. 宿主上升级iproute包,以便支持ip netns指令:
        yum install -y http://rdo.fedorapeople.org/rdo-release.rpm
        yum update -y iproute
    
      2. 在宿主上配置好桥接:
        一. 方法1(不推荐): 敲命令配置桥接(很容易导致网络中断,需要ILO连上操作)
          1) 创建桥接网卡br1并激活:brctl addbr br1; ip link set br1 up
          2) 配置br1的mac地址,和宿主准备桥接的网卡mac相同,通常为内网网卡eth1:ip link set br1 address xx:xx:xx:xx:xx:xx
          3) 给br1配置一个ip地址,或者将eth1的ip地址配置在br1上,2种方法任选其一都可行:
           前者:
           ifconfig br1 192.168.2.1 netmask 255.255.255.0
           后者:
           ifconfig eth1 0.0.0.0; ifconfig br1 192.168.2.2 netmask 255.255.255.0
          4) 配置宿主网关,从br1出
           ip ro del default
           ip ro add default via 192.168.2.254 dev br1
          5) 将eth1桥接至br1:
           brctl addif br1 eth1
        二. 方法2(推荐):写网卡配置文件
          ifcfg-br1:
    		DEVICE="br1"
    		TYPE="Bridge"
    		NM_CONTROLLED="no"
    		ONBOOT="yes"
    		BOOTPROTO="static"
    		IPADDR=192.168.2.2
    		NETMASK=255.255.255.0
    
          ifcfg-eth1:
    		DEVICE="eth1"
    		BRIDGE="br1"
    		BOOTPROTO="none"
    		NM_CONTROLLED="no"
    		ONBOOT="yes"
    		TYPE="Ethernet"
    
          注意:要在/etc/sysconfig/network-scripts/ifup-eth里if [ "${TYPE}" = "Bridge" ]; then -> fi段落最后(fi前)加个ip link set br1 address $(get_hwaddr eth1),防止桥接网卡mac地址随机生成导致网络暂时中断
    
        service network restart		# 重启网络生效
    
    II. 容器配置:
      1. 启动docker容器:
           docker run -t -i -d --name="net_test" --net=none centos:latest /bin/bash
           记录下输出(即CONTAINER ID),然后通过docker inspect -f '{{.State.Pid}}' CONTAINER ID获得该容器的pid(也即容器首进程在宿主上的pid),假设为1000
      2. 为容器创建网卡命名空间,建立点对点连接(容器命名空间网卡和宿主上生成的网卡点对点)
           mkdir -p /var/run/netns		#创建网络命名空间目录,ip netns会读取该目录下的文件名
           ln -s /proc/1000/ns/net /var/run/netns/1000		#将网络命名空间文件软链接到/var/run/netns,以便ip netns能够读取
           ip link add vethA type veth peer name vethB		#在宿主上创建2张直连网卡(vethA与vethB直连),将vethA作为容器里的网卡,vethB作为宿主上能看到的网卡
           ip link set vethB up			# 激活网卡vethB
           ip link set vethA netns 1000		# 将刚才创建的网卡归位网络命名空间
           配置vethA网卡参数:
             ip netns exec 1000 ip link set vethA name eth1
             ip netns exec 1000 ip addr add 192.168.2.3/24 dev eth1
             ip netns exec 1000 ip link set eth1 up
             ip netns exec 1000 ip route add default via 192.168.2.254 dev eth1
           brctl addif br1 vethB			# 将eth1桥接至br1
      3. 测试:
          docker attach登录容器,查看是否能ping通网关及其他子网或公网
    

3.1. ENV(环境变量)

  • Dockerfile支持ENV参数,表示启动容器时候设置变量。

只在CMD启动的进程export设置变量,而不是将变量赋值命令写入/etc/profile等脚本里,因此通过ssh方式登录容器获得的shell是没有这个变量的

4. port map

  • docker支持端口映射,通过iptables DNAT将宿主上的端口转发至容器ip对应端口。

虽然配置了端口映射后,在宿主上通过netstat -lntpu可以看到docker进程会监听这个端口,但是还没发现其作用,因为流量直接从iptables就进入容器里。

  • docker run -p、docker run -P、docker port作用:
    docker run -P 就是将image定好的port给做个端口映射(若没指定-p,则外部端口随机)
    docker run -p "8080:80" 启动容器时候做端口映射:宿主的0.0.0.0:8080 -> 容器80
    docker run -P -p "8080:80" 假如image已经有一个port 22的配置,那么就会做2个端口映射:宿主0.0.0.0:xxxxx -> 容器22、宿主0.0.0.0:8080 -> 容器80
    docker port 是查看容器已经做了端口映射的端口被映射到了哪个端口上,其实直接用docker ps就能看到,使用docker port可能是为了方便二次开发
    

5. 直接使用docker默认分配的IP对外提供服务(测试中)

5.1. 使用参数以及将docker0的ip配为机房内网网段

  • 将宿主eth1桥接到docker0上,将docker0的ip更改为原来eth1的ip(机房内网网段)

存在一个问题:docker run时候分配的ip是从1开始,到254。因此存在和网关或者其他机器ip冲突的可能,无法避免。因此docker分配ip不会做ping检查是否存活

docker run分配出去的ip,docker kill并且docker rm都不会收回并重新使用,而是重启docker daemon才会将ip收回

  • –iptables=false
    使用这个参数后,就不会再往iptables里生成nat、forward等信息了。
    
    这样启动的容器,登录容器能看到网关是宿主docker0的ip,这样网络是通的,是可以访问外网,但路是这么走的:
    1. 容器里的数据包将数据经过point-to-point网卡传送到宿主的对应veth网卡上
    2. 宿主veth网卡接收到数据包后发现网关是254,于是通过icmp数据包告知网关是254,然后容器发送数据包时自动将网关更改为254,可以从ping的输出看到:
    [ 17:37:23-root@21e77bf38fc0:~ ]#ping www.baidu.com
    PING www.a.shifen.com (115.239.210.27) 56(84) bytes of data.
    64 bytes from 115.239.210.27: icmp_seq=1 ttl=55 time=13.9 ms
    From 192.168.3.1: icmp_seq=2 Redirect Host(New nexthop: 192.168.3.254)
    64 bytes from 115.239.210.27: icmp_seq=2 ttl=55 time=13.6 ms
    From 192.168.3.1: icmp_seq=3 Redirect Host(New nexthop: 192.168.3.254)
    64 bytes from 115.239.210.27: icmp_seq=3 ttl=55 time=13.6 ms
    From 192.168.3.1: icmp_seq=4 Redirect Host(New nexthop: 192.168.3.254)
    64 bytes from 115.239.210.27: icmp_seq=4 ttl=55 time=14.0 ms
    From 192.168.3.1: icmp_seq=5 Redirect Host(New nexthop: 192.168.3.254)
    64 bytes from 115.239.210.27: icmp_seq=5 ttl=55 time=14.7 ms
    From 192.168.3.1: icmp_seq=6 Redirect Host(New nexthop: 192.168.3.254)
    64 bytes from 115.239.210.27: icmp_seq=6 ttl=55 time=13.8 ms
    64 bytes from 115.239.210.27: icmp_seq=7 ttl=55 time=13.7 ms
    From192.168.3.1: icmp_seq=8 Redirect Host(New nexthop: 192.168.3.254)
    64 bytes from 115.239.210.27: icmp_seq=8 ttl=55 time=13.8 ms
    64 bytes from 115.239.210.27: icmp_seq=9 ttl=55 time=13.6 ms
    64 bytes from 115.239.210.27: icmp_seq=10 ttl=55 time=13.5 ms
    From 192.168.3.1: icmp_seq=11 Redirect Host(New nexthop: 192.168.3.254)
    64 bytes from 115.239.210.27: icmp_seq=11 ttl=55 time=13.8 ms
    64 bytes from 115.239.210.27: icmp_seq=12 ttl=55 time=14.1 ms
    64 bytes from 115.239.210.27: icmp_seq=13 ttl=55 time=13.8 ms
    64 bytes from 115.239.210.27: icmp_seq=14 ttl=55 time=13.6 ms
    64 bytes from 115.239.210.27: icmp_seq=15 ttl=55 time=13.7 ms
    64 bytes from 115.239.210.27: icmp_seq=16 ttl=55 time=13.8 ms
    From 192.168.3.1: icmp_seq=17 Redirect Host(New nexthop: 192.168.3.254)
    64 bytes from 115.239.210.27: icmp_seq=17 ttl=55 time=13.6 ms
    64 bytes from 115.239.210.27: icmp_seq=18 ttl=55 time=13.8 ms
    64 bytes from 115.239.210.27: icmp_seq=19 ttl=55 time=13.7 ms
    64 bytes from 115.239.210.27: icmp_seq=20 ttl=55 time=14.1 ms
    64 bytes from 115.239.210.27: icmp_seq=21 ttl=55 time=13.7 ms
    64 bytes from 115.239.210.27: icmp_seq=22 ttl=55 time=13.8 ms
    64 bytes from 115.239.210.27: icmp_seq=23 ttl=55 time=13.7 ms
    64 bytes from 115.239.210.27: icmp_seq=24 ttl=55 time=13.9 ms
    64 bytes from 115.239.210.27: icmp_seq=25 ttl=55 time=14.2 ms
    64 bytes from 115.239.210.27: icmp_seq=26 ttl=55 time=13.7 ms
    64 bytes from 115.239.210.27: icmp_seq=27 ttl=55 time=13.7 ms
    64 bytes from 115.239.210.27: icmp_seq=28 ttl=55 time=13.8 ms
    64 bytes from 115.239.210.27: icmp_seq=29 ttl=55 time=13.9 ms
    64 bytes from 115.239.210.27: icmp_seq=30 ttl=55 time=14.1 ms
    64 bytes from 115.239.210.27: icmp_seq=31 ttl=55 time=13.9 ms
    64 bytes from 115.239.210.27: icmp_seq=32 ttl=55 time=13.9 ms
    64 bytes from 115.239.210.27: icmp_seq=33 ttl=55 time=14.0 ms
    64 bytes from 115.239.210.27: icmp_seq=34 ttl=55 time=13.7 ms
    64 bytes from 115.239.210.27: icmp_seq=35 ttl=55 time=14.0 ms
    64 bytes from 115.239.210.27: icmp_seq=36 ttl=55 time=14.4 ms
    64 bytes from 115.239.210.27: icmp_seq=37 ttl=55 time=13.9 ms
    

docker服务启动时候会把内核参数ip.forward给打开(数据包转发)

6. Docker COMMAND

6.1. docker参数

  • –api-enable-cors
    开启cors,以便浏览器能够通过ajax调用。但是若开启了tls,使用cors就变得困难了,目前网络上还未找到解决方案
    

6.2. run

  • –link:2个容器互通
    其实就做3件事:
    1. 若有端口映射,则在iptables的FORWARD链里将端口ACCEPT
    2. /etc/hosts:做link的容器的/etc/hosts能看到被link的容器的hosts条目
    3. 环境变量:做link的容器可以看到被link的容器的环境变量(仅为--env变量),如:ALIAS_ENV_变量名、ALIAS_NAME=xxx
    
  • –volume: 目录共享
    支持2种模式:
    1. 从宿主挂载:-v /tmp:/tmp/foo 表示将宿主的/tmp目录挂载至容器的/tmp/foo目录,可读可写,和mount --bind的效果类似
    2. 容器之间共享:
      启动第一个容器时带参数-v /tmp/foo表示在宿主上创建/var/lib/docker/vfs/dir/xxxxx(id,但不是容器id),然后挂进容器的/tmp/foo目录;
      启动第二个容器时带参数--volumes-from=b5f8320cf019(*第一个容器id)表示和第一个容器共享挂载,因此第二个容器启动后也能从df -h看到/tmp/foo目录被挂载。从inspect也可以很容易看出来(2个容器的inspect以下内容相同):
        "Volumes": {
            "/opt": "/var/lib/docker/vfs/dir/a7b1b03773d9391718b8524e7ac001bb877eb6d0596fa2a4328435d8c49f2415"
        },
        "VolumesRW": {
            "/opt": true
        }
    

7. 搭建私有Registry注册中心

7.1. 下载软件

  • 安装pip:
    yum install python-devel libevent-devel python-pip gcc xz-devel
    
  • 安装registry:
    pip install docker-registry
    pip install docker-registry[BUGSNAG]
    pip install -i http://pypi.douban.com/simple/ backports.lzma
    pip install --upgrade -i http://pypi.douban.com/simple/ backports.lzma
    

7.2. 启动服务

  • 使用默认配置文件启动服务:
    cd /usr/lib/python2.6/site-packages/config
    cp config_sample.yml config.yml
    mkdir /tmp/registry                 # config_sample.yml的默认配置
    gunicorn --access-logfile - --debug -k gevent -b 0.0.0.0:5000 -w 1 docker_registry.wsgi:application
    
  • 加入开机启动
    可以将以下内容写入/etc/rc.local,或者放在一个脚本里,/etc/rc.local调用这个脚本
    pkill gunicorn
    sleep 1
    rm -f /data/docker-registry.db
    sleep 1
    /usr/bin/gunicorn --access-logfile /opt/logs/docker-registry/access.log --error-logfile /opt/logs/docker-registry/error.log --daemon --debug -k gevent -b 0.0.0.0:5000 -w 8 docker_registry.wsgi:application
    sleep 3
    pkill gunicorn
    sleep 1
    /usr/bin/gunicorn --access-logfile /opt/logs/docker-registry/access.log --error-logfile /opt/logs/docker-registry/error.log --daemon --debug -k gevent -b 0.0.0.0:5000 -w 8 docker_registry.wsgi:application
    
  • 启动不成功FAQ
    1. 日志目录没有创建
    2. .db文件已存在,启动前删掉就行
    排错可通过error.log分析
    

7.3. 使用

  • 上传镜像
    docker tag busybox localhost:5000/busybox
    docker push localhost:5000/busybox
    
    上传成功后在/tmp/registry上应该能看到2个目录:images和repositories
    
  • 下载镜像
    docker pull localhost:5000/busybox
    
    若想给下载的镜像取个别名:
    docker tag localhost:5000/busybox aliasname 或 docker tag id aliasname  #id为localhost:5000/busybox的image id
    
    删除别名:
    docker rmi aliasname
    当然也可以选择把原始名字删掉:
    docker rmi localhost:5000/busybox
    

若一个镜像有至少2个tag,那么通过docker rmi删除的只是别名,若只有1个名字(没有别名),那么删除的是真正的镜像。

已测试:当镜像还在上传过程中时,其他机器是无法pull下来的。因此不会生成不完整的镜像

  • 搜索镜像
    curl --silent "http://192.168.1.1:5000/v1/search?q="  | json_reformat
    

7.4. 套一层透明代理(不推荐,有bug)

  • 配置nginx,vhost内容如下
    upstream docker-registry {
          server 127.0.0.1:5000;
    }
    
    server {
          listen 192.168.1.1:80;
          server_name registry.17173ops.com;
    
          proxy_set_header Host       $host;
          proxy_set_header X-Real-IP  $remote_addr;
          
          client_max_body_size 0; # disable any limits to avoid HTTP 413 for large image uploads
    
          # required to avoid HTTP 411: see Issue #1486 (https://github.com/dotcloud/docker/issues/1486)
          chunked_transfer_encoding on;
          
          location / {
                proxy_pass http://docker-registry;
          }
    }
    
  • 注意:只有以下2种情况,套一层透明代理是行的通的:
    1. nginx监听80端口,gunicorn监听0.0.0.0
    2. nginx监听非80端口,且gunicorn监听在网络可通的ip,如192.168.1.1这样的内网ip,而不能监听在127.0.0.1
    
    测试发现,当端口不为80时,在docker push时候,会首先连接image name中的ip、端口,如registry.17173ops.com:80,然后连接实际的gunicorn端口。因此当gunicorn端口无法访问时,就会报错。
    而端口为80时候就不会这样,原因未知,应该是代码逻辑。
    <=1.1.2版本都有如上共性,>1.1.2的尚未测试。
    

7.5. Web UI

  • 下载
    docker pull atcol/docker-registry-ui
    
  • 启动
    docker run --name "registry_UI" -tid -p 127.0.0.1:5001:8080 -e REG1=http://registry.17173ops.com/v1/ atcol/docker-registry-ui:latest
    
  • 开机启动:
    echo 'docker start registry_UI' >> /etc/rc.local
    
  • 套一层透明代理
    配置nginx,vhost内容如下
    upstream docker-registry-web {
          server 127.0.0.1:5001;
    }
    
    server {
          listen 192.168.1.1:80;
          server_name registry-web.17173ops.com;
    
          location / {
                proxy_pass http://docker-registry-web;
          }
    }
    
  • 使用
    http://registry-web.17173ops.com/
    
  • 注意:页面上的删除镜像只是删除tag标签,实际id和image不会删除
    docker镜像的元数据里记录着FROM哪个镜像,如果真的删除镜像所有数据,正常逻辑应该是A->B B->C -C->D D->E,这样一连串都给删除了,但是其中的某个镜像可能又被其他镜像给依赖了,猜测正是基于这种逻辑,才只删除了tag
    

8. docker with HTTPS

8.1. 原理

  • Docker HTTPS原理:双向验证。官方说明(https://docs.docker.com/articles/https/):
    In daemon mode, it will only allow connections from clients authenticated by a certificate signed by that CA. 
    In client mode, it will only connect to servers with a certificate signed by that CA.
    核心:服务端和客户端的数字证书都由同一个CA签发,因此双方在认证通讯时使用和签发时的同一个CA就能互相认证。
    

8.2. 使用

8.2.1. 创建CA(证书颁发中心)

  • 测试
    echo 01 > ca.srl
    openssl genrsa -des3 -out ca-key.pem 2048
    openssl req -new -x509 -days 3650 -key ca-key.pem -out ca.pem
    

8.2.2. 创建服务端公钥和私钥

  • 生成私钥:
    openssl genrsa -des3 -out server-key.pem 2048
    
  • 生成公钥(数字签名证书):
    1. 生成CSR文件(Certificate Signing Request 证书签名请求):openssl req -subj '/CN=docker.17173ops.com' -new -key server-key.pem -out server.csr
    2. 编写openssl.conf文件,内容如下:
    ------------- BEGIN ---------------------------------
    [req]
    distinguished_name = req_distinguished_name
    req_extensions = v3_req
    
    [req_distinguished_name]
    
    [ v3_req ]
    subjectAltName = @alt_names
    
    [alt_names]
    DNS.1 = *.h.173ops.com
    DNS.2 = *.docker.17173ops.com
    ------------- END ------------------------------------
    
  • 生成公钥(数字签名证书):
    openssl x509 -req -days 3650 -in server.csr -CA ca.pem -CAkey ca-key.pem -out server-cert.pem -extensions v3_req -extfile openssl.conf
    

8.2.3. 创建客户端公钥和私钥

  • 生成私钥:
    openssl genrsa -des3 -out client-key.pem 2048
    
  • 生成公钥(数字签名证书):
    1. 生成CSR文件(Certificate Signing Request 证书签名请求):openssl req -subj '/CN=client' -new -key client-key.pem -out client.csr
    2. echo extendedKeyUsage = clientAuth > extfile.cnf
    3. 生成公钥(数字签名证书):openssl x509 -req -days 3650 -in client.csr -CA ca.pem -CAkey ca-key.pem -out client-cert.pem -extfile extfile.cnf
    

8.2.4. 移除服务端私钥、客户端私钥密码

  • 服务端
    openssl rsa -in server-key.pem -out server-key.pem
    
  • 客户端
    openssl rsa -in client-key.pem -out client-key.pem
    

8.2.5. 使用

  • 将服务端3个文件拷贝到docker daemon(假设为192.168.1.2)的/root/.docker/下:
    1. scp ca.pem server-cert.pem server-key.pem 192.168.1.2:/root/.docker/
    2. 登录192.168.1.2
       cd /root/.docker/
       chmod 0600 *
    3. 添加启动参数:
       修改/etc/sysconfig/docker中的other_args值,添加以下内容,如:other_args="--graph /opt/docker --tlsverify --tlscacert=/root/.docker/ca.pem --tlscert=/root/.docker/server-cert.pem --tlskey=/root/.docker/server-key.pem -H unix:///var/run/docker.sock -H tcp://192.168.1.2:2376 -H tcp://127.0.0.1:2376"
    
  • 将客户端3个文件拷贝至”中控机”,然后就可以通过HTTPS远程对docker daemon进行操作:
    docker --tlsverify --tlscacert=/opt/docker_tls/ca.pem --tlscert=/opt/docker_tls/client-cert.pem --tlskey=/opt/docker_tls/client-key.pem -H localhost:2376 images
    

8.2.6. 管理

  • 查看CSR文件:
    openssl req -noout -text -in server.csr
    
  • 查看签名证书(server-cert.pem、client-cert.pem):
    openssl x509 -noout -text -in server-cert.pem
    

9. Docker Web-UI(shipyard)

9.1. 工作原理

  • 在每台docker宿主机上启动一个容器(shipyard/agent),这个容器通过挂载宿主的/var/run/docker.sock文件来获取该宿主上容器、镜像的信息,同时在容器启动时将agent注册到管理中心(shipyard/deploy)上,实现从Web查看和操作docker容器与镜像
  • 每个docker宿主启动一个agent(shipyard/agent),管理中心启动一个server(shipyard/deploy)

9.2. server配置

9.2.1. 下载镜像

  • docker pull shipyard/deploy

9.2.2. 启动容器(自动完成部署)

  • docker run -i -t -v /var/run/docker.sock:/docker.sock shipyard/deploy setup
    执行该命令时,实际做了如下操作:
    在本地宿主上依次下载并启动
    1. shipyard/redis
    2. shipyard/router
    3. shipyard/lb(Load Balance的意思)
    4. shipyard/db
    5. shipyard/shipyard(Web-UI)
    
    如何做到上述的:docker run -i -t -v /var/run/docker.sock:/docker.sock shipyard/deploy setup会将socket文件挂载进容器里,这样在容器里就能对宿主的镜像和容器进行管理,而shipyard/deploy的CMD是一个脚本,这个脚本就是进行上述操作
    

9.2.3. 验证

  • docker ps查看是否都已启动

注意docker logs shipyard_router可能会看到大量报错,并且占用大量CPU,负载也会升高。暂时不知原因,已提交至github.com,等待答复,详见https://github.com/shipyard/docker-shipyard-router/issues/3

9.3. agent配置

9.3.1. 下载镜像

  • docker pull shipyard/agent

9.3.2. 启动容器(自动注册到server)

  • docker run -i -t -v /var/run/docker.sock:/docker.sock -e IP=`ip -4 address show br1 | grep ‘inet ‘ | sed ‘s/.*inet \([0-9\.]\+\).*/\1/’` -e URL=http://192.168.1.1:8000 -p 4500:4500 shipyard/agent
    如果看不懂,可以这么写:
    docker run -i -t -v /var/run/docker.sock:/docker.sock -e IP=192.168.1.2 -e URL=http://192.168.1.1:8000 -p 4500:4500 shipyard/agent
    (192.168.1.2是docker宿主ip)
    
    注意:要先启动server,才能启动agent,否则agent注册到server可能会失败。
    

9.4. 页面配置

9.4.1. 登录页面

9.4.2. 接受agent注册

9.5. 注意

9.5.1. 页面上的Images(http://192.168.1.1:8000/images/)进行镜像删除要注意

  • 自动进行了去重(根据image id),因此从页面上删除镜像时会将相同image id的全部删除

9.5.2. server端管理

  • 移除:docker run -i -t -v /var/run/docker.sock:/docker.sock shipyard/deploy cleanup
  • 重启:docker run -i -t -v /var/run/docker.sock:/docker.sock shipyard/deploy restart
  • 升级:docker run -i -t -v /var/run/docker.sock:/docker.sock shipyard/deploy upgrade

9.5.3. 不建议生产使用,可作为学习借鉴

  • 原因1:封装太多,用户无法定制修改(20140903)
  • 原因2:一些细节功能方面,例如不同host的容器打印在一张表里,连排序功能都没有(20140903)

10. 镜像制作

10.1. 远程编译Dockerfile

  • docker -H tcp://xxx:2376 build –force-rm –no-cache -t foo/rhel6.5:1.0 /path/to/ 那么这个/path/to/指的是本地文件,而非远程编译机上的文件。
  • /path/to/Dockerfile文件会在命令执行之初就通过远端2376端口将/path/to/*(Dockerfile所在目录下的所有文件)传送到编译机上
    Sending build context to Docker daemon xx MB过程就是将文件发送过去
    
  • Dockerfile除了支持文件方式外,还支持URL,即/path/to/可以改为http://xxx(尚未测试过)

11. 内置bridge(nat)和自建网络桥接使用区别

  • 内置bridge(nat)
    优点:
    1. 节省IP
    
    缺点:
    1. 需要配套服务注册/发现,否则宿主上端口分配困难,容易冲突。
    2. 由于每个容器暴露的端口都不一致,造成前端路由层nginx配置(proxy_pass)里无法使用dns的方式。
    3. 端口映射要在容器启动时就指定好,后期无法变更。
    4. 测试发现nat不支持websocket。
    
  • 自建桥接网络
    优点:
    1. 每个容器都有独立ip,对外提供服务,如nginx+php,nginx+resin,都可以使用默认的80端口
    2. 由于容器暴露端口都可以使用80端口,因此前端路由层nginx配置(proxy_pass)里可以使用dns的方式。
    3. 无需为了后期端口映射添加而烦恼
    4. 桥接支持websocket
    
    缺点:
    1. 每个容器都需要一个IP(内网ip是不需要钱的)
    

12. Docker Event事件监听

12.1. 方法1:使用remote api

  • 还未找出靠谱的阻塞方式

12.2. 方法2:使用unix socket

  • 测试发现:docker服务关闭时,/var/run/docker.sock文件并不会自动删掉
    使用python连接/var/run/docker.sock文件会一直连着,docker daemon停止后,文件仍在,连接也仍在,因此不适用于事件监听。
    

12.3. 方法3: 使用docker events命令

  • 测试发现:输出内容与api的输出不完全一样,比如docker events输出的时间格式为可读的格式,而api输出的是unix timestamp。
  • 可以通过python的模块os.popen(‘docker events’)来建立监听连接
    # -*- coding: utf-8 -*-
    import os
    handler = os.popen('docker -H 127.0.0.1:2376 events')
    while True:
        res = handler.readline()
        if res:
            print res
        else:
            print "Null"
            exit(1)
    

13. 神器

13.1. nsenter(无需sshd、无需attach也可以登录容器)

  • 原理:进入namespace(通过/proc/xxxx/ns/)
  • 安装:docker run -v /usr/local/bin:/target registry.17173ops.com:5000/jpetazzo/nsenter:latest
    执行成功后会在宿主的/usr/local/bin下生成1个二进制程序nsenter和1个shell脚本docker-enter
    
    docker-enter是对nsenter用法的封装,让使用更加简单
    
  • 使用:docker-enter 容器id(或容器名)
    docker-enter是一个shell脚本,其实是调用nsenter(二进制文件),因此可以直接使用nsenter:
    nsenter --target $PID --mount --uts --ipc --net --pid		# 这个$PID指容器里的任意进程在宿主上的真实PID
    

14. FAQ

14.1. sshd服务起不来

  • docker在源码里就关掉了audit_control(linux Capabilities),而/etc/pam.d/sshd里有一行session required pam_loginuid.so,把这行删掉就可以了

14.2. ulimit无法更改open-file、max processes

  • docker容器默认移除sys_resource(Linux能力),因而ulimit -n设置只能改小无法改大,改大会报错:ulimit: open files: cannot modify limit: Operation not permitted。
  • 红帽7下docker run可以使用–privileged选项来不移除Linux能力,但docker默认移除这个Linux能力肯定是有安全方面的考量,因此尽量别用该选项

红帽6下要使用–privileged,docker版本不能>=1.0.1,否则会报错;stat /dev/.udev/db/cpuid:cpu0: no such file or directory。

  • 解决方法:
    若启动docker使用sysV服务,则在/etc/init.d/functions最开头添加一行:ulimit -u 204800 -HSn 204800
    
    若启动docker使用命令,如docker -d,那么在启动之前先执行ulimit -u 204800 -HSn 204800即可
    

14.3. 改变/var/lib/docker路径

  • 使用–graph参数:docker –graph=/opt/docker -d,会自动生成/opt/docker目录(0700),并在该目录下创建docker相关文件

原来的镜像和容器都找不到了,因为路径改了(原来的镜像是在/var/lib/docker/devicemapper/devicemapper/{data,metadata})

14.4. 将指定镜像标识为latest

  • docker tag 镜像id cyent/rhel6.5:latest

14.5. docker push报错

14.5.1. HTTP code 403 while uploading metadata: invalid character ‘<‘ looking for beginning of value

  • 报错示例:
    [ 14:50:44-root@localhost:vhosts ]#docker push registry.17173ops.com:82/crosbymichael/dockerui
    The push refers to a repository [registry.17173ops.com:82/crosbymichael/dockerui] (len: 1)
    Sending image list
    Pushing repository registry.17173ops.com:82/crosbymichael/dockerui (1 tags)
    511136ea3c5a: Pushing 
    2014/09/01 14:50:46 HTTP code 403 while uploading metadata: invalid character '<' looking for beginning of value
    
  • 解决方法:在nginx配置里注释掉”proxy_set_header Host $host;”

14.5.2. dial tcp 127.0.0.1:5000: connection refused

  • 报错示例:
    [ 18:24:35-root@localhost:~ ]#docker push registry.17173ops.com:81/17173/as6.5-ng1.4:1.4
    The push refers to a repository [registry.17173ops.com:81/17173/as6.5-ng1.4] (len: 1)
    Sending image list
    Pushing repository registry.17173ops.com:81/17173/as6.5-ng1.4 (1 tags)
    511136ea3c5a: Pushing 
    2014/09/01 18:27:27 Failed to upload metadata: Put http://127.0.0.1:5000/v1/images/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/json: dial tcp 127.0.0.1:5000: connection refused
    
  • 解决方法:不要这么用,不要套nginx,gunicorn直接对外使用

14.6. CMD 和 ENTRYPOINT的区别

  • CMD是可以在docker run时候被覆盖的,而ENTRYPOINT无法被覆盖
  • CMD通常被用于调试,可以选择不同的CMD,可支持带参数
  • 而ENTRYPOINT通常被用于应用发布,也可支持带参数
  • 举例说明:
    假设CMD和ENTRYPOINT都是/bin/run.sh,run.sh的内容是echo "Hello,$1"
    那么,若为CMD,则docker run -t -i xxx/yyy /bin/ls /root就会列出/root目录下的内容
    若为ENTRYPOINT,则docker run -t -i xxx/yyy /bin/ls /root就会打印出Hello,/bin/ls
    
  • 最重要的区别:ENTRYPOINT是会被继承下去的
    比如做镜像A的时候Dockerfile里写了一行ENTRYPOINT /usr/local/bin/run.sh,那么之后做的镜像(FROM:)的Dockerfile里如果不指定ENTRYPOINT,而是指定CMD,那么这个CMD虽然从inspect里看是存在的,但却是无效的。

【荐】用iopp代替iotop

1、为什么推荐iopp

iotop对内核及python版本都有一定要求,有时候无法用上,这时候就可以使用iopp作为替代方案。在有些情况下可能无法顺利使用iotop,这时候就可以选择iopp了。它的作者是Mark Wong,用C开发,代码仅有532行,非常简洁。

iopp的项目地址:https://github.com/markwkm/iopp

2、安装iopp

下载源码到本地后,执行下面的命令即可完成安装:
cmake CMakeLists.txt
make && make install
 
#或者指定安装的目标路径到 /usr/bin 下
make install DESTDIR=/usr

3、使用iopp

iopp使用起来非常简便,用法:

iopp [-ci] [-k|-m] [delay [count]]

几个常用参数见下,比较简单,就不一一解释了:

-c, --command display full command line
-h, --help display help
-i, --idle hides idle processes
-k, --kilobytes display data in kilobytes
-m, --megabytes display data in megabytes
-u, --human-readable display data in kilo-, mega-, or giga-bytes

iopp输出的结果也比较清晰易懂,简单解释下:

pid 进程ID
rchar 预计发生磁盘读的字节数
wchar 预计发生磁盘写的字节数
syscr I/O读次数
syscw I/O写此书
rbytes 真正发生磁盘读的字节数
wbytes 真正发生磁盘写的字节数
cwbytes 因为清空页面缓存而导致没有发生操作的字节数
command 命令行名称

tpcc-mysql安装、使用、结果解读

TPC-C是专门针对联机交易处理系统(OLTP系统)的规范,一般情况下我们也把这类系统称为业务处理系统。
tpcc-mysql是percona基于TPC-C(下面简写成TPCC)衍生出来的产品,专用于MySQL基准测试。其源码放在launchpad上,用bazaar管理,项目地址:https://code.launchpad.net/~percona-dev/perconatools/tpcc-mysql

一、 下载源码包
安装epel包后以便安装bzr客户端:

rpm -Uvh http://dl.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm

然后就可以开始安装bzr客户端了:

yum install bzr

之后,就可以开始用bzr客户端下载tpcc-mysql源码了。

cd /tmp
bzr branch lp:~percona-dev/perconatools/tpcc-mysql

MySQL中文网便捷下载地址:

http://imysql.com/wp-content/uploads/2014/09/tpcc-mysql-src.tgz

下载到本地后,先执行 gunzip 解压缩文件,再执行 tar xf 解包,直接 tar zxf 可能会报告异常。

tpcc-mysql的业务逻辑及其相关的几个表作用如下:

New-Order:新订单,主要对应 new_orders 表
Payment:支付,主要对应 orders、history 表
Order-Status:订单状态,主要对应 orders、order_line 表
Delivery:发货,主要对应 order_line 表
Stock-Level:库存,主要对应 stock 表

其他相关表:
客户:主要对应 customer 表
地区:主要对应 district 表
商品:主要对应 item 表
仓库:主要对应 warehouse 表

二、编译安装
编译非常简单,只需要一个 make 即可。

cd /tmp/tpcc-mysql/src
make
如果 make 没有报错,就会在 /tmp/tpcc-mysql 下生成 tpcc 二进制命令行工具 tpcc_load 、 tpcc_start

三、TPCC测试前准备
初始化测试库环境

cd /tmp/tpcc-mysql
mysqladmin create tpcc1000
mysql -f tpcc1000 < create_table.sql

初始化完毕后,就可以开始加载测试数据了

tpcc_load用法如下:
tpcc_load [server] [DB] [user] [pass] [warehouse]
或者
tpcc_load [server] [DB] [user] [pass] [warehouse] [part] [min_wh] [max_wh]

选项 warehouse 意为指定测试库下的仓库数量。

真实测试场景中,仓库数一般不建议少于100个,视服务器硬件配置而定,如果是配备了SSD或者PCIE SSD这种高IOPS设备的话,建议最少不低于1000个

执行下面的命令,开始灌入测试数据:

cd /tmp/tpcc-mysql
./tpcc_load localhost tpcc1000 tpcc_user "tpcc_password" 1000

在这里,需要注意的是 tpcc 默认会读取 /var/lib/mysql/mysql.sock 这个socket 文件。
因此,如果你的 socket 文件不在相应路径的话,可以做个软连接,或者通过TCP/IP的方式连接测试服务器,例如:

cd /tmp/tpcc-mysql
./tpcc_load 1.2.3.4:3306 tpcc1000 tpcc_user "tpcc_password" 1000

加载测试数据时长视仓库数量而定,若过程比较久需要稍加耐心等待。

四、进行TPCC测试
tpcc_start 工具用于tpcc压测,其用法如下:

tpcc_start -h server_host -P port -d database_name -u mysql_user \
 -p mysql_password -w warehouses -c connections -r warmup_time \
 -l running_time -i report_interval -f report_file

几个选项稍微解释下

-w 指定仓库数量
-c 指定并发连接数
-r 指定开始测试前进行warmup的时间,进行预热后,测试效果更好
-l 指定测试持续时间
-i  指定生成报告间隔时长
-f 指定生成的报告文件名

现在我们来开启一个测试案例:

tpcc_start -hlocalhost -d tpcc1000 -u tpcc_user -p "tpcc_password" \
 -w 1000 -c 32 -r 120 -l 3600 \
 -f tpcc_mysql_20140921.log >> tpcc_caseX_20140921.log 2>&1

即:模拟 1000个仓库规模,并发 16个线程进行测试,热身时间为 60秒, 压测时间为 1小时。

真实测试场景中,建议预热时间不小于5分钟,持续压测时长不小于30分钟,否则测试数据可能不具参考意义。

五、TPCC测试结果解读:

发起测试:

./tpcc_start -h 1.2.3.4 -P 3306 -d tpcc10 -u tpcc -p tpcc \
 -w 10 -c 64 -r 30 -l 120 \
 -f tpcclog_201409211538_64_THREADS.log >> tpcc_noaid_2_20140921_64.log 2>&1

测试结果输出如下:

-- 本轮tpcc压测的一些基本信息
***************************************
*** ###easy### TPC-C Load Generator ***
***************************************
option h with value '1.2.3.4'   -- 主机
option P with value '3306'             -- 端口
option d with value 'tpcc10'         -- 数据库
option u with value 'tpcc'             -- 账号
option p with value 'tpcc'             -- 密码
option w with value '10'                 -- 仓库数
option c with value '64'                 -- 并发线程数
option r with value '30'                 -- 数据预热时长
option l with value '120'               -- 压测时长
option f with value 'tpcclog_20140921_64_THREADS.res'  -- 输出报告日志文件

     [server]: 1.2.3.4
     [port]: 3306
     [DBname]: tpcc10
       [user]: tpcc
       [pass]: tpcc
  [warehouse]: 10
 [connection]: 64
     [rampup]: 30 (sec.)
    [measure]: 120 (sec.)

RAMP-UP TIME.(30 sec.)

-- 预热结束,开始进行压测
MEASURING START.

-- 每10秒钟输出一次压测数据
  10, 8376(0):2.744|3.211, 8374(0):0.523|1.626, 838(0):0.250|0.305, 837(0):3.241|3.518, 839(0):9.086|10.676
  20, 8294(0):2.175|2.327, 8292(0):0.420|0.495, 829(0):0.206|0.243, 827(0):2.489|2.593, 827(0):7.214|7.646
…
 110, 8800(0):2.149|2.458, 8792(0):0.424|0.710, 879(0):0.207|0.244, 878(0):2.461|2.556, 878(0):7.042|7.341
 120, 8819(0):2.147|2.327, 8820(0):0.424|0.568, 882(0):0.208|0.237, 881(0):2.483|2.561, 883(0):7.025|7.405
-- 以逗号分隔,共6列
-- 第一列,第N次10秒
-- 第二列,总成功执行压测的次数(总推迟执行压测的次数):90%事务的响应时间|本轮测试最大响应时间
-- 第三列,新订单业务成功执行次数(推迟执行次数):90%事务的响应时间|本轮测试最大响应时间
-- 第四列,支付业务的结果,后面几个的意义同上
-- 第五列,发货业务的结果,后面几个的意义同上
-- 第六列,库存业务的结果,后面几个的意义同上

-- 压测结束
STOPPING THREADS................................................................

   -- 第一次粗略结果统计
  [0] sc:100589  lt:0  rt:0  fl:0    -- New-Order,新订单业务成功(success,简写sc)次数,延迟(late,简写lt)次数,重试(retry,简写rt)次数,失败(failure,简写fl)次数
  [1] sc:100552  lt:0  rt:0  fl:0    -- Payment,支付业务统计,其他同上
  [2] sc:10059  lt:0  rt:0  fl:0    -- Order-Status,订单状态业务统计,其他同上
  [3] sc:10057  lt:0  rt:0  fl:0    -- Delivery,发货业务统计,其他同上
  [4] sc:10058  lt:0  rt:0  fl:0    -- Stock-Level,库存业务统计,其他同上
 in 120 sec.

    -- 第二次粗略统计结果,其他同上
  [0] sc:100590  lt:0  rt:0  fl:0 
  [1] sc:100582  lt:0  rt:0  fl:0 
  [2] sc:10059  lt:0  rt:0  fl:0 
  [3] sc:10057  lt:0  rt:0  fl:0 
  [4] sc:10059  lt:0  rt:0  fl:0 

 (all must be [OK])       -- 下面所有业务逻辑结果都必须为 OK 才行
 [transaction percentage]
        Payment: 43.47% (>=43.0%) [OK]      -- 支付成功次数(上述统计结果中 sc + lt)必须大于43.0%,否则结果为NG,而不是OK
   Order-Status: 4.35% (>= 4.0%) [OK]       -- 订单状态,其他同上
       Delivery: 4.35% (>= 4.0%) [OK]       -- 发货,其他同上
    Stock-Level: 4.35% (>= 4.0%) [OK]       -- 库存,其他同上
 [response time (at least 90% passed)]      -- 响应耗时指标必须超过90%通过才行
      New-Order: 100.00%  [OK]              -- 下面几个响应耗时指标全部 100% 通过
        Payment: 100.00%  [OK]
   Order-Status: 100.00%  [OK]
       Delivery: 100.00%  [OK]
    Stock-Level: 100.00%  [OK]


                 50294.500 TpmC                      -- TpmC结果值

script目录下的一些脚本主要是一些性能数据采集以及分析的,可以自行摸索下怎么用。

其他推荐:
TPCC-MySQL使用手册

搜狐视频:MySQL DBA成长之路 – tpcc-mysql安装、使用、结果解读

redis主从实例间快速迁移案例

操作步骤
1. 在slave server新建一个redis实例,建议认证密码(“requirepass”)与master server一致,但是不要在redis.conf里面设置同步配置(“slaveof”“masterauth”)
2. 启动新的slave server;
3. 在线配置同步:
config set masterauth xxxxxxxx (config set masterauth “master server的认证密码”)
slaveof 1.2.3.4 6379 (slaveof “master server IP” “master server port” )
4. 使用info指令确认同步完成(这个步骤所需时间与数据量成正比);
5. 设置slave server可写:
config set slave-read-only no
6. 至此迁移完成,随后就可以进行各种切换(或者IP,或者DNS,或者直接修改代码中的数据库连接串);
7. 确认原先的master server无新数据写入之后,将slave server提升成为新的主节点,并使用info观察:
slaveof no one
8. 修改原slave server的数据指向(如果第6步是进行IP切换,那这步就可以省略,旧的slave server会自动切换master)。

注意事项
1. 在第6步切换的时候需要注意配置更新延时或者更新不一致,导致存在redis双写导致数据不一致或者逻辑功能异常等现象;
2. 迁移切换对于redis来说依然是bgsave->sync->load的过程,尽量选在闲时进行处理。

总结
比起传统的:环境部署–>bgsave–>rsync–>启动服务这样的迁移方式,上文提到的不停服务在线切换其实原理是一样,只是操作相对复杂那么一些些,但是使用上述方法可以有效缩短服务不可用时间。

一个php进程cpu %nice很高的原因详解

一、 现象描述:

1、 CPU的%user、%sys占用的CPU不高,但%nice占用了大量的CPU资源,最高占用CPU的60%以上;
2、 ps -elf中PRI为90,NI为10,top看到PR值为30,NI 10;

问题:为什么从ps中看到的priority值和top中的不同?
top中的PR是:The priority of the task(取值范围:-20最高,19最低)
ps中的PRI是:realtime priority(根据 nice( ) 和setpriority( ) 计算,Static priority 取值1-99,Dynamic priority还没有看懂)

3、sar信息收集

CPU:
Linux 2.6.32-279.el6.x86_64 (web2.17173ops.com) 05/09/2014 _x86_64_ (8 CPU)

12:00:04 AM CPU %user %nice %system %iowait %steal %idle
12:01:01 AM all 0.39 26.67 1.84 30.15 0.00 40.95
12:02:03 AM all 0.30 24.07 2.74 30.10 0.00 42.80
12:03:01 AM all 0.27 23.61 1.65 34.48 0.00 40.00
12:04:02 AM all 0.26 23.37 1.96 34.87 0.00 39.54
12:05:04 AM all 0.31 24.08 3.74 36.66 0.00 35.21
12:06:02 AM all 0.43 21.96 2.09 34.93 0.00 40.59
12:07:16 AM all 0.25 23.66 2.07 59.63 0.00 14.38
12:08:02 AM all 0.33 30.21 2.01 36.88 0.00 30.57

二、服务器硬/软件:

Product Name: PowerEdge R410
CPU:Intel(R) Xeon(R) CPU E5606 @ 2.13GHz
Memory:24GB 4*6
OS:Red Hat Enterprise Linux Server release 6.3 (Santiago)
Web server:nginx version: nginx/1.4.4
CGI server:php-5.3.21

三、 判断问题:
1、 NICE资源一般是用户端控制的行为产生;
2、 除非程序中有大量的使用sleep,或者是调用了nice等函数,对自定义了优先级别,但一般程序不会这么变态;

四、 查找问题过程:
1、习惯性认为是程序开发的问题,在代码中不断查找类似sleep、nice的字眼,浪费了一些时间;
2、ps -elf | grep master,发现php-fpm启动时间不一致,有的是03:57,有的是4:03,如果是crontab定义的,时间肯定不会这么不一致;
3、查找/etc/crontab,文件里面没有定义任何定时任务;
4、RHEL6的定时任务被anacrontab接管,anacrontab是crontab的补充,内容如下:(问题也就出在下面红色+备注的那行)

# cat /etc/anacrontab 
# /etc/anacrontab: configuration file for anacron

# See anacron(8) and anacrontab(5) for details.

SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22

#period in days delay in minutes job-identifier command
1 5 cron.daily nice run-parts /etc/cron.daily ## 日志切割定时任务在此
7 25 cron.weekly nice run-parts /etc/cron.weekly
@monthly 45 cron.monthly nice run-parts /etc/cron.monthly

五、 思考、优化:
1、不建议在服务器上使用anacrontab,继续使用crontab;(RHEL5不存在此问题);
2、其实anacrontab使用nice指令也不会出现问题,主要是和php-fpm启动脚本中的restart配合导致这种情况的发生;
3、php-fpm使用reload或者是kill -USR2,nice都不会生效,会按原PID的优先等级。因为手工启动php-fpm时我们肯定不会带上nice。

sendmail阻挡发往未知域邮件的配置方法

linux的sendmail /etc/mail/access不支持通配符以阻挡发往未知域的邮件,查询了一番,发现最好的方式是阻挡所有顶级域,步骤如下:

1. 从http://www.iana.org/domains/root/db获得顶级域列表
2. 添加至/etc/mail/access,格式:

To:17173ops.com RELAY
To:173ops.com RELAY
To:com REJECT
To:cn REJECT
To:net REJECT
...

3. service sendmail restart

按照如上配置方法,仅有收件域为 17173ops.com173ops.com 的邮件可以发出,其他收件域的都会被拒绝。