https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/
docker info 之cgroupdriver和runc
关于cgroupdriver:
docker 默认的cgroupdriver为cgroupfs,也可以手动指定(通过环境变量、命令行参数、daemon.json)systemd,有些docker的rpm包会在 /usr/lib/systemd/system/docker.service 中通过命令行的方式指定cgroupdriver为systemd, 当前支持的cgroupdriver有: cgroupfs、systemd; 命令行设置方式:
1 |
--exec-opt native.cgroupdriver=systemd |
虽然创建容器时没有指定cgroupdriver的选项,但是通过修改cgroupdriver重启daemon,可以使得同一个daemon下的容器使用不同的cgroupdriver的(仅仅是出于理解技术实现的思考,实践中似乎没有任何必要)
至于容器使用的native.cgroupdrive 是cgroupfs 还是 systemd 不是配置在容器上的,而是取决于dockerd启动时的配置,如果dockerd配置的是cgroupfs,则容器启动的时候就是使用的cgroupfs,如果dockerd在配置为cgroupfs时已经启动了几个容器,然后修改配置为systemd,则现在启动的容器就是systemd的了,这时候,两种类型的cgroupdriver的容器是并存的,切换cgroupdriver只需要修改dockerd配置后重启容器即可
曾经有文章中疑惑,有的docker容器的cgroup都是在相同的docker目录下的,有的却是docker-xxxx.scope; 原因就在于前者是通过cgroupfs来实现的,后者是通过systemd来实现的; 另外 docker daemon也可以通过选项–cgroup-parent 来指定一个父cgroup(cgroup是有层级结构的),默认为docker,根据cgroupdriver的不同,该cgroup-parent的表现形式也不同,cgroupfs中表现为父目录,systemd中表现为前缀(参考: https://docs.docker.com/engine/reference/commandline/dockerd/#default-cgroup-parent );每个容器都可以有自己的–cgroup-parent,这个对于不同容器进行分组时似乎是不错的。
关于runc:
默认runc为 docker-runc,其实runc表现为一个可执行的二进制文件,docker info中显示的只是一个配置时指定的名字,至于该名字对应哪个二进制文件,是在配置的时候指定的,也就是说,你可以在不修改配置的情况下,直接修改对应的二进制文件来修改runc,重启容器就会生效; 另外,创建容器的时候,可以指定runc,指定的是配置daemon时使用的名字,每个容器可以有不同的runc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# docker info Containers: 162 Running: 56 Paused: 0 Stopped: 106 Images: 161 Server Version: 1.12.5 Storage Driver: devicemapper Pool Name: data-docker_thinpool Pool Blocksize: 524.3 kB Base Device Size: 32.21 GB Backing Filesystem: xfs Data file: Metadata file: Data Space Used: 469.4 GB Data Space Total: 644.2 GB Data Space Available: 174.9 GB Metadata Space Used: 73.79 MB Metadata Space Total: 16.98 GB Metadata Space Available: 16.9 GB Thin Pool Minimum Free Space: 64.42 GB Udev Sync Supported: true Deferred Removal Enabled: true Deferred Deletion Enabled: false Deferred Deleted Device Count: 0 Library Version: 1.02.135-RHEL7 (2016-11-16) Logging Driver: journald Cgroup Driver: systemd Plugins: Volume: local Network: null overlay host bridge Swarm: inactive Runtimes: runc docker-runc Default Runtime: docker-runc Security Options: seccomp Kernel Version: 3.10.0-514.6.1.el7.x86_64 Operating System: CentOS Linux 7 (Core) OSType: linux Architecture: x86_64 Number of Docker Hooks: 2 CPUs: 40 Total Memory: 94.14 GiB Name: VM-2-10-12 ID: NYBE:NZML:4KQQ:PF2J:RXCB:IPPI:Y3BI:CY7E:RVAC:WVWV:VDM2:3EEK Docker Root Dir: /data1/docker Debug Mode (client): false Debug Mode (server): false Registry: https://index.docker.io/v1/ Insecure Registries: docker-registry.i.bbtfax.com:5000 127.0.0.0/8 Registries: docker.io (secure) |
磁盘调度算法
参考: http://blog.csdn.net/theorytree/article/details/6259104
说明: 显示所有支持的调度策略, 方框内的是当前启用的调度策略
查看当前系统支持的调度算法:
1 2 3 4 |
# dmesg | grep -i scheduler [ 0.605910] io scheduler noop registered [ 0.605916] io scheduler deadline registered (default) [ 0.605974] io scheduler cfq registered |
难道调度算法和设备本身也有关系?从下面来看,阿里云的云盘不支持任何调度策略:
1 2 |
# cat /sys/block/xvda/queue/scheduler none |
但是:
值得一提的是,Anticipatory算法从Linux 2.6.33版本后,就被移除了,因为CFQ通过配置也能达到Anticipatory算法的效果。
查资料发现, 调度策略为 ‘none’ 的现象和阿里云虚拟机没关系,和阿里云云盘没关系,和操作系统版本也没有(直接)关系,仅仅和内核版本有关系, linux内核从3.13开始引入 blk-mq 队列机制,并在3.16得以全部实现,上面看到的非‘none’的情况,内核版本都在3.10之前,为‘none’的情况是被我手动升级内核到4.4.61 的
如何验证是否启用了blk-mq机制?可以通过查看是否存在mq目录,如下:
目录不存在,说明没有启用该机制
存在mq目录,说明使用的是blk-mq机制
参考: https://www.thomas-krenn.com/en/wiki/Linux_Multi-Queue_Block_IO_Queueing_Mechanism_(blk-mq)
块儿设备操作
centos中有rpm包util-linux,包含 blockdev命令(其实很多常用的命令都在这里),常见功能如下:
查看预读大小:(单位为扇区)
1 2 |
# blockdev --getra /dev/xvda 256 |
设置预读大小:
1 |
# blockdev --setra 2048 /dev/xvda |
查询是否为只读:( 1 为只读 )
1 2 |
# blockdev --getro /dev/xvda 0 |
设置设备为只读:
1 |
# blockdev --setro /dev/xvda |
设置设备为读写:
1 |
# blockdev --setrw /dev/xvda |
查看扇区大小: (一般都是512)
1 2 |
# blockdev --getss /dev/xvda 512 |
查看设备容量: (单位为 扇区 )
1 2 |
# blockdev --getsz /dev/xvda 83886080 |
查看块儿大小: (单位为字节)
1 2 |
# blockdev --getbsz /dev/xvda 4096 |
查看块儿设备综合信息:
1 2 3 |
# blockdev --report /dev/xvda RO RA SSZ BSZ StartSec Size Device rw 256 512 4096 0 42949672960 /dev/xvda |
其中:
RO: 是否只读
RA(read ahead):预读扇区数
SSZ(sector size): 扇区大小
BSZ(block size): 块儿大小
注意: 这里关于设备的设置在设备重新挂载后会失效,需要重新设置
java core 调试
根据java core文件打印java堆栈信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# jstack /usr/bin/java ../core.java Attaching to core ../core.java from executable /usr/bin/java, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.45-b02 Deadlock Detection: No deadlocks found. Thread 21928: (state = BLOCKED) Thread 21927: (state = BLOCKED) - java.lang.Object.wait(long) @bci=0 (Interpreted frame) - java.lang.ref.ReferenceQueue.remove(long) @bci=59, line=143 (Interpreted frame) - java.lang.ref.ReferenceQueue.remove() @bci=2, line=164 (Interpreted frame) - java.lang.ref.Finalizer$FinalizerThread.run() @bci=36, line=209 (Interpreted frame) Thread 21926: (state = BLOCKED) - java.lang.Object.wait(long) @bci=0 (Interpreted frame) - java.lang.Object.wait() @bci=2, line=502 (Interpreted frame) - java.lang.ref.Reference$ReferenceHandler.run() @bci=36, line=157 (Interpreted frame) Thread 21896: (state = BLOCKED) - java.lang.Thread.sleep(long) @bci=0 (Interpreted frame) - Hello.main(java.lang.String[]) @bci=11, line=4 (Interpreted frame) |
java 之遗失的libjli.so
java 都用了N长时间了,突然,意外地发现有一个依赖的so文件从来没找见过
1 2 3 4 5 6 7 |
# ldd /usr/bin/java linux-vdso.so.1 => (0x00007fffba769000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00000038c4000000) libjli.so => not found libdl.so.2 => /lib64/libdl.so.2 (0x0000003da7c00000) libc.so.6 => /lib64/libc.so.6 (0x0000003da7400000) /lib64/ld-linux-x86-64.so.2 (0x0000003da6c00000) |
其实这个so文件在java的安装包中是有的,只是没有添加了ld_path 里面,解决办法:
1 2 3 |
# rpm -ql jdk1.8.0_45|grep libjli /usr/java/jdk1.8.0_45/jre/lib/amd64/jli/libjli.so /usr/java/jdk1.8.0_45/lib/amd64/jli/libjli.so |
然后,将/usr/java/jdk1.8.0_45/jre/lib/amd64/jli/ 或 /usr/java/jdk1.8.0_45/lib/amd64/jli/ 添加到文件: /etc/ld.so.conf 中,然后执行ldconfig 即可
最后,libjli.so 是个啥玩意儿,为啥从来没找见过,却也从来没报错过?
制作openvswitch rpm包
openvswitch已经提供了rpm包的spec文件,所以制作openvswitch rpm包非常容易:
- 下载需要的源文件tar包
http://openvswitch.org/releases/openvswitch-2.7.0.tar.gz - 安装rpm-build
- 构建build环境(这里只创建了源码目录)
1mkdir -p {$HOME}/rpmbuild/SOURCES - 将源码包放入源码目录
1mv openvswitch-2.7.0.tar.gz {$HOME}/rpmbuild/SOURCES - 从tar包中提取spec文件 (理论上rpmbuild -tb openvswitch-2.7.0.tar.gz 可以自动搜索spec文件的,但是,该tar包中有多个spec文件,搜索到的未必是我们想使用的)
1tar -zxf openvswitch-2.7.0.tar.gz openvswitch-2.7.0/rhel/openvswitch.spec - 制作rpm包 (注意: 提示需要安装啥就先安装啥)
1rpmbuild -bb openvswitch-2.7.0/rhel/openvswitch.spec
make其实还是比较快的,主要是test过程长的要命
openvswitch 入门
openvswitch 安装,参考: https://phpor.net/blog/post/5102
启动相关进程:
创建数据库:
1 |
ovsdb-tool create |
语法说明: ovsdb-tool [options] create [db [schema]]
默认数据库位置: /etc/openvswitch/conf.db
默认的表结构: /usr/share/openvswitch/vswitch.ovsschema (schema 是一个很大的json)
启动ovsdb-server:
1 |
ovsdb-server --detach --remote=punix:/var/run/openvswitch/db.sock --log-file |
语法说明: ovsdb-server [database]… [–remote=remote]… [–run=command] –log-file[=/var/log/openvswitch/ovsdb-server.log]
默认数据库文件: /etc/openvswitch/conf.db
监听socket文件: 通过 –remote指定,可以是ip:port ,没有默认值
日志文件: 通过–log-file 开启日志,通过 –log-file=/var/log/ovsdb-server.log 来指定文件位置,默认位置: /var/log/openvswitch/ovsdb-server.log
通过ovsdb-client 操作数据库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# ovsdb-client list-dbs Open_vSwitch # ovsdb-client list-tables Open_vSwitch Table ------------------------- Controller Bridge Queue IPFIX NetFlow Open_vSwitch QoS Port sFlow SSL Flow_Sample_Collector_Set Mirror Flow_Table Interface AutoAttach Manager |
ovsdb-client – command-line interface to ovsdb-server
默认的server socket文件: unix:/var/run/openvswitch/db.sock
加载openvswitch 内核模块:
1 |
modprobe openvswitch |
如果不加载该模块,则ovs-vswitchd启动时会报错
该模块内核中已经有了,加载即可:
1 2 3 4 5 |
[root@localhost ~]# find /lib/modules -name openvswitch.ko /lib/modules/3.10.0-514.el7.x86_64/kernel/net/openvswitch/openvswitch.ko /lib/modules/4.4.61-1.el7.elrepo.x86_64/kernel/net/openvswitch/openvswitch.ko [root@localhost ~]# rpm -qf /lib/modules/4.4.61-1.el7.elrepo.x86_64/kernel/net/openvswitch/openvswitch.ko kernel-lt-4.4.61-1.el7.elrepo.x86_64 |
启动ovs-vswitchd: ovs-vswitchd – Open vSwitch daemon
1 |
ovs-vswitchd --detach --log-file |
语法说明: ovs-vswitchd [database]
database 就是ovsdb-server listen的unix socket或ip:port ,这里默认位置为: unix:/var/run/openvswitch/db.sock
日志通过 –log-file 打开,默认位置: /var/log/openvswitch/ovs-vswitchd.log
查看网络配置:
1 2 3 4 5 6 7 8 |
# ovs-vsctl show 72cc794c-0319-4794-bbd6-649b867aee46 Bridge "br0" Port "br0" Interface "br0" type: internal Port "86406999afdb4_l" Interface "86406999afdb4_l" |
ovs-vsctl 通过
各组件之间的关系:
数据库文件是静态文件,如同sqlitedb,需要通过工具(ovsdb-tool)创建;对于mysql数据库,本身就可以直接提供网络服务,但是这里的数据库文件却仅仅是个文件,需要 ovsdb-server 来提供网络服务,ovsdb-server 只知道如何操作数据库(表),对网络却一无所知,但是ovs-vswitchd却是懂网络的,ovs-vswitchd 通过ovsdb-server数据库的变更,执行网络配置,提供一个配置网络的服务;ovs-vsctl 通过ovsdb-server (/var/run/openvswitch/db.sock)操作数据库
ftp 代理之iptables实现
参考: http://www.linuxidc.com/Linux/2012-06/61959.htm
ip_nat_ftp 和 ip_conntrack_ftp 模块可以帮助实现ftp代理
iptables 之 表、链
iptables中,target/jump决定了符合条件的包到何处去,语法是–jump target或-j target。
通过-N参数创建自定义链:
1 |
iptables -N BLOCK |
之后将BLOCK链作为jump的目标:
1 |
iptables -I INPUT 6 -p tcp --dport 80 -i p3p1 -j BLOCK |
如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[root@cz ~]# iptables -vnL Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 230K 118M ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED 2939 247K ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 4882 293K ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 24 1432 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 0 0 BLOCK tcp -- p3p1 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 38897 3908K REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited Chain OUTPUT (policy ACCEPT 17 packets, 1604 bytes) pkts bytes target prot opt in out source destination Chain BLOCK (1 references) pkts bytes target prot opt in out source destination |
这样从INPUT链中匹配规则6的包都会跳入BLOCK链中,若到达了BLOCK链的结尾(即未被链中的规则匹配),则会回到INPUT链的下一条规则。如果在子链中被ACCEPT了,则就相当于在父链中被ACCEPT了,那么它不会再经过父链中的其他规则。但要注意这个包能被其他表的链匹配;
我们也发现了Chain BLOCK的引用数量为1,就是说有一个规则跳转到了这个链; -j 不仅仅可以是accept和reject,还可以是chain,正是这个才让自定义的chain生效的
为BLOCK链增加规则:
1 |
iptables -A BLOCK -p tcp -s 10.1.1.92/32 -i p3p1 --dport 80 -j DROP |
查看如下:
1 2 3 |
Chain BLOCK (1 references) pkts bytes target prot opt in out source destination 18 912 DROP tcp -- p3p1 * 10.1.1.92 0.0.0.0/0 tcp dpt:80 |
参考:
http://man.chinaunix.net/network/iptables-tutorial-cn-1.1.19.html good
http://blog.csdn.net/yu_xiang/article/details/9218589 good
http://arster.blog.51cto.com/714732/908486 good