Acessar dados do Workday com o Spark

O Serviço Gerenciado para Apache Spark permite consultar e analisar dados comerciais externos diretamente das suas cargas de trabalho do Spark. Use o driver JDBC do Workday Live Data Query para se conectar ao seu locatário do Workday, carregar conjuntos de dados em tempo real do Workday em DataFrames do Spark e integrar esses dados ao seu pipeline de análise no Google Cloud.

Neste guia, você vai configurar o Workday Live Data Query, armazenar sua chave privada com segurança no Secret Manager e usar a extensão do Google Cloud Data Agent Kit para executar um notebook do PySpark no Visual Studio Code ou no editor do Cloud Shell no Serviço Gerenciado para Apache Spark.

Custos

A extensão do Data Agent Kit está disponível sem custo financeiro adicional. No entanto, este guia usa os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços.

Antes de começar

  1. Faça login na sua conta do Google Cloud . Se você começou a usar o Google Cloudagora, crie uma conta para avaliar o desempenho dos nossos produtos em situações reais. Clientes novos também recebem US $300 em créditos para executar, testar e implantar cargas de trabalho sem custos financeiros.
  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

Papéis necessários do Identity and Access Management (IAM)

Alguns papéis do IAM são necessários para executar os exemplos nesta página. Dependendo das políticas da organização, esses papéis podem já ter sido concedidos. Para verificar as concessões de papéis, consulte Você precisa conceder papéis?. Para mais informações sobre a concessão de papéis, consulte Gerenciar o acesso a projetos, pastas e organizações.

Os papéis necessários dependem de como a carga de trabalho é executada. O Serviço Gerenciado para Apache Spark sem servidor 2.3 e os ambientes de execução sem servidor anteriores executam cargas de trabalho como uma conta de serviço. Os ambientes de execução sem servidor 3.0 e mais recentes do Serviço Gerenciado para Apache Spark executam cargas de trabalho com suas credenciais de usuário final (EUC, na sigla em inglês) por padrão. Portanto, você concede papéis de acesso a dados à sua conta de usuário em vez de uma conta de serviço. Se você especificar uma conta de serviço personalizada para um ambiente de execução 3.0 ou posterior, a carga de trabalho será executada com as credenciais da conta de serviço. Para mais informações, consulte Papéis de implantações sem servidor.

Papéis do usuário

Para receber as permissões necessárias para criar uma sessão de notebook interativo do Serviço Gerenciado para Apache Spark e acessar recursos, peça ao administrador para conceder a você os seguintes papéis do IAM:

  • Use o ambiente de execução sem servidor 2.3 ou anterior:
  • Usar o ambiente de execução sem servidor 3.0 ou mais recente: Editor do Dataproc sem servidor (roles/dataproc.serverlessEditor) no projeto
  • Ler o driver JDBC do Cloud Storage com credenciais de usuário final: Leitor de objetos do Storage (roles/storage.objectViewer) no bucket que contém o driver
  • Ler o secret da chave privada do Secret Manager com as credenciais do usuário final: Acessador de secrets do Secret Manager (roles/secretmanager.secretAccessor) no secret ou projeto

Papéis da conta de serviço

Conceda os seguintes papéis à conta de serviço que executa sua carga de trabalho. Os ambientes de execução sem servidor 3.0 e mais recentes do Serviço Gerenciado para Apache Spark precisam desses papéis somente se a carga de trabalho for executada com credenciais da conta de serviço em vez das credenciais de usuário final (EUC, na sigla em inglês) padrão.

Para garantir que a conta de serviço padrão do Compute Engine ou uma conta de serviço personalizada tenha as permissões necessárias para executar cargas de trabalho do Managed Service para Apache Spark, ler o driver e acessar secrets, peça ao administrador para conceder os seguintes papéis do IAM à conta de serviço padrão do Compute Engine ou a uma conta de serviço personalizada:

Pré-requisitos do Workday

Antes de se conectar ao Workday usando a consulta de dados em tempo real, conclua a seguinte configuração:

  1. Faça o download do driver JDBC do Workday Live Data Query no Workday.

  2. Faça upload do arquivo JAR do driver JDBC que você baixou para um bucket do Cloud Storage no seu projeto.

  3. Conclua as etapas de configuração da Consulta de Dados em Tempo Real no seu locatário do Workday, conforme descrito na documentação da Consulta de Dados em Tempo Real do Workday.

    Durante a configuração no Workday, crie um usuário do sistema de integração (ISU), registre um cliente de API e gere um par de chaves privadas.

  4. Anote os seguintes valores de configuração do Workday:

    • Host do Workday: o host do locatário do Workday, por exemplo, services1.workday.com.

    • Nome do locatário: o nome do seu locatário do Workday.

    • ID do cliente: o ID do cliente da API registrado do seu locatário do Workday.

    • Nome de usuário do ISU: o nome de usuário do usuário do sistema de integração.

Armazenar a chave privada no Secret Manager

Armazene o arquivo de chave privada gerado durante a configuração da consulta de dados em tempo real do Workday com segurança no Secret Manager:

  1. No Cloud Shell, execute o seguinte comando para gerar um par de chaves x509:

    openssl req -x509 -newkey rsa:2048 -keyout private-key.pem -out public-key.cert -sha256 -days 365 -nodes
    
  2. Para visualizar a chave privada, execute o seguinte comando:

    cat private-key.pem
    
  3. Armazene a chave privada no Secret Manager:

    1. No console do Google Cloud , acesse a página Secret Manager:

      Acessar o Secret Manager

    2. Clique em Criar secret.

    3. No campo Nome, insira um nome para o secret, como workday-private-key.

    4. Em Valor do secret, faça upload de private-key.pem ou cole o conteúdo da chave privada, incluindo as linhas de cabeçalho e rodapé -----BEGIN PRIVATE KEY----- e -----END PRIVATE KEY-----.

    5. Clique em Criar secret.

  4. Para ver a chave pública, execute o seguinte comando:

    cat public-key.cert
    
  5. No Workday, pesquise Create x509 Public Key, cole a chave pública de public-key.cert e clique em OK.

  6. No cliente da API do Workday criado anteriormente, atualize o certificado x509 para a nova chave x509.

Configurar o Data Agent Kit no ambiente de desenvolvimento integrado

Para configurar o Data Agent Kit, instale a extensão no Visual Studio Code ou em outro ambiente de desenvolvimento integrado compatível ou use a extensão pré-instalada no Editor do Cloud Shell.

Para configurar o Data Agent Kit no seu IDE ou no editor do Cloud Shell, siga estas etapas:

  1. Configure o Data Agent Kit:

    1. Instale a extensão do Data Agent Kit.

    2. Configure o Data Agent Kit.

Criar um notebook

Usando o Kit de Agentes de Dados, crie um notebook interativo do PySpark:

  1. No IDE ou no editor do Cloud Shell, abra o painel do Data Agent Kit.

  2. Em Apache Spark, selecione Novo notebook e, em seguida, Kernel local.

  3. Selecione um ambiente de execução na lista de ambientes disponíveis (também chamados de modelos de sessão).

  4. Selecione Começar com um novo notebook.

    Um novo notebook é aberto com uma célula inicial preenchida com o código de inicialização da sessão do Spark Connect.

  5. Na parte de cima do editor de notebook, clique em Selecionar kernel e escolha um kernel local do Python para seu notebook.

Consultar dados do Workday no PySpark

  1. Na primeira célula do notebook, instale as bibliotecas de cliente necessárias e configure as variáveis de ambiente:

    %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. Em uma nova célula, conceda o papel de acessador de secrets do Secret Manager (roles/secretmanager.secretAccessor) à conta de serviço padrão do 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. Em uma nova célula, copie o driver JDBC do Cloud Storage para o diretório local:

    gsutil cp gs://$JDBC_DRIVER_GCS_BUCKET/$JDBC_DRIVER_FILE_NAME $JDBC_DRIVER_LOCAL_DIRECTORY
    
  4. Em uma nova célula, instale a biblioteca de cliente do Secret Manager:

    %%bash
    pip install google-cloud-secret-manager
    
  5. Em uma nova célula, verifique se o arquivo JAR do driver foi baixado localmente:

    !gsutil cp gs://$JDBC_DRIVER_GCS_BUCKET/$JDBC_DRIVER_FILE_NAME /tmp/
    !ls -lh /tmp/ldq-jdbc-driver-1.1.0-all.jar
    
  6. Em uma nova célula, injete o JAR do driver do Workday no diretório de JARs do 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. Em uma nova célula, configure o ambiente do Spark e recupere a chave privada x509 do 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. Em uma nova célula, inicialize a sessão do Spark com a configuração do caminho de classe:

    # 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. Em uma célula final, inicie a conexão e consulte o 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)
    

    Substitua:

    • PROJECT_ID: o ID do projeto Google Cloud .
    • LOCATION: a região do Google Cloud modelo de sessão do Serviço Gerenciado para Apache Spark, por exemplo, us-central1.
    • RUNTIME_ID: o ID do modelo de sessão do Serviço Gerenciado para Apache Spark.
    • BUCKET_NAME: o nome do bucket do Cloud Storage que contém o driver JDBC do Workday.
    • PATH/workday-live-data-query-jdbc-driver.jar: o caminho do objeto para o JAR do driver JDBC do Workday no bucket do Cloud Storage.
    • WORKDAY_HOST: o host do locatário do Workday, por exemplo, services1.workday.com.
    • TENANT_NAME: o nome do locatário do Workday.
    • CLIENT_ID: o ID do cliente da API registrado do locatário do Workday.
    • ISU_USERNAME: o nome de usuário do usuário do sistema de integração do locatário do Workday.
  10. Clique em Executar célula (o ícone de reprodução) ao lado da célula ou pressione Shift+Enter.

    O Serviço Gerenciado para Apache Spark executa a consulta e mostra a saída do DataFrame do Workday abaixo da célula.

Limpar

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados neste guia, encerre as sessões interativas ativas ou exclua os recursos criados.

Encerrar sessões interativas

  1. No ambiente de desenvolvimento integrado ou no editor do Cloud Shell, encerre a sessão ativa do notebook quando a carga de trabalho terminar.

  2. Opcional: exclua o bucket do Cloud Storage ou o arquivo JAR do driver JDBC se não precisar mais deles.

  3. Opcional: exclua o secret do Secret Manager se ele não for mais necessário:

    gcloud secrets delete workday-private-key
    

A seguir