Docker Operation

wangxin511 8年前

来自: http://wangzhezhe.github.io/blog/2016/01/29/docker-operation/

主要是记录一些平时使用中常见的一些关于docker的小坑,就是所谓的docker operation吧,希望能通过现象看到本质才好,以后相关的问题都一点一点整理到这部分来。

dockero operation的一些基本的技巧:

批量删除的常用命令

  • 批量删除停止运行的容器 docker rm $(docker ps -a -q) 不加-f的话 正在运行的容器就不会被强制删除。
  • 列出tag为none的相关镜像 docker images |grep none |awk 'print{$1}'

参考: http://dockerone.com/question/197 容器镜像本身问题比较大: 1)可能是container本身启动就没成功。这种情况下,通过docker ps | grep ${CONTAINER_ID}看到的container的status应该是非0,执行docker logs ${CONTAINER_ID}可以看到具体错误信息; 2)可能是container里根进程跑完退出了。这种情况下,通过docker ps | grep ${CONTAINER_ID}看到的container的status应该是非0。

关于打tag时候的一些说明

普通的情况下,容器的命名方式有两种,一种是普通仓库中构建出来的镜像: 用户名/仓库名:标签 如果在构建的时候不指定标签的话,系统会默认指定标签为latest,如果已经有了latest的标签,则可以在 tag 的时候 使用-f 参数 将旧的容器覆盖掉。 还有一种是顶层仓库,就是在dockerhub中官方指定的,就需要再加上用户名了,比如ubuntu:latest这种的。

当使用私有的registry的时候,需要在镜像的名称前面加上私有registry的ip以及端口的前缀,最后的镜像名称的完整的形式就是 私有registryip:端口/仓库名/镜像名:tag 这样的形式,总之要把整个命名方式以及逻辑结构搞明白,之后再做一些事情的时候才能顺手。

关于批量清理镜像的相关问题(具体见博客中所说的): 这个命令可以列出当前 host 中所有的容器 后面可以跟 -a 或者是 -l -l表示仅仅显示出最近的一个容器的 相关的信息 使用docker ps –n x 可以查看最后x个容器的信息,这个形式查看起来比较方便。 使用 –notrunc 命令可以查看出镜像的全部的信息 使用-q参数可以仅仅列出id 批量删除的时候会方便很多 比如使用几个命令结合起来 docker kill $(docker ps -a -q) 这个命令可以删除所有的正在正在运行和停止运行的容器,-a参数是列出全部的容器,-q参数是仅仅列出容器的id,kill命令会结束对应的容器的进程。 还有许多类似的批量清理的操作,比如 http://www.jb51.net/article/56051.htm ,可以参考网上的相关内容。 关于批量删除none镜像的命令 这个后续要好好调研一下 http://www.tuicool.com/articles/R7jMZfq

这个blog中说明了镜像的存储结构 具体涉及了 多个文件夹中存储的内容 感觉还比较详细: http://liubin.org/2014/03/10/about-docker-images-storage/

今天测试集群的时候 发现在从私有的registry中pull镜像的时候报了一个奇怪的错误

开始的时候还真是白死不得其解 后来在老师的提醒下才发现 竟然是硬盘大小的原因 当然在资源不足的时候,registry并没有很友善地提醒用户 no space left on device的信息 ,而是直接在pull镜像的过程中报一个 FATA[0000]Error: 镜像名称not foud的信息 这个还真是不太友好,要不是老师比较有经验,还不知道要再这里多久。

关于v2的registry的digest(push镜像时候的一个小trick): 在v2的registry支持degist的特性 如果 镜像名相同的话 貌似之前已经传好的镜像就可以复用 不用重新上传了 要是镜像名不同的话 貌似即使是相同的镜像层也会重新被push一下 被覆盖掉

虽然镜像的id 是一样的 但是digist很可能是不一样的 看一下 v2 registry的挂载的目录 可以看到 是按照仓库名来进行分类的 里面有每个仓库中的镜像的具体信息 _layers可能就是每个层的具体的信息???

https://blog.docker.com/2015/04/docker-release-1-6/ https://github.com/docker/docker/pull/11109

这个关于docker operation的操作很实用 里面有很多很好的技巧 http://segmentfault.com/a/1190000000482229

在构建image的时候,如果需要执行的直接是一个二进制的文件,就直接使用busybox作为基础镜像就比较好,不需要直接down一个ubuntu作为base image出来,busybox里面直接有linux的那些东西,体积比较小,适合那种二进制文件的直接运行。

版本升级的时候的一个bug 1.7.0刚开始的时候,bridge==none与net=host是有冲突的,之前卡了好久,还是jw翻源码才解决的。具体的issue可以参考这个: https://github.com/docker/docker/issues/14106

docker 安装指定版本: 虽然docker官方建议的安装命令为sudo apt-get install lxc-docker(当然这之前还有不少的安装和配置),

但是我一般都习惯于安装指定版本,如安装1.4.1时使用命令sudo apt-get install lxc-docker-1.4.1,安装1.5.0的话就卸掉,直接使用命令sudo apt-get install lxc-docker-1.5.0

或许对你的问题帮助不大

如果你清楚了如何直接升级,也请留言通知哈

这里有一个版本: 采用不同的keys: http://stackoverflow.com/questions/27657888/how-to-install-docker-specific-version 这个可以安装指定版本的docker就很简单的几步命令 https://get.docker.com/ubuntu/

今天又遇到几个坑 首先一个是部集群的时候,发现有时候docker deamon无法正常启动 常见的原因可能有几个方面: 第一个是/etc/default/docker文件中,格式书写错误,有的参数是空的,导致service docker restart之后,deamon仍然无法正常工作。

由于集群是自动部署的,在回复集群的脚本中,/etc/default/docker文件没有被提前删除掉,之后又把新的内容写到了文件中。(删除指令是写在clean脚本中,使用ssh命令让clean脚本执行,但有时总是删不干净,后来干脆先ssh 删除一下,再执行)

还有一些及特殊的情况 docker 监听的默认unix sock (默认位于/var/run/docker.sock)的文件属性发生变化,有时会莫名其妙的变成一个目录,这个时候,就会非常奇怪,虽然docker deamon进程是运行着的 但是docker 的命令并不能正常执行。此时要注意看docker deamon的日志文件 ,位于/var/log/upstart文件夹下,能看到一些信息。

关于docker deamon的 binaries 安装方式 应该会有一点启发 https://docs.docker.com/installation/binaries/

how to gracefully stop a container (valuabe) https://labs.ctl.io/gracefully-stopping-docker-containers/

questions: docker deamon died or forcely be killed 这个deamon所管理的那些容器 没有被发送 kill 信号 这回导致它们尝试 unmounting the device mappings created by volume shares with the docker host machine.

http://techblog.newsweaver.com/cleaning-up-device-mappings-after-docker-daemon-death/ delete the loop device : http://stackoverflow.com/questions/5881134/cannot-delete-device-dev-loop0

As a consequence, when we tried to restart the docker daemon it was not able to open /var/lib/docker/devicemapper/devicemapper/data because it was still mounted by the previous docker containers' volume shares (which do not show up with basic ls and lsof commands).

具体的解决方案是这个: 就是解除挂载再将挂载的文件删除掉 就好了 for dm in /dev/mapper/docker-*; do umount $dm; dmsetup remove $dm; done

可以尝试使用下面的命令 来找出所有挂载在指定目录上的设备 之后 unmount 这些设备 之后就可以删除新启动的docker的文件夹了 mount |grep /var/lib/docker-bootstrap |awk ‘{print $1}’

关于mount命令以及unmount命令

采用 https://github.com/justone/dockviz 采用里面的二进制包就可以直接 使用 docker image -t 的功能

关于 docker 镜像 docker history 可以查看镜像的层次结构 每一层会执行了哪些操作

具体的目录位置 以 /var/lib/docker/aufs 里面有三层个子目录 : diff layers mnt 其中 diff中有每一层实际存储进去的内容 layers中有每个镜像所依赖的层次结构的id 最上面的一层 就是文件的名称

镜像的具体配置信息存储在 /var/lib/docker/graph 目录下 里面有三个文件 checksum json 以及layersize 其中layersize记录了该层镜像的总大小

在centos里面 重新配置docker0网桥的时候和在ubuntu中的不太一样: https://github.com/docker/docker/issues/14425

遇到了一个docker -d 启动是的时候网络的错误 Error starting daemon: Error initializing network controller: Error creating default “bridge” network: failed to parse pool request for address space “LocalDefault” pool “” subpool “”: could not find an available predefined network 大概意思就是初始化网络设备的时候,路由规则有冲突了。 解决方法就是 把eth0或者eth1关掉 再重启docker就ok了 之后再把对应的网卡在启动起来 貌似是那个路由规则冲突了 网上有些对应的阿里云启动docker 第一次失败的例子 也是类似的原因 主要就是 设置了某些路由规则 把docker0的那个pool给占用了 或者是使用了隧道之后也不行 要把对应的设备关掉 其实具体原因 还是不知道 只是知道大概怎么解决。。。

问题 df du 显示结果不一致

df与du是查看磁盘占用空间常用命令,比如发现机器上的磁盘空间被占满了,于是先用df看下整体的情况,之后看哪个盘占满了,之后看哪个文件占了比较大的空间,于是从根目录开始,每次向下递归一层,一次用如下命令查找: du -ah --max-depth=1 (--ah 表示all 以及 humanreadable)

df的时候发现磁盘空间是100%,du的时候,发现实际的文件并没有占用那么大的空间。

最后lsof |grep delete发现好多docker的容器,之后把docker deamon重启了一下,空间利用率就恢复正常了。df的时候也正常了。

df(disk free)与du(disk usage)的原理

du的时候往往会发现当文件夹下面的文件数目比较多的时候,速度会比较慢,因为du会逐个调用文件的fstat命令来查看文件的实际大小。

df使用的是statfs这个系统调用,会获取到整个文件系统的状态信息,相当于直接读取的是元数据。

什么情况下会导致du与df显示的结果不一致呢?

有进程占用了硬盘上的文件,而这些文件可能已经用rm删除。实际上这些已删除的文件 并未释放硬盘空间 ,只是看不到而已,占用它的进程还在不停地往文件里写,必须重启这些占用进程才能释放空间。 通常这种情况下可以使用 lsof |grep deleted 来看,之后再把对应的占用这些文件描述符的进程kill或者重启,这样这些空间就被释放了。

比如在docker容器正常运行的时候,把它的某个很大的日志文件给删掉,就会造成类似上面的情况。(事实上,在实际机器上,自己为了图方便,常常执行这样的操作),造成的后果就是df与du的结果不一致。

比如把一个正在运行中的容器的<容器id>.log文件强行删除掉,这样在lsof |deleted的时候会看到众多的对于这个文件的写操作。

如果想清空某个正在使用的文件,并且还保留之前的文件描述符,可以用O_TRUNC的标记来打开文件,这样会把文件的长度设置为0并且丢弃文件中已有的内容。

相关参考: http://www.cnblogs.com/heyonggang/p/3644736.html