[AWS] 로그 데이터 수집 파이프라인 구축 with streamlit

sunnyjjang·2024년 10월 13일

AWS

목록 보기
21/21
post-thumbnail

LAB. Kinesis를 활용한 로그 데이터 수집 파이프라인 구축 : applog1

워크플로우
1. 역할 생성
2. API GateWay 생성
3. Kinesis Data Streams 생성
4. S3 버킷 생성
5. Data Firehose 생성
6. 웹서버 구성
7. TEST
8. Web APP TEST with Streamlit

1. 역할 생성

  • 역할 생성 - API Gateway가 Kenesis에 데이터를 저장할 수 있는 권한
    • 사용 사례 : API Gateway
    • 권한 : AmazonAPIGatewayPushToCloudWatchLogs
    • name : apigateway2kinesis-role
    • arn 정보 확인 : arn:aws:iam::11699…:role/apigateway2kinesis-role (보안 중요)
  • 역할 > 권한 추가
    • 정책 연결 : AmazonKinesisFullAccess

2. API GateWay 생성

  • API Gateway 생성
    • 유형 : REST API
    • name : applog
  • 리소스 생성
    • 리소스 경로 : /
    • 리소스 이름 : v1
  • 리소스 생성
    • 리소스 경로 : /v1/
    • 리소스 이름 : {stream-name}
  • 메소드 생성 - /v1/{stream-name}
    • 메서드 유형 : POST - 외부에서 요청받을 때 사용할 메서드
    • 통합유형 : AWS 서비스
    • AWS 리전 : ap-northeast-2
    • AWS 서비스 : Kinesis
    • HTTP 메서드 : POST - API Gateway가 Kinesis 로 보낼 때 적용할 메서드
    • 작업이름 : PutRecord (등록된 함수이기 때문에 대소문자 구분해서 정확하게 입력)
    • 실행 역할 : apigateway2kinesis-role의 arn 입력
    • 보안 인증 캐시 : Default
    • 콘텐츠 처리 : Default
    • 통합 시간 초과 : Default
  • 통합 요청 편집
    • /v1/{stream-name}/POST > 통합 요청 > 편집
    • URL 요청 헤더 파라미터 추가
      • 이름 : Content-Type
      • 다음에서 매핑됨 : 'application/x-amz-json-1.1'
    • 매핑 템플릿 추가
      • 콘텐츠 유형 : application/json
      • 템플릿 생성 : Empty
      • 템플릿 본문
        #set ( $enter = "
        ")
        #set($json = "$input.json('$')$enter")
        {
            "Data": "$util.base64Encode("$json")",
            "PartitionKey": "$input.params('X-Amzn-Trace-Id')",
            **"StreamName": "$input.params('stream-name')"**
        }
        • application/json 형식으로 요청이 들어오면 json 타입의 base64로 인코딩해서 전달. 이름은 ‘stream-name’의 변수값으로 설정
          "StreamName": "$input.params('stream-name')"
          API 요청 파라미터로 전달된 stream-name 값을 사용하여 스트림 이름을 설정함
  • API 배포
    • 새 스테이지 이름 : dev
    • 엔드포인트 확인
  • 메서드 재정의
    • 스테이지 > POST > 재정의 생성
    • CloudWatch Logs : 오류 및 정보로그
    • 조절 중 활성화
      • 요율 : 10000
      • 버스트: 5000
  • 설정 확인
    • 로깅 편집 : apigateway2kinesis-role의 ARN - API Gateway에서 만들어진 로그를 모두 CloudWatch에 남기기
    • apigateway2kinesis-role의 arn

3. Kinesis Data Streams 생성

  • 데이터 스트림 생성
    • 유형 : Kinesis Data Streams
    • name : applog1
    • 용량 모드 : 프로비저닝
    • 프로비저닝된 샤드 : 1

4. S3 버킷 생성

  • name : app-log-sunny
  • 퍼블릭 엑세스 차단 해제

5. Data Firehose 생성

  • Firehose 스트림 생성
    • 소스 및 대상 선택
      • 소스 : Amazon Kinesis Data Streams
      • 대상 : Amazon S3
    • 소스 설정
      • Kinesis 데이터 스트림 : arn:aws:kinesis:ap-northeast-2:1169...:stream/applog1
    • Firehose 스트림 이름 : applog1
    • 대상 설정 - 수집한 데이터를 저장할 위치로 버킷을 선택
      • S3 버킷 : s3://app-log-sunny
      • S3 버킷 접두사 : rawdata/applog1/
    • 버퍼 힌트, 압축, 파일 확장자 및 암호화
      • S3 버퍼 힌트 - 버퍼가 1M가 되거나 10초마다 한번 씩 S3에 sync를 맞춤
        • 버퍼크기 : 1MiB
        • 버퍼간격: 10초
      • 데이터 레코드 압축 - 활성화 되지 않음 (실제 현업에서는 압축해서 원시 데이터를 저장해 보호함)
      • S3 버킷 암호화 설정 사용

6. 웹서버 구성

  • name : manage-sg
    • vpc : lab-vpc
    • inbound : ssh - anywhere
  • 인스턴스 생성
    • name : manage-ec2
      • AMI : Amazon Linux
      • 인스턴스 유형 : t2.micro
      • key : lab-key
      • 네트워크
        • vpc : lab-vpc
        • subnet : public-ap-northeast-2a
        • 퍼블릭 IP : 활성화
        • 보안 그룹 : 기존 보안 그룹 선택 - manage-sg
      • 스토리지 : 30G, gp3

7. TEST

  • 원격 로그인 후 aws 프로파일 등록
    • AWS CLI 설치
      [ec2-user@ip-10-0-0-120 ~]$ sudo yum remove awscli
      [ec2-user@ip-10-0-0-120 ~]$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
      [ec2-user@ip-10-0-0-120 ~]$ unzip awscliv2.zip
      [ec2-user@ip-10-0-0-120 ~]$ sudo ./aws/install
      [ec2-user@ip-10-0-0-120 ~]$ aws --version
      aws-cli/2.17.36 Python/3.11.9 Linux/6.8.0-1012-aws exe/x86_64.ubuntu.24
      
      # 위에 명령어가 실행되지 않는 경우 path를 변경해주어야함
      [ec2-user@ip-10-0-0-120 ~]$ /usr/local/bin/aws --version
      aws-cli/2.17.60 Python/3.12.6 Linux/6.1.109-118.189.amzn2023.x86_64 exe/x86_64.amzn.2023
      [ec2-user@ip-10-0-0-120 ~]$ sudo mv /usr/local/bin/aws /usr/bin/
    • AWS 프로 파일 등록
      # AWS CLI에서 AWS 서비스에 접근할 수 있는 자격 증명을 설정
      [ec2-user@ip-10-0-0-126 ~]$ aws configure
      AWS Access Key ID [None]: AROA4MTW...
      AWS Secret Access Key [None]: U6/kl2L7Zni5CM4LU1...
      Default region name [None]: 리젼
      Default output format [None]: json
      
      # 인증된 자격 증명 불러오기
      [ec2-user@ip-10-0-0-126 ~]$ aws sts get-caller-identity
      {
          "UserId": "AROA4MTW...",
          "Account": "851725267032",
          "Arn": "arn:aws:sts::851...:assumed-role/manage-role/i-0c3ea16fa09b2404b"
      }
      
      # 버킷 확인
      [ec2-user@ip-10-0-0-126 ~]$ aws s3 ls
      2024-10-10 02:46:58 api-data-stream-bucket
  • 카프카 설치를 위한 java 설치
    
    [ec2-user@ip-10-0-0-126 ~]$ sudo yum search java-11
    [ec2-user@ip-10-0-0-126 ~]$ sudo yum install -y java-11-amazon-corretto
    [ec2-user@ip-10-0-0-126 ~]$ java --version
  • API Gateway > 스테이지
    • POST URL : https://k6fdj18hji.execute-api.ap-northeast-2.amazonaws.com/dev/v1/{stream-name}

      # Post json  
      [ec2-user@ip-10-0-0-126 ~]$ 
      curl -d "{\"value\":\"30\",\"type\":\"Tip 3\"}" \
         -H "Content-Type: application/json" \
         -X POST \
         https://nc5ay6eyhd.execute-api.ap-northeast-2.amazonaws.com/dev/v1/applog1
      
      # shell 에서 shard의 값을 확인 
      {"SequenceNumber":"49656616735127373407378311837199279454763322191160279042","ShardId":"shardId-000000000000"}
      # 스트림 이름 변경
      [ec2-user@ip-10-0-0-179 ~]$ SHARD_ITERATOR=$(aws kinesis get-shard-iterator --shard-id shardId-000000000000 --shard-iterator-type TRIM_HORIZON --stream-name applog1 --query 'ShardIterator')
      # 
      [ec2-user@ip-10-0-0-179 ~]$ aws kinesis get-records --shard-iterator $SHARD_ITERATOR
      {
          "Records": [],
          "NextShardIterator": "AAAAAAAAAAHnbzk8aRRtM0PHrBwbXwAQdNdBHTMjP6cYs+V3pgP6F2IOZUFWlDLvC5tf987Yje2wrfvBC50kMDz7fffsw/cVsXpxrl1WU78D7TH+QKDb6/n47j59UoWndURSejfKVTozqBt1r/Mw95FTu94Qf73byiGf3NjbAOVBXP2q4sPIeITPRX6p330qmI/uU6KmWWQ57sOd1g2rE03mbl9wOR1PyNaIpdOHnoK03xYBU2LvrQ==",
          "MillisBehindLatest": 0
      }

8. Web APP TEST with Streamlit

  • streamlit 설치

    [ec2-user@ip-10-0-0-179 ~]$ python3 --version 
    [ec2-user@ip-10-0-0-179 ~]$ sudo yum install pip -y 
    [ec2-user@ip-10-0-0-179 ~]$ pip install streamlit
    
    # 샘플 데모 앱 실행
    [ec2-user@ip-10-0-0-179 ~]$ streamlit hello
  • 인바운드 추가

    • name : manage-sg
    • inbound : 8501 - anywhere
  • 접속 확인

  • 실습을 위한 Streamlit 프로그램 작성 : event_click_log.py

    [ec2-user@ip-10-0-0-179 ~]$ cat > event_click_log.py
    
    import streamlit as st
    import requests
    import datetime
    import json
    
    button_style = """
        padding: 10px 20px; /* Adjust padding to change button size */
        font-size: 16px;   /* Adjust font size */
        width: 80%;
    """
    
    st.markdown(f'<style>{button_style}</style>', unsafe_allow_html=True)
    
    # Function to send a POST request based on the button clicked
    # 이곳의 URL을 API URL로 수정
    def send_post_request(event_params):
      **# 여기 수정**
      url = "https://nc5ay6eyhd.execute-api.ap-northeast-2.amazonaws.com/dev/v1/applog1"
      json_data = json.dumps(event_params)
      response = requests.post(url, data=json_data, headers={'Content-Type': 'application/json'})
    
    # 현재 날짜 및 시간을 얻습니다.
    current_time = datetime.datetime.now()
    
    # 현재 날짜 및 시간을 long 값으로 변환 (Unix 시간의 밀리초 버전)
    long_timestamp = int(current_time.timestamp() * 1000)
    print("Long 값:", long_timestamp)
    # 현재 시간을 원하는 형식으로 출력
    formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S")
    print("현재 시간 (형식화):", formatted_time)
    
    # Define your event data
    event_data = [
        {'event_name':'zb_app_install','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10310425','screen_name':'권한 요청','FALSE':'phone_id'} , 
        {'event_name':'view_item','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10442413','screen_name':'내정보','':'phone_id'} , 
        {'event_name':'zb_dialog_show','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10493118','screen_name':'내정보 번호변경','':'phone_id'} , 
        {'event_name':'zb_screen_list_click','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10465756','screen_name':'내정보 비밀번호변경','':'phone_id'} , 
        {'event_name':'oneroom_filter_results','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10420590','screen_name':'내정보 이름변경','':'phone_id'} , 
        {'event_name':'zb_app_install','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10238586','screen_name':'로그인','':'phone_id'} , 
        {'event_name':'screen_view','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10432617','screen_name':'로그인 비밀번호찾기','':'phone_id'} , 
        {'event_name':'zb_vr_loading','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10435194','screen_name':'로그인 이메일','':'phone_id'} , 
        {'event_name':'zb_view_contents','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10456187','screen_name':'로그인 이메일찾기','':'phone_id'} , 
        {'event_name':'user_engagement','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10440393','screen_name':'로그인 이메일확인','':'phone_id'} , 
        {'event_name':'zb_screen_click','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10478202','screen_name':'로그인 인증','':'phone_id'} , 
        {'event_name':'sign_up','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10446125','screen_name':'메인','':'phone_id'} , 
        {'event_name':'apt_filter_results','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10469918','screen_name':'문의하기','':'phone_id'} , 
        {'event_name':'session_start','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10171775','screen_name':'문자보내기','':'phone_id'} , 
        {'event_name':'view_item','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10450963','screen_name':'스플래시','':'phone_id'} , 
        {'event_name':'ecommerce_purchase','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10393346','screen_name':'아파트 검색','':'phone_id'} , 
        {'event_name':'add_to_wishlist','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10454369','screen_name':'아파트 교통정보상세','':'phone_id'} , 
        {'event_name':'first_open','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10281808','screen_name':'아파트 단지목록','':'phone_id'} , 
        {'event_name':'zb_screen_view','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10473864','screen_name':'아파트 리뷰등록','':'phone_id'} , 
        {'event_name':'zb_way_searching','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10455493','screen_name':'아파트 리뷰목록','':'phone_id'} , 
        {'event_name':'zb_vr_moving','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10409337','screen_name':'아파트 매물','':'phone_id'} , 
        {'event_name':'_exp_set','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10408885','screen_name':'아파트 매물내놓기','':'phone_id'} , 
        {'event_name':'gtm.load','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'9581516','screen_name':'아파트 매물목록','':'phone_id'} , 
        {'event_name':'add_to_cart','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10374993','screen_name':'아파트 매물상세','':'phone_id'} , 
        {'event_name':'view_search_results','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10341308','screen_name':'아파트 매물필터','':'phone_id'} , 
        {'event_name':'zb_screen_list_state','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10338845','screen_name':'아파트 면적유형상세','':'phone_id'} , 
        {'event_name':'zb_screen_map_click','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10273273','screen_name':'아파트 문의한매물','':'phone_id'} , 
        {'event_name':'zb_way_searching','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10310425','screen_name':'아파트 상세','':'phone_id'} , 
        {'event_name':'zb_vr_moving','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10442413','screen_name':'아파트 수정요청등록','':'phone_id'} , 
        {'event_name':'_exp_set','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10493118','screen_name':'아파트 시세변동필터','':'phone_id'} , 
        {'event_name':'gtm.load','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10465756','screen_name':'아파트 시세정보상세','':'phone_id'} , 
        {'event_name':'add_to_cart','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10420590','screen_name':'아파트 시세지도','':'phone_id'} , 
        {'event_name':'view_search_results','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10238586','screen_name':'아파트 시세필터','':'phone_id'} , 
        {'event_name':'zb_screen_list_state','gtmLongTime':long_timestamp,'base_dt':formatted_time,'item_id':'10432617','screen_name':'아파트 실거래이력','':'phone_id'} 
    ]
    
    # Number of columns in the grid
    num_columns = 4
    
    # Streamlit app title
    st.title("Event Data \n Post Request Button Grid")
    
    # Create a button grid
    for i in range(0, len(event_data), num_columns):
        button_row = event_data[i:i+num_columns]
        # col1, col2, col3, col4,col5,col6,col7,col8 = st.columns(num_columns)
        col1, col2, col3, col4 = st.columns(num_columns)
        for j, event_params in enumerate(button_row):
            with globals()[f"col{j+1}"]:
                if st.button(event_params['screen_name'], key=event_params['screen_name']):
                    send_post_request(event_params)
  • 프로그램 실행

    [ec2-user@ip-10-0-0-179 ~]$ streamlit run event_click_log.py
    
    ****Collecting usage statistics. To deactivate, set browser.gatherUsageStats to false.
    
      You can now view your Streamlit app in your browser.
    
      Local URL: http://localhost:8501
      Network URL: http://10.0.0.179:8501
      External URL: http://43.203.206.143:8501

  • 데이터 파이프 라인 확인

    • CloudWatch에서 확인 : apiGateway의 ID를 이용해서 확인
    • S3에서 확인
profile
지금 이 순간이 다시 넘겨볼 수 있는 한 페이지가 될 수 있게

0개의 댓글