Melhores Práticas para Criar Aplicativos Spark Streaming Baseados em Python

Dicas e melhores práticas para configurar projetos de build Python no serviço Data Flow.

Os aplicativos Python Spark são mais complexos que os aplicativos Java, pois as dependências são necessárias ao mesmo tempo para runtimes JVM e Python. Cada um deles tem seus próprios sistemas de gerenciamento de projetos e pacotes. Para ajudar com dependências de pacote de diferentes runtime, o Data Flow tem uma ferramenta de pacote de dependências. A ferramenta pacotes de todas as dependências em um único archive que precisa ser transferido por upload para o serviço Object Storage. O serviço Data Flow fornece dependências desse arquivo compactado para o aplicativo Spark.

O arquivo compactado garante a disponibilidade quando armazenado no Oracle Cloud Infrastructure Object Storage, a mesma reprodutibilidade (a antifactory é dinâmica e, portanto, pode produzir uma árvore de dependências diferente) e para de fazer download das mesmas dependências de fontes externas.

Mais informações sobre como configurar e usar o gerenciador de dependências estão disponíveis na seção em Funcionalidade Spark-Submit no Serviço Data Flow.

Ao criar o archive.zip para sua lista de aplicativos, as bibliotecas Java necessárias no packages.txt e o pacote de dependência os empacotam junto com suas dependências.
Forneça uma dependência usando qualquer uma das seguintes opções:
  • Use a opção --packages ou a configuração do Spark spark.jars.packages. Um Aplicativo em execução em um ponto final privado deve permitir que o tráfego da sub-rede privada vá para a internet pública para o download do pacote.
  • Forneça o local do Object Storage em --jars ou spark.jars como uma lista separada por vírgulas.
  • Use python ou structured_streaming_java_dependencies_for_python create archive.zip.
Por exemplo, para incluir spark-sql-kafka-0-10_2.12, adicione-o em packages.txt:
org.apache.spark:spark-sql-kafka-0-10_2.12:3.2.1
Execute este comando:
docker run --pull always --rm -v $(pwd):/opt/dataflow -it phx.ocir.io/oracle/dataflow/dependency-packager:latest
Resultando em um arquivo archive.zip:
  adding: java/ (stored 0%)
  adding: java/org.lz4_lz4-java-1.7.1.jar (deflated 3%)
  adding: java/org.slf4j_slf4j-api-1.7.30.jar (deflated 11%)
  adding: java/org.xerial.snappy_snappy-java-1.1.8.2.jar (deflated 1%)
  adding: java/org.apache.spark_spark-token-provider-kafka-0-10_2.12-3.0.2.jar (deflated 8%)
  adding: java/org.apache.spark_spark-sql-kafka-0-10_2.12-3.0.2.jar (deflated 5%)
  adding: java/com.github.luben_zstd-jni-1.4.4-3.jar (deflated 1%)
  adding: java/org.spark-project.spark_unused-1.0.0.jar (deflated 42%)
  adding: java/org.apache.kafka_kafka-clients-2.4.1.jar (deflated 8%)
  adding: java/org.apache.commons_commons-pool2-2.6.2.jar (deflated 9%)
  adding: version.txt (deflated 59%)
archive.zip is generated!
Observação

No Spark 3.2.1, a maioria do código de origem e das bibliotecas usadas para executar o Data Flow estão ocultas. Você não precisa mais corresponder às versões do SDK do serviço Data Flow e não tem mais conflitos de dependência de terceiros com o serviço Data Flow. Consulte o tutorial Desenvolver Aplicativos Oracle Cloud Infrastructure Data Flow Localmente, Implantar na Nuvem para obter mais informações. Para obter as versões de biblioteca necessárias, consulte Migrar o Serviço Data Flow para o Spark 3.2.1. Resolva a dependência de streaming usando as opções mencionadas anteriormente. Exemplos de dependências Java de streaming estruturado para Python estão disponíveis no github.

Talvez seja necessário sombrear algumas bibliotecas Java.
Se você estiver usando o Spark 2.4.4 ou o Spark 3.0.2, talvez precise sombrear suas bibliotecas. Crie um projeto Maven separado para criar um Fat JAR que contenha todas as dependências Java e outros ajustes, como sombra em um só lugar. Inclua-o como JAR personalizado usando o pacote de dependência. Por exemplo, usando oci-java-sdk-addons-sasl, pois o SDK do Oracle Cloud Infrastructure é compilado em versões posteriores de algumas bibliotecas de terceiros e, portanto, podem ocorrer falhas de runtime.

Um exemplo de projeto Maven:

<?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>
 
  <groupId>com.example</groupId>
  <artifactId>SaslFat</artifactId>
  <version>1.0-SNAPSHOT</version>
 
  <properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <spark.version>3.0.2</spark.version>
  </properties>
 
  <dependencies>
    <dependency>
      <groupId>com.oracle.oci.sdk</groupId>
      <artifactId>oci-java-sdk-addons-sasl</artifactId>
      <optional>false</optional>
      <version>1.36.1</version>
    </dependency>
    <dependency>
      <groupId>com.google.protobuf</groupId>
      <artifactId>protobuf-java</artifactId>
      <version>3.11.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.spark</groupId>
      <artifactId>spark-core_2.12</artifactId>
      <version>${spark.version}</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.spark</groupId>
      <artifactId>spark-sql_2.12</artifactId>
      <version>${spark.version}</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.spark</groupId>
      <artifactId>spark-sql-kafka-0-10_2.12</artifactId>
      <version>${spark.version}</version>
    </dependency>
  </dependencies>
 
  <build>
    <plugins>
 
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</version>
        <configuration>
          <createDependencyReducedPom>false</createDependencyReducedPom>
          <transformers>
            <transformer
              implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
            <transformer
              implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"/>
          </transformers>
          <relocations>
            <relocation>
              <pattern>com.google.</pattern>
              <shadedPattern>com.shaded.google.</shadedPattern>
            </relocation>
            <relocation>
              <pattern>com.oracle.bmc.</pattern>
              <shadedPattern>com.shaded.oracle.bmc.</shadedPattern>
              <excludes>
                <exclude>com.oracle.bmc.auth.sasl.*</exclude>
              </excludes>
            </relocation>
          </relocations>
          <!-- exclude signed Manifests -->
          <filters>
            <filter>
              <artifact>*:*</artifact>
              <excludes>
                <exclude>META-INF/*.SF</exclude>
                <exclude>META-INF/*.DSA</exclude>
                <exclude>META-INF/*.RSA</exclude>
              </excludes>
            </filter>
          </filters>
          <artifactSet>
            <excludes>
              <exclude>${project.groupId}:${project.artifactId}</exclude>
            </excludes>
          </artifactSet>
        </configuration>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
 
    </plugins>
  </build>
</project>
Coloque SaslFat-1.0-SNAPSHOT.jar no diretório de trabalho do gerenciador de pacotes de dependência e execute o comando:
docker run --pull always --rm -v $(pwd):/opt/dataflow -it phx.ocir.io/oracle/dataflow/dependency-packager:latest
SaslFat-1.0-SNAPSHOT.jar é compactado no arquivo archive.zip como uma dependência Java:
  adding: java/ (stored 0%)
  adding: java/SaslFat-1.0-SNAPSHOT.jar (deflated 8%)
  adding: version.txt (deflated 59%)
archive.zip is generated!
Ou você pode criar manualmente um archive.zip que contenha a pasta java com SaslFat-1.0-SNAPSHOT.jar.