[HADOOP问题] 常见问题解决

问题1:在程序的日志中看到,在reduce阶段出现了异常:Shuffle Error: Exceeded MAX_FAILED_UNIQUE_FETCHES; bailing-out ,程序里需要打开文件,系统默认为1024,也可以通过ulimit -a查看

编辑文件/etc/security/limits.conf 在文件后面添加:

# End of file
*       soft       nofile  102400
*       hard       nofile  409600

遇到这种错误网上也有不同的可能解决方法和解释,你们可以自己找找。

问题2: yarn日志页面出现异常:

Java HotSpot(TM) 64-Bit Server VM warning: Insufficient space for shared memory file:
   /tmp/hsperfdata_hdp/6676
Try using the -Djava.io.tmpdir= option to select an alternate temp location

原因是根目录空间不足,解决问题的办法之一是清理根目录下不必要的文件,解决办法二就是,上面也就有提示了,不多说了。

/tmp/hsperfdata_username 目录的作用是什么呢?

jvm运行时在linux下默认在/tmp下生成上面的目录,目录下存放pid文件,和一些jvm进程信息,jmap、jstack等工具会读取该目录下的pid文件获取链接信息

问题3: 当任务不能跑满集群的时,为什么集群的节点会出现几个节点跑满容器,而其他节点则非常空闲?

原因是集群调度器默认处于批处理模式下,一个心跳会尽可能的分配任务,心跳先到达则会优先领取任务,我们可以通过参数yarn.scheduler.fair.max.assign参数设置为1,就可以大致的均衡任务到不同的节点

更新时间2015-07-18

[原]如何利用hadoop RPC框架实现和NameNode的交互

这篇文章主要介绍如何在已有的Hadoop RPC框架上,自定义新的方法实现和NameNode的交互。

在此之前,我们需要准备:

  • hadoop的源码
  • protobuf 2.5版本
  • JDK

hadoop 2.x版本中采用了Protocol Buffer (简称protobuf)作为序列化和反序列化的工具,所以我们在修改源码时需要按照相应规则编写message来实现数据的传输。

什么是protobuf?

protobuf是Google 公司内部的混合语言数据标准,它很适合做数据存储或 RPC 数据交换格式。是一种可用于通讯协议、数据存储等领域,并且和语言无关、平台无关、可扩展的序列化结构数据格式。 简单说来 Protobuf 的主要优点就是:简单,快。

安装protobuf和编译hadoop的过程网上的资料很多,我就直接跳过了,我们可以通过Idea导入hadoop的Maven项目,方便对源码的修改

1.修改proto文件,定义message和service

假设我们现在要实现的是一个检查某个文件或文件夹权限是否符合755,并对客户端返回boolean值。 这是一个属于Client和NameNode交互的一个方法,所以我们在Idea中ctrl+shift+N快速的找到ClientNamenodeProtocol.proto,添加对应的message(结构化数据被称为message)

message CheckPermissionRequestProto {
    required string src = 1;
}
message CheckPermissionResponseProto {
    required bool checkPerm = 1;
}

我们在这个文件中会看到除了string、bool类型的前面会有三种消息成员的规则,他们的含义分别是:

  • required:这个域在消息中必须刚好有1个
  • optional:这个域在消息中可以有0或1个
  • repeated:这个域在消息中可以有从多个,包括0个

在文件中找到service,并添加方法checkPermission方法

service ClientNamenodeProtocol { 
 ...... 
rpc checkPermission(CheckPermissionRequestProto) returns(CheckPermissionResponseProto);
}

接下来编译,编译之后你可以在ClientNamenodeProtocolProtos类(编译后生成)的接口ClientNamenodeProtocol,看到新增加的方法了。

2.ClientNamenodeProtocolPB文件

这个接口是client用来和NameNode进行交互的,它继承了ClientNamenodeProtocol接口,即新生成的接口也在其中,这里不用做修改

3.ClientNamenodeProtocolTranslatorPB类

这个类是将对ClientProtocol中方法的调用转化为RPC调用Namenode的服务,并将调用参数转化为PB的类型。 所以,我们需要在ClientProtocol增加checkPermission方法,并在这个类中进行Override

在ClientProtocol中增加

  @Idempotent
  public boolean checkPermission(String src)
       throws AccessControlException, FileNotFoundException,
       UnresolvedPathException, IOException;

在ClientNamenodeProtocolTranslatorPB类中

  @Override
  public boolean checkPermission(String src) throws AccessControlException,
            FileNotFoundException, UnresolvedPathException, IOException {
     CheckPermissionRequestProto req = CheckPermissionRequestProto.newBuilder()
            .setSrc(src).build();
     try {
         return rpcProxy.checkPermission(null,req).getCheckPerm();
     } catch (ServiceException e) {
         throw ProtobufHelper.getRemoteException(e);
     }
  }

注意:要把CheckPermissionRequestProto进行import,否则编译你懂的。

相应的我们也需要在NameNodeRpcServer类中Override该方法,因为NamenodeProtocols继承了ClientProtocol,这类负责处理所有到达NN的RPC call,他负责将请求转化为NN的方法的调用,因此也可以看出它们的实现分层是很清晰的。

  @Override // ClientProtocol
  public boolean checkPermission(String src)
      throws AccessControlException, FileNotFoundException, UnresolvedPathException, IOException {
      return namesystem.checkPermission(src);
  }

4.ClientNamenodeProtocolServerSideTranslatorPB类

该类是server端用来将ClientNamenodeProtocolTranslatorPB生成的PB格式的数据转化为本地调用的数据类型,所以增加改方法

  @Override
  public CheckPermissionResponseProto checkPermission(RpcController controller,
                                                      CheckPermissionRequestProto req)
           throws ServiceException {
      try {
          boolean result = server.checkPermission(req.getSrc());
          return CheckPermissionResponseProto.newBuilder().setCheckPerm(result).build(); //将结果返回
      } catch (IOException e) {
          throw new ServiceException(e);
      }

  }

这里的server就是NameNodeRpcServer,相当于调用NameNodeRpcServer的checkPermission方法,并在NameNodeRpcServer中调用FSNamesystem完成最后的逻辑

Namesystem类增加方法

  boolean checkPermission(String src) throws IOException {  
      readLock();
      try {
          HdfsFileStatus fileStatus = getFileInfo(src,false); //这个方法我有做过修改,你们可能不一样
          FsPermission fsPermission = new FsPermission((short)0755);
          if(fileStatus != null && fsPermission.equals(fileStatus.getPermission())) {
              return true;
          }
      } finally {
          readUnlock();
      }
      return false;
  }

接下来我就举个例子怎么调用,这只是部分代码,详细的自己看看源码吧。

  proxyInfo = NameNodeProxies.createProxy(conf, nameNodeUri,
      ClientProtocol.class, nnFallbackToSimpleAuth);
  this.namenode = proxyInfo.getProxy();
  namenode.checkPermission(src);

5.最后一步就是编译了

希望通过这个例子能够加深对hadoop实现的理解

参考资料

Google Protocol Buffer 的使用和原理

[译]Docker容器真的安全?

文章来源:opensource 作者:Daniel J Walsh

Containers do not contain

我听到和读到很多人把docker假设为一个沙箱的应用程序——这意味着他们可以通过Docker以root用户随机跑一些应用程序在他们的系统,他们相信Dokcer contianer将会真实地保护他们的主机。

  • 我听到有人说Docker container中运行一些进程和在VMs/KVM一样安全。
  • 我知道有些人随机的下载一些Docker镜像并运行在他们的主机中
  • 我甚至见过PaaS服务器允许用户上传自己的镜像在一个多租户的系统(multi-tenant system)上运行
  • 我有一个同事说:“Docker就是从网上下载代码并以root用户运行”

停止假设Docker和linux kernel会在恶意软件中保护你。

你关心吗?

如果你没有运行Docker在一个多租户的系统和你有一个好的安全实践在container里面运行服务,你可能不需要担心。但可以说拥有特权的进程运行在container里面和运行在container外面是没有区别的。

一些人错误的认为container是一个更好和更快的运行虚拟机的方式,从安全的角度上来说,容器是很弱的,我将会在稍后的文中介绍到。

如果你相信我,Docker container应该被对待为“容器服务”——意味着对待container运行的Apache服务和在系统中运行Apache服务一样,这意味着你要做到一下几点:

  • 尽可能快的关闭特权
  • 尽可能的不用root去运行你的服务
  • 在容器里面对待root权限和在容器外对待root权限一样

目前,我们正在告诉人们使用通用的标准处理容器里面的特权进程和就像在容器外处理特权进程一样。

不要随便的在你的系统中运行一个未知的Docker镜像,在很多方面我看到Docker container的革命就像1999年linux的革命一样,当管理员听到一个新的酷的linux服务,他们会:

  • 在internet上查找这个包,例如在rpmfind.net或者任意的网站。
  • 下载这个程序到系统中
  • 通过RPM或者make install安装
  • 用特权运行它

What could go wrong?

两个星期之后管理员听到了关于zlib的漏洞并要找出它,同时希望和祷告这并不是漏洞,他们的软件是脆弱的。

所以,只运行可信方的容器,如果你的代码并非来自内部和信任的第三方,别依赖容器技术去保护你的主机。

So what is the problem? Why don’t containers contain?

最大的问题是任何东西在linux是没有命名空间的,当前,Docker使用五个namespace去改变系统的进程视图:Process、NetWork、Mount、Hostname、Shared Memory。

虽然这些给予用户的的安全级别并不是最全面的,像KVM,在KVM环境中进程在虚拟机里面是不会和主机的kernel直接交互的,他们也不会访问任何像/sys,和/sys/fs,/proc/*,等kernel的文件系统。

Device node是和VMs的kernel交互而不是主机的kernel,因此为了提升拥有整个VMs的特权,这个进程必须有subvirt在虚拟机的内核中,而且要找到HyperVisor的漏洞,突破SELinux的控制,这些对与VM来说是非常密封的,直到最后才能攻击主机的内核。

而当你运行一个container的时候,它就已经和你的kernel在交互了。

没有namespace的主要kernel子系统有:

  • SELinux
  • Cgroup
  • 在/sys下的文件系统
  • /proc/sys, /proc/sysrq-trigger, /proc/irq, /proc/bus

没有命名空间的Device有:

  • /dev/mem
  • /dev/sd* 文件系统devices
  • kernel Modules

如果你用一个特权进程去和上面其中一个进行通信或者攻击,你就可以拥有这个系统。