Athena 사용법: https://realyun99.tistory.com/147

 

[AWS] Amazon Athena 사용법

먼저 Athena가 뭐하는 친군지 살펴보자. Amazon Athena 표준 SQL을 사용해 S3에 저장된 데이터를 간편하게 분석할 수 있는 대화식 쿼리 서버리스 서비스 그냥 단순히 S3에 저장된 데이터를 가리키고 스

realyun99.tistory.com

Athena+Glue(Crawler) 활용: https://realyun99.tistory.com/148

 

[AWS] Amazon Athena + Glue(Crawler) 활용

Athena 사용법: https://realyun99.tistory.com/147 [AWS] Amazon Athena 사용법 먼저 Athena가 뭐하는 친군지 살펴보자. Amazon Athena 표준 SQL을 사용해 S3에 저장된 데이터를 간편하게 분석할 수 있는 대화식..

realyun99.tistory.com

저번 포스팅들에 이어서 계속 진행해볼 것...


  • Athena CTAS를 사용한 ETL

대부분의 경우 데이터 레이크 또는 테이블로 들어오는 raw data는 csv 또는 텍스트 형식이다.

이런 형식은 Athena, 기타 엔진으로 쿼리하는 데 최적이 아니다. 데이터를 parquet과 같은 포맷으로 변환하는 것이 좋다.

CTAS(Create Table As Select) 쿼리를 사용해 테이블을 tsv 형식에서 parquet 포맷으로 변환하고 압축 및 분할을 통해 저장할 것..

 

저장된 쿼리에서 Athena_ctas_reviews를 실행해보자

(s3 버킷 이름을 잘 설정 해주자!)

다음 쿼리를 실행하면 다음과 같이 결과를 볼 수 있다.

명령들을 좀 살펴보자!

CREATE TABLE amazon_reviews_by_marketplace
WITH ( format='PARQUET', parquet_compression = 'SNAPPY', partitioned_by = ARRAY['marketplace', 'year'], 
external_location =   's3://<<Bucket-name>>/athena-ctas-insert-into/') AS
SELECT customer_id,
        review_id,
        product_id,
        product_parent,
        product_title,
        product_category,
        star_rating,
        helpful_votes,
        total_votes,
        verified_purchase,
        review_headline,
        review_body,
        review_date,
        marketplace,
        year(review_date) AS year
FROM amazon_reviews_tsv
WHERE "$path" LIKE '%tsv.gz';

/* Let's try to find the products and their corresponding category by number of reviews and avg star rating for US marketplace in year 2015 */

SELECT product_id,
        product_category,
        product_title,
        count(*) AS num_reviews,
        avg(star_rating) AS avg_stars
FROM amazon_reviews_by_marketplace
WHERE marketplace='US'
AND year=2015
GROUP BY  1, 2, 3
ORDER BY  4 DESC limit 10;

parquet_compression = 'SNAPPY' 같은 경우는 압축 방식을 정하는 것..

https://docs.aws.amazon.com/ko_kr/athena/latest/ug/compression-formats.html

 

Like 구문: SELECT * FROM [table name] WHERE [column] LIKE [condition];

부분적으로 일치하는 컬럼을 찾을 때 사용

 

  • Athena Workgroups

Workgroup: 사용자, 팀, 애플리케이션 또는 워크로드를 분리하고 각 쿼리 또는 전체 workgroup이 처리할 수 있는 데이터 양에 대한 제한을 설정하고 비용을 추적한다.

"Workgroup이 리소스 역할을 하기 때문에 리소스 수준 ID 기반 정책을 사용해 특정 workgroup에 대한 액세스 제어 가능!"

 

저번 포스팅의 테이블 생성 실습에서 primary 작업 그룹에 대한 CloudWatch 메트릭을 활성화했다.

CloudWatch 지표를 확인해보자(작업 그룹 → primary → 지표)

 

데이터 사용 제한을 설정해보자.

workgroupA를 클릭한 뒤 편집에 들어가 해당 데이터를 제한하자.

CloudFormation을 열고 스택 출력에 들어가 ConsolePassword 링크 클릭(AWS Secrets Manager로 이동)

해당 암호 값 검색을 통해 비밀번호를 확인하자.

 

시크릿 탭을 열어

IAM user name: userA

Password: 복사한 비밀번호 로 로그인하자.

 

Athena 콘솔을 열고 쿼리 결과 위치에 primary 위치를 입력한 다음 wrokgroupA를 선택하자.

(참고로 현재 실습은 버지니아 북부에서 진행 중이다!)

쿼리를 실행해보면 실행 불가!

실행했던 쿼리문이다..

/* Let's try to find the products and their corresponding category by number of reviews and avg star rating on parquet table */

SELECT product_id, product_category, product_title, count(*) as num_reviews, avg(star_rating) as avg_stars
FROM amazon_reviews_parquet 
GROUP BY 1, 2, 3
ORDER BY 4 DESC
limit 10;

 

다음으로 workgroupB로 전환하고 userA가 workgroupB에서 동일한 쿼리를 실행할 수 있는지 확인해보자.

권한이 없어 할 수 없다는 오류가 발생한다.

 

 

'Cloud > AWS' 카테고리의 다른 글

[AWS] AppStream 2.0  (0) 2022.11.26
[AWS] Amazon QuickSight 개념 및 실습  (0) 2022.11.20
[AWS] Amazon Athena 사용법 -2 + Glue(Crawler) 활용  (1) 2022.11.19
[AWS] Amazon Athena 사용법 -1  (1) 2022.11.19
[AWS] Step Functions  (0) 2022.11.18

Athena 사용법: https://realyun99.tistory.com/147

 

[AWS] Amazon Athena 사용법

먼저 Athena가 뭐하는 친군지 살펴보자. Amazon Athena 표준 SQL을 사용해 S3에 저장된 데이터를 간편하게 분석할 수 있는 대화식 쿼리 서버리스 서비스 그냥 단순히 S3에 저장된 데이터를 가리키고 스

realyun99.tistory.com

저번 포스팅에 이어서 Glue까지 사용을 해보자. 사용 전에 Glue가 뭔지 부터 확인하자.

 


AWS Glue

  • 분석, 기계 학습 및 애플리케이션 개발을 위해 데이터를 쉽게 탐색, 준비, 조합할 수 있도록 지원하는 서버리스 데이터 통합 서비스
  • 데이터 통합: 해당 개발을 위해 데이터를 준비하고 결합하는 프로세스
    • 데이터 검색 및 추출
    • 데이터 강화, 정리, 정규화 및 결합
    • 데이터베이스, 데이터 웨어하우스 및 데이터 레이크에 데이터 로드 및 구성 등
  • 기능
    • Data Catalog: 모든 데이터 자산을 위한 영구 메타데이터 스토어
      • 모든 AWS 데이터 세트에서 검색: 자동으로 통계를 계산하고 파티션을 등록, 데이터 변경 사항 파악
      • Crawlers: 소스/대상 스토어에 연결해 우선순위가 지정된 Classifiers을 거치면서 데이터의 스키마 결정, 메타 데이터 생성
      • Stream schema registries: Apache Avro 스키마를 사용해 스트리밍 데이터의 변화를 검증하고 제어
    • Data Integration and ETL(Extract / Transform / Load)
      • Studio Job Notebooks: Studio에서 최소한으로 설정할 수 있는 서버리스 노트북
      • Interactive Sessions: 데이터 통합 작업 개발을 간소화, 엔지니어와 대화식으로 데이터 탐색, 준비
      • ETL 파이프라인 구축: 여러 개의 작업을 병렬로 시작하거나 작업 간에 종속성을 지정
      • Studio: 분산 처리를 위한 확정성이 뛰어난 ETL 작업 가능, 에디터에서 ETL 프로세스를 정의하면 Glue가 자동으로 코드 생성
      • 등등...
    • Glue DataBrew: 시각적 데이터 준비 도구(사전 빌드된 250개 이상의 변환 구성 중 선택해서 코드 없이 가능)


솔직히 뭐라고 하는지 모르겠다.. 직접 써보는게 답!

Crawler로 실습을 진행해보자.

 

  • Glue로 테이블 만들기

크롤러를 생성해주자.

데이터 스토어는 저번 포스팅 때 가져왔었던 데이터셋이 위치한 버킷으로 지정할 것!

Create new IAM role을 통해 새로 생성하고 지정해준다. 타겟 DB까지 정해주면 끝!

크롤러를 돌려주자(생성된 크롤러 선택 후 Run 버튼 클릭)

Glue → Tables에 들어가면 테이블이 하나 더 추가된 것을 볼 수 있다.

Athena로 돌아가 다음 쿼리를 실행해 데이터를 확인해보자.

select * from amazon_review_glue_parquet limit 10;

 

  • Views 만들기

다시 Athena로 돌아왔다.(Athena의 view는 물리적 테이블이 아닌 논리적 테이블!)

저장된 쿼리에서 Athena_create_view_top_rated를 클릭한다.

위 이미지 처럼 선택 후 실행한다. 아래와 같이 보기에 추가된 것을 확인할 수 있다.

두번째 쿼리를 선택한 뒤 실행해 등급별로 상위 10개 제품을 확인해보자.

 

  • 결과 확인(S3 버킷)

S3에서 athena-workshop-<account-id> 로 시작하는 버킷을 찾아 들어가면 처음엔 비어있던 버킷이 Athena에서 돌렸던 쿼리문을 기준으로 폴더들이 생성되어 있는 것을 볼 수 있고, Athena_compare_reviews 접두사 중 하나를 찾아보면 쿼리 ID와 함께 저장된 결과를 볼 수 있음.

 

 

 

❗ 그래서 결과적으로 Glue Crawler가 무슨 역할을 했냐...? ❗

크롤러 작동 방식:

데이터를 분류하여 raw data의 포맷, 스키마 및 관련 속성 결정 / 데이터를 테이블 혹은 파티션으로 분류 / 메타데이터를 Data Catalog에 작성

 

위와 같은 것들을 crawler를 생성하고 실행하면 알아서 해주는 느낌인듯?

데이터 스토어를 지금은 하나를 지정했지만, 여러 개 병렬로도 가능하고...

지금 했던 실습 같은 경우는 아주 단순한 경우인 거고, 여러 방식으로 활용이 가능하다!

(그리고 Athena는 쿼리를 돌려서 테이블을 만들었지만, 크롤러는 그런게 없이 알아서 돌려주네?!)

'Cloud > AWS' 카테고리의 다른 글

[AWS] Amazon QuickSight 개념 및 실습  (0) 2022.11.20
[AWS] Amazon Athena 사용법 -3  (0) 2022.11.20
[AWS] Amazon Athena 사용법 -1  (1) 2022.11.19
[AWS] Step Functions  (0) 2022.11.18
[AWS] Spot Fleet  (0) 2022.11.16

먼저 Athena가 뭐하는 친군지 살펴보자.


Amazon Athena

  • 표준 SQL을 사용해 S3에 저장된 데이터를 간편하게 분석할 수 있는 대화식 쿼리 서버리스 서비스
  • 그냥 단순히 S3에 저장된 데이터를 가리키고 스키마를 정의한 후 표준 SQL을 사용하여 쿼리를 시작하면 됨
  • AWS Glue Data Catalog와 즉시 통합됨
    • 다양한 서비스에 걸쳐 통합된 메타데이터 리포지토리를 생성하고, 데이터 원본을 크롤링하여 스키마를 검색하고 카탈로그를 신규 및 수정된 테이블 정의와 파티션 정의로 채우며, 스키마 버전을 관리할 수 있음
  • 기능
    • 표준 SQL을 사용한 간편한 쿼리: 오픈 소스 분산 SQL 쿼리 엔진(Presto) 사용
    • 쿼리당 비용 지불: 실행한 쿼리에 대한 비용 지불(각 쿼리에서 스캔한 데이터 양에 따라 요금 부과)
    • 빠른 성능: 자동으로 쿼리를 병렬로 실행, 대규모 데이터 세트에서도 빠른 결과 얻음
    • 기계학습: SQL 쿼리에서 SageMaker 기계학습 모델을 호출하여 추론 실행 가능

등.. 많은 기능이 있다! 그러면 이러한 좋은 서비스 Athena를 사용해볼까?


aws workshop을 따라 가는 중..

  • CloudFormation

CloudFormation(버지니아 북부)를 통해 밑바탕을 깔자.

(template)

더보기

# Purpose:  Creates Athena Workgroups, Named Queries, IAM Users via AWS CloudFormation
#===============================================================================
AWSTemplateFormatVersion: "2010-09-09"
Description: |
  Athena Immersion Day - Creates Athena Workgroups, Named Queries, IAM Users

Resources:
  AthenaWorkShopBucket:
    Type: "AWS::S3::Bucket"
    Properties: 
      BucketName: !Join [ "-", ["athena-workshop", Ref: "AWS::AccountId"]] 

  workgroupA:
    Type: AWS::Athena::WorkGroup
    Properties:       
      Name: workgroupA
      RecursiveDeleteOption: true
      WorkGroupConfiguration:
        PublishCloudWatchMetricsEnabled: true
        ResultConfiguration:
          OutputLocation: !Join [ "", ["s3://" , Ref: AthenaWorkShopBucket, "/"]]

  workgroupB:
    Type: AWS::Athena::WorkGroup
    Properties:       
      Name: workgroupB
      RecursiveDeleteOption: true

  
  workgroupIcebergpreview:
    Type: AWS::Athena::WorkGroup
    Properties:       
      Name: AmazonAthenaIcebergPreview
      RecursiveDeleteOption: true
      WorkGroupConfiguration:
        EnforceWorkGroupConfiguration: true
        EngineVersion:
         SelectedEngineVersion: Athena engine version 2       
        PublishCloudWatchMetricsEnabled: true
        ResultConfiguration:
          OutputLocation: !Join [ "", ["s3://" , Ref: AthenaWorkShopBucket, "/"]]

  amazonreviewstsv:
    Type: AWS::Athena::NamedQuery
    Properties:
      Database: "default"
      Description: "Create table amazon_reviews_tsv"
      Name: "Athena_create_amazon_reviews_tsv"
      QueryString: |
                    CREATE EXTERNAL TABLE amazon_reviews_tsv (
                    marketplace string, 
                    customer_id string, 
                    review_id string, 
                    product_id string, 
                    product_parent string, 
                    product_title string, 
                    product_category string, 
                    star_rating int, 
                    helpful_votes int, 
                    total_votes int, 
                    vine string, 
                    verified_purchase string, 
                    review_headline string, 
                    review_body string, 
                    review_date date,
                    year int)
                    ROW FORMAT DELIMITED
                    FIELDS TERMINATED BY '\t'
                    ESCAPED BY '\\'
                    LINES TERMINATED BY '\n'
                    LOCATION
                    's3://amazon-reviews-pds/tsv/'
                    TBLPROPERTIES ("skip.header.line.count"="1");  

  amazonreviewsparquet:
    Type: AWS::Athena::NamedQuery
    Properties:
      Database: "default"
      Description: "Create table amazon_reviews_parquet"
      Name: "Athena_create_amazon_reviews_parquet"
      QueryString: |
                    CREATE EXTERNAL TABLE amazon_reviews_parquet(
                    marketplace string, 
                    customer_id string, 
                    review_id string, 
                    product_id string, 
                    product_parent string, 
                    product_title string, 
                    star_rating int, 
                    helpful_votes int, 
                    total_votes int, 
                    vine string, 
                    verified_purchase string, 
                    review_headline string, 
                    review_body string, 
                    review_date bigint, 
                    year int)
                    PARTITIONED BY (product_category string)
                    ROW FORMAT SERDE 
                    'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe' 
                    STORED AS INPUTFORMAT 
                    'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat' 
                    OUTPUTFORMAT 
                    'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
                    LOCATION
                    's3://amazon-reviews-pds/parquet/'; 

                    /* Next we will load the partitions for this table */
                    MSCK REPAIR TABLE amazon_reviews_parquet;

                    /* Check the partitions */
                    SHOW PARTITIONS amazon_reviews_parquet;

  qryamazonreviewstsv:
    Type: AWS::Athena::NamedQuery
    Properties:
      Database: "default"
      Description: "Reviews Ratings table amazon_reviews_tsv"
      Name: "Athena_compare_reviews"
      QueryString: |
                    /* Let's try to find the products and their corresponding category by number of reviews and avg star rating */
                    SELECT product_id, product_category, product_title, count(*) as num_reviews, avg(star_rating) as avg_stars
                    FROM amazon_reviews_tsv 
                    GROUP BY 1, 2, 3
                    ORDER BY 4 DESC
                    limit 10;

                    /* Let's try to find the products and their corresponding category by number of reviews and avg star rating on parquet table */
                    SELECT product_id, product_category, product_title, count(*) as num_reviews, avg(star_rating) as avg_stars
                    FROM amazon_reviews_parquet 
                    GROUP BY 1, 2, 3
                    ORDER BY 4 DESC
                    limit 10;
                    
                    /* Let's try to find the products by number of reviews and avg star rating in Mobile_Apps category */
                    SELECT product_id, product_title, count(*) as num_reviews, avg(star_rating) as avg_stars
                    FROM amazon_reviews_tsv where product_category='Mobile_Apps'
                    GROUP BY 1, 2
                    ORDER BY 3 DESC
                    limit 10;

                    /* Let's try to find the products by number of reviews and avg star rating in Mobile_Apps category */
                    SELECT product_id, product_title, count(*) as num_reviews, avg(star_rating) as avg_stars
                    FROM amazon_reviews_parquet where product_category='Mobile_Apps'
                    GROUP BY 1, 2
                    ORDER BY 3 DESC
                    limit 10;

  TopReviewedStarRatedProductsv:
    Type: AWS::Athena::NamedQuery
    Properties:
      Database: "default"
      Description: "Create View TopRatedProducts"
      Name: "Athena_create_view_top_rated"
      QueryString: |
                    CREATE view topratedproducts AS
                    SELECT product_category,
                            product_id,
                            product_title,
                            count(*) count_reviews
                    FROM amazon_reviews_parquet
                    WHERE star_rating=5
                    GROUP BY  1, 2, 3
                    ORDER BY  4 desc;

                    Select * from topratedproducts limit 10;

  ctas:
    Type: AWS::Athena::NamedQuery
    Properties:
      Database: "default"
      Description: "CTAS Amazon Reviews by Marketplace"
      Name: "Athena_ctas_reviews"
      QueryString: |
                    CREATE TABLE amazon_reviews_by_marketplace
                    WITH ( format='PARQUET', parquet_compression = 'SNAPPY', partitioned_by = ARRAY['marketplace', 'year'], 
                    external_location =   's3://<<Athena-WorkShop-Bucket>>/athena-ctas-insert-into/') AS
                    SELECT customer_id,
                            review_id,
                            product_id,
                            product_parent,
                            product_title,
                            product_category,
                            star_rating,
                            helpful_votes,
                            total_votes,
                            verified_purchase,
                            review_headline,
                            review_body,
                            review_date,
                            marketplace,
                            year(review_date) AS year
                    FROM amazon_reviews_tsv
                    WHERE "$path" LIKE '%tsv.gz';

                    /* Let's try to find the products and their corresponding category by number of reviews and avg star rating for US marketplace in year 2015 */

                    SELECT product_id,
                            product_category,
                            product_title,
                            count(*) AS num_reviews,
                            avg(star_rating) AS avg_stars
                    FROM amazon_reviews_by_marketplace
                    WHERE marketplace='US'
                    AND year=2015
                    GROUP BY  1, 2, 3
                    ORDER BY  4 DESC limit 10; 

  comparereviews:
    Type: AWS::Athena::NamedQuery
    Properties:
      Database: "default"
      Description: "Compare query performance"
      Name: "Athena_compare_reviews_marketplace"
      QueryString: |
                    SELECT product_id, COUNT(*) FROM amazon_reviews_by_marketplace
                    WHERE marketplace='US' AND year = 2013
                    GROUP BY 1 ORDER BY 2 DESC LIMIT 10;

                    SELECT product_id, COUNT(*) FROM amazon_reviews_parquet
                    WHERE marketplace='US' AND year = 2013
                    GROUP BY 1 ORDER BY 2 DESC LIMIT 10;

                    SELECT product_id, COUNT(*) FROM amazon_reviews_tsv
                    WHERE marketplace='US' AND extract(year from review_date) = 2013
                    GROUP BY 1 ORDER BY 2 DESC LIMIT 10;
  
  flights:
    Type: AWS::Athena::NamedQuery
    Properties:
      Database: "default"
      Description: "Top 10 routes delayed by more than 1 hour"
      Name: "Athena_flight_delay_60"
      QueryString: |
                    SELECT origin, dest, count(*) as delays
                    FROM flight_delay_parquet
                    WHERE depdelayminutes > 60
                    GROUP BY origin, dest
                    ORDER BY 3 DESC
                    LIMIT 10;

  LabsUserPassword:
    Type: "AWS::SecretsManager::Secret"
    Properties:
      Description: Athena Workshop User Password
      Name: "/athenaworkshopuser/password"
      GenerateSecretString:
        SecretStringTemplate: '{}'
        GenerateStringKey: "password"
        PasswordLength: 30

  userA:
    Type: "AWS::IAM::User"
    Properties: 
      Path: "/"
      LoginProfile: 
        Password: !Sub '{{resolve:secretsmanager:${LabsUserPassword}:SecretString:password}}'
        PasswordResetRequired: false
      Policies: 
        - PolicyName: "Athena-WorkgroupA-Policy"
          PolicyDocument: 
            Version: '2012-10-17'
            Statement:
            - Effect: Allow
              Action:
              - s3:Put*
              - s3:Get*
              - s3:List*
              - glue:*
              - cloudwatch:*
              - athena:ListNamedQueries
              - athena:ListWorkGroups
              - athena:GetExecutionEngine
              - athena:GetExecutionEngines
              - athena:GetNamespace
              - athena:GetCatalogs
              - athena:GetNamespaces
              - athena:GetTables
              - athena:GetTable
              Resource: "*"
            - Effect: Allow
              Action:
              - athena:StartQueryExecution
              - athena:GetQueryResults
              - athena:DeleteNamedQuery
              - athena:GetNamedQuery
              - athena:ListQueryExecutions
              - athena:StopQueryExecution
              - athena:GetQueryResultsStream
              - athena:ListNamedQueries
              - athena:CreateNamedQuery
              - athena:GetQueryExecution
              - athena:BatchGetNamedQuery
              - athena:BatchGetQueryExecution
              Resource:
              - !Join [ "", ["arn:aws:athena:us-east-1:", Ref: "AWS::AccountId" , ":workgroup/workgroupA"]]              
            - Effect: Allow
              Action:
              - athena:DeleteWorkGroup
              - athena:UpdateWorkGroup
              - athena:GetWorkGroup
              - athena:CreateWorkGroup
              Resource:
              - !Join [ "", ["arn:aws:athena:us-east-1:", Ref: "AWS::AccountId" , ":workgroup/workgroupA"]]
      UserName: "userA"

  userB:
    Type: "AWS::IAM::User"
    Properties: 
      Path: "/"
      LoginProfile: 
        Password: !Sub '{{resolve:secretsmanager:${LabsUserPassword}:SecretString:password}}'
        PasswordResetRequired: false
      Policies: 
        - PolicyName: "Athena-WorkgroupA-Policy"
          PolicyDocument: 
            Version: '2012-10-17'
            Statement:
            - Effect: Allow
              Action:
              - s3:Put*
              - s3:Get*
              - s3:List*
              - glue:*
              - athena:ListWorkGroups
              - athena:GetExecutionEngine
              - athena:GetExecutionEngines
              - athena:GetNamespace
              - athena:GetCatalogs
              - athena:GetNamespaces
              - athena:GetTables
              - athena:GetTable
              Resource: "*"
            - Effect: Allow
              Action:
              - athena:StartQueryExecution
              - athena:GetQueryResults
              - athena:DeleteNamedQuery
              - athena:GetNamedQuery
              - athena:ListQueryExecutions
              - athena:StopQueryExecution
              - athena:GetQueryResultsStream
              - athena:ListNamedQueries
              - athena:CreateNamedQuery
              - athena:GetQueryExecution
              - athena:BatchGetNamedQuery
              - athena:BatchGetQueryExecution
              Resource:
              - !Join [ "", ["arn:aws:athena:us-east-1:", Ref: "AWS::AccountId" , ":workgroup/workgroupB"]]              
            - Effect: Allow
              Action:
              - athena:DeleteWorkGroup
              - athena:UpdateWorkGroup
              - athena:GetWorkGroup
              - athena:CreateWorkGroup
              Resource:
              - !Join [ "", ["arn:aws:athena:us-east-1:", Ref: "AWS::AccountId" , ":workgroup/workgroupB"]]
      UserName: "userB"  
    
Outputs:
  S3Bucket:
    Description: S3 bucket
    Value: !Ref AthenaWorkShopBucket

  ConsoleLogin:
    Description: LoginUrl
    Value: !Join ["", ["https://", Ref: "AWS::AccountId" , ".signin.aws.amazon.com/console"]]

  ConsolePassword:
    Description: AWS Secrets URL to find the generated password for User A and User B
    Value: !Sub 'https://console.aws.amazon.com/secretsmanager/home?region=${AWS::Region}#/secret?name=/athenaworkshopuser/password'  

스택의 리소스나 이벤트를 확인해보면 생성 결과 → 쿼리문 파일들 + Athena 작업그룹 + IAM User A/B + S3 버킷 정도?

사실 이번 포스팅에서 User를 나누는 의미는 없다. 나중에 이어서 실습할 내용들에 있어서 필요한 유저들..

 

  • Dataset 확인

데이터셋은 workshop에서 제공해주는 s3 버킷에 담겨있다.

더보기

https://console.aws.amazon.com/s3/home?region=us-east-1&bucket=amazon-reviews-pds

 

  • Athena 기본 설정

작업 그룹에 대해 CloudWatch 지표를 활성화 한다.

CloudFormation을 통해 만들어진 작업 그룹들이다. 현재는 primary 기준으로 진행할 것!

(primary → 편집)

쿼리들을 실행하기 전에 먼저 Athena 설정에서 S3 버킷을 지정해줘야한다.

쿼리 편집기 → 설정보기

CloudFormation에서 생성했던 s3로 지정해준다.

 

  • Athena - 테이블 생성 및 쿼리 실행

default 데이터베이스에서 실행할 예정. 만약 데이터베이스가 없다면 'CREATE database default' 실행.

저장된 쿼리에서 Athena_create_amazon_review_tsv를 클릭후 편집기에 해당 쿼리문이 열리면 실행하자.

위 이미지와 같이 테이블이 만들어지면 성공

 

다음으론 저장된 쿼리에서 Athena_create_amazon_reviews_parquet를 클릭.

쿼리문에서 한 번에 하나의 쿼리를 선택하고 실행하자.(주석 기준으로 나눠서 드래그 후 실행)

딱 여기까지.. 다음 쿼리문들은 파티셔닝 관련 명령이다.

테이블 하나 더 생김!

 

  • Athena - Partitioning Data

데이터를 분할하여 각 쿼리에서 스캔하는 데이터의 양을 제한하여 성능을 개선하고 비용을 절감할 수있음.

모든 키로 데이터를 분할할 수 있다. (보통 시간을 기준으로 데이터를 분할)

- 참고: https://docs.aws.amazon.com/ko_kr/athena/latest/ug/partitions.html

 

그럼 amazon_reviews_parquet 테이블을 기준으로 파티셔닝을 해보자.

위와 같이 두 명령을 각각 실행해보자.

그러면 파티션 추가 완료.

 

  • Athena - 테이블 간 성능 테스트

저장된 쿼리에서 Athena_compare_reviews를 열자.

쿼리를 하나씩 선택해서 실행해 각 결과를 비교해보자.(스캔한 데이터 양과 소요 시간 차이 확인)

 

파티셔닝 전의 테이블: amazon_reviews_tsv

평균 리뷰별 상위 10개 제픔
모바일 앱 카테고리의 평균 리뷰 기준 상위 10개 제품

파티셔닝 후의 테이블: amazon_reviews_parquet

평균 리뷰별 상위 10개 제품
모바일 앱 카테고리의 평균 리뷰 기준 상위 10개 제품

 

확실히 파티셔닝 후의 테이블의 성능이 뛰어나다는 것을 확인할 수 있다.

 

 

기본적인 SQL문(Select From 뭐시기... 이런 것)들은 알고 있는데..

파티셔닝 관련한 명령은 알지 못해서.. 간단히 정리해본다.

 

파티션을 사용하는 테이블을 생성하려면 CREATE TABLE 문에 PARTITIONED BY 절을 사용

기본 문법:

CREATE EXTERNAL TABLE users (
first string,
last string,
username string
)
PARTITIONED BY (id string)
STORED AS parquet
LOCATION 's3://DOC-EXAMPLE-BUCKET/folder/'

테이블을 생성하고 쿼리를 위해 파티션에 데이터를 로드하는 명령

(참고로 parquet(파켓)은 Apache Hadoop 에코 시스템의 오픈소스 열 지향 데이터 저장 형식)

이후에 Hive 스타일 파티션의 경우 MSCK REPAIR TABLE을 실행(지금 했던 방식)

아니라면 ALTER TABLE ADD PARTITON을 사용해 파티션을 수동으로 추가

CREATE EXTERNAL TABLE amazon_reviews_parquet(
marketplace string, 
customer_id string, 
review_id string, 
product_id string, 
product_parent string, 
product_title string, 
star_rating int, 
helpful_votes int, 
total_votes int, 
vine string, 
verified_purchase string, 
review_headline string, 
review_body string, 
review_date bigint, 
year int)
PARTITIONED BY (product_category string)
ROW FORMAT SERDE 
'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe' 
STORED AS INPUTFORMAT 
'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat' 
OUTPUTFORMAT 
'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION
's3://amazon-reviews-pds/parquet/'; 

/* Next we will load the partitions for this table */
MSCK REPAIR TABLE amazon_reviews_parquet;

/* Check the partitions */
SHOW PARTITIONS amazon_reviews_parquet;

위의 코드블럭은 실습 진행하면서 사용했던 쿼리문이다.(어렵다 어려워...)

 

 

 

❗ 다음 포스팅에선 Glue와 어떤 식으로 통합해 사용하는지 알아보자 ❗

'Cloud > AWS' 카테고리의 다른 글

[AWS] Amazon Athena 사용법 -3  (0) 2022.11.20
[AWS] Amazon Athena 사용법 -2 + Glue(Crawler) 활용  (1) 2022.11.19
[AWS] Step Functions  (0) 2022.11.18
[AWS] Spot Fleet  (0) 2022.11.16
[AWS] Lambda와 RDS Proxy  (0) 2022.11.14

Step Fuctions 작동 원리

  • Step Functions 상태머신: 실행될 워크플로우를 선언적으로 정의하는 그래프
    • 하나의 워크플로우는 여러 개의 작업 단위로 구성될 수 있음
    • Action: Lambda  함수 호출, SNS로의 publish, ECS task 실행 등과 같은 다른 AWS 서비스를 활용하여 작업 수행
    • Flow: task들의 제어 흐름을 정의하는 7가지 상태
      • Choice: 조건문에 따른 분기를 정의
      • Parallel: 동일 입력에 대해 여러 작업을 병렬 처리
      • Map: 배열과 같은 Iterable 입력에 대해 task의 동적 병렬 처리를 수행
      • Pass: 디버깅 / Wait: 타이머 기능 / Success/Fail: 실행 성공 여부 확인
    • Action과 Flow의 조합으로 Start 상태부터 End 상태까지 다양한 워크플로우를 쉽게 정의
  • 모니터링: try - catch - finally 패턴을 통해 timeout, retry, error message 등을 한번에 관리 가능
  • 자동 스케일링, 고가용성 제공

 

Step Functions 개발 옵션

  • Workflow Studio:
    • GUI 인터페이스만으로 워크플로우 배포 가능
    • Production 환경에서 얘만 사용하면 유지보수에 어려움이 있을 수 있음
  •  IaC 프레임 워크 활용: AWS CDK, Terraform + 서버리스 개발용 라이브러리: AWS Chalice(파이썬)
    • 위의 상황은 디커플링된 상태: 애플리케이션과 인프라가 디커플링
    • 각 각에 대해 코드 수정을 해야하는 비효율성이 생길 수 있음
  • 애플리케이션과 인프라를 커플링 → 서버리스 프레임워크

 

개발 팁

  • 서비스 할당량
    • 소프트 할당량(별도의 요청으로 상한을 올릴 수 있음) / 하드 할당량(상한을 올릴 수 없음)
    • 자주 접할 수 있는 하드 할당량:
      • 페이로드의 최대 크기: 256KB 넘길 수 없음
        • 상태머신의 페이로드는 필수 정보, 상태 정보 등.. 
        • 이외의 데이터는 다른 서비스나 서버에 저장 후 불러와라
          • (S3, DynamoDB, Aurora, Kinesis Data Streams, SQS)
      • 실행 기록의 최대 이벤트 개수: 최대 25000개 까지 기록(넘어가면 실행 즉시 실패)
        • 이벤트 개수 줄이기 → 비추천
        • Lambda 함수를 통해 상태머신 재실행(AWS 측 추천)
          • 워크플로우 과정 중 현재 실행의 실행기록이 최대치에 근접할 때, 상태머신 재실행 하는 판단 부분 삽입
          • 해당 진행사항을 저장하는 부분도 필요함: 재실행할 때 정보가 변하면 안됨!
  • 동적 병렬처리
    • MaxConcurrency: 동시 실행 가능한 작업자의 최대 개수
    • Map 상태가 최대 동시성 값에 따라 병렬로 실행할 작업자의 개수를 결정
    • 문제: 작업의 동적할당을 위해선 작업을 배열의 형태로 만들어야함. 근데 페이로드 안에 다 있어야하니까.. 최대 페이로드 크기에 걸릴 수 있음
      • 전체 작업을 처리 가능한 크기의 작업으로 분할 후 반복실행을 통해 순차적으로 처리 
  • 외부 서비스 통합: Activity Worker 패턴 이용
    •  동기적으로 처리하기 위함..
    • Activity: 상태머신의 진행을 비동기적으로 바꾸기 위함
      • 상태머신이 Activity 대기 상태로 들어가면 해당 Activity 하위에 새로운 작업을 생성 후 실행을 잠시 정지 시킴
      • AWS SDK를 활용해 GetActivityTask 호출:
        • 지정한 Activity에 처리되지 않은 작업이 있으면 해당 작업을 할당받음과 동시에 작업의 입력값과 토큰을 받음
        • 이후 전달받은 입력값으로 완료되면 SendTaskSuccess를 이용해 작업의 성공을 알림
      • 생성한 작업이 완료되면 대기 상태 해체 후 실행 재개

 

 

 

❗ 솔직히 AWS 내에서 서버리스로 다 구성하려면... 이거 활용하면 좋을듯!!! ❗

'Cloud > AWS' 카테고리의 다른 글

[AWS] Amazon Athena 사용법 -2 + Glue(Crawler) 활용  (1) 2022.11.19
[AWS] Amazon Athena 사용법 -1  (1) 2022.11.19
[AWS] Spot Fleet  (0) 2022.11.16
[AWS] Lambda와 RDS Proxy  (0) 2022.11.14
[AWS] EC2 인스턴스 자동 중지 및 시작  (0) 2022.11.13

GPU 인스턴스를 사용할 때 스팟 인스턴스를 많이들 활용한다고 한다. 비용 때문이다!

근데, GPU 인스턴스의 경우 GPU 리소스의 부족 현상이 있을 수 있음...😅


Spot Fleet

  • 사용자가 지정한 기준에 따라 시작되는 스팟 인스턴스의 집합(선택적으로 온디맨드 설정 가능)
  • 요청 유형
    • request: 원하는 용량을 얻기 위한 비동기식 일회성 요청(목표 용량 유지 비활성화)
    • maintain: 원하는 용량을 얻기 위한 비동기식 요청, 중단된 모든 스팟 인스턴스를 자동으로 보충해 용량 유지(목표용량 유지 활성화)

  • 인터럽트 방식: 스팟 서비스가 중단된 스팟 인스턴스 처리 방식(종료 / 중지 / 최대 절전 모드(수면))
  • 용량 재조정:
    • 시작 전용: 기존 스팟 인스턴스에 대해 재조정 알림이 전송될 때 대체 스팟 인스턴스 시작
      • 이전 인스턴스를 종료하거나 실행 중인 상태로 두는 건 사용자의 선택
    • 종료 전 시작: 새로운 대체 스팟 인스턴스가 시작된 후 재조정 알림을 받는 스팟 인스턴스 종료(termination-delay)
      • 인스턴스 종료 절차가 완료되는데 걸리는 시간 예측 가능할 때 사용

  • 할당 방식
    • 가격 용량 최적화(priceCapacityOptimized): 가용성이 가장 높은 풀 안에서 가장 저렴한 가격
      • 여기서 말하는 가용성은 중단 가능성이 가장 낮음을 말한다
    • 용량 최적화(capacityOptimized): 가용성이 가장 높은 풀 식별, 유형에 대한 우선 순위 설정 가능
    • 최저 가격(lowestPrice): 사용 가능한 용량이 있는 최저 가격 풀에서
    • 모든 풀에서 다각화(diversified): 모든 풀에 분산
  • 단점
    • 아무래도 스팟 인스턴스다 보니... 언제 중단될 지 모른다. 종료 2분 전 경고 알림
    • maintain이라는 유형이 있긴 하지만.. GPU 인스턴스의 경우 말이 다르다! GPU 리소스에 대한 수요가 높아 부족할 수도 있기 때문이다.

 

위의 단점을 피하기 위해 스팟 플릿에 대한 모니터링은 필수다!

EventBridge 서비스를 활용해 알림을 받을 수 있게 규칙을 설정해두는 걸 추천!

해당 이벤트 패턴에 Spot Fleet 알림 받는 소스를 제공해준다!

알림 대상을 SNS 주제로 설정해도 괜찮고 다른 여러 서비스들이 있으니 잘 활용하면 좋을 듯:)

CloudWatch로 트리거 잡아 Lambda로 설정해도 괜찮은데.. 귀찮잖아~

 

 

'Cloud > AWS' 카테고리의 다른 글

[AWS] Amazon Athena 사용법 -1  (1) 2022.11.19
[AWS] Step Functions  (0) 2022.11.18
[AWS] Lambda와 RDS Proxy  (0) 2022.11.14
[AWS] EC2 인스턴스 자동 중지 및 시작  (0) 2022.11.13
[AWS] GPU 인스턴스 Spot Fleet  (0) 2022.11.12

일단은 Proxy가 정확하게 뭔지 잘 몰라서 해당 내용 부터 살펴 본다면,

Proxy

'대리', '대신' 의 뜻, 주로 보안 상의 문제를 방지하기 위해 직접 통신하지 않고 중계자를 거친다는 개념

여기서의 중계자가 'Proxy Server'

클라이언트가 프록시 서버에 요청한 내용을 서버에 캐시로 저장해두면, 전송 시간을 절약할 수 있고, 특정 사이트는 접근 불가능하도록 제한을 걸 수도 있다. → Forward Proxy

 

클라이언트가 바로 서버에 데이터를 요청해 받을 수 있지만, DB가 노출될 수 있는 위험이 존재한다. 중간에 프록시 서버를 두고 내부망을 보호하는 역할을 할 수도 있다. → Reverse Proxy

 

RDS Proxy

  • Connection Pooling
    • 커넥션을 열고 닫으며 많은 커넥션을 동시에 열린 상태로 유지하는 데 관련된 오버헤드를 줄이는 최적화 기능
    • connection multiplexing: 커넥션 재사용(하나의 DB 연결을 사용해 한 트랜잭션에 대한 모든 작업 수행)
  • 데이터베이스 장애조치(failover)와 같은 오류 시나리오 동안 애플리케이션 가용성 개선
    • 장애 조치 동안 애플리케이션 연결 유지
  • TLS/SSL 및 IAM을 포함한 RDS 보안 기능을 사용해 애플리케이션 코드에서 연결에 대한 자격 증명 불필요
    • AWS Secrets Manager와 통합되어 하드 코딩할 필요가 없음(DB 자격 증명을 중앙에서 관리)
  • CloudWatch 메트릭 및 로깅
    • 주요 메트릭: ClientConnections, QueryRequests, DatabaseConnections
    • 로그 그룹 내에서 모니터링: /aws/rds/proxy/[proxy-name]

 

VPC 생성

cloudformation으로 생성

더보기

AWSTemplateFormatVersion: "2010-09-09"
Description: 'Cloudformation template to create VPC for workshop (Optimize Serverless Application on AWS)'
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: serverless-app
  
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: serverless-app-igw

  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: ap-northeast-2a
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: lambda-subnet-a

  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: ap-northeast-2c
      CidrBlock: 10.0.2.0/24
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: lambda-subnet-c

  PrivateSubnet3:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: ap-northeast-2a
      CidrBlock: 10.0.10.0/24
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: rds-subnet-a

  PrivateSubnet4:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: ap-northeast-2c
      CidrBlock: 10.0.20.0/24
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: rds-subnet-c

  PrivateSubnet5:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: ap-northeast-2a
      CidrBlock: 10.0.100.0/24
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: secret-subnet-a

  PrivateSubnet6:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: ap-northeast-2c
      CidrBlock: 10.0.200.0/24
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: secret-subnet-c

  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: ap-northeast-2a
      CidrBlock: 10.0.0.0/24
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: cloud9-subnet-a

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: serverless-app-routes

  DefaultPublicRoute:
    Type: AWS::EC2::Route
    DependsOn: InternetGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet1

Outputs:
  VPC:
    Description: serverless-app-vpc
    Value: !Ref VPC

보안 그룹 생성

  • lambda-sg
  • rds-sg: MySQL / lambda-sg

 

RDS 생성

  • 서브넷 그룹 생성: 10.0.10.0/24(a az), 10.0.20.0/24 (c az)
  • DB 생성:
    • MySQL 5.7.33 버전
    • db.m5.large / gp2 20GB
    • VPC쪽 위에서 생성했던 것들..

 

Lambda 생성

  • python 3.8
  • VPC 활성화: 10.0.1.0/24, 10.0.2.0/24 /  lambda-sg
  • 코드
더보기
import json
import pymysql

def lambda_handler(event, context):
    db = pymysql.connect(
        host='YOUR RDS ENDPOINT', 
        user='YOUR DATABASE MASTER USERNAME', 
        password='YOUR MASTER PASSWORD'
        )

    cursor = db.cursor()
    
    cursor.execute("select now()")
    result = cursor.fetchone()

    db.commit()
    db.close()
    
    return {
        'statusCode': 200,
        'body': json.dumps(result[0].isoformat())
    }

현재는 db 연결 테스트를 위한 작업으로 로그인이 하드코딩으로 이루어져 있다. 이후에 RDS Proxy를 통해 변경할 예정

 

Lambda Layer 추가

pymysql 패키지를 추가해줘야...

 

그 후에 lambda test를 돌려보자("statusCode: 200"이면 성공한 것)

 

API Gateway 구성

  • REST API / 새 api
  • 리소스 - 작업 / GET 추가
    • lambda func: 위에서 설정했던 lambda 이름
  • 리소스 - 작업 / API 배포: 새 스테이지 - 이름

URL 클릭해보면 아까 lambda에서 테스트 했던 결과와 똑같이 나옴..

lambda 함수 개요로 다시 돌아가보면 api gateway가 트리거로 잡혀있다.


https://github.com/aws-samples/the-evolution-of-aws-serverless-applications/blob/main/module3/README.md

이제 RDS Proxy를 적용해보자!

 

AWS Secrets Manager 구성

암호는 RDS 생성 시 넣어줬던 비밀번호로 설정해주면 된다.

자동 교체의 경우는 비활성화로 진행한다.(테스트 용이니까)

 

Secrets Manager를 사용하면 기본적으로 퍼블릭 통신을 통해 DB 크리덴셜을 가져오지만, VPC Endpoints를 사용하면 프라이빗 엔드포인트를 통해 VPC 내의 리소스가 직접 액세스 할 수 있음!

 

VPC로 돌아가 생성되었던 VPC 선택 후

  • 작업 → DNS 호스트 이름 편집 활성화

VPC Endpoints 설정에는 보안 그룹이 필요하다. Secrets Manager의 경우 Lambda에 접근 가능해야함..

  • 보안그룹: secret-sg / HTTPS - lambda-sg

VPC Endpoints 설정

  • AWS 서비스 - 위에서 설정했던 Secrets Manager
  • VPC: 위에서 생성했던 대로.. 서브넷 (secret-subnet-a/c), 보안그룹: secret-sg

 

RDS Proxy 구성

서버리스에서 사실 RDS를 사용하기엔 어려움이 있다.

(서버리스 아키텍처를 기반으로 구축된 애플리케이션은 DB에 다수의 커넥션을 만들어 max_connections 옵션을 초과하는 에러가 발생하거나 빠른 속도로 DB 커넥션을 여닫아 과도하게 메모리와 컴퓨팅 리소스를 소진할 수도 있음.)

RDS Proxy를 사용할 경우 애플리케이션과 DB 사이의 연결을 풀링하고 공유가 가능해 조금이나마 도움이 될 수 있음!

따라서 lambda와 rds 커넥션을 하고 싶다면 Proxy를 활용해라

  • MySQL
  • 위에서 생성했던 rds, secrets manager, 서브넷은 rds-subnet-a/c 확인 후 나머지 제거
  • 보안그룹 rds-sg 추가

 

Lambda 함수 변경

구성 → 권한 → IAM Role 수정

해당 Secrets Manager에 대한 권한을 부여한다.

그 후에 코드 변경

더보기
import json
import pymysql
import boto3
import base64
import time
from botocore.exceptions import ClientError

secret_name = "serverless-app-rds-secret"
region_name = "ap-northeast-2"

def get_secret():    
    session = boto3.session.Session()
    client = session.client(
        service_name = 'secretsmanager',
        region_name = region_name
    )

    get_secret_value_response = client.get_secret_value(
        SecretId=secret_name
    )

    if 'SecretString' in get_secret_value_response:
        secret = get_secret_value_response['SecretString']
        return secret
    else:
        decoded_binary_secret = base64.b64decode(get_secret_value_response['SecretBinary'])
        return decoded_binary_secret

def lambda_handler(event, context):
    secret = get_secret()
    json_secret = json.loads(secret)

    db = pymysql.connect(
        host = 'YOUR RDS PROXY ENDPOINT',
        user = json_secret['username'], 
        password = json_secret['password']
        )

    cursor = db.cursor()
    
    cursor.execute("select now()")
    result = cursor.fetchone()

    db.commit()
    
    return {
        'statusCode': 200,
        'body': json.dumps(result[0].isoformat())
    }

해당 RDS Proxy 엔드포인트 쪽만 변경

 

이제 다 구성했으니 제대로 작동하는지 확인해보자.

  • API Gateway로 이동해 아까 생성했던 스테이지로 가서 Invoke URL 후 호출해보자
  • 연결하는데 시간이 초과한다는 에러가 나면 lambda 함수 제한 시간을 늘려주자..ㅠ
    • 이래서 서버리스는 서버리스끼리 사용하라는 듯..
    • 아니면 구성을 다시 한번 되집어 보자...(중간에 엇갈렸었음;;)

부하테스트

사실상 메인 테스트, 내가 하고 싶은 테스트를 진행해볼까 한다!

 

Cloud9 - 부하테스트 도구 Locust 구성

  • c5.24xlarge
  • VPC 변경- cloud9-subnet
pip3 install locust
locust -v

 

locustfile.py 파일 생성 후 코드 넣어주기(단순히 GET을 통한 테스트)

import time
from locust import HttpUser, task, between

class QuickstartUser(HttpUser):

    @task
    def hello_world(self):
        self.client.get("/")

 

터미널에 다음 명령어 입력해 실행하기

locust

 

web interface에서 편하게 진행하기 위해 ec2로 간다.

해당 cloud9 인스턴스에서 보안그룹을 변경해준다.

  • Custom TCP 8089 / Anywhere

http://public ip:8089를 입력해 Locust web interface에 접속한다.

  • Number of users: 동시에 실행하는 최대의 Locust user
  • Spawn rate: 초당 생성하는 Locust user
  • Host: 부하를 발생할 호스트 - 오늘의 테스트는 API Gateway Endpoint

 

1차 부하테스트

  • Number of users: 10000 / Spawn rate: 500 / Host: API Gateway Invoke URL

chart메뉴

Lambda 모니터링으로 돌아와 확인해보면, Burst Limit에 도달한 뒤 1분당 500씩 Concurrent executions이 증가하는 것을 볼 수 있다. 최초 스케일링 전 스로틀 발생했다가 Lambda가 스케일링 되면서 해소되는 것을 확인 할 수 있다.

(but, AWS에서 제공하는 기본 Concurrent executions 제한이 1000이여서 스케일링에 대한 부분 확인이 어려울 수 있음)

 

Lambda 코드 최적화

Lambda의 스로틀링을 회피해야 성능이 좋아질 듯?

그 방법에는

1) Lambda provisioned concurrency를 통해 지정한 갯수 만큼의 실행 환경을 구성해 두는 것

2) 동시성의 최소화를 위해 Lambda 함수의 실행 시간을 최적화 하는 것(얘는 모범 사례 중 하나)

 

지금 현재의 코드는 lambda_handler() 내에서 get_secret()을 통해 DB 크리덴셜 정보를 읽고 DB와 연결을 맺는 구조로 되어 있음. 이는 Lambda 가 호출될 때마다 고정된 값인 DB 크리덴셜을 읽고 새롭게 DB와 연결하는 구조라 비효율적..

 

이를 최적화 한다. 의 의미는 실행환경의 재사용성을 극대화 하는 것

현재 Lambda test를 해보면 실행시간이 약 700ms이다.

 

import json
import pymysql
import boto3
import base64

secret_name = "serverless-app-rds-secret"
region_name = "ap-northeast-2"

def get_secret():    
    session = boto3.session.Session()
    client = session.client(
        service_name = 'secretsmanager',
        region_name = region_name
    )

    get_secret_value_response = client.get_secret_value(
        SecretId=secret_name
    )

    if 'SecretString' in get_secret_value_response:
        secret = get_secret_value_response['SecretString']
        return secret
    else:
        decoded_binary_secret = base64.b64decode(get_secret_value_response['SecretBinary'])
        return decoded_binary_secret

secret = get_secret()
json_secret = json.loads(secret)

db = pymysql.connect(
    host = 'YOUR RDS PROXY ENDPOINT', 
    user = json_secret['username'], 
    password = json_secret['password']
    )

cursor = db.cursor()

def lambda_handler(event, context):
    cursor.execute("select now()")
    result = cursor.fetchone()

    db.commit()
    
    return {
        'statusCode': 200,
        'body': json.dumps(result[0].isoformat())
    }

위 코드와 같이 최적화 후에 테스트 해보면,

실행시간이 약 7ms로 최적화 된 것을 확인할 수 있음!!!

 

2차 부하테스트

Newtest 클릭!

위의 설정과 같게 돌려보자.

1차 테스트와 비교해보면 코드 최적화 이후 줄어든 Throttles와 Concurrent executions, Duration 등을 확인할 수 있다.

(Lambda 모니터링에서)

 

 

 

❗ 여기에 덧붙일만한 건 X-Ray로 모니터링 추적하는 기능 추가 정도...? 이는 다른 포스팅에 있으니.. 확인하시오!! ❗

'Cloud > AWS' 카테고리의 다른 글

[AWS] Step Functions  (0) 2022.11.18
[AWS] Spot Fleet  (0) 2022.11.16
[AWS] EC2 인스턴스 자동 중지 및 시작  (0) 2022.11.13
[AWS] GPU 인스턴스 Spot Fleet  (0) 2022.11.12
[AWS] GPU 인스턴스 유형(EC2)  (0) 2022.11.12

+ Recent posts