Spark:Container exited with a non-zero exit code 137

        最近公司用户量暴涨,服务器压力骤然上升,最近大数据集群在跑Spark任务时老是报“Container exited with a non-zero exit code 137”这样的错误,详细日志如下:

WARN YarnSchedulerBackend$YarnSchedulerEndpoint: Requesting driver to remove executor 4 for reason Container marked as failed: container_e52_1650941597513_0076_01_000007 on host: host0. Exit status: 137. Diagnostics:Container killed on request. Exit code is 137
Container exited with a non-zero exit code 137. 
Killed by external signal

虽然日志级别是warn,而且yarn会重启一个新的container来重新执行先前失败的task,但是毕竟有的时候重启次数太多了会导致任务执行时间很长甚至于整个任务失败,所以这个问题还是不容忽视的。那到底是因为什么导致容器被killed了呢?是谁把容器给kill了呢?怎么解决这个问题呢?

        最开始的时候百度了一下找到一篇文章(Spark 中的“Container killed on request.Exit code is 137”)讲了这个问题,但是折腾半天不好使,后面仔细分析一下问题,才发现是Spark executor运行时占用内存太多而物理内存不足,Linux内核开启了oom killer机制(Linux内核OOM机制的详细分析),Linux内核会根据一定机制选择内存占用过大的进程kill掉。而错误日志里的“Killed by external signal”中的“external signal”指的是“kill -9”(SIGKILL)这个信号(Linux Signal信号详解),而这个信号对应的进程退出码(Linux and Unix exit code tutorial with examples)就是137。而且在Spark报137错误时,能够在被killed 掉的容器所在的主机上的日志文件/var/log/messages能看到如下的日志:

host0 kernel: Out of memory: Kill process 12290 (java) score 152 or sacrifice child
host0 kernel: Killed process 12290 (java), UID 1011, total-vm:23715088kB, anon-rss:8738028kB, file-rss:0kB, shmem-rss:20kB
host0 kernel: java invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0

日志里面我们会看到“Out of memory”、“java invoked oom-killer”字样。

         现在知道这个问题是我们的服务器内存不足导致的,但是怎么解决呢?把oom killer机制关了?这个属于掩耳盗铃。申请升级服务器?不好意思预算紧张。总不能用我的工资升级服务器吧,所以还得自己想其他办法节省内存来规避这个问题。对于Spark要节省内存无非一下几个办法:

        1.使用Kryo序列化。

        Spark默认使用Java序列化,而使用Kryo会大大减少对象序列化后数据的大小,当在代码中使用了RDD持久化时,会大大减少存储空间的使用。

        2.调整RDD持久化。

        通过观察Spark UI中Storage页面可以看到持久化的具体统计数据,项目代码里StorageLevel原本设置为MEMORY_ONLY,这会占用大量的内存,可以在启动参数中把--executor-memory适当调小,并在代码中把StorageLevel设置为 MEMORY_AND_DISK或者MEMORY_AND_DISK_SER(MEMORY_AND_DISK_SER会占用更少的存储空间,但是会占用更多的CPU时间)甚至于DISK_ONLY。虽然这样使一部分数据持久化在磁盘里,但是总比容器被killed然后有重启付出的代价少,而且现在很多云服务器可以用SSD,相比HDD也是鸟枪换了炮。

        3.调整并发度与并行度

        可以通过调大spark.sql.shuffle.partitions、spark.default.parallelism来增加RDD的分区数来减少每个分区的数据量;同时可以减少spark.executor.cores,减少同时运行的task的数量以减少内存的使用。

         4.祛除数据倾斜。

        内存占用过高还有一种情况就是数据倾斜,如果老是某些节点的container被killed掉,很可能就是存在数据倾斜,关于数据倾斜具体可参考:Spark性能优化指南

         经过实践最有用的是前两种办法,基本上就可以解决这个问题。还有一点就是在配置yarn资源时,不要把内存上限设置得太满,留一定的冗余也不会出现这个问题,我们的集群就是没有注意这个问题,yarn.nodemanager.resource.memory-mb设置得太高了。

        最后还想说一点就是在分析这个问题的时候发现Spark on YARN在生成运行executor的脚本文件(就是launch_container.sh)时,最后生成运行executor的shell命令如下:

exec /bin/bash -c "LD_LIBRARY_PATH="/usr/hdp/current/hadoop-client/lib/native:/usr/hdp/current/hadoop-client/lib/native/Linux-amd64-64:$LD_LIBRARY_PATH" $JAVA_HOME/bin/java -server -Xmx10240m '-XX:+UseNUMA' -Djava.io.tmpdir=$PWD/tmp '-Dspark.history.ui.port=18081' '-Dspark.driver.port=37119' -Dspark.yarn.app.container.log.dir=/hadoop/yarn/log/application_1650941597513_0025/container_e52_1650941597513_0025_01_000003 -XX:OnOutOfMemoryError='kill %p' org.apache.spark.executor.CoarseGrainedExecutorBackend --driver-url spark://CoarseGrainedScheduler@had3:37119 --executor-id 2 --hostname had3 --cores 3 --app-id application_1650941597513_0025 --user-class-path file:$PWD/__app__.jar 1>/hadoop/yarn/log/application_1650941597513_0025/container_e52_1650941597513_0025_01_000003/stdout 2>/hadoop/yarn/log/application_1650941597513_0025/container_e52_1650941597513_0025_01_000003/stderr"

 其中-Xmx10240m中的数值就对应的是--executor-memory的设置,而且会默认添加一个参数:-XX:OnOutOfMemoryError='kill %p',意思是当出现OutOfMemory时会执行kill命令将当前的进程杀死,至于Spark为什么要这样做源代码解释如下:

  /**
   * Kill if OOM is raised - leverage yarn's failure handling to cause rescheduling.
   * Not killing the task leaves various aspects of the executor and (to some extent) the jvm in
   * an inconsistent state.
   * TODO: If the OOM is not recoverable by rescheduling it on different node, then do
   * 'something' to fail job ... akin to blacklisting trackers in mapred ?
   *
   * The handler if an OOM Exception is thrown by the JVM must be configured on Windows
   * differently: the 'taskkill' command should be used, whereas Unix-based systems use 'kill'.
   *
   * As the JVM interprets both %p and %%p as the same, we can use either of them. However,
   * some tests on Windows computers suggest, that the JVM only accepts '%%p'.
   *
   * Furthermore, the behavior of the character '%' on the Windows command line differs from
   * the behavior of '%' in a .cmd file: it gets interpreted as an incomplete environment
   * variable. Windows .cmd files escape a '%' by '%%'. Thus, the correct way of writing
   * '%%p' in an escaped way is '%%%%p'.
   */
  private[yarn] def addOutOfMemoryErrorArgument(javaOpts: ListBuffer[String]): Unit = {
    if (!javaOpts.exists(_.contains("-XX:OnOutOfMemoryError"))) {
      if (Utils.isWindows) {
        javaOpts += escapeForShell("-XX:OnOutOfMemoryError=taskkill /F /PID %%%%p")
      } else {
        javaOpts += "-XX:OnOutOfMemoryError='kill %p'"
      }
    }
  }

最开始的怀疑kill的问题跟这个有关,但是其实不是的,因为kill对于的退出码是143,而137对应的是“kill -9”,应该是在jvm出现OutOfMemoryError之前就已经被Linux给kill掉了,如果你的日志里面出现的exit code是143,那么大概率是出现了OutOfMemoryError。

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
根据提供的引用内容,"container exited with a non-zero exit code 239. Error file: prelaunch.err" 是一个错误信息,表示容器以非零退出代码239退出,并且错误文件为prelaunch.err。这通常意味着在容器运行过程中发生了错误。 要解决这个问题,可以尝试以下几个步骤: 1. 检查错误文件:prelaunch.err是一个错误文件,其中可能包含有关容器退出的更多详细信息。你可以查看该文件以了解更多信息,例如具体的错误消息或异常堆栈跟踪。 2. 检查日志文件:除了prelaunch.err之外,还应该检查其他日志文件,例如应用程序的日志文件或容器管理器的日志文件。这些日志文件可能包含有关容器退出的更多信息,帮助你确定问题的根本原因。 3. 检查资源限制:容器退出的原因可能是由于资源限制导致的。你可以检查容器的资源配置,例如内存限制、CPU限制等,确保它们足够满足应用程序的需求。 4. 检查应用程序配置:容器退出的原因可能与应用程序的配置有关。你可以检查应用程序的配置文件,确保它们正确设置,并且没有任何错误或冲突。 5. 检查依赖项:容器退出的原因可能是由于缺少或错误的依赖项导致的。你可以检查应用程序的依赖项,确保它们正确安装并且与应用程序兼容。 6. 检查网络连接:容器退出的原因可能与网络连接有关。你可以检查网络连接是否正常,并确保容器可以访问所需的资源和服务。 请注意,以上步骤仅为一般性建议,具体解决方法可能因环境和具体情况而异。如果以上步骤无法解决问题,建议查阅相关文档或寻求专业支持以获取更详细的帮助。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值