You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@flink.apache.org by "Arseniy Tashoyan (Jira)" <ji...@apache.org> on 2021/08/25 15:32:00 UTC

[jira] [Created] (FLINK-23972) NoClassDefFoundError: Jars from yarn.ship-files are missing in the classpath of the client

Arseniy Tashoyan created FLINK-23972:
----------------------------------------

             Summary: NoClassDefFoundError: Jars from yarn.ship-files are missing in the classpath of the client
                 Key: FLINK-23972
                 URL: https://issues.apache.org/jira/browse/FLINK-23972
             Project: Flink
          Issue Type: Bug
          Components: Client / Job Submission
    Affects Versions: 1.13.2, 1.12.4
         Environment: Flink 1.12.4 or 1.13.2

YARN

Running the application as follows:
{code:bash}
# Find additional jars under $lib_dir
# Join jars with semicolon
jars=$(mk_string \; $(find "$lib_dir" -type f -name \*.jar))

# Provide additional jars via yarn.ship-files
/opt/flink/bin/flink \
  run \
  --target yarn-per-job \
  --detached \
  --class com.example.DummyMain \
  -Dyarn.ship-files="$jars" \
  dummy-app-1.0.jar
{code}
            Reporter: Arseniy Tashoyan


Flink provides a way to inject additional jars into the classpath of the Job Manager and Task Manager: the configuration option [yarn.ship-files|https://ci.apache.org/projects/flink/flink-docs-release-1.13/docs/deployment/config/#yarn-ship-files]. However, an applications fails to submit to YARN if it uses a class from the additional jars. The execution graph is built inside the client before the application goes to the YARN cluster. Meanwhile, the client part (the command-line tool *flink*) misses the additional jars in its classpath.

*Example with Scalaz library.*
{code:scala}
package com.example

import ALenses.aLens
import org.apache.flink.streaming.api.scala.{StreamExecutionEnvironment, createTypeInformation}
import scalaz.Lens
import scalaz.Lens.lensu

object DummyMain {

  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment

    /* Way 1: Using Scalaz inside the client code. */
    val inputData = Seq(1, 2, 3).map { i => aLens.set(A(0), i) }
    /* Way 2: Not using Scalaz inside the client code. */
    // val inputData = Seq(1, 2, 3).map(A)

    val input = env.fromCollection(inputData)
    val output = input
      .map { a =>
        /* Using Scalaz inside Task Manager. */
        s"DUMMY: ${aLens.get(a)}"
      }
    output.print()
    env.execute()
    ()
  }

}

case class A(
    i: Int
)

object ALenses {

  val aLens: Lens[A, Int] = lensu[A, Int](
    set = (a, i1) => a.copy(i = i1),
    get = _.i
  )

}
{code}
When using "Way 1: Using Scalaz inside the client code", the application fails immediately - inside the client, before getting to YARN:
{code:none}
java.lang.NoClassDefFoundError: scalaz/Lens$
	at com.example.ALenses$.<init>(DummyMain.scala:38)
	at com.example.ALenses$.<clinit>(DummyMain.scala)
	at com.example.DummyMain$.$anonfun$main$1(DummyMain.scala:17)
	at com.example.DummyMain$.$anonfun$main$1$adapted(DummyMain.scala:16)
	at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:233)
	at scala.collection.immutable.List.foreach(List.scala:388)
	at scala.collection.TraversableLike.map(TraversableLike.scala:233)
	at scala.collection.TraversableLike.map$(TraversableLike.scala:226)
	at scala.collection.immutable.List.map(List.scala:294)
	at com.example.DummyMain$.main(DummyMain.scala:16)
	at com.example.DummyMain.main(DummyMain.scala)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.apache.flink.client.program.PackagedProgram.callMainMethod(PackagedProgram.java:349)
	at org.apache.flink.client.program.PackagedProgram.invokeInteractiveModeForExecution(PackagedProgram.java:219)
	at org.apache.flink.client.ClientUtils.executeProgram(ClientUtils.java:114)
       ...
{code}
Remarkable: if using "Way 2: Not using Scalaz inside the client code", the application successfully runs on YARN. The subsequent usage of Scalaz inside Task Managers does not cause a failure.

This behavior is a blocker - the setting yarn.ship-files is actually non-working.

Flink provides no way to customize the classpath of the *flink* client. The function *constructFlinkClassPath()* in *FLINK_HOME/**bin/config.sh* does not provide any means for customization. Instead this function could prepend the resulting classpath with a variable *CUSTOM_CLIENT_CLASSPATH* settable by user.

 

There are 2 alternate ways to circumvent this problem - both are not suitable.

1. Pack everything in a fat-jar. Fat-jars have numerous disadvantages, like huge file size, many multiplied classes across different applications, untraceable dependencies, classpath conflicts.

2. Put application-level jars into *FLINK_HOME/lib*. We have the same Flink installation for many different applications belonging to different teams. We do not want to pollute the Flink installation with user's jars. *FLINK_HOME* is located on a filesystem not writable for ordinary users.

Discussion on StackOverflow: [Flink on yarn: how to add external jars class path in local Client|https://stackoverflow.com/questions/49423861/flink-on-yarn-how-to-add-external-jars-class-path-in-local-client].



--
This message was sent by Atlassian Jira
(v8.3.4#803005)