2月 042017
 

理论上来讲,mount操作一定是发生在容器启动之后的(包括宿主机也如此);但是,docker只是在create好run的时候提供了挂载卷的方法,一旦容器被创建,我们将再没有机会给容器挂载新的卷了;如果非要这么做的话,只能销毁、重新创建容器,好生繁琐。

既然理论上可以在容器启动之后挂载新的卷,docker没有提供相应的API,那么又该如何做呢?

  1. 办法一: hack一下docker,提供这方面的功能; 缺点: 随着docker版本的升级,代码维护起来比较麻烦
  2. 办法二: 从系统层面来实现,参考: https://jpetazzo.github.io/2015/01/13/docker-mount-dynamic-volumes/

办法二还是比较科学的,这里测试了一下,确实好用,脚本如下:

 

docker-enter 下载地址: https://github.com/jpetazzo/nsenter

注意:

  1. 这里用到了一个importenv的程序,可以参考容器环境变量来设置执行命令的环境变量,是个不错的办法,只是引入了一个其他的程序
  2. 这个要求容器中安装mount命令,该命令的依赖比较多,而且很少使用,所以,容器中一般都没有,需要手动安装,如果去掉那些依赖,单独安装一个mount命令会好一些
  3. 这个方法不适用于不基于块设备的文件系统,只有在/proc/mounts能正确得到块设备节点(上面谈到,并不总是能正确得到)的时候才能起作用。(比如: cephfs挂在的目录就不行)
 Posted by at 下午 4:52
12月 162016
 

docker命令行会有一系列的操作容器的方法,如: create 、run、start、exec等等,这些都是通过docker daemon的sock文件由docker daemon实现的,那么docker daemon又是如何实现的呢?

docker-runc就是最终执行的程序,docker-runc也提供了一系列类似于docker的操作,如: create、start、run、exec等,当然,也不完全一样;docker-runc  exec的时候可以指定环境变量、进程的能力等,所以,我们可以直接使用docker-runc来在已有的容器内部执行命令,需要注意的是,docker create时定义的环境变量需要通过选项指定,因为docker-runc不知道这些(奇怪的是,预定义的进程的能力似乎是知道的)

 Posted by at 下午 7:06
12月 162016
 

下图是一个容器中的进程:

  1. 每个容器总是有一个1号进程
  2. 1号进程的父进程在容器中显示为0号进程,而在容器外表现为进程 docker-containerd-shim
  3. 容器中的1号进程不同于宿主机的1号进程那么NB,宿主机的1号进程直接或间接地领导所有其它进程;而容器中的1号进程并不一定直接或间接地领导所有进程,取决于这些进程是否1号进程来创建的,这些进程的父进程死掉后会最终归属到容器内所谓的0号进程而非1号进程,不过,容器中所谓的0号进程并非都是同一个进程
  4. docker top 显示的容器中的进程可能不太全,与是否该进程归属于1号进程没有任何关系;与进程是否最终归属于该容器的管理进程docker-containerd-shim也没有关系,如果是nsenter进入容器,则启动的进程在docker top中是看不到的,虽然该进程在容器中显示的ppid也是0,其实同样是0的ppid却可能不是同一个进程,因为,只要父进程在容器外部,则容器内部显示的ppid就统一为0; 为什么docker top可能看到的不全?docker top是如何实现的呢?参看: https://phpor.net/blog/post/4420
 Posted by at 下午 12:09
11月 282016
 

问题现象:

问题环境:

dockerd 打开的文件数太多了, ~ 4w

类似如下:

和哪个容器相关还是和特定容器无关?

主要和docker-registry的那个容器相关(应该其他容器的个数少,不一定完全正常)

杀掉docker-registry这个容器,dockerd内存使用并未减少

但是相应的文件是不存在的了:

然后,只好重启dockerd试试了:

重启dockerd,问题解决(还好我们的dockerd重启不会影响容器的正常工作)

 

问题的非最终原因(但是也进了一步):

我们对所有的容器都是有监控的,基本手段是使用docker exec 在容器内执行命令,docker-registry这个容器比较特殊,没有我们要执行的命令,除了我们无法获取该容器信息外,没执行一次不存在的命令,dockerd就会在目录/run/docker/libcontainerd/ 下创建一个FIFO文件,并且始终打开着,不关闭(如果要执行的命令存在,则不会存在不关闭的情况)

详细测试结果如下:

对于一个返回值非零的命令,如果使用exec -i 选项,则不会残留打开的文件,如果不使用 exec -i 选项,会残留打开的文件(但是继续跟踪发现,该残留的时间不会太长)

对于一个不存在的命令,不管是否使用 -i 选项,总是会残留打开的文件;

解决办法:

为确保要执行的命令存在,可以使用如下方式:

该方式不但不会产生多余的未关闭文件描述符,而且可以执行一个命令序列,而直接docker exec -it cmd 只能执行一条命令

参考资料: https://github.com/docker/docker/issues/22095

 

 Posted by at 下午 7:03
11月 042016
 

问题:

如何在已创建的容器上暴露更多端口?

分析:

一般来讲,我们在构建镜像或创建容器的时候指定容器暴露哪些端口,Docker没有提供其他的方法来暴露容器中的端口;

看看Docker暴露端口的手段发现暴露端口这事儿是通过传统的iptables来实现的,于是乎,只需要添加相应的iptables规则就可以搞定了,如:

to expose the container’s port 8000 on your localhosts port 8001:

 

参考:

http://stackoverflow.com/questions/19897743/exposing-a-port-on-a-live-docker-container

https://www.google.com.hk/?gws_rd=cr,ssl#safe=strict&q=docker+port+mapping+after+run

 Posted by at 下午 4:33
10月 302016
 

问题

docker 容器中创建很大的文件会占用较多的docker存储空间(很正常啊),在容器内部df可以看到容量的变化,在宿主机上docker info也能看到data空间的变化;

当把那个(或那些)很大的文件删除后,在容器内部df可以看到已用容量减小,可用容量变大,但是宿主机上docker info看到的已用空间却没有减少,也就是说,已经分配出去的就收不回来了。

怎么办?

也不是完全回收不了,当把那个容器删除掉,docker info看到的已用空间就会减少了

 

所以

  1. 必要的时候可以重建容器
  2. 不是特别需要的话,不要在容器内部写太多的数据,大数据写到卷上
 Posted by at 下午 12:45
9月 172016
 
  1. docker容器其实(可以)在同一个父的cgroup下的,可以对这个父的cgroup进行限制,避免总量超限
  2. 一个机器上可以有多个docker daemon的,截止该文章诞生的时候,该功能还处于试验阶段,至少理论上是可以的: 参考: https://docs.docker.com/engine/reference/commandline/dockerd/#/running-multiple-daemons
  3. docker daemon可以使用不同的运行时,默认为containnerd,自动启动,并且通过socket通信;· 参考: https://docs.docker.com/engine/reference/commandline/dockerd/#/docker-runtime-execution-options
 Posted by at 下午 9:31
9月 112016
 

Docker daemon 启动的时候可以有很多的命令行选项,这些选项也可以写到一个配置文件中,默认位置: /etc/docker/daemon.json ,key、value都和命令行一一对应,基本一样,但就是有些key在daemon.json中是复数形式,如: insecure-registries、storage-opts

 

docker-1.12 中 dm.basesize 默认10G, docker-1.9.11中默认 100G

storage-opts相关配置: https://docs.docker.com/engine/reference/commandline/dockerd/#/storage-driver-options

dm.fs 容器文件系统类型,目前支持ext4和xfs, 默认xfs

dm.loopdatasize 修改配置后,重启生效,只能改大,不能改小

dm.basesize 每次启动daemon都参考,一旦设置,无法修改,除非把daemon数据都删除重来;如果已经创建了容器了,不管该容器是死了、还是活着的、还是已删除,dm.basesize 都不能修改

参考: https://github.com/docker/docker/blob/f7d48d74a3dd44171d5e9dbcf33ba6de40baa08a/docs/reference/commandline/dockerd.md#daemon-configuration-file

 

有一些docker配置选项可以在不重启daemon的时候修改并生效,直接kill -HUP PID_OF_DAEMON 就可以,这种配置不多:

The list of currently supported options that can be reconfigured is this:

  • debug: it changes the daemon to debug mode when set to true.
  • cluster-store: it reloads the discovery store with the new address.
  • cluster-store-opts: it uses the new options to reload the discovery store.
  • cluster-advertise: it modifies the address advertised after reloading.
  • labels: it replaces the daemon labels with a new set of labels.
  • live-restore: Enables keeping containers alive during daemon downtime.
  • max-concurrent-downloads: it updates the max concurrent downloads for each pull.
  • max-concurrent-uploads: it updates the max concurrent uploads for each push.
  • default-runtime: it updates the runtime to be used if not is specified at container creation. It defaults to “default” which is the runtime shipped with the official docker packages.
  • runtimes: it updates the list of available OCI runtimes that can be used to run containers

有些配置重启daemon都不能生效

参考: https://docs.docker.com/engine/reference/commandline/dockerd/#/configuration-reloading

 Posted by at 下午 11:33
9月 092016
 

原理:

通过cgroup限制,相关cgroup项如下:

这里暂且只讨论:

  1. blkio.throttle.read_bps_device
  2. blkio.throttle.read_iops_device
  3. blkio.throttle.write_bps_device
  4. blkio.throttle.write_iops_device

较旧的docker版本不支持设置这些,但是,只要你的内核支持上面这些,就可以直接修改cgroup实现。

我这里使用的是devicemapper,宿主机只有一个设备/dev/sda ,如下:

devicemapper信息如下:

从上面可以看出:

  1. docker 使用的mapper-pool为 /dev/dm-0,设备号: 253,0
  2. 该机器上一个容器,该容器挂载的设备为 /dev/dm-1 , 设备号: 253,1
  3. 物理磁盘 /dev/sda 设备号: 8,0

容器不仅用到了devicemapper(这里是 /dev/dm-1),而且还挂载了外部的磁盘,下面是关于容器限速100mb的写法:

关于读速度,这里先不举例了,还有iops都如法炮制

 

 

 Posted by at 上午 10:58