Lambda를 사용하여 UDF 생성 및 배포 - Amazon Athena

Lambda를 사용하여 UDF 생성 및 배포

사용자 지정 UDF를 생성하려면 UserDefinedFunctionHandler 클래스를 확장하여 새 Java 클래스를 생성합니다. SDK의 UserDefinedFunctionHandler.java용 소스 코드는 GitHub의 awslabs/aws-athena-query-federation/athena-federation-sdk 리포지토리에서 이용할 수 있습니다. 이와 함께 사용자 지정 UDF를 생성하기 위해 검토하고 수정할 수 있는 UDF 구현 예제도 제공합니다.

이 섹션의 단계에서는 명령줄과 배포에서 Apache Maven을 사용하여 사용자 지정 UDF Jar 파일을 작성하고 구축하는 방법을 보여줍니다.

Maven을 사용하여 Athena용 사용자 지정 UDF를 생성하려면 다음 단계를 수행합니다.

SDK 복제 및 개발 환경 준비

시작하기 전에 git가 sudo yum install git -y를 사용하여 시스템에 설치되어 있는지 확인하세요.

AWS query federation SDK를 설치하려면
  • 명령줄에 다음을 입력하여 SDK 리포지토리를 복제합니다. 이 리포지토리에는 SDK, 예제 및 데이터 소스 커넥터 제품군이 포함되어 있습니다. 데이터 소스 커넥터에 대한 자세한 내용은 Amazon Athena 페더레이션 쿼리 사용 단원을 참고하세요.

    git clone https://github.com/awslabs/aws-athena-query-federation.git
이 절차의 사전 조건을 설치하려면

이미 Apache Maven, AWS CLI 및AWS Serverless Application Model 빌드 도구가 설치된 개발 머신에서 작업하는 경우 이 단계를 건너뛸 수 있습니다.

  1. 복제할 때 생성한 aws-athena-query-federation 디렉터리의 루트에서 개발 환경을 준비하는 prepare_dev_env.sh 스크립트를 실행합니다.

  2. 을 업데이트하여 설치 프로세스에서 생성된 새 변수를 소싱하거나 터미널 세션을 다시 시작합니다.

    source ~/.profile
    중요

    이 단계를 건너뛰면 나중에 Lambda 함수를 게시할 수 없는 AWS CLI 또는 AWS SAM 빌드 도구에 대한 오류가 발생합니다.

Maven 프로젝트 만들기

다음 명령을 실행하여 Maven 프로젝트를 만듭니다. groupId를 고유한 조직 ID로 바꾸고 my-athena-udf를 애플리케이션 이름으로 바꿉니다. 자세한 내용은 Apache Maven 문서에서 첫 번째 Maven 프로젝트를 만들려면 어떻게 해야 합니까?를 참조하세요.

mvn -B archetype:generate \ -DarchetypeGroupId=org.apache.maven.archetypes \ -DgroupId=groupId \ -DartifactId=my-athena-udfs

Maven 프로젝트에 종속성 및 플러그인 추가

Maven 프로젝트 pom.xml 파일에 다음 구성을 추가합니다. 예를 들어 GitHub의 pom.xml 파일을 참조하세요.

<properties> <aws-athena-federation-sdk.version>2022.47.1</aws-athena-federation-sdk.version> </properties> <dependencies> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-athena-federation-sdk</artifactId> <version>${aws-athena-federation-sdk.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.1</version> <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> </plugin> </plugins> </build>

UDF에 대한 Java 코드 작성

UserDefinedFunctionHandler.java를 확장하여 새 클래스를 만듭니다. 클래스 내부에 UDF를 작성합니다.

다음 예제에서는 UDF에 대한 두 개의 Java 메서드인 compress()decompress()가 클래스 MyUserDefinedFunctions 내에 만들어집니다.

*package *com.mycompany.athena.udfs; public class MyUserDefinedFunctions extends UserDefinedFunctionHandler { private static final String SOURCE_TYPE = "MyCompany"; public MyUserDefinedFunctions() { super(SOURCE_TYPE); } /** * Compresses a valid UTF-8 String using the zlib compression library. * Encodes bytes with Base64 encoding scheme. * * @param input the String to be compressed * @return the compressed String */ public String compress(String input) { byte[] inputBytes = input.getBytes(StandardCharsets.UTF_8); // create compressor Deflater compressor = new Deflater(); compressor.setInput(inputBytes); compressor.finish(); // compress bytes to output stream byte[] buffer = new byte[4096]; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(inputBytes.length); while (!compressor.finished()) { int bytes = compressor.deflate(buffer); byteArrayOutputStream.write(buffer, 0, bytes); } try { byteArrayOutputStream.close(); } catch (IOException e) { throw new RuntimeException("Failed to close ByteArrayOutputStream", e); } // return encoded string byte[] compressedBytes = byteArrayOutputStream.toByteArray(); return Base64.getEncoder().encodeToString(compressedBytes); } /** * Decompresses a valid String that has been compressed using the zlib compression library. * Decodes bytes with Base64 decoding scheme. * * @param input the String to be decompressed * @return the decompressed String */ public String decompress(String input) { byte[] inputBytes = Base64.getDecoder().decode((input)); // create decompressor Inflater decompressor = new Inflater(); decompressor.setInput(inputBytes, 0, inputBytes.length); // decompress bytes to output stream byte[] buffer = new byte[4096]; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(inputBytes.length); try { while (!decompressor.finished()) { int bytes = decompressor.inflate(buffer); if (bytes == 0 && decompressor.needsInput()) { throw new DataFormatException("Input is truncated"); } byteArrayOutputStream.write(buffer, 0, bytes); } } catch (DataFormatException e) { throw new RuntimeException("Failed to decompress string", e); } try { byteArrayOutputStream.close(); } catch (IOException e) { throw new RuntimeException("Failed to close ByteArrayOutputStream", e); } // return decoded string byte[] decompressedBytes = byteArrayOutputStream.toByteArray(); return new String(decompressedBytes, StandardCharsets.UTF_8); } }

JAR 파일 구축

mvn clean install을 실행하여 프로젝트를 빌드합니다. 성공적으로 빌드되면 JAR 파일이 artifactId-version.jar이라는 프로젝트의 target 폴더에 생성됩니다. 여기서 artifactId는 Maven 프로젝트에서 제공한 이름입니다(예: my-athena-udfs).

AWS Lambda에 JAR 배포

Lambda에 코드를 배포하는 두 가지 옵션이 있습니다.

  • AWS Serverless Application Repository을 사용하여 배포(권장)

  • JAR 파일에서 Lambda 함수 만들기

옵션 1: AWS Serverless Application Repository에 배포

AWS Serverless Application Repository에 JAR 파일을 배포할 때 애플리케이션의 아키텍처를 나타내는 AWS SAM 템플릿 YAML 파일을 만듭니다. 그런 다음 이 YAML 파일과, 애플리케이션의 아티팩트를 업로드하여 AWS Serverless Application Repository에 공개하는 Amazon S3 버킷을 지정합니다. 아래 절차에서는 앞서 복제했던 Athena Query Federation SDK의 athena-query-federation/tools 디렉터리에 위치한 publish.sh 스크립트를 사용합니다.

자세한 내용과 요구 사항은 AWS Serverless Application Repository 개발자 안내서애플리케이션 게시, AWS Serverless Application Model 개발자 안내서AWS SAM 템플릿 개념AWS SAM CLI를 사용하여 서버리스 애플리케이션 게시를 참조하세요.

다음 예제에서는 YAML 파일의 파라미터를 보여줍니다. YAML 파일에 유사한 파라미터를 추가하고 프로젝트 디렉터리에 저장합니다. 전체 예제는 GitHub에서 athena-udf.yaml을 참조하세요.

Transform: 'AWS::Serverless-2016-10-31' Metadata: 'AWS::ServerlessRepo::Application': Name: MyApplicationName Description: 'The description I write for my application' Author: 'Author Name' Labels: - athena-federation SemanticVersion: 1.0.0 Parameters: LambdaFunctionName: Description: 'The name of the Lambda function that will contain your UDFs.' Type: String LambdaTimeout: Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)' Default: 900 Type: Number LambdaMemory: Description: 'Lambda memory in MB (min 128 - 3008 max).' Default: 3008 Type: Number Resources: ConnectorConfig: Type: 'AWS::Serverless::Function' Properties: FunctionName: !Ref LambdaFunctionName Handler: "full.path.to.your.handler. For example, com.amazonaws.athena.connectors.udfs.MyUDFHandler" CodeUri: "Relative path to your JAR file. For example, ./target/athena-udfs-1.0.jar" Description: "My description of the UDFs that this Lambda function enables." Runtime: java8 Timeout: !Ref LambdaTimeout MemorySize: !Ref LambdaMemory

YAML 파일을 저장한 프로젝트 디렉터리에 publish.sh 스크립트를 복사하고 다음 명령을 실행합니다.

./publish.sh MyS3Location MyYamlFile

예를 들어 버킷 위치가 s3://amzn-s3-demo-bucket/mysarapps/athenaudf이고 YAML 파일이 my-athena-udfs.yaml로 저장된 경우:

./publish.sh amzn-s3-demo-bucket/mysarapps/athenaudf my-athena-udfs
Lambda 함수를 생성하는 방법
  1. https://console.aws.amazon.com/lambda/에서 Lambda 콘솔을 열고 함수 생성을 선택한 다음 서버리스 앱 리포지토리 찾아보기를 선택합니다.

  2. 프라이빗 애플리케이션을 선택하거나 목록에서 애플리케이션을 찾거나 키워드를 사용하여 애플리케이션을 검색한 다음 선택합니다.

  3. 애플리케이션 세부 정보를 검토하고 제공한 다음 배포를 선택합니다.

    이제 Lambda 함수 JAR 파일에 정의된 메서드 이름을 Athena의 UDF로 사용할 수 있습니다.

옵션 2: 직접 Lambda 함수 생성

콘솔 또는 AWS CLI를 사용하여 직접 Lambda 함수를 만들 수도 있습니다. 다음은 Lambda create-function CLI 명령을 사용하는 예제입니다.

aws lambda create-function \ --function-name MyLambdaFunctionName \ --runtime java8 \ --role arn:aws:iam::1234567890123:role/my_lambda_role \ --handler com.mycompany.athena.udfs.MyUserDefinedFunctions \ --timeout 900 \ --zip-file fileb://./target/my-athena-udfs-1.0-SNAPSHOT.jar