אפליקציות Spark מסתמכות לעיתים קרובות על ספריות Java או Scala של צד שלישי. אלה הגישות המומלצות לכלול את התלות האלה כששולחים משימת Spark לאשכול Managed Service for Apache Spark:
כששולחים משימה מהמחשב המקומי באמצעות הפקודה
gcloud dataproc jobs submit, משתמשים בדגל--properties spark.jars.packages=[DEPENDENCIES].
לדוגמה:gcloud dataproc jobs submit spark \ --cluster=my-cluster \ --region=region \ --properties=spark.jars.packages='com.google.cloud:google-cloud-translate:1.35.0,org.apache.bahir:spark-streaming-pubsub_2.11:2.2.0'
כששולחים עבודה ישירות באשכול, משתמשים בפקודה
spark-submitעם הפרמטר--packages=[DEPENDENCIES].
לדוגמה:spark-submit --packages='com.google.cloud:google-cloud-translate:1.35.0,org.apache.bahir:spark-streaming-pubsub_2.11:2.2.0'
כשכוללים חבילות או קובצי JAR מותאמים אישית:
- תלות ספציפית בעבודה: מציינים את הנתיב ב-Cloud Storage לקובץ ה-JAR באמצעות הדגל
--jars, השדה Jar files במסוף Cloud de Confiance או השדהjarFileUrisב-API כששולחים עבודה. - תלויות ברמת האשכול: משתמשים ב
spark:spark.jarsמאפיין האשכול במהלך יצירת האשכול (--properties="spark:spark.jars=gs://<var>your-bucket</var>/<var>your-jarfile</var>.jar").
- תלות ספציפית בעבודה: מציינים את הנתיב ב-Cloud Storage לקובץ ה-JAR באמצעות הדגל
מניעת התנגשויות בין תלויות
הגישות הקודמות עלולות להיכשל אם יש התנגשות בין התלויות של אפליקציית Spark לבין התלויות של Hadoop. התנגשות כזו יכולה להתרחש כי Hadoop מחדיר את התלויות שלו לנתיב המחלקה של האפליקציה, ולכן התלויות שלו מקבלות עדיפות על פני התלויות של האפליקציה. כשמתרחשת התנגשות,
יכולות להיווצר NoSuchMethodError או שגיאות אחרות.
דוגמה:
Guava היא ספריית הליבה של Google ל-Java, שמשמשת ספריות ומסגרות רבות, כולל Hadoop. יכול להיות שיהיה ניגוד בין תלות אם משימה או התלויות שלה דורשות גרסה של Guava שהיא חדשה יותר מהגרסה שבה נעשה שימוש ב-Hadoop.
Hadoop v3.0 resolved
הבעיה הזו, אבל אפליקציות שמסתמכות על גרסאות קודמות של Hadoop דורשות פתרון עקיף שמורכב משני חלקים כדי למנוע התנגשויות אפשריות בין תלויות.
- יוצרים קובץ JAR יחיד שמכיל את חבילת האפליקציה ואת כל התלויות שלה.
- כדי למנוע התנגשות בין שמות הנתיבים של חבילות התלות לבין אלה של חבילות התלות של Hadoop, צריך להעביר את חבילות התלות המתנגשות בתוך קובץ ה-JAR הגדול. במקום לשנות את הקוד, אפשר להשתמש בתוסף (ראו בהמשך) כדי לבצע את ההעברה הזו (שנקראת גם 'הצללה') באופן אוטומטי כחלק מתהליך האריזה.
יצירת קובץ JAR גדול עם הצללה באמצעות Maven
Maven הוא כלי לניהול חבילות שמשמש לפיתוח אפליקציות ב-Java. אפשר להשתמש בפלאגין Maven scala כדי ליצור אפליקציות שנכתבו ב-Scala, השפה שבה משתמשים באפליקציות Spark. Maven shade
אפשר להשתמש בפלאגין כדי ליצור קובץ JAR מוצלל.
הקובץ הבא הוא קובץ הגדרות pom.xml לדוגמה שבו מוגדרת הצללה לספריית Guava, שנמצאת בחבילה com.google.common. ההגדרה הזו
מורה ל-Maven לשנות את השם של חבילת com.google.common ל-repackaged.com.google.common ולעדכן את כל ההפניות למחלקות מהחבילה המקורית.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <groupId><!-- YOUR_GROUP_ID --></groupId> <artifactId><!-- YOUR_ARTIFACT_ID --></artifactId> <version><!-- YOUR_PACKAGE_VERSION --></version> <dependencies> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.11</artifactId> <version><!-- YOUR_SPARK_VERSION --></version> <scope>provided</scope> </dependency> <!-- YOUR_DEPENDENCIES --> </dependencies> <build> <plugins> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> </execution> </executions> <configuration> <scalaVersion><!-- YOUR_SCALA_VERSION --></scalaVersion> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass><!-- YOUR_APPLICATION_MAIN_CLASS --></mainClass> </transformer> <!-- This is needed if you have dependencies that use Service Loader. Most Google Cloud client libraries do. --> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> </transformers> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/maven/**</exclude> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters> <relocations> <relocation> <pattern>com</pattern> <shadedPattern>repackaged.com.google.common</shadedPattern> <includes> <include>com.google.common.**</include> </includes> </relocation> </relocations> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
כדי להריץ את הבנייה:
mvn package
הערות לגבי pom.xml:
- ManifestResourceTransformer מעבד מאפיינים בקובץ המניפסט של ה-uber JAR (
MANIFEST.MF). המניפסט יכול גם לציין את נקודת הכניסה לאפליקציה. - ההיקף של Spark הוא
provided, כי Spark מותקן ב-Managed Service for Apache Spark. - מציינים את הגרסה של Spark שמותקנת באשכול Managed Service for Apache Spark (ראו רשימת הגרסאות של Managed Service for Apache Spark). אם האפליקציה שלכם דורשת גרסת Spark ששונה מהגרסה שמותקנת באשכול Managed Service for Apache Spark, אתם יכולים לכתוב פעולת אתחול או ליצור תמונה בהתאמה אישית שמתקינה את גרסת Spark שבה האפליקציה משתמשת.
- הערך
<filters>מחריג קובצי חתימה מהספריות של התלויות שלכםMETA-INF. בלי הרשומה הזו, יכולה להתרחש חריגה בזמן הריצה שלjava.lang.SecurityException: Invalid signature file digest for Manifest main attributesכי קובצי החתימה לא תקפים בהקשר של קובץ ה-JAR הגדול. - יכול להיות שתצטרכו להצל את כל הספריות. כדי לעשות זאת, צריך לכלול כמה נתיבים.
בדוגמה הבאה מוצגות ספריות Guava ו-Protobuf.
<relocation> <pattern>com</pattern> <shadedPattern>repackaged.com</shadedPattern> <includes> <include>com.google.protobuf.**</include> <include>com.google.common.**</include> </includes> </relocation>
יצירת קובץ JAR גדול עם הצללה באמצעות SBT
SBT הוא כלי לבניית אפליקציות Scala. כדי ליצור קובץ JAR מוצלל באמצעות SBT, מוסיפים את
התוסף
sbt-assembly
plugin להגדרת ה-build, קודם יוצרים קובץ בשם assembly.sbt בתיקייה project/:
├── src/
└── build.sbt
└── project/
└── assembly.sbt
… ואז מוסיפים את השורה הבאה בקובץ assembly.sbt:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")הדוגמה הבאה היא קובץ הגדרות build.sbt שמסתיר את ספריית Guava שנמצאת ב-com.google.common package:
lazy val commonSettings = Seq( organization := "YOUR_GROUP_ID", name := "YOUR_ARTIFACT_ID", version := "YOUR_PACKAGE_VERSION", scalaVersion := "YOUR_SCALA_VERSION", ) lazy val shaded = (project in file(".")) .settings(commonSettings) mainClass in (Compile, packageBin) := Some("YOUR_APPLICATION_MAIN_CLASS") libraryDependencies ++= Seq( "org.apache.spark" % "spark-sql_2.11" % "YOUR_SPARK_VERSION" % "provided", // YOUR_DEPENDENCIES ) assemblyShadeRules in assembly := Seq( ShadeRule.rename("com.google.common.**" -> "repackaged.com.google.common.@1").inAll )
כדי להריץ את הבנייה:
sbt assembly
הערות לגבי build.sbt:
- יכול להיות שכלל ההצללה בדוגמה הקודמת לא יפתור את כל הבעיות שקשורות לתלות, כי SBT משתמש בשיטות מחמירות לפתרון בעיות שקשורות לתלות. לכן, יכול להיות שתצטרכו לספק כללים מפורטים יותר שממזגים באופן מפורש סוגים ספציפיים של קבצים סותרים באמצעות אסטרטגיות
MergeStrategy.first,last,concat,filterDistinctLines,renameאוdiscard. פרטים נוספים מופיעים במאמר בנושא אסטרטגיית המיזוג שלsbt-assembly. - יכול להיות שתצטרכו להצל את כל הספריות. כדי לעשות זאת, צריך לכלול כמה נתיבים.
בדוגמה הבאה מוצגות ספריות Guava ו-Protobuf.
assemblyShadeRules in assembly := Seq( ShadeRule.rename("com.google.common.**" -> "repackaged.com.google.common.@1").inAll, ShadeRule.rename("com.google.protobuf.**" -> "repackaged.com.google.protobuf.@1").inAll )
שליחת קובץ ה-JAR הגדול אל Managed Service for Apache Spark
אחרי שיוצרים קובץ JAR מוצלל שמכיל את אפליקציית Spark ואת התלויות שלה, מעלים את קובץ ה-JAR ל-Cloud Storage ושולחים משימה ל-Managed Service for Apache Spark (באמצעות הדגל --jars ב-Google Cloud CLI, השדה Jar files במסוף Cloud de Confiance , השדה jarFileUris ב-API או על ידי ציון קובץ ה-JAR המוצלל כקובץ ה-JAR של האפליקציה הראשית).
המאמרים הבאים
- אפשר לעיין ב-spark-translate, אפליקציית Spark לדוגמה שמכילה קובצי הגדרות גם ל-Maven וגם ל-SBT.
- כתיבה והרצה של משימות Spark Scala ב-Managed Service for Apache Spark. במדריך לתחילת העבודה מוסבר איך לכתוב ולהריץ משימות Spark Scala באשכול Managed Service for Apache Spark.