elasticsearch不能触碰的配置

    有几个elasticsearch的热点配置,似乎没有办法避免调整。我们熟知的:配置按钮都希望被打开。然而把所有的配置项都打开,你觉得就可以息事宁人了吗。这些配置常常被滥用,将会导致糟糕的稳定性和糟糕的性能问题,乃至这两种问题。 垃圾收集器(...
继续阅读 »
eslogo.png

    有几个elasticsearch的热点配置,似乎没有办法避免调整。我们熟知的:配置按钮都希望被打开。然而把所有的配置项都打开,你觉得就可以息事宁人了吗。这些配置常常被滥用,将会导致糟糕的稳定性和糟糕的性能问题,乃至这两种问题。


垃圾收集器(Garbage Collector)


    在这里简单介绍了垃圾收集器基本认识,JVM使用垃圾收集器来释放回收内存。这个技巧确实是最后一个技巧的延伸,但是值得强调的是:
    不要更改默认的垃圾收集器配置!
Elasticsearh默认的GC是CMS。让GC并发的运行应用程序,以便最大限度的减少暂停时间。然而它有两个stop-the-world阶段,在回收大量的heaps。

尽管有这些缺点,它是目前对于像Elasticsearch低延迟服务器软件的最佳的GC。 官方建议使用CMS。

有一个新的GC叫垃圾第一GC(g1gc)。这个新的GC的设计是为了尽量减少停顿比CMS更大的堆,和操作。它的工作原理是将堆分割成区域,预测区域包含最可回收的空间。通过收集这些区域(第一),它可以最大限度地减少暂停和操作非常大的堆。

听起来很棒!不幸的是,g1gc仍然是新的,和新的漏洞被发现的常规。这些错误通常会导致各种各样的段错误,硬盘崩溃。Lucene的测试套件是残酷的GC算法,似乎g1gc没有扭结的工作了。

我们想总有一天会推荐g1gc,但现在,它只是不够稳定满足Elasticsearch和Lucene的要求





线程池(Threadpoolsedit)


每个人都喜欢好的线程池。不管出于什么原因,人们似乎无法抗拒增加线程数。索引很多?更多线程!搜索很多?更多线程!节点空闲时间95%的时间?更多线程!    

在Elasticsearch默认的线程池的设置是非常明智的。 对于所有的线程池(除了search )的经纬被设置为CPU核心的数量。 如果你有八个核心,你可以同时运行的只有八个线程。 它有道理仅分配八个线程在任何特定的线程池。

搜索得到一个较大的线程池,并且被配置为 # cores * 3

你可能会争辩说,一些线程可以阻止(如在一个磁盘上IO操作),这就是为什么你需要更多的线程。这不是一个问题:在Elasticsearch的磁盘I/O是由Lucene的线程处理,不是Elasticsearch。

此外,合作的线程池通过传递彼此之间的工作。 您不必再担心网络线程阻塞,因为它正在等待磁盘写入。 联网线程都有,因为另一个线程池转交给了工作单位长期得到回网络。

最后,你的程序的计算能力是有限的。 有更多的线程只是迫使处理器切换线程上下文。 处理器可以一次运行只有一个线程,因此当它需要切换到一个不同的线程,它存储了当前的状态(寄存器,等等),并装载另一个线程。 如果你是幸运的,交换机将发生在相同的核心。 如果你运气不好,开关可能会迁移到不同的核心,并要求运输核间通信总线上。

这个上下文切换吃掉周期只需做管理清洁; 估计能挂它高达对现代的CPU为30μs。 所以,除非该线程将被阻塞比为30μs长,极有可能是那个时候会被更好地用刚刚加工和提早完成。

人们通常设置线程池错误的值。 八核的机器,我们已经与60,100,甚至1000个线程运行在整个的configs。 这些设置将让CPU大于得到真正的工作要做。

所以。 下次你要调整一个线程池,请不要。 如果你绝对无法抗拒 ,请保持你的核心数量在心中,也许设置数量增加一倍。 更重要的是仅仅是一种浪费。
原文地址:https://www.elastic.co/guide/en/elasticsearch/guide/current/_don_8217_t_touch_these_settings.html
收起阅读 »

elasticsearch配置文件详解

elasticsearch的config文件夹里面有两个配置文件:elasticsearch.yml和logging.yml,第一个是es的基本配置文件,第二个是日志配置文件,es也是使用log4j来记录日志的,所以logging.yml里的设置按普通...
继续阅读 »
es1.jpg
elasticsearch的config文件夹里面有两个配置文件:elasticsearch.yml和logging.yml,第一个是es的基本配置文件,第二个是日志配置文件,es也是使用log4j来记录日志的,所以logging.yml里的设置按普通log4j配置文件来设置就行了。
下面主要讲解下elasticsearch.yml这个文件中可配置的东西:

cluster.name: elasticsearch
配置es的集群名称,默认是elasticsearch,es会自动发现在同一网段下的es,如果在同一网段下有多个集群,就可以用这个属性来区分不同的集群。

node.name: "Franz Kafka"
节点名,默认随机指定一个name列表中名字,该列表在es的jar包中config文件夹里name.txt文件中,其中有很多作者添加的有趣名字。

node.master: true
指定该节点是否有资格被选举成为node,默认是true,es是默认集群中的第一台机器为master,如果这台机挂了就会重新选举master。

node.data: true
指定该节点是否存储索引数据,默认为true。

index.number_of_shards: 5
设置默认索引分片个数,默认为5片。

index.number_of_replicas: 1
设置默认索引副本个数,默认为1个副本。

path.conf: /path/to/conf
设置配置文件的存储路径,默认是es根目录下的config文件夹。

path.data: /path/to/data
设置索引数据的存储路径,默认是es根目录下的data文件夹,可以设置多个存储路径,用逗号隔开,例:
path.data: /path/to/data1,/path/to/data2

path.work: /path/to/work
设置临时文件的存储路径,默认是es根目录下的work文件夹。

path.logs: /path/to/logs
设置日志文件的存储路径,默认是es根目录下的logs文件夹

path.plugins: /path/to/plugins
设置插件的存放路径,默认是es根目录下的plugins文件夹

bootstrap.mlockall: true
设置为true来锁住内存。因为当jvm开始swapping时es的效率会降低,所以要保证它不swap,可以把ES_MIN_MEM和ES_MAX_MEM两个环境变量设置成同一个值,并且保证机器有足够的内存分配给es。同时也要允许elasticsearch的进程可以锁住内存,linux下可以通过`ulimit -l unlimited`命令。

network.bind_host: 192.168.0.1
设置绑定的ip地址,可以是ipv4或ipv6的,默认为0.0.0.0。


network.publish_host: 192.168.0.1
设置其它节点和该节点交互的ip地址,如果不设置它会自动判断,值必须是个真实的ip地址。

network.host: 192.168.0.1
这个参数是用来同时设置bind_host和publish_host上面两个参数。

transport.tcp.port: 9300
设置节点间交互的tcp端口,默认是9300。

transport.tcp.compress: true
设置是否压缩tcp传输时的数据,默认为false,不压缩。

http.port: 9200
设置对外服务的http端口,默认为9200。

http.max_content_length: 100mb
设置内容的最大容量,默认100mb

http.enabled: false
是否使用http协议对外提供服务,默认为true,开启。

gateway.type: local
gateway的类型,默认为local即为本地文件系统,可以设置为本地文件系统,分布式文件系统,hadoop的HDFS,和amazon的s3服务器,其它文件系统的设置方法下次再详细说。

gateway.recover_after_nodes: 1
设置集群中N个节点启动时进行数据恢复,默认为1。

gateway.recover_after_time: 5m
设置初始化数据恢复进程的超时时间,默认是5分钟。

gateway.expected_nodes: 2
设置这个集群中节点的数量,默认为2,一旦这N个节点启动,就会立即进行数据恢复。

cluster.routing.allocation.node_initial_primaries_recoveries: 4
初始化数据恢复时,并发恢复线程的个数,默认为4。

cluster.routing.allocation.node_concurrent_recoveries: 2
添加删除节点或负载均衡时并发恢复线程的个数,默认为4。

indices.recovery.max_size_per_sec: 0
设置数据恢复时限制的带宽,如入100mb,默认为0,即无限制。

indices.recovery.concurrent_streams: 5
设置这个参数来限制从其它分片恢复数据时最大同时打开并发流的个数,默认为5。

discovery.zen.minimum_master_nodes: 1
设置这个参数来保证集群中的节点可以知道其它N个有master资格的节点。默认为1,对于大的集群来说,可以设置大一点的值(2-4)

discovery.zen.ping.timeout: 3s
设置集群中自动发现其它节点时ping连接超时时间,默认为3秒,对于比较差的网络环境可以高点的值来防止自动发现时出错。

discovery.zen.ping.multicast.enabled: false
设置是否打开多播发现节点,默认是true。

discovery.zen.ping.unicast.hosts: ["host1", "host2:port", "host3[portX-portY]"]
设置集群中master节点的初始列表,可以通过这些节点来自动发现新加入集群的节点。

下面是一些查询时的慢日志参数设置
index.search.slowlog.level: TRACE
index.search.slowlog.threshold.query.warn: 10s
index.search.slowlog.threshold.query.info: 5s
index.search.slowlog.threshold.query.debug: 2s
index.search.slowlog.threshold.query.trace: 500ms

index.search.slowlog.threshold.fetch.warn: 1s
index.search.slowlog.threshold.fetch.info: 800ms
index.search.slowlog.threshold.fetch.debug:500ms
index.search.slowlog.threshold.fetch.trace: 200ms
收起阅读 »

openstack nova集成docker

    docker已经可以作为compute driver来使用,脱离了原来HEAT的模式,可以做到真正地使用nova来启动容器.这里记录一下openstack Kilo + docker 1.8的集成过程.所有组件环境基于centos7.  架构...
继续阅读 »
opdock1.png

    docker已经可以作为compute driver来使用,脱离了原来HEAT的模式,可以做到真正地使用nova来启动容器.这里记录一下openstack Kilo + docker 1.8的集成过程.所有组件环境基于centos7. 
架构图如下: 
openstack1.png


安装docker


    在compute node节点上安装docker,强烈建议安装docker-engine 1.8,需要linux3.1的kernal版本,拥有较高的生产稳定性,并且有启动用户组,旧版的docker-io是没有用户组,集成的时候docker.sock的权限每次都是手工修改很不方便.
# curl -sSL https://get.docker.com/ | sh
# usermod -aG docker nova
# systemctl enable docker.service
# systemctl start docker.service


安装novadocker


直接从github上clone安装
# pip install -e git+https://github.com/stackforge/nova-docker#egg=novadocker
# cp -R src/etc/nova/rootwrap.d /etc/nova/
# chmod -R root.nova /etc/nova/rootwrap.d
# cd src/novadocker/
# python setup.py install


配置nova调用docker驱动


# vi /etc/nova/nova.conf
compute_driver = novadocker.virt.docker.DockerDriver


配置glance支持容器格式


# vi /etc/glance/glance-api.conf (修改后重启glance-api服务)
container_formats = ami,ari,aki,bare,ovf,docker


Fix bug设置


    由于novadocker开发的版本是基于nova比较新的版本,在现在发行的版本中使用会有一个BUG,下面是修复记录.
    
1.重启openstack-nova-compute服务时提示没有找到oslo_log模块
# pip install oslo.log
2.重启openstack-nova-compute服务时提示没有找到hv_type模块
通过代码定位发现目前的版本中模块是nova.compute.hvtype 
修改/usr/lib/python2.7/site-packages/novadocker/virt/docker/driver.py
把from nova.compute import hv_type 改为 from nova.compute import hvtype
3.直接导入driver测试提示报错,CONF没有’my_ip’这个opt配置
通过代码排查发现目前的openstack是使用oslo.config这个模块包来做cfg的导入和导出 
但是在novadocker整个项目里面使用的oslo_config这个独立的模块
修改driver.py, client.py, hostinfo.py, vifs.py模块
from oslo_config import cfg 改为 from oslo.config import cfg
4.直接导入driver测试提示继续报错,没有找到hvtype.DOCKER属性
修改 /usr/lib/python2.7/site-packages/nova/compute/hvtype.py 
在# specific ‘baremetal’ & ‘fake’ then added in.下面增加
DOCKER=’docker’
    此时启动openstack-nova-compute已经正常


glance导入docker镜像


# docker pull hipache
# docker save hipache | glance image-create --is-public=True --container-format=docker --disk-format=raw --name hipache
    需要注意的是glance导入后的名字一定要和docker images下显示的名字一模一样,否则创建时会提示无法找到镜像


启动容器实例


nova boot --image hipache --flavor 1 --nic net-id=342a0eef-e03d-4fd8-af3c-1ed485bee989 docker
openstack2.png

1.此时启动容器实例报错失败,从nova-compute.log的日志看异常信息是”attach vif error”,具体是无法使用nova-rootwrap来提权进入namespace创建实例的网络接口
google找到的这个BUG的解决是禁用compute节点的selinux
2.继续下一个BUG,日志抛出一个Python异常是没有找到hardware.InstanceInfo模块
进入目录/usr/lib/python2.7/site-packages/nova/virt 
查看hardware.py的代码的确没有找到这个类或者函数,在github上找到nova项目的最新代码可以看到是有的.
openstack3.png

把这个类的代码复制到本地的hardware.py里面
 
3.重启启动一个实例后,异常继续保持 
openstack4.png
异常提示InstanceInfo没有getitem这个内置方法 
根据driver.py里面的调用可以发现get_info调用的时候是hardware.InstanceInfo是返回一个字典
删除刚才在hardware.py复制的代码,重写InstanceInfo
def InstanceInfo(state=None, max_mem_kb=0, mem_kb=0, num_cpu=0,cpu_time_ns=0, id=None):
Info={}
Info['state'] = state
Info['max_mem_kb'] = max_mem_kb
Info['self.mem_kb'] = mem_kb
Info['num_cpu'] = num_cpu
Info['cpu_time_ns'] = cpu_time_ns
Info['id'] = id
return Info
此时启动实例正常无报错
[root@openstack-k ~]# nova list
+--------------------------------------+---------+--------+------------+-------------+-------------------------------------------+
| ID | Name | Status | Task State | Power State | Networks |
+--------------------------------------+---------+--------+------------+-------------+-------------------------------------------+
| c9d7ef23-f0fe-488d-88b1-3b5650901820 | hipache | ACTIVE | - | Running | admin_private=192.168.0.101, 172.24.4.228 |
+--------------------------------------+---------+--------+------------+-------------+-------------------------------------------+
[root@openstack-k ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
14583d5d6308 hipache "supervisord -n" 5 hours ago Up 5 hours nova-c9d7ef23-f0fe-488d-88b1-3b5650901820
[root@openstack-k ~]#
参考连接:
https://wiki.openstack.org/wiki/Docker 
http://docs.docker.com/installation/centos/
https://bugs.launchpad.net/nova-docker/+bug/1480246
https://github.com/openstack/nova/
https://github.com/stackforge/nova-docker
原文链接:原文地址 收起阅读 »

控制Elasticsearch分片和副本的分配

    ES集群中索引可能由多个分片构成,并且每个分片可以拥有多个副本。通过将一个单独的索引分为多个分片,我们可以处理不能在一个单一的服务器上面运行的大型索引,简单的说就是索引的大小过大,导致效率问题。不能运行的原因可能是内存也可能是存储。由于每个分片可以有多...
继续阅读 »
    ES集群中索引可能由多个分片构成,并且每个分片可以拥有多个副本。通过将一个单独的索引分为多个分片,我们可以处理不能在一个单一的服务器上面运行的大型索引,简单的说就是索引的大小过大,导致效率问题。不能运行的原因可能是内存也可能是存储。由于每个分片可以有多个副本,通过将副本分配到多个服务器,可以提高查询的负载能力。
 
    为了进行分片和副本的操作,ES需要确定将这些分片和副本放到集群节点的哪个位置,就是需要确定把每个分片和副本分配到哪台服务器/节点上。
 


一、显式控制分配


生产情景:
比如生产环境有三个索引分别为 man、woman、katoey
希望达到的效果:
man索引放置在一些集群节点上
woman索引又单独放置到集群的另外一些集群节点上
katoey索引希望放置在所有放置man索引和woman索引的集群节点上

这么做是因为katoey索引比其他两个索引小很多,因此我们可以将它和其他两个索引一起分配。
但是基于ES默认算法的处理方法,我们不能确定分片和副本的存放位置,但是ES允许我们对其做相应的控制!

1、指定节点的参数
epei1.png

    如上图所示,我们将ES集群划分为两个"空间"。当然你也可以叫做区域,随便命名。我们将左边的三台ES节点服务器放置到zone_one的空间上面,将右边的三台ES节点服务器放到zone_two的空间上。
 
配置
    为了做到我们需要的效果,我们需要将如下属性配置到左边三台ES集群节点服务器的elasticsearch.yml配置文件中
node.zone: zone_one
    将如下属性配置到右边的三台ES集群节点服务器elasticsearch.yml配置文件中
node.zone: zone_two
 
索引创建
    当所有节点配置文件属性配置完成后,我们就可以根据空间名称,我们就可以创建索引放到指定的空间。
    首先我们运行如下命令,来创建man索引:
# curl -XPOST "http://ESnode:9200/man'
# curl -XPUT "http://ESnode:9200/man/_settings' -d '{
"index.routing.allocation.include.zone" : "zone_one"
}'
    第一条命令是创建man索引;第二条命令是发送到_settings REST端点,用来指定这个索引的其他配置信息。我们将index.routing.allocation.include.zone属性设置为zone_one值,就是我们所希望的把man索引放置到node.zone属性值为zone_one的ES集群节点服务器上。
 
    同样对woman索引我们做类似操作:
# curl -XPOST "http://ESnode:9200/woman'
# curl -XPUT "http://ESnode:9200/woman/_settings' -d '{
"index.routing.allocation.include.zone" : "zone_two"
}'
    不同的是,这次指定woman索引放置在node.zone属性值为zone_two的ES集群节点服务器上
 
    最后我们需要将katoey索引放置到上面所有的ES集群节点上面,配置设置命令如下:
# curl -XPOST "http://ESnode:9200/katoey"
# curl -XPUT "http://ESnode:9200/katoey/_settings" -d '{
"index.routing.allocation.include.zone" : "zone_one,zone_two"
}'

 2、分配时排除节点
    跟我们上面操作为索引指定放置节点位置一样,我们也可以在索引分配的时候排除某些节点。参照之前的例子,我们新建一个people索引,但是不希望people索引放置到zone_one的ES集群节点服务器上,我们可以运行如下命令操作:
# curl -XPOST "http://EScode:9200/people"
# curl -XPUT "http://EScode:9200/people/_settings" -d '{
"index.routing.allocation.exclude.zone" : "zone_one"
}'
    请注意,在这里我们使用的是index.routing.allocation.exclude.zone属性而不是index.routing.allocation.include.zone属性。
 
使用IP地址进行分配配置
    除了在节点的配置中添加一些特殊的属性参数外,我们还可以使用IP地址来指定你将分片和副本分配或者不分配到哪些节点上面。为了做到这点,我们应该使用_ip属性,把zone换成_ip就好了。例如我们希望lucky索引分配到IP地址为10.0.1.110和10.0.1.119的节点上,我们可以运行如下命令设置:
# curl -XPOST "http://ESnode:9200/lucky"
# curl -XPUT "http://ESnode:9200/lucky/_settings" -d '{
"index.routing.allocation.include._ip" "10.0.1.110,10.0.1.119"
}'


二、集群范围内分配


    除了索引层面指定分配活着排除分配之外(上面我们所做的都是这两种情况),我们还可以指定集群中所有索引的分配。例如,我们希望将所有的新索引分配到IP地址为10.0.1.112和10.0.1.114的节点上,我们可以运行如下命令设置:
# curl -XPUT "http://ESnode:9200/_cluster/settings" -d '{
"transient" : {
"cluster.routing.allocation.include._ip" "10.0.1.112,10.0.1.114"
}
}'
    集群级别的控制后续还会分享transient和persistent属性介绍
 


三、每个节点上分片和副本数量的控制


    除了指定分片和副本的分配,我们还可以对一个索引指定每个节点上的最大分片数量。例如我们希望ops索引在每个节点上只有一个分片,我们可以运行如下命令:
# curl -XPUT "http://ESnode:9200/ops/_settings" -d '{
"index.routing.allocation.total_shards_per_node" : 1
}'
    这个属性也可以直接配置到elasticsearch.ym配置文件中,或者使用上面命令在活动索引上更新。如果配置不当,导致主分片无法分配的话,集群就会处于red状态。
 


四、手动移动分片和副本


    接下来我们介绍一下节点间手动移动分片和副本。可以使用ElasticSearch提供的_cluster/reroute REST端点进行控制,能够进行下面操作:
    []将一个分片从一个节点移动到另外一个节点[/][]取消对分片的分配[/][]强制对分片进行分配[/]

 
移动分片
    假设我们有两个节点:es_node_one和es_node_two,ElasticSearch在es_node_one节点上分配了ops索引的两个分片,我们现在希望将第二个分片移动到es_node_two节点上。可以如下操作实现:
# curl -XPOST "http://ESnode:9200/_cluster/reroute" -d  '{
"commands" : [ {
"move" : {
"index" : "ops",
"shard" : 1,
"from_node" : "es_node_one",
"to_node" : "es_node_two"
}
}]
}'
    我们通过move命令的index属性指定移动哪个索引,通过shard属性指定移动哪个分片,最终通过from_node属性指定我们从哪个节点上移动分片,通过to_node属性指定我们希望将分片移动到哪个节点。
 
取消分配
    如果希望取消一个正在进行的分配过程,我们通过运行cancel命令来指定我们希望取消分配的索引、节点以及分片,如下所示:
# curl -XPOST "http://ESnode:9200/_cluster/reroute" -d '{
"commands" : [ {
"cancel" : {
"index" : "ops",
"shard" : 0,
"node" : "es_node_one"
}
} ]
}'
    运行上面的命令将会取消es_node_one节上ops索引的第0个分片的分配
 
分配分片
    除了取消和移动分片和副本之外,我们还可以将一个未分配的分片分配到一个指定的节点上。假设ops索引上有一个编号为0的分片尚未分配,并且我们希望ElasticSearch将其分配到es_node_two上,可以运行如下命令操作:
# curl -XPOST "http://ESnode:9200/_cluster/reroute' -d '{
"commands" : [ {
"allocate" : {
"index" : "ops",
"shard" : 0,
"node" : "es_node_two"
}
} ]
}'
一次HTTP请求包含多个命令
    我们可以在一次HTTP请求中包含多个命令,例如:
# curl -XPOST "http://ESnode:9200/_cluster/reroute" -d '{
"commands" : [
{"move" : {"index" : "ops", "shard" : 1, "from_node" : "es_node_one", "to_node" : "es_node_two"}},
{"cancel" : {"index" : "ops", "shard" : 0, "node" : "es_node_one"}}
]
}'
收起阅读 »

HDFS高可用方案之QJM

喜欢一个人,可以为TA做任何事,得到不接受却依然心甘情愿鞍前马后,苦苦等候那一线希望。对,这就是备胎,挂在汽车背后,可能一辈子也用不到的那个圆圈状的玩意儿,大部分情况下,它都会默默地挂在那里,等待几千分之一的机会,有个倒霉的轮子兄弟出事了,于是它就能派上用场了...
继续阅读 »
喜欢一个人,可以为TA做任何事,得到不接受却依然心甘情愿鞍前马后,苦苦等候那一线希望。对,这就是备胎,挂在汽车背后,可能一辈子也用不到的那个圆圈状的玩意儿,大部分情况下,它都会默默地挂在那里,等待几千分之一的机会,有个倒霉的轮子兄弟出事了,于是它就能派上用场了……(摘自豆瓣)
 
在Hadoop的分布式文件系统HDFS中,NameNode用来保存文件系统的元数据(包含目录结构/数据块位置等),如果NameNode上的数据丢失,HDFS上对应的文件数据就无法找回来了。Hadoop在2.0.0之前的版本,使用SecondaryNameNode备份NameNode的数据,但SecondaryNameNode无法转成NameNode,如果NameNode挂了,整个HDFS就会挂掉,无法实现真正的failover。这篇博文总结了5种Hadoop HA(High Available,高可用)方案,Hadoop2之后官方引入了QJM(Quorum Journal Manager)和NFS用于NameNode的备份和切换。本方将介绍的是QJM方案,它使用第二个NameNode实时同步当前NameNode的数据,相比于SecondaryNameNode,他可以随时切换成为真正的NameNode(一个可转正的高级备胎)。
 
先看看没有HA的HDFS的系统架构(用draw.io画的,尼马这么好的网站也被墙了):
hd21.png

然后有HA方案的系统架构:
hd22.png

以下的实验基于4个节点的Hadoop集群。其中每个节点的运行的进程列表如下:
hd23.png

实验环境中,所有节点的运行环境基本相同:
    []Ubuntu14.04 X64[/][]4G内存[/][]OpenJDK-1.7.0[/][]100Mbps以太网[/]
下面是实现这个系统的流程(官方文档+个人注解+辅助Shell命令)。

[list=1]

  • 安装Hadoop系统
  • 严格按照单节点搭建集群搭建两个步骤,系统建起来完全没压力。我遇到的问题是刚开始在配置文件(salves和core-site.xml等文件)中使用的是ip地址而非主机名,然后在log文件里看到各种无法连接。解决方案是修改主机名并在hosts文件里建立映射关系。
    hostname {new_hostname} # 修改主机名,只有当前Session有效sudo vi /etc/hostname   # 永久修改主机名的方法
    另外,对于64位的系统,最好重新编译源码。

    [list=1]

  • 修改配置文件
  • hdfs-site.xml文件:
          dfs.namenode.name.dir    /data/hadoop/namenode        dfs.datanode.data.dir    /data/hadoop/datanode        dfs.replication    2        dfs.nameservices    mycluster        dfs.ha.namenodes.mycluster    nn1,nn2        dfs.namenode.rpc-address.mycluster.nn1    hd1:8020        dfs.namenode.rpc-address.mycluster.nn2    hd3:8020        dfs.namenode.http-address.mycluster.nn1    hd1:50070        dfs.namenode.http-address.mycluster.nn2    hd3:50070        dfs.namenode.shared.edits.dir    qjournal://hd1:8485;hd2:8485;hd4:8485/mycluster        dfs.client.failover.proxy.provider.mycluster    org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider        dfs.ha.fencing.methods    sshfence        dfs.ha.fencing.ssh.private-key-files    /home/hduser/.ssh/id_rsa        dfs.journalnode.edits.dir    /data/hadoop/journalnode  
      []其中nameservices是集群的命名空间,即使有多个集群,可以共用配置文件,但是要注意某些配置项的顺序。[/][]dfs.ha.namenodes.mycluster中的mycluster是可以任取的,但是要和dfs.nameservices对应。[/][]dfs.namenode.rpc-address.mycluster.nn1参考上一条。[/][]dfs.namenode.shared.edits.dir值的格式是"qjournal://host1:port1;host2:port2;host3:port3/journalId",用来指定对应的JN节点,journalId建议使用和nameservices相同的名称。[/][]dfs.client.failover.proxy.provider.mycluster指定激活NameNode的Java类,目前Hadoop内置的只有上面那个。[/][]dfs.ha.fencing.methods是来用来隔离失效的NameNode的方法,有sshfence和Shell两种方式。sshfence需要配置dfs.ha.fencing.ssh.private-key-files私钥文件,以便交互的过程不需要输入密码。[/][]dfs.journalnode.edits.dir是JN保存数据的文件。[/]
     core-site.xml文件:
                fs.defaultFS        hdfs://mycluster    
      []注意mycluster要和dhfs-site.xml中的dfs.nameservices对应。fs.defaultFS不用端口号。[/]
     

    [list=1]

  • 部署
  • 改好配置文件好,就要将配置文件同步到所有的机器上了。可以用rsync将文件同步到多台机器上。rsync是一个增量同步工具,需要先安装。下面的rsync.sh的功能是将当前目录的所有文件发送到文件或参数对应的机器上。
    $ cat rsync.sh #! /bin/bashdir=`pwd`pdir=`dirname $dir`send(){    echo "Sending to $2:$1"    rsync -avez -e ssh $1 $2:$3}mul_send(){    while read host    do        send $dir $host $pdir    done < $1}[ -f $1 ] && mul_send $1 || send $dir $1 $pdir
    将rsync.sh放在etc/hadoop目录下,进入目录运行
    chmod +x rsync.sh./rsync.sh slaves# or ./rsync.sh hostname
    发送完文件之后,就是启动系统。步骤如下: 启动JNs.在所有JournalNode上运行
    sbin/hadoop-daemon.sh --script hdfs start journalnode
    启动NameNode. 在原NameNode上运行
    bin/hadoop --script hdfs start namenode # NameNode需要已经format。[url=使用上面的rsync.sh文件]/code[/url]将原NameNode(nn1)上的数据复制到第二个NameNode(nn2)。然后在nn2上运行:[code]bin/hdfs namenode -bootstrapStandby
    启动其他节点在NameNode上运行
    sbin/start-dfs.sh

    [list=1]

  • 切换NameNode

  • 手动方式

    上面的NameNode默认以standby的状态启动,这时因为没有active的NameNode,所以是不能在HDFS读写文件,需要将其中的一个转成active状态。比如将nn1(对应前面的配置)转成Active:
    bin/hdfs haadmin -transitionToActive nn1
    然后在NameNode的web页面上部的括号里的standby变成active。
    转成standby的命令是:
    bin/hdfs haadmin -transitionToStandby nn1
     
    自动切换
    在当前NameNode不能使用时自动切换到第二个NameNode上,需要借助于ZooKeeper[url=ZK]/url

    ZK的安装过程和Hadoop差不多,就是下载文件、修改配置、复制到所有机器、启动。具体步骤在这里。

    配置文件conf/zoo.conf:
    tickTime=2000
    dataDir=/data/hadoop/zookeeper
    clientPort=2181
    initLimit=5
    syncLimit=2
    server.1=hd2:2888:3888
    server.2=hd3:2888:3888
    server.3=hd4:2888:3888
    hd2,hd3,hd4是主机名,至少需要三台,这个在一台机挂了整个系统还能用,ZK的数量一般是奇数,为什么为奇数可以参考这里。
     
    然后要在hdfs-site.xml上添加配置:

    dfs.ha.automatic-failover.enabled
    true


    ha.zookeeper.quorum
    hd2:2181,hd3:2181,hd4:2181
    然后就是在NameNode的机器上初始化NameNode在ZK的状态了:
    bin/hdfs zkfc -formatZK
    重启HDFS或手动启动DFSZKFailoverController(ZKFC):
    sbin/stop-dfs.sh # 重启hdfs
    sbin/start-dfs.sh
    sbin/hadoop-daemon.sh start zkfc # 启动ZKFC
    在该HA方案中,每一个NameNode都有一个对应的ZKFC。ZKFC会随NameNode启动。
     
    测试
     
    在当前NameNode运行jps看NameNode的进程ID,然后kill掉。通过Web页面( http://hdx:50070 ),可以看到standby的NameNode几乎在kill的同时转成active了。
    转载地址:原文地址 收起阅读 »

    HDFS中Datanode存在大量没有删除的Block磁盘被占满案例分析

    最近发现hdfs中,有的datanode节点,磁盘发生告警,然后分析了一下。 然后上网搜索了一下为什么,分析如下:   查看对应datanode的数据目录,发现确实有很多的数据块(量非常大,都超过了实际hdfs中的数据块总量)   这时候,猜测应该是有很多需要...
    继续阅读 »
    最近发现hdfs中,有的datanode节点,磁盘发生告警,然后分析了一下。
    然后上网搜索了一下为什么,分析如下:
     
    查看对应datanode的数据目录,发现确实有很多的数据块(量非常大,都超过了实际hdfs中的数据块总量)
     
    这时候,猜测应该是有很多需要被删除的数据块没有被删除。猜测可能是NameNode和DataNode之间的通讯出现异常导致。于是查看NameNode和DataNode日志,发现并没有任何异常信息,只是发现NameNode定时对其中的三台机器发出了删除指令
    BLOCK[i] ask 192.168.200.8:50010 to delete  blk_7080908721303033545_7530145 BLOCK[/i] ask 192.168.200.9:50010 to delete  blk_-6550808355677895247_7465333 BLOCK* ask 192.168.200.7:50010 to delete  blk_2415291932316966347_7460687

    按照网上的方法,通过dfsadmin证实了,确实是有大量的block在等待删除
    hadoop dfsadmin -metasave meta.txt
    然后到logs目录下查看meta.txt文件结果如下:
    Metasave: Blocks 572428 waiting deletion from 8 datanodes.
    显示有几十万的block等待删除
     
    没有办法,只好从源码着手。在FSNameSystem.java文件里面找到了最终问题的所在:
    public int computeDatanodeWork() throws IOException {
    int workFound = 0;
    int blocksToProcess = 0;
    int nodesToProcess = 0;
    // blocks should not be replicated or removed if safe mode is on
    if (isInSafeMode())
    return workFound;
    synchronized(heartbeats) {
    blocksToProcess = (int)(heartbeats.size()
    * ReplicationMonitor.REPLICATION_WORK_MULTIPLIER_PER_ITERATION);
    nodesToProcess = (int)Math.ceil((double)heartbeats.size()
    * ReplicationMonitor.INVALIDATE_WORK_PCT_PER_ITERATION / 100);

    }

    workFound = computeReplicationWork(blocksToProcess);

    // Update FSNamesystemMetrics counters
    synchronized (this) {
    pendingReplicationBlocksCount = pendingReplications.size();
    underReplicatedBlocksCount = neededReplications.size();
    scheduledReplicationBlocksCount = workFound;
    corruptReplicaBlocksCount = corruptReplicas.size();
    }

    workFound += computeInvalidateWork(nodesToProcess);

    return workFound;
    }
    computeInvalidateWork就是用于计算这次需要删除的数据块。但是并不是每次都把所有的节点都处理一遍,而是每次只处理nodesToProcess个节点,而这个数量决定于datanode的总数(heartbeats.size,我这儿是8)和一个系数(INVALIDATE_WORK_PCT_PER_ITERATION,写死的32)。
     
    也就是说每次只处理

    8*32% = 3(这就解释了为啥每次只删除三台数据节点上的数据块。)

    再查看节点选择部分:
    ……
    private Map> recentInvalidateSets =
    new TreeMap>();

    ……
    String firstNodeId = recentInvalidateSets.keySet().iterator().next();

    ……
    发现是通过iterator遍历的,然后悲剧的发现recentInvalidateSets用的是TreeMap,也就是说是有序的……

    所以只要这三个节点有数据需要删除,就不会删除到其他节点
     
    这时候,发现这个问题是调整的时候,修改了一个配置项(dfs.replication.interval,默认是3秒,我修改成了30秒)导致的,当时修改的初衷是防止过早出现数据块复制。但是修改这个配置项以后,数据块副本数检查的间隔拉长了,导致30秒内,有几台机器一直有数据块需要删除,从而无法删除其他节点上的数据块,最终导致磁盘空间无法释放。因为INVALIDATE_WORK_PCT_PER_ITERATION是系统写死的,所以只能通过把dfs.replication.interval改回来,暂时解决这个问题。
      收起阅读 »

    KVM性能测试报告

    Kvm
    本测试试图找出以下问题的答案:            1. 虚拟机相对于物理机性能损失有多严重(CPU/MEM/NET/DISK)             2. 对于多个虚拟机,KVM 对资源的分配是否公正            3. 找出性能最好的...
    继续阅读 »
    kvmlogo.gif

    本测试试图找出以下问题的答案:
               1. 虚拟机相对于物理机性能损失有多严重(CPU/MEM/NET/DISK) 
               2. 对于多个虚拟机,KVM 对资源的分配是否公正
               3. 找出性能最好的物理机磁盘类型和虚拟机磁盘类型及参数 
                   A) 物理机使用 ext4 的最优化设置 
                         ➔  使用 raw 磁盘的最优性能设置 
                               ➢  cache类型 
                                     • none 
                                     • writeback 
                                     • writethrough 
                               ➢  驱动类型              
                                     • ide 
                                     • virtio 
                         ➔  使用 qcow2 磁盘的最优性能设置 
                               ➢  cache类型
                                     • none        
                                     • writeback 
                                     • writethrough 
                               ➢  驱动类型 
                                     • ide 
                                     • virtio 
                               ➢  是否preallocation  
                          ➔  比较得出使用 ext4 的虚拟机磁盘类型最优化设置 
     
                  B) 物理机使用xfs磁盘的最优化设置 
                          ➔  使用 raw 磁盘的最优性能设置
                                ➢  cache类型 
                                     1. none
                                     2. writeback
                                     3. writethrough
                                ➢  驱动类型 
                                      1. ide
                                      2. virtio
                          ➔  使用 qcow2 磁盘的最优性能设置
                                ➢  cache类型 
                                      1. none
                                      2. writeback
                                      3. writethrough
                                ➢  驱动类型 
                                      1. ide
                                      2. virtio
                                ➢ 是否preallocation
                          ➔ 比较得出使用 ext4 的虚拟机磁盘类型最优化设置 
      
                  C) 比较得出ext4和xfs那个更优秀 


    物理机配置


    服务器型号:DELL R410
    CPU型号:CPU:Intel(R) Xeon(R) CPU E5620 2.4G*2
    内存大小:8GB
    硬盘大小:300*2
    网卡:Broadcom Corporation NetXtreme II BCM5709 Gigabit Ethernet * 2


    虚拟机配置


    在测试一个参数的时候为了防止其它因素的影响,不同的测试虚拟机配置会不同


    CPU 的测试


    虚拟机与物理机之间的性能对比 
    主要测试相对于物理机,虚拟机的性能损失有多少


    虚拟机配置


    1个VCPU    2G 内存    20G 硬盘
    用一个寻找质数的 C 程序,分别测试找出一百万,一千万,1 亿以内的质数所用的时间 
    kc1.png

    kc2.png

    从上面的图可以看到,KVM 虚拟机相对于物理机的 CPU 性能损失非常小(不到 1%) 
     
    由于物理机是 8 核的 CPU,我们猜想是不是他们运行在各自的 CPU,所以我们分别测试了同时在 2 个,4 个,6 个,8 个和 10 个虚拟机中运行测试程序所使用的时间,得到的结果是他们使用的时间相差不大,这里是同时在 10 个虚拟机中运行测试程序找出 1 亿中的质数所使用的时间对比图,我们在最后加上了在物理机中的运行时间 
    kc3.png

    我们猜想当同时运行的程序数(10)多于 CPU 数(8)时会有资源争抢或资源分配不均的情况出现,从上图可以看 到 KVM 于这些处理的很好,资源利用率很高,物理机和最慢的相差不到 20 秒(6%),资源分配也比较公正 


    内存 


    对于内存的测试多们使用了一个程序 memtester(注意不是 memtest),它能取给定大小的内存进行读写等一系 列的测试操作 
    kc4.png

    从这个图可以看出,相对于物理机,虚拟机的性能损失非常小 
    kc5.png

    上图是两个虚拟机同时运行测试所用的时间和物理机之间的对比,可以看出 KVM 对内存资源的分配很公平 


    网络


    我们在连接在相同交换机的另一台服务器上搭建 Web 服务器,让测试机和虚拟机同时下载 5G 的文件 
    kc6.png

    从这个图可以看到,公认的性能比较好的 virtio 驱动将虚拟机的网络性能发挥到极致,和物理机不相上下 
    kc7.png

    这个图再一次反映了 KVM 对资源分配的公平性 
     
    我们还测试了同一台物理机上的虚拟机之间的传输速度可以稳定在 122MB/s 


    硬盘 


    磁盘的测试花了一些时间,因为有很多因素会影响磁盘性能,这个测试也没有覆盖所有可能的因素 先上数据 
    kc8.png

    对表的说明: 
          • 这个表记录了不同的参数对安装一个 CentOS 到虚拟机中所用的时间的影响
          • 安装使用相同的 kickstart
          • 使用 time 命令记录安装过程所使用的时间
          • 最后一列是将前一列的时间转化为秒数
          • 第一列的 xfs 和 ext4 指宿主机的存放虚拟磁盘镜像文件的分区类型,挂载参数都使用 default
          • Raw 和 Qcow2 指虚拟机的磁盘类型
          • ide 和 virtio 指虚拟机磁盘的驱动类型
          • pre 和 nopre 只是针对 qcow2 磁盘类型,创建磁盘文件时加上参数 preallocation=metadata 既为 pre
          • none,wt,wb,分别指缓存类型为 none, writethrough, writeback
          • 虚拟机都使用 1vcpu,2G 内存,40G 磁盘
     
    下面从总体上看下他们的性能如何
    kc9.png

    从这个图不太容易看出哪个配置使用的时间最短,但非常容易看出最耗时的一个: 
     
    宿主机使用 xfs,虚拟机使用 qcow2 磁盘,ide 驱动和 writethrough 缓存类型 
     
    下面我们以由小到大的顺序,找出每个区域的最高性能,然后他们之间做对比 


    首先看一下不同的缓存类型对性能的影响 


    kc10.png

    kc11.png

    从两幅图可以得到的结论是:使用 writethrough 性能最差,none 和 writeback 相差不大 
     
    这里插点缓存的知识
    KVM 缓存模式概览 通常,虚拟机使用宿主机上的一个文件保存它的数据,这个文件代表一个虚拟机作为正常的物理磁盘使用的虚 拟磁盘,然而,从宿主机的角度来看这个虚拟磁盘只是一个正常的数据文件并且可以对它缓存

    在这种情况下,缓存是隐藏一些磁盘数据在内存中.当我们只在内存中存储上次读的数据,我们称之为读缓存,或 者 write-through 缓存.当我们还存储一些要写到硬盘的数据在内存中,在以后的某个时间再写到磁盘,我们称 之为读写缓存,或者 write-back 缓存,write-back 缓存通过将写磁盘请求缓存到高速内存中,有更高的性能,然 而它也比 write-through 更有可能丢失数据,因为后者只缓存写请求,写请求立即写到磁盘里 
     
    因为磁盘 I/O 是非常重要的因素,Linux 和 Windows 系统通常使用 write-back 策略周期性的写到磁盘里,然 而,当然后虚拟化管理程序虚拟一个机器时,你可以有效的缓存一些数据两次(一次在宿主机的内在中,一次在虚 拟机的内存中),所以你可以取消宿主机对磁盘文件的缓存,让虚拟机自己管理它的缓存.些外,宿主机端的 write- back 策略很大程序上增加了数据丢失的可能性,然而,你很快会发现,新特性”barrier passing”的加入,你不用 再担心这个 


    我们可以用一个图来更好的理解


    kc12.png

    假设传统的(没有 barrier-passing)程序:当虚拟机里的程序要写一些数据,通常数据保存到虚拟机的页缓存中. 页缓存会周期性的冲数据到虚拟磁盘设备也就是宿主机上的磁盘文件,另一方面,如果应用程序有一些非常重 要的数据要写,它可以绕过页缓存并且使用同步写方式,只有当写操作成功才返回,确保了所有的数据写到了虚 拟机的永久存储系统 
     
    然而,在这一点上(写到虚拟机磁盘上)有三种可能:
             • 如果缓存策略是'writeback',数据会缓存到宿主机的页缓存中(红箭头) 
             • 如果缓存策略是'none',数据立即写到物理磁盘(灰箭头)
             • 如果缓存策略是'writethrough',数据将立即写到物理磁盘(蓝箭头) 
    更多详情


    下面我们看看 qcow2 磁盘使用和不使用 preallocation 的性能差异有多大 


    kc13.png

    从上图可以看出,不管宿主机使用 ext4 还是 xfs,虚拟机使用 ide 还是 virtio 驱动,使用 preallocation 都有性能 的提升,
    这里有一个不问题:如果宿主机使用 xfs,创建 qcow2 磁盘镜像使用 preallocation 会很慢(40G 需要 8 分钟) 


    下面我们来看看 ide 和 virtio 他们的性能对比 


    kc14.png

    很明显使用 virtio 是更明智的选择 


    下面我们再看看 raw 和 qcow2 之间的对比


    这里插一点 Raw 和 qcow2 的知识:
    raw 的优势(能找到的相关资料太少,不知道是不是理解有误):
    • 简单,并能够导出为其他虚拟机的虚拟硬盘格式
    • 根据实际使用量来占用空间使用量,而非原先设定的最大值(比如设定最高 20G,而实际只使用
    3G)。——需要宿主分区支持 hole(比如 ext2 ext3 ntfs 等)
    • 以后能够改变空间最大值(把最高值 20G 提高到 200G,qcow2 也可以,不过要转为 raw)
    • 能够直接被宿主机挂载,不用开虚拟机即可在宿主和虚拟机间进行数据传输(注意,此时虚拟机不要开)
    而qcow2 的优势:
    • 更小的虚拟硬盘空间(尤其是宿主分区不支持 hole 的情况下)
    • optional AES encryption, zlib based compression and support of multiple VM snapshots.
    原文在这里:http://www.linuxsir.org/bbs/thread368695.html 
    kc15.png

    从图表中可以看出,如果 qcow2 使用 preallocation, raw 的性能优势已经不是很明显 


    下面看看 ext4 和 xfs 之间 的对比 


    kc16.png

    可以看到 ext4 性能要比 xfs 高,这可能是默认的挂载参数不能充分发挥 xfs 的性能 


    最后,我们把时间最短的放到一起看到底那个最快 


    kc17.png


    总结 


    CPU,内存都不是问题,对于硬盘,通过前面一系列的测试,我推荐使用以下配置:
    • 使用 Ext4 做为宿主机磁盘格式
    • 虚拟机用 qcow2+preallocation,
    • 缓存使用 writeback
    • 虚拟机网卡驱动使用 virtio
    文章来源于网络,如果有问题联系社区相关人员!
     
      收起阅读 »

    浅谈docker文件系统分层与隔离

            Docker 的很多特性都表现在它所使用的文件系统上,比如大家都知道docker的文件系统是分层的,所以它可以快速迭代,可以回滚。下面就聊一下我对docker文件系统的理解       Docker 使用的支持的文件系统有以下几种: a...
    继续阅读 »
    df1.png
       
        Docker 的很多特性都表现在它所使用的文件系统上,比如大家都知道docker的文件系统是分层的,所以它可以快速迭代,可以回滚。下面就聊一下我对docker文件系统的理解
     
        Docker 使用的支持的文件系统有以下几种: aufs、devicemapper、btrfs  Vfs 我们先来介绍一下aufs


    一、  Aufs(advanced multi layered unification filesystem)


        Aufs直译过来就是高级分层统一文件系统。做为一种Union FS 它支持将不同的目录挂载到同一个虚拟文件系统下. 这个怎么理解呢。通过一条命令我们来看一下吧。
    mount -t aufs -o br=/tmp/dir1=ro:/tmp/dir2=rw none /tmp/newfs

      []-o 指定mount传递给文件系统的参数[/][]br 指定需要挂载的文件夹,这里包括dir1和dir2[/][]ro/rw 指定文件的权限只读和可读写[/][]none 这里没有设备,用none表示[/]

        这个结果是什么样子的呢。 就是把/tmp/dir1 t和/tmp/dir2  合并之后挂载到/tmp/newfs ,如果这时在/tmp/dir1 下创建一个文件a  /tmp/dir2下创建一个文件b 则  在/tmp/newfs 会看到a,b 这两个文件,并且a 是只读的, 如果有相同的文件则以先挂载的为准,后面挂载的操作会被忽略掉
     
       通过对Aufs的理解,大家可以想像一下docker所谓的“layer”的概念。还是实际的例子说明一下。

        一个镜像通过docker save  保存之后 会被打成一个tar 包,我们来看下这个tar包里都有些什么?
    docker save cloud_jiankongbao:01.tar cloud_jiankongbao:01
        通过上面的语句我们把镜像保存出下来。可以看到,保存下来的是tar 包。 不是.iso文件^_^,镜像解压之后是什么呢?
    ls . 
    a005304e4e74c1541988d3d1abb170e338c1d45daee7151f8e82f8460634d329
    d9bde94c518a16a886514758b6b4431200145ecd58e30c5633ac3c0256544d77
    f1b10cd842498c23d206ee0cbeaa9de8d2ae09ff3c7af2723a9e337a6965d639
    fb9cc58bde0c0a8fe53e6fdd23898e45041783f2d7869d939d7364f5777fde6f
    repositories
        出现了四个目录文件,再通过
    docker images --tree
    └─f1b10cd84249 Virtual Size: 0 B
    └─fb9cc58bde0c Virtual Size: 203.1 MB
    └─a005304e4e74 Virtual Size: 203.1 MB
    └─d9bde94c518a Virtual Size: 1.957 GB Tags: cloud_jiankongbao:01
        大家可以看到,4个目录其实分别是4个ID(注每次使用docker commit 提供对docker的修改之后就会产生一个新的id,就是通过这个ID可以实现对镜像的回滚)。每个目录下有json  layer.tar  VERSION 这三个文件。我们再看一下layer.tar 
    cd fb9cc58bde0c0a8fe53e6fdd23898e45041783f2d7869d939d7364f5777fde6f;tar -xf layer.tar;ls

    ls fb9cc58bde0c0a8fe53e6fdd23898e45041783f2d7869d939d7364f5777fde6f/
    bin etc json lib lost+found mnt proc sbin srv tmp var
    dev home layer.tar lib64 media opt root selinux sys usr VERSION
        这里存放的系统文件。

        我们再看一下镜像的4个不同ID的系统。

        f1b10cd84249 这个镜像是初始镜像,大小为0, fb9cc58bde0c 这个镜像是在f1b10cd84249基础上创建新的镜像,a005304e4e74是以fb9cc58bde0c为基础创建新的镜像。是树状继承的关系。我们再看下bin目录下的文件
    ls a005304e4e74c1541988d3d1abb170e338c1d45daee7151f8e82f8460634d329/bin/
    gtar tar
    ls fb9cc58bde0c0a8fe53e6fdd23898e45041783f2d7869d939d7364f5777fde6f/bin/
    arch cpio egrep gunzip logger mountpoint raw sleep true
    awk cut env gzip login mv readlink sort umount
    basename date ex hostname ls netstat rm stty uname
    bash dd false ipcalc lsblk nice rmdir su unlink
    cat df fgrep iptables-xml mkdir nisdomainname rpm sync usleep
    chgrp dmesg find iptables-xml-1.4.7 mknod ping rvi taskset vi
    chmod dnsdomainname findmnt kill mktemp ping6 rview touch view
    chown domainname gawk link more ps sed tracepath ypdomainname
    cp echo grep ln mount pwd sh tracepath6 zcat
        a005304e4e74 只有两个文件 fb9cc58bde0c包括了大部分bin下的文件,这就是Aufs,理解起来感觉有点像增量备份。


    二、简单的说一下devicemapper 


        devicemapper是利用了Snapshot 和Thinly-Provisioned Snapshot两种原理。将多个快照挂在同一个卷下从而实现文件系统的分层。不过使用devicemapper 的话一个container的大小最大只能是10G。
     
        在启动docker daemon时用参数-s 指定:  docker -d -s devicemapper
     
        关于隔离是怎么实现的呢,当容器基于镜像启动之后,每个容器都会获得自己的写读可写的文件系统层。原镜像的那部分文件系统是只读的,从而实现每个容器的在文件系统上的离隔。
     
       平时大家都在说dokcer 是弱隔离的,为什么呢?因为他没有隔离的很彻底,比如内核,内核是跟大家共用的,跟宿主机共用同一个内核,SELinux、 Cgroups以及/sys、/proc/sys、/dev/sd*等目录下的资源是与宿主机共用的。

       如果要隔离的彻底那就是VM了,而且如果dockers要想实现这些隔离就必然要牺牲一下现在轻量级的特性。那还不如直接用虚拟机好了!
    文章转载出处 收起阅读 »

    Docker主机间怎么共享数据卷

         Docker容器在不同的主机间共享数据是一件很棘手的事情。本文从"Docker容器实践"中采取,我们将研究一个轻量级,一个更复杂的方式在不同的主机间共享Docker数据卷。      尽管容器之间共享同一台主机上的数据变得更为简便,跨主机间...
    继续阅读 »
    dk1.png

         Docker容器在不同的主机间共享数据是一件很棘手的事情。本文从"Docker容器实践"中采取,我们将研究一个轻量级,一个更复杂的方式在不同的主机间共享Docker数据卷。
         尽管容器之间共享同一台主机上的数据变得更为简便,跨主机间共享Docker数据卷比较棘手。数据容器是解决这个问题的一个方法,但如果数据经常变化或特别大,这样做会变得笨拙。
         我们要考察两个解决这个问题的方法。第一个是一个轻量级的分布式解决方案使用BitTorrent协议,仅仅需要安装Docker。 第二是使用NFS,并引入基础设施的容器的概念的更复杂的解决方案。


    方法一:分布式数据卷BitTorrent的同步


         当Docker在一个组中实验,希望在组内共享大量数据,但是可能共享服务器没有足够的能力分配资源。懒惰的解决方案是在你需要他们的时候复制其他团队成员的最新文件,这将对一个更大的组失去控制。
         对此的解决方案是使用一个分散的工具共享文件–没有专门的资源要求。
    问题:
         你想在互联网上共享跨主机的卷
    解决方法:
         使用BitTorrent同步image共享卷
    讨论:
        下面说明图显示了我们的目标,结束与设置:
    dk2.png
       
         A - 该BTSync服务器是一个拥有我们将要共享的/data数据卷Docker容器
    B - 在同一主机上设置一个容器从BTSync服务器上挂载数据卷
    C - 在一个单独的网络中的其他主机,在BTSync服务器生成一个密钥,客户端可以通过引用BitTorrent协议来访问共享数据。
    D - 在另外一台主机上的BTSync客户端,从第一主机BTSync服务器挂载数据同步更新/data数据。
    E - 容器从BTSync客户端挂载数据卷
         最终的结果是卷/data-即方便地同步在互联网上,而不需要任何复杂的设置。
         在您的主服务器上运行这些命令来设置第一台主机上的容器:
    [host1]$ [#A]docker run -d -p 8888:8888 -p 55555:55555 --name btsync ctlc/btsync
    $ [#B]docker logs btsync
    Starting btsync with secret: [#C]ALSVEUABQQ5ILRS2OQJKAOKCU5SIIP6A3 By using this application, you agree to our Privacy Policy and Terms. http://www.bittorrent.com/legal/privacy http://www.bittorrent.com/legal/terms-of-use
    total physical memory 536870912 max disk cache 2097152 Using IP address 172.17.4.121
    [host1]$ [#D]docker run -i -t --volumes-from btsync ubuntu /bin/bash
    $ touch /data/shared_from_server_one [#E] $ ls /data shared_from_server_one
         A -运行发表CTLC / btsync image btsync守护程序容器调用并打开所需的端口
         B -获取 btsync容器的输出,所以我们可以记下key
         C -记下key-不同的运行产生有所不同
         D-启动一个交互式的容器从btsync服务器的卷
         E-添加文件到/data
         在第二个服务器上打开一个终端,运行以下命令来设置数据同步
    [host2]$ docker run -d
    --name btsync-client -p 8888:8888 -p
    55555:55555 ctlc/btsync ALSVEUABQQ5ILRS2OQJKAOKCU5SIIP6A3 [#A]
    [host2]$ docker run -i -t --volumes-from btsync-client ubuntu bash [#B] $ ls /data shared_from_server_one [#C] $ touch /data/shared_from_server_two [#D] $ ls /data
    shared_from_server_one shared_from_server_two
         A -启动 btsync客户端容器与host1上的守护程序运行生成的密钥守护进程
         B -启动安装卷从我们的客户守护进程的互动容器
         C - host1上创建的文件已被转移到主机2
         D -创建 host2上的第二个文件
         回到主机1的运行容器,我们应该看到的文件,就像第一次是在主机之间进行同步:
    $ ls /data
    shared_from_server_one shared_from_server_two
          注:文件同步是没有时间保证,所以你可能要等待同步数据。这是特别真实的更大的文件。

          警告:由于数据被发送到互联网和处理的协议,你没有控制,不要依赖于此,如果你有任何有意义的安全性,可扩展性或性能约束。


    方法二:通过NFS共享数据


          在一些大公司极有可能已经在使用NFS,NFS是一种行之有效的选项作为文件中心服务。Docker想要工作,它能够访问这些共享文件通常是很重要的!
          然而,Docker不支持NFS开箱即用和每个集装箱上安装NFS客户端,以便能够挂载远程文件夹不被认为是最佳时实践。相反,建议的方法是有一个容器用作从NFS翻译为Docker更友好的概念–卷!
    问题:
          你要无缝访问远程文件系统通过NFS
    解决方法:
          使用基础设施的数据容器代理访问
    讨论
          这种技术建立在数据容器技术之上。
    下图给出了这个抽象的概念:
    dk3.png

          NFS服务器暴露内部目录的文件夹/出口,这是安装在主机绑定。Docker主机再将这个文件夹使用NFS协议mount到/mnt文件夹。然后,一个所谓的基础设施容器被创建,它结合了安装文件夹。
         这似乎有点过度设计乍一看,但好处是,它提供了一个间接层就Docker容器而言:他们需要做的是安装卷从预先约定的基础设施的容器,谁是负责基础设施可以担心的内部管道,可用性,网络等。
         彻底处理NFS是超出了本书的范围。然而,我们去通过一台主机上建立这样一个共享的步骤(即NFS服务器的元素在同一主机作为Docker容器)。这已经在Ubuntu 14.04测试。
         想象一下,你想分享你的主机/opt/test/db,其中包含的文件mybigdb.db。
         使用root,安装NFS服务器创建/export目录,并开放权限:
    apt-get install nfs-kernel-server mkdir /export chmod 777 /export
         现在挂载db目录/opt/test/db到/export
    $ mount --bind /opt/test/db /export
         现在你应该可以看到 /opt/test/db中的挂载的内容
         现在加入这一行到您的etc/exports文件中
    /export       [#A]127.0.0.1([#B]ro,fsid=0,insecure,no_subtree_check,async)
          A -对于这个概念证明的例子中我们对127.0.0.1,这违背了对象有点安装在本地。 在真实的场景中,你会锁定下来的一类IP地址,如192.168.1.0/24的。 如果你真的很喜欢玩火,你可以打开它的世界,而不是127.0.0.1!
         B -为安全起见,我们安装件只读这里,*但是你可以用RW更换RO安装读写。 请记住,如果你这样做,那么你就需要异步标志出现后添加no_root_squash会标志 - 但考虑安全性在此之前沙坑外面去!
    挂载NFS上的目录/ mnt目录,出口,我们在指定的文件系统的/ etc / exports的上面,然后重新启动NFS服务,拿起变化: 
    $ mount -t nfs 127.0.0.1:/export /mnt
    $ exportfs -a
    $ service nfs-kernel-server restart
         现在您已经准备好运行基础设施的容器:
    $ docker run -ti --name nfs_client --privileged -v /mnt:/mnt busybox /bin/true
          TODO:检查一个以上要求
          现在,我们可以运行 - 没有特权,或底层实现知识 - 目录,我们希望访问:
    $ docker run -ti --volumes-from nfs_client debian /bin/bash root@079d70f79d84:/# ls /mnt myb root@079d70f79d84:/# cd /mnt root@079d70f79d84:/mnt# touch asd touch: cannot touch `asd': Read-only file system
         提示:如果你有很多这些容器来管理使用的运营效率的命名约定,你可以通过命名约定管理操作更容易,例如——名为nfs_client_opt_database_live容器暴露/opt/database/live路径。
    这种模式的一个共享资源,在多个容器中使用的其他人使用的共享资源,是一个强大的,可以使开发工作流程更简单。
    原文地址 收起阅读 »

    SELinux防护KVM安全

    Kvm
          SELinux最初是由美国安全局NSA发起的项目,是基于强制访问控制(MAC)策略的,为每一个主体和客户都提供了个虚拟的安全“沙箱”,只允许进程操作安全策略中明确允许的文件。当Linux开启了SELinux安全策略,所有的主体对客户的访问...
    继续阅读 »
    se1.jpg

          SELinux最初是由美国安全局NSA发起的项目,是基于强制访问控制(MAC)策略的,为每一个主体和客户都提供了个虚拟的安全“沙箱”,只允许进程操作安全策略中明确允许的文件。当Linux开启了SELinux安全策略,所有的主体对客户的访问必须同时满足传统的自主访问控制(DAC)和SELinux提供的强制访问控制(MAC)策略。
     
          在虚拟化环境下,通常是多个VM运行在同一个宿主机(物理机)上,通常由同一个用户启动多个VM管理进程(如:qemu-kvm或者vmx等),而这些VM可能为不同的租户服务,如果其中一个VM由于某些脆弱性被恶意控制了,而hypervisor层有缺陷,攻击者就有机会利用被控制的VM发起对同宿主机或其它VM进行恶意攻击。如下图:宿主机中的SELinux安全策略阻止了VM的Attack行为。
    se2.png


    SELinux与KVM


          sVirt 项目集成SELinux强制访问控制 (MAC) 安全和基于 Linux 的KVM虚拟化。
    确认SELinux策略正常开启
    se3.png

    创建img文件
         通过dd命令创建两个大小为20GB的img文件,作为虚拟机的磁盘:
    dd if=/dev/zero of=/opt/vm/vm01.img bs=1M count=20480
    dd if=/dev/zero of=/opt/vm/vm02.img bs=1M count=20480
          查看img文件安全上下文
    se4.png

         从上图可知两个img文件的安全上下文,其中用户域为unconfined_u,表示不限制用户;角色域为object_r通常表示普通文件或目录;类型域为usr_t;MCS为s0;
    qemu-kvm命令配合VNC​
    /usr/libexec/qemu-kvm -cpu qemu64 \
    -drive file=/opt/vm/vm01.img \
    -m 2048 \
    -cdrom \
    /opt/CentOS-6.5-x86_64-bin-DVD1.iso \
    -vnc :1

    /usr/libexec/qemu-kvm \
    -cpu qemu64 \
    -drive file=/opt/vm/vm02.img \
    -m 2048 \
    -cdrom \
    /opt/CentOS-6.5-x86_64-bin-DVD1.iso \
    -vnc :2
         给两个img文件安装上CentOS-6.5-x86_64操作系统并处于运行状态,此时的两个img文件的安全上下文还是与之前的保持不变。(注:实际在部署KVM虚拟机里以上步骤不是必须,这里是为了演示SELinux安全上下文的变化而考虑的)
    virt-manager启动KVM虚拟机
         结束qemu-kvm进程,用专用的virsh或virt-manager工具(这里以图形工具virt-manager为例)启动两个虚拟机,先ssh –X连接到宿主机,再执行virt-manager命令:
    se5.png

         在弹出的窗口里“新建”一个虚拟机,输入名称,单选“Import existing disk image”根据向导创建并启动虚拟机:
    se6.png

         当两个虚拟机都通过virt-manager启动之后管理窗口如下:
    se7.png

    确认安全上下文变化
         此时再看一下两个img文件的安全上下文,除了角色域object_r没有变化之外,用户域、类型域、MCS都发生了变化
    se8.png

         再确认一下对应进程的安全上下文:
    se9.png

         可以看出img文件的MCS与进程的MCS是严格对应的,且对虚拟机进行重启操作时会自动给进程和img文件重新分配MCS。这种安全机制可以严格控制威胁的范围,提高云平台的安全性。


    SELinux阻止非法访问


         介绍一下MCS不同时阻止相互访问,将/bin/bash拷贝一个为/opt/test_sh,并查看一下当前的安全上下文
    se10.png

         由于在SELinux的默认安全策略里,使用svirt_t类型的入口程序必须类型为qemu_exec_t,这里修改一下test_sh的类型:
    se11.png

    MCS为s0:c0,c1的操作
         以特定的SELinux安全策略来运行,这里切换一下类型为svirt_t和MCS为s0:c0,c1,并查看当前用户的安全上下文以确认,之后通过echo命令向/tmp/test文件(由于SELinux安全策略限制,此时只能向/tmp目录下写新建文件)里随便写内容,写成功后再查看一下/tmp/test的安全上下文。MCS都是一致的
    se12.png

    MCS为s0:c0,c2的操作
          再以特定的SELinux安全策略来运行,这里切换一下类型为svirt_t和MCS为s0:c0,c2,并查看当前用户的安全上下文以确认,之后通过echo命令向/tmp/test文件(这个文件是前一步创建的)里随便写内容,由于安全策略的限制会写入失败,提示:Permission denied(下图红线部分)。确认文件MCS与当前环境的MCS是不一致的。
    se13.png


    来点感动自己的鸡汤


          所有被SELinux安全策略阻止的操作都会有相应的日志记录,当系统auditd服务启动时会记录在audit.log日志,否则记录在message日志里。
    se14.png
    收起阅读 »