docker 之 bash命令行自动补全

很多命令都会提供一个bash-complete的脚本,在执行该命令时,敲tab可以自动补全参数,会极大提高生产效率。docker亦如此,如:yum install docker后,会有一个文件: /usr/share/bash-completion/completions/docker ; 显然,该脚本是用于bash自动补全的,但是,不幸的是,直接 source 执行该脚本后,docker命令后敲tab,有如下错误:

难道docker的bash自动补全还能有问题?

放狗一搜,解决办法如下:

我这里虽然安装了bash-completion ,但是没有执行其中的一个文件:

该文件中有上面缺少的命令,自然,执行该文件就解决了。

其实 bash_completion 包中已经包含了一堆常用命令的bash自动补全的脚本的:

 

bash补全脚本编写:(实例)

 

complete -F func cmd 其中func的规则:

func可以接受到3个参数:

$0: 就是bash

$1: 命令名

$2: 需要补全的单词的一部分

$3: 上一个完整的单词 , 如果当前要补全的是第一个参数,则上一个完整的单词就是命令名

 

使用命令(任何语言)来实现命令补全(不过还是shell方便,实在不会shell就选择其他语言):

通过命令 mycomplete 给命令phpor来补全:

其中: mycomplete 命令将接到三个参数:

$1: 命令名(就是phpor)

$2: 需要补全的单词

$3: 命令行中出现的最后一个完整的单词

mycomplete要做的事情就是根据最后一个完整的单词来推测下个(要补全的)单词;只是如果命令行中已经包含了超过3个单词,则第二个单词信息将得不到; 其实不是的,只是从命令行参数中得不到,从环境变量是可以得到的,环境变量 COMP_LINE 保存了整个命令行的内容,命令行参数只是有助于我们定位光标的位置。


complete -C  和 -F 的说明

-C 指定一个命令; -F 指定一个函数 。 那么, -C 指定一个函数能行吗?  -F 指定函数一定能行吗?

其实:

  1. -C 也能指定一个函数, 这两个选项的本质不是函数或者二进制程序的命令; 而是传递信息的方式不同(未验证,有时间再验证)
    1. 如果指定-C ,那么会通过参数和export出来的环境变量来传递
    2. 如果指定-F, 那么会通过参数和特定的全局变量来传递

 


可以通过是strace -e file bash 来了解bash自动补全的逻辑,可以通过函数命令complete来设置自动补全函数,如果不存在自动补全函数的话,会自动在补全目录下搜索当前执行的命令为名称的文件,如  ~/.local/share/bash-completion/completions/byssh ,我们可以在这里添加自动补全逻辑

 

complete 的更多用法参考: man bash  搜索: Programmable Completion

如果我们需要补全一些常规得到东西,可以使用 -A ,如:

  1. 补全当前shell中的函数: complete -A function xxx
  2. 补全当前shell中的alias : complete -A alias xxx

参考资料:

Docker 存储

且看下docker info:

metadata一般不会太大,暂且不论。

  1. data默认107.4G (话说这还是centos7上的,曾经似乎只有10G),如何修改?
  2. 43个容器,57个镜像; 看来所有容器数据、和镜像数据都在这里了
  3. 从上面信息来看,设备数据已经满了,如下也能查看
  4. 镜像依赖:
    如果 image2是在image1上修改得到的,则image1无法先于image2被删除
  5. 容器依赖:
    如果有容器在使用某镜像,则该镜像无法被删除
  6. 删除文件后空间不会收缩
    创建一个大文件,空间膨胀;然后再删除大文件,空间不会缩回;但是,似乎会被重用这部分空间;问题:如何查看不能被重用的空间的大小呢?

 

问题:

一个docker存储空间被占满了:

  1. 如果扩容?
  2. 扩容必丢数据?如何备份数据?又如何导入数据?

 

关于loop-lvm 和 direct-lvm:

  1. loop-lvm不需要过多的配置,出于开箱即用的考虑,docker存储驱动默认使用的是loop-lvm,但是,非常不建议生产环境使用该方式(为什么?)
  2. direct-lvm配置比较复杂

docker存储空间扩容(不丢数据): https://docs.docker.com/engine/userguide/storagedriver/device-mapper-driver/

docker在线扩容脚本: http://phpor.net/blog/post/3624

 

扩容注意事项:

扩容不要等到一点儿空间也没有的时候才做,否则后果有二:

  1. 要么进程申请不到存储资源,使得负载奇高,而且进程无法被杀死,只能重启docker宿主机
  2. 要么所有docker容器存储系统变为只读,只能重启docker daemon(所有容器都将需要重启)

参考资料:

  1. http://coolshell.cn/articles/17200.html
  2. https://jpetazzo.github.io/2014/01/29/docker-device-mapper-resize/
  3. https://docs.docker.com/engine/userguide/storagedriver/device-mapper-driver/

 

shadowsocks vs vpn

话说有一个叫做影梭的app,可以基于shadowsocks(一下简称ss)在本地启动一个vpn,vpn流量走的是ss,相当安全;既然ss可以翻墙,也同样可以翻到内网;那么,ss能替代vpn吗?

下面讨论ss与vpn的一些区别:

  1. 账号
    1. ss每个server只有一个共享密码,没有用户名的概念;
    2. vpn可以有多个用户名、密码
  2. 验证
    1. 虽然ss有密码,但不做验证,其实是一个共享秘钥,直接用于数据加密;就算秘钥错了,server端也不做验证(或者无法验证,或者未提供验证逻辑),所以,使用ss时,密码错误是没有提示的
    2. vpn有密码验证阶段
  3. 如果非要把ss当做vpn来使用,则
    1. 充其量也就是个单用户的vpn
    2. ss client端需要实现虚拟网卡部分的功能,可能工作量比较大一些,好在影梭app已经这么做了(没发现类似的桌面软件或命令行软件)
  4. 鉴于ss的代码并不复杂,似乎可以给ss添加对读用户支持的验证逻辑的;相信ss距离vpn远不止这些,试图把ss改造成vpn可能不是一个对的想法

存储故障导致的

从来没想到过,存储故障能导致系统必须重启才能解决问题,尤其是,必须断电重启,reboot都不好使。

为什么呢?

存储故障导致进程因处于等待资源状态而处于D状态,该状态无法响应任何信号,即使 -9 信号也不好使。此种场景一般来讲dmesg能看到错误信息,如下图所示:

默认当等待资源超过120s时,内核会打印错误如下错误信息。

具体可能出现很多表现,如: top 、ps、ls 等卡死; 甚至访问/proc目录下的东西(如: /proc/xxx/cmdline ) 都能卡死,原以为/proc 下的东西都在内存中,其实完全不是,/proc 根本就是个接口

 

还有比reboot -f都需要等待更悲催的吗?

参考文章:

https://rachelbythebay.com/w/2014/10/27/ps/

http://baike.baidu.com/link?url=-dJFqkX4THUzLRJ9MtVJr1utLBa8tYn9x_APkLF_JvC-p57waIjtQ1HxlPJLuJGggYE0PZGCQ-h0BOftzW2eua

如何释放linux系统的cache

我们知道,linux会将文件尽可能的cache到内存里面,系统运行一段时间后,我们发现内存几乎被用完了,其实大部分是cache;而且,说实在的,真的没有必要去清理这个cache,内存真的不够用的时候,这些cache会自动释放的。

你们我们这人就是洁癖,就想把这些cache从内存中请出去,该用哪个命令呢?

我没有找到使用哪个命令,但是dd和rm可以做到:

  1. 如果cache使用了N个G,你们可以用dd创建一个N个G的大文件;这时候cache中的东西就被这N个G的文件给挤出去了
  2. 使用rm删除这个N个G的大文件;这时候cache中的这个N个G的大文件就被释放掉了,然后cache就空了,对应的内存就free了

 

通过drop_cache 实现:

 

Docker之Volume

情景

出于某种目的,docker容器挂在了外部的文件,而且期望当外部文件发生变化的时候,docker容器对应的文件也发生变化。

难道本来不是这样的吗?是也不是:

是:  echo “append info” >>/the/file    立即反应到docker容器内部了

不是: 通过vim编辑,添加一行,docker容器内部文件没有变化

分析: 应该是echo和vim有不同

当不知道的时候,结果就是: 有时候可以,有时候不可以,恶心不恶心

放狗搜索,这里说的详细: https://github.com/docker/docker/issues/15793

总结一下:

  1. docker (或者说linux mount)挂卷是基于inode的
  2. echo 是在原文件基础上修改文件的,没有改变文件的inode
  3. vim 编辑的时候改变了文件的inode; (上面文章有说,可以通过 set noswapfile来禁用此特性,测试不好使,字面来看也不太是这么回事儿,实际测试应如此设置: set backupcopy=yes)
  4. 其他编辑器也可能改变inode,有些也可以设置不改变inode
  5. sed 编辑文件默认也是修改inode的,可以使用-c选项避免inode发生变化

有解决办法吗?

只要想办法确保inode不变即可,如: 编辑文件的时候,先copy走,编辑完再cat 到原文件中。

什么? 你使用vim修改挂载的文件之后,docker容器中的文件立刻就变化了?

先确认下该文件的inode是否变化了(我不想去证明vim修改文件肯定会导致inode发生变化)

 

还有别的解决办法吗?

  1. 请确认必须挂在文件而不是也可以挂在目录。挂载目录的话,docker内外是完全同步的
  2. 如果挂载目录的话,目录中的软连接只能在挂载的目录内有效,如果软链的是其它目录的文件,则容器内外是不同的

关于挂载目录

如果容器内挂载了宿主机目录/home/phpor/test 到容器内部 /test , 现在创建目录 /home/phpor/test/taoyi ;然后在另一个终端容器进入容器/test/taoyi 目录, 这时候,如果宿主机上mv /home/phpor/test/taoyi /home/phpor/taoyi , 然后 创建文件 /home/phpor/taoyi/file ; 这时候,在另一个终端下对于原本已经进入 /test/taoyi 的回话将可以看到file文件

 

关于TCP之序号与确认号

 

  1. tcp序号是两个方向的,每个方向有自己的序列号
  2. 依次发出的两个tcp数据包的序号可能相同、可能加1、也可能加更多,规则:
    1. 如果上个数据包不需要确认,则下个数据包的序号不增加
    2. 如果上个数据包没有数据,但是也需要确认,如SYN,则下个数据包的序号加1
    3. 如果上个数据包有n个数据,则下个数据包的序号加n
  3. 确认号比较简单,就是要确认的数据包的序号加1
  4. 确认包可以是包含在响应的数据包中的
  5. 不是每个数据包都要对应一个确认包的,一个确认包可以确认好几个数据包的,如:
    1. 由于接收方buffer比较大(就是tcp的win比较大),发送方连续发送了3个数据包,这时候,接收方可以只确认最后一个数据包就足以说明前两个数据包都已经收到了

wireshark 问题包分析

问题包如下图:

138 数据包问题: 其实 138、139、140 三个包是在某个设备上因超过mtu值而被拆分的一个包; 138是首先被收到的一个子包,所以有此标识

140数据包问题: 其实从ip的标识头(序号自增)来看,140应该出现在139前面,所以说:Out-Of-Order (就是所谓的乱序)

139数据包问题: 从ip的标识头来看,139前面应该有一个数据包没被抓到(其实就是140),实际是迟到了那么几微妙; 也就是说,如果140数据包能赶到139前面,那么139和140的问题就都没有了

142数据包问题: 从tcp协议的seq来看,141和142是重复了; 从ip数据包来看,这不是同一个数据包的重传,至于为啥会重复ack,不知道啊!

 

问题: ip分包标识是在ip协议上的,这里的重组为什么是reassembled TCP Segments

Etcd初步

源码: https://github.com/coreos/etcd

说明:

  1. master分支可能还不稳定,使用最新的release分支(下面测试的是2.3.6)
  2. 源码编译需要下载100MB左右的源文件,包含依赖文件(如果是在新的golang环境上编译的话)
  3. 如果在etcd目录下go install的话,只编译安装etcd,不编译etcdctl;需要进入ectdetl目录下go install,才会有etcdctl; 基本上就这两个二进制文件了
  4. 直接下载编译的文件比较快捷,而且这里有最新的release的二进制文件: https://github.com/coreos/etcd/releases/
  5. https://github.com/coreos/etcd 下面有一些参考文档的链接

 

集群配置:

https://github.com/coreos/etcd/blob/master/Documentation/op-guide/clustering.md

 

集群实现方式有三种:

今天了解了DNS Discovery,其中:

  1. DNS server使用的是dnsmasq
  2. DNS配置
    添加文件: /etc/dnsmasq.d/etcd.srv.conf

    其中:_etcd-server._tcp.        _etcd-client._tcp.  都是etcd自动添加的
    _etcd-server._tcp.    用于server之间交互,使用 2380 端口
    _etcd-client._tcp.     用于client(如: etcdctl )发现server,使用 2379端口

问题:

上面的dns配置基本是可用的,目前发现的唯一问题是: etcdctl –discovery-srv  etcd.i.beebank.com 来工作时,期望srv记录中拿到的是域名,而不是ip;不过etcd –discovery-srv  etcd.i.beebank.com 却没有这种苛求; 解决办法,把上面的IP换成域名,然后对域名做解析,而且,同一个域名解析出来多个IP也是不错的

 

修改如下:

/etc/hosts

/etc/dnsmasq.d/etcd.srv.conf

 

今天的测试没有涉及认证,server端启动参数:

其中:

–discovery-srv: 用于服务发现

–initial-advertise-peer-urls: 告诉其他成员,通过这个地址来联系我(对于服务发现的时候似乎用途不大,因为dns上已经注册了呀)

–initial-cluster-token: 应该是加群的暗号,暗号对不上是不让进群的

 

测试点:

  1. 通过proxy进行读写
  2. 同时通过单个实例进行读写
  3. 通过etcdctl的–discovery-srv进行读写
  4. 其中一个实例死掉一会儿,重启后依然能读到死后写入的数据
  5. 集群中一个节点到另一个节点之间都不止一个长连接
  6. 稍后测试一个全新的节点接入会是什么样子

======= v3.0.4 试用 ======

server端通过srv记录发现时,如果srv记录中解析到的是域名,则不会发现该域名下的所有IP (这个行为和v2.3.6不一致)

client端通过srv记录发现时,如果srv记录中解析到的是多个IP:PORT, 则etcdctl会试图解析已解析到的IP而报错

所以配置可修改成这样:

看起来好恶心