גישה לנתונים ב-Workday באמצעות Spark

בעזרת Managed Service for Apache Spark אתם יכולים לשלוח שאילתות ולנתח נתונים עסקיים חיצוניים ישירות מעומסי העבודה ב-Spark. אתם יכולים להשתמש במנהל ההתקן של Workday Live Data Query JDBC כדי להתחבר לדייר שלכם ב-Workday, לטעון מערכי נתונים של Workday בזמן אמת ל-Spark DataFrames ולשלב את הנתונים האלה בצינור עיבוד הנתונים לניתוח ב- Google Cloud.

במדריך הזה תגדירו את Workday Live Data Query, תאחסנו את המפתח הפרטי שלכם באופן מאובטח ב-Secret Manager ותשתמשו בתוסף Google Cloud Data Agent Kit כדי להריץ קובץ Notebook של PySpark ב-Visual Studio Code או ב-Cloud Shell Editor מול Managed Service for Apache Spark.

עלויות

התוסף Data Agent Kit זמין ללא עלות נוספת. עם זאת, במדריך הזה משתמשים ברכיבים הבאים של Google Cloud, והשימוש בהם כרוך בתשלום:

כדי להעריך את העלויות בהתאם לשימוש הצפוי, אתם יכולים להשתמש במחשבון העלויות.

לפני שמתחילים

  1. נכנסים לחשבון Google Cloud . אנחנו ממליצים למשתמשים חדשים ב- Google Cloud ליצור חשבון כדי שיוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 300$ להרצה, לבדיקה ולפריסה של עומסי העבודה.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that you have the permissions required to complete this guide.

  4. Verify that billing is enabled for your Google Cloud project.

  5. Enable the Dataproc, Dataplex, Cloud Storage, and Secret Manager APIs, if any are not already enabled.

    Roles required to enable APIs

    To enable APIs, you need the serviceusage.services.enable permission. If you created the project, then you likely already have this permission through the Owner role (roles/owner). Otherwise, you can get this permission through the Service Usage Admin role (roles/serviceusage.serviceUsageAdmin). Learn how to grant roles.

    Enable the APIs

  6. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  7. Verify that you have the permissions required to complete this guide.

  8. Verify that billing is enabled for your Google Cloud project.

  9. Enable the Dataproc, Dataplex, Cloud Storage, and Secret Manager APIs, if any are not already enabled.

    Roles required to enable APIs

    To enable APIs, you need the serviceusage.services.enable permission. If you created the project, then you likely already have this permission through the Owner role (roles/owner). Otherwise, you can get this permission through the Service Usage Admin role (roles/serviceusage.serviceUsageAdmin). Learn how to grant roles.

    Enable the APIs

התפקידים הנדרשים בניהול הזהויות והרשאות הגישה (IAM)

כדי להריץ את הדוגמאות שבדף הזה, צריך תפקידים מסוימים ב-IAM. יכול להיות שהתפקידים האלה כבר הוקצו, בהתאם למדיניות הארגון. כדי לבדוק את התפקידים שהוקצו, קראו את המאמר האם צריך להקצות תפקידים?. כדי לקרוא הסבר על מתן תפקידים, קראו איך מנהלים את הגישה ברמת הפרויקט, התיקייה והארגון.

התפקידים שאתם צריכים תלויים באופן שבו עומס העבודה פועל. בגרסאות 2.3 ומטה של Managed Service for Apache Spark serverless, סביבות זמן הריצה ללא שרת מריצות עומסי עבודה בתור חשבון שירות. בסביבות זמן ריצה ללא שרת (serverless) בגרסה 3.0 ואילך של Managed Service for Apache Spark, עומסי העבודה פועלים כברירת מחדל עם פרטי הכניסה של משתמשי הקצה (EUC). לכן, אתם צריכים להקצות תפקידים של גישה לנתונים לחשבון המשתמש שלכם, ולא לחשבון שירות. אם מציינים חשבון שירות בהתאמה אישית לסביבת זמן ריצה בגרסה 3.0 ואילך, עומס העבודה פועל עם פרטי כניסה לחשבון שירות. מידע נוסף זמין במאמר תפקידים של פריסות ללא שרת (serverless).

תפקידי משתמשים

כדי לקבל את ההרשאות שדרושות ליצירת סשן של Notebook אינטראקטיבי ב-Managed Service for Apache Spark ולגישה למשאבים, בקשו מהאדמין להקצות לכם את תפקידי ה-IAM הבאים:

  • שימוש בסביבת זמן ריצה ללא שרת (serverless) בגרסה 2.3 או גרסה קודמת:
  • שימוש בסביבת זמן ריצה ללא שרת (serverless) בגרסה 3.0 ואילך: עריכה ב-Dataproc Serverless (roles/dataproc.serverlessEditor) בפרויקט
  • קריאת מנהל ההתקן של JDBC מ-Cloud Storage באמצעות פרטי הכניסה של משתמש הקצה: Storage Object Viewer (roles/storage.objectViewer) בקטגוריה שמכילה את מנהל ההתקן
  • קריאת הסוד של המפתח הפרטי מ-Secret Manager באמצעות פרטי הכניסה של משתמש הקצה: Secret Manager Secret Accessor (roles/secretmanager.secretAccessor) בסוד או בפרויקט

תפקידים של חשבון שירות

מקצים לחשבון השירות שמריץ את עומס העבודה את התפקידים הבאים: בגרסאות 3.0 ואילך של סביבות זמן ריצה ללא שרת (serverless) של Managed Service for Apache Spark, צריך את התפקידים האלה רק אם עומס העבודה פועל עם פרטי כניסה לחשבון השירות במקום עם פרטי הכניסה של משתמש הקצה (EUC) שמוגדרים כברירת מחדל.

כדי לוודא שלחשבון השירות שמוגדר כברירת מחדל ב-Compute Engine או לחשבון שירות בהתאמה אישית יש את ההרשאות שנדרשות להרצת עומסי עבודה של Managed Service for Apache Spark, לקריאת הדרייבר ולגישה לסודות, בקשו מהאדמין להקצות את תפקידי ה-IAM הבאים לחשבון השירות שמוגדר כברירת מחדל ב-Compute Engine או לחשבון שירות בהתאמה אישית:

  • שימוש בסביבת זמן ריצה ללא שרת (serverless) בגרסה 2.3 או גרסה קודמת: Dataproc Worker (roles/dataproc.worker) בפרויקט
  • משתמשים בסביבת זמן ריצה ללא שרת בגרסה 3.0 ואילך עם פרטי הכניסה של חשבון השירות, במקום פרטי הכניסה של משתמש הקצה (EUC) שמוגדרים כברירת מחדל:
  • קריאת מנהל ההתקן של JDBC מ-Cloud Storage: צפייה באובייקט אחסון (roles/storage.objectViewer) בקטגוריה שמכילה את מנהל ההתקן
  • קריאת הסוד של המפתח הפרטי מ-Secret Manager: מנהל גישה לסודות ב-Secret Manager (roles/secretmanager.secretAccessor) בסוד או בפרויקט

דרישות מוקדמות ב-Workday

לפני שמתחברים ל-Workday באמצעות Live Data Query, צריך לבצע את ההגדרה הבאה:

  1. מורידים את מנהל ההתקן של JDBC לשאילתות נתונים בזמן אמת ב-Workday.

  2. מעלים את קובץ ה-JAR של מנהל ההתקן של JDBC שהורדתם לקטגוריה של Cloud Storage בפרויקט.

  3. מבצעים את שלבי ההגדרה של Live Data Query בדייר Workday, כפי שמתואר במסמכי התיעוד של Workday Live Data Query.

    במהלך ההגדרה ב-Workday, יוצרים משתמש מערכת משולב (ISU), רושמים לקוח API ויוצרים זוג מפתחות פרטיים.

  4. שימו לב לערכי ההגדרות הבאים של Workday:

    • מארח ב-Workday: המארח של הדייר ב-Workday, לדוגמה, services1.workday.com.

    • שם הדייר: שם הדייר ב-Workday.

    • מזהה לקוח: מזהה הלקוח הרשום של ה-API מהדייר שלכם ב-Workday.

    • ‫ISU username: שם המשתמש של ה-ISU.

אחסון המפתח הפרטי ב-Secret Manager

שומרים באופן מאובטח ב-Secret Manager את קובץ המפתח הפרטי שנוצר במהלך ההגדרה של Workday Live Data Query:

  1. ב-Cloud Shell, מריצים את הפקודה הבאה כדי ליצור זוג מפתחות x509:

    openssl req -x509 -newkey rsa:2048 -keyout private-key.pem -out public-key.cert -sha256 -days 365 -nodes
    
  2. כדי לראות את המפתח הפרטי, מריצים את הפקודה הבאה:

    cat private-key.pem
    
  3. מאחסנים את המפתח הפרטי ב-Secret Manager:

    1. נכנסים לדף Secret Manager במסוף Google Cloud :

      כניסה ל-Secret Manager

    2. לוחצים על Create Secret.

    3. בשדה Name, מזינים שם לסוד, למשל workday-private-key.

    4. בקטע Secret value, מעלים את private-key.pem או מדביקים את תוכן המפתח הפרטי, כולל שורות הכותרת העליונה והתחתונה -----BEGIN PRIVATE KEY----- ו------END PRIVATE KEY-----.

    5. לוחצים על Create Secret.

  4. כדי לראות את המפתח הציבורי, מריצים את הפקודה הבאה:

    cat public-key.cert
    
  5. ב-Workday, מחפשים את Create x509 Public Key, מדביקים את המפתח הציבורי מ-public-key.cert ולוחצים על OK.

  6. ב-Workday, בלקוח ה-API שיצרתם קודם, מעדכנים את x509 Certificate למפתח x509 החדש.

הגדרת Data Agent Kit ב-IDE

כדי להגדיר את Data Agent Kit, אתם יכולים להתקין את התוסף ב-Visual Studio Code או בסביבת פיתוח משולבת (IDE) נתמכת אחרת, או להשתמש בתוסף שמותקן מראש ב-Cloud Shell Editor.

כדי להגדיר את Data Agent Kit ב-IDE או ב-Cloud Shell Editor:

  1. מגדירים את Data Agent Kit:

    1. מתקינים את התוסף Data Agent Kit.

    2. מגדירים את Data Agent Kit.

יצירת תיקייה

באמצעות Data Agent Kit, יוצרים Notebook אינטראקטיבי של PySpark:

  1. ב-IDE או ב-Cloud Shell Editor, פותחים את החלונית Data Agent Kit.

  2. בקטע Apache Spark, לוחצים על New Notebook ואז על Local Kernel.

  3. בוחרים סביבת זמן ריצה מרשימת סביבות זמן הריצה הזמינות (שנקראות גם תבניות סשן).

  4. לוחצים על התחלת תיקיית Notebook חדשה.

    ייפתח Notebook חדש עם תא ראשוני שמאוכלס בקוד אתחול של סשן Spark Connect.

  5. בחלק העליון של עורך ה-notebook, לוחצים על Select Kernel ובוחרים ליבת Python מקומית ל-notebook.

שליחת שאילתות על נתוני Workday ב-PySpark

  1. בתא הראשון של ה-notebook, מתקינים את ספריות הלקוח הנדרשות ומגדירים את משתני הסביבה:

    %pip install dataproc-spark-connect google-cloud-dataproc google-cloud-secret-manager google-cloud-storage
    
    #@title Set up all environment variables
    
    # Replace with your Project ID
    %env PROJECT_ID=PROJECT_ID
    
    # Replace with the name of the Secret Manager global secret in the project
    %env SECRET_NAME=workday-private-key
    # Replace with the version of the secret to use
    %env SECRET_VERSION=latest
    
    # Replace with the Cloud Storage bucket where you have the Workday Data Cloud JDBC Driver file stored
    %env JDBC_DRIVER_GCS_BUCKET=BUCKET_NAME
    # Replace with the name of the Workday Data Cloud JDBC Driver file in the Cloud Storage bucket
    %env JDBC_DRIVER_FILE_NAME=ldq-jdbc-driver-1.1.0-all.jar
    # Replace with the local directory of the notebook to copy the JDBC Driver File to
    %env JDBC_DRIVER_LOCAL_DIRECTORY=/tmp
    # Replace with the Driver Class for the Workday Data Cloud JDBC Driver
    %env WORKDAY_DRIVER_CLASS=com.workday.ldq.jdbc.Driver
    
    # ------------------------------------------------------------------------------
    
    # Replace with the Workday URL of your tenant
    %env WORKDAY_HOST=WORKDAY_HOST
    # Replace with your Workday tenant name
    %env TENANT_NAME=TENANT_NAME
    # Replace with the Client ID of the "API Client"
    %env CLIENT_ID=CLIENT_ID
    # Replace with the Integration System User username
    %env ISU_USERNAME=ISU_USERNAME
    
  2. בתא חדש, מקצים את התפקיד "גישה לסודות ב-Secret Manager" (roles/secretmanager.secretAccessor) לחשבון השירות שמוגדר כברירת מחדל ב-Compute Engine:

    %%bash
    COMPUTE_ENGINE_DEFAULT_SERVICE_ACCOUNT=$(gcloud iam service-accounts list --project="${PROJECT_ID}" \
      --filter="email ~ -compute@developer.gserviceaccount.com" \
      --format="value(email)")
    
    echo "COMPUTE_ENGINE_DEFAULT_SERVICE_ACCOUNT: ${COMPUTE_ENGINE_DEFAULT_SERVICE_ACCOUNT}"
    
    gcloud secrets add-iam-policy-binding ${SECRET_NAME} \
      --project="${PROJECT_ID}" \
      --member="serviceAccount:${COMPUTE_ENGINE_DEFAULT_SERVICE_ACCOUNT}" \
      --role="roles/secretmanager.secretAccessor"
    
  3. בתא חדש, מעתיקים את מנהל ההתקן של JDBC מ-Cloud Storage לספרייה המקומית:

    gsutil cp gs://$JDBC_DRIVER_GCS_BUCKET/$JDBC_DRIVER_FILE_NAME $JDBC_DRIVER_LOCAL_DIRECTORY
    
  4. בתא חדש, מתקינים את ספריית הלקוח של Secret Manager:

    %%bash
    pip install google-cloud-secret-manager
    
  5. בתא חדש, מוודאים שקובץ ה-JAR של הדרייבר הורד באופן מקומי:

    !gsutil cp gs://$JDBC_DRIVER_GCS_BUCKET/$JDBC_DRIVER_FILE_NAME /tmp/
    !ls -lh /tmp/ldq-jdbc-driver-1.1.0-all.jar
    
  6. בתא חדש, מוסיפים את קובץ ה-JAR של מנהל ההתקן של Workday לספריית קובצי ה-JAR של PySpark:

    %%bash
    
    echo "Downloading Workday JAR from Cloud Storage..."
    # Downloads your specific 1.1.0 driver JAR into the notebook's /tmp folder
    gcloud storage cp gs://$JDBC_DRIVER_GCS_BUCKET/$JDBC_DRIVER_FILE_NAME /tmp/
    
    echo "Dynamically finding the PySpark jars directory..."
    PYSPARK_JARS=$(python -c "import os, pyspark; print(os.path.join(os.path.dirname(pyspark.__file__), 'jars'))")
    echo "Found PySpark jars at: $PYSPARK_JARS"
    
    echo "Injecting Workday driver into PySpark core..."
    cp /tmp/$JDBC_DRIVER_FILE_NAME "$PYSPARK_JARS/"
    
    echo "Success! The driver is now a native part of PySpark."
    
  7. בתא חדש, מגדירים את סביבת Spark ומאחזרים את המפתח הפרטי x509 מ-Secret Manager:

    #@title Configure the Spark environment and retrieve the x509 private key from Secret Manager
    
    import os
    import pyspark
    from pyspark.sql import SparkSession
    from google.cloud import secretmanager
    
    PROJECT_ID = os.environ["PROJECT_ID"]
    SECRET_NAME = os.environ["SECRET_NAME"]
    SECRET_VERSION = os.environ["SECRET_VERSION"]
    JDBC_DRIVER_LOCAL_DIRECTORY = os.environ["JDBC_DRIVER_LOCAL_DIRECTORY"]
    JDBC_DRIVER_FILE_NAME = os.environ["JDBC_DRIVER_FILE_NAME"]
    LOCAL_JAR_PATH = JDBC_DRIVER_LOCAL_DIRECTORY + "/" + JDBC_DRIVER_FILE_NAME
    WORKDAY_HOST = os.environ["WORKDAY_HOST"]
    TENANT_NAME = os.environ["TENANT_NAME"]
    CLIENT_ID = os.environ["CLIENT_ID"]
    ISU_USERNAME = os.environ["ISU_USERNAME"]
    WORKDAY_DRIVER_CLASS = os.environ["WORKDAY_DRIVER_CLASS"]
    
    jdbc_url = f"jdbc:workday://{WORKDAY_HOST}:443/system/information_schema?SSLVerification=CA"
    
    def access_private_key_secret():
        print(f"Fetching secret contents from Secret Manager: {SECRET_NAME}...")
        client = secretmanager.SecretManagerServiceClient()
        secret_name = f"projects/{PROJECT_ID}/secrets/{SECRET_NAME}/versions/{SECRET_VERSION}"
        response = client.access_secret_version(request={"name": secret_name})
        print(f"Fetched secret contents from Secret Manager: {SECRET_NAME}")
        return response.payload.data.decode("UTF-8")
    
    private_key_content = access_private_key_secret()
    
  8. בתא חדש, מפעילים את הסשן של Spark עם הגדרת ה-classpath:

    # Stop the old cached Spark session so your newly downloaded JAR is not ignored!
    try:
        spark.stop()
        print("Stopped existing Spark session.")
    except Exception:
        pass
    
    print("Initializing fresh Spark session with Classpath configuration...")
    print(f"Loading JAR from: {LOCAL_JAR_PATH}")
    
    # Rebuild Spark session with explicit Driver & Executor classpaths
    spark = SparkSession.builder \
        .appName("WorkdayJDBC Connection Example") \
        .config("spark.jars", LOCAL_JAR_PATH) \
        .config("spark.driver.extraClassPath", LOCAL_JAR_PATH) \
        .config("spark.executor.extraClassPath", LOCAL_JAR_PATH) \
        .getOrCreate()
    print("Spark Session successfully booted with JAR!")
    
  9. בתא האחרון, יוצרים את החיבור ושולחים שאילתה ל-Workday:

    import os
    import subprocess
    import zipfile
    from pyspark.sql import SparkSession
    
    LOCAL_JAR_PATH = "/tmp/ldq-jdbc-driver-1.1.0-all.jar"
    
    # Load bucket and file name from your Cell 1 environment variables
    GCS_BUCKET = os.environ.get("JDBC_DRIVER_GCS_BUCKET", "BUCKET_NAME")
    GCS_FILE = os.environ.get("JDBC_DRIVER_FILE_NAME", "ldq-jdbc-driver-1.1.0-all.jar")
    GCS_URI = f"gs://{GCS_BUCKET}/{GCS_FILE}"
    
    # 1. Ensure the JAR file is in the notebook's /tmp directory
    if not os.path.exists(LOCAL_JAR_PATH):
        print(f"Downloading {GCS_URI} to {LOCAL_JAR_PATH}...")
        subprocess.run(["gsutil", "cp", GCS_URI, LOCAL_JAR_PATH], check=True)
    print(f"JAR verified on disk: {LOCAL_JAR_PATH} ({os.path.getsize(LOCAL_JAR_PATH):,} bytes)")
    
    # 2. Read the real JDBC driver class name (skipping any '#' comment lines!)
    with zipfile.ZipFile(LOCAL_JAR_PATH, "r") as z:
        raw_lines = (
            z.read("META-INF/services/java.sql.Driver")
            .decode("utf-8")
            .splitlines()
        )
        # Filter out empty lines and '#' comment lines
        valid_classes = [
            line.strip()
            for line in raw_lines
            if line.strip() and not line.strip().startswith("#")
        ]
        driver_class = valid_classes[0]
    
    print(f"Real Driver Class inside JAR: {driver_class}")
    
    # 3. Get or create the Spark session
    spark = SparkSession.builder.appName("WorkdayJDBC Connection Example").getOrCreate()
    
    # 4. Inject the JAR directly into the running Java URLClassLoader (and Scala SparkContext)
    jvm = spark.sparkContext._jvm
    jar_url = jvm.java.io.File(LOCAL_JAR_PATH).toURI().toURL()
    
    # Add to both the Spark classloader and the current thread's context classloader
    jvm.org.apache.spark.util.Utils.getContextOrSparkClassLoader().addURL(jar_url)
    jvm.java.lang.Thread.currentThread().getContextClassLoader().addURL(jar_url)
    spark.sparkContext._jsc.sc().addJar(LOCAL_JAR_PATH)
    
    # Register the driver class with Spark's JDBC DriverRegistry
    jvm.org.apache.spark.sql.execution.datasources.jdbc.DriverRegistry.register(driver_class)
    print(f"Successfully registered {driver_class} in Spark's live JVM!")
    
    # 5. Query Workday
    print("Querying Workday...")
    df_data = (
        spark.read.format("jdbc")
        .option("driver", driver_class)
        .option("url", jdbc_url)
        .option("wd.authn.clientId", CLIENT_ID)
        .option("wd.authn.isu", ISU_USERNAME)
        .option(
            "wd.authn.accessTokenEndpoint",
            f"https://{WORKDAY_HOST}/ccx/oauth2/{TENANT_NAME}/token",
        )
        .option("wd.authn.privateKey", private_key_content)
        .option("query", "SELECT * FROM system.jdbc.catalogs")
        .load()
    )
    
    print("Connection Successful! Data Retrieved:")
    df_data.show(50, truncate=False)
    

    מחליפים את מה שכתוב בשדות הבאים:

    • ‫PROJECT_ID: מזהה הפרויקט ב- Google Cloud .
    • ‫LOCATION: האזור של תבנית הסשן של Managed Service for Apache Spark, לדוגמה us-central1. Google Cloud
    • ‫RUNTIME_ID: המזהה של תבנית הסשן ב-Managed Service for Apache Spark.
    • ‫BUCKET_NAME: השם של הקטגוריה ב-Cloud Storage שמכילה את מנהל ההתקן של Workday JDBC.
    • ‫PATH/workday-live-data-query-jdbc-driver.jar: נתיב האובייקט לקובץ ה-JAR של מנהל ההתקן של Workday JDBC בקטגוריה של Cloud Storage.
    • ‫WORKDAY_HOST: המארח של הדייר ב-Workday, לדוגמה, services1.workday.com.
    • ‫TENANT_NAME: השם של הדייר שלכם ב-Workday.
    • ‫CLIENT_ID: מזהה הלקוח הרשום של ה-API מהדייר ב-Workday.
    • ‫ISU_USERNAME: שם המשתמש של Integration System User מהדייר שלכם ב-Workday.
  10. לוחצים על Execute Cell (סמל ההפעלה) לצד התא, או מקישים על Shift+Enter.

    ‫Managed Service for Apache Spark מריץ את השאילתה ומציג את הפלט של Workday DataFrame מתחת לתא.

הסרת המשאבים

כדי להימנע מצבירת חיובים בחשבון Google Cloud על המשאבים שבהם השתמשתם במדריך הזה, אתם צריכים לסיים את הסשנים האינטראקטיביים הפעילים או למחוק את המשאבים שנוצרו.

סיום סשנים אינטראקטיביים

  1. ב-IDE או ב-Cloud Shell Editor, סוגרים את הסשן הפעיל של ה-Notebook כשהעומס על המערכת מסתיים.

  2. אופציונלי: אם אתם לא צריכים יותר את הקטגוריה של Cloud Storage או את קובץ ה-JAR של מנהל ההתקן של JDBC, אתם יכולים למחוק אותם.

  3. אופציונלי: אם לא צריך יותר את הסוד, מוחקים אותו מ-Secret Manager:

    gcloud secrets delete workday-private-key
    

המאמרים הבאים