인생은 고통의 연속

수제 FinOps - Lambda편 본문

개발스터디&모임/커뮤니티

수제 FinOps - Lambda편

gnidoc 2023. 7. 14. 04:49
    반응형

    https://www.meetup.com/ko-KR/awskrug/events/294199401/

     

    Login to Meetup | Meetup

    Not a Meetup member yet? Log in and find groups that host online or in person events and meet people in your local community who share your interests.

    www.meetup.com

    본 내용은 AWSKRUG #architecture 43번째 모임에서 발표했던 내용 중 일부을 글로 다시 작성한 내용입니다

     


     

    FinOps

    FinOps = Finance + DevOps

    FinOps란 용어의 정의는 위와 같죠
    어쨋든 지속적인 모니터링을 통해서 꾸준하게 비용을 최적화하는게 목적입니다

    대부분 처음부터 Cloud쓰기보단 On-Premise(IDC)을 사용하시다가 Cloud로 이전하신 경우가 많으실텐데
    이게 참 trade-off되는게 Cloud로 이전하면 절감된 인건비와 Cloud 이용 비용이 반비례하게 되죠

    그래서 사실 AWS(Cloud) 엔지니어의 역할은 적절한 아키텍처를 구성하면서도
    성능 저하없이 인프라 비용을 절감하는게 주된 역할이기도 합니다

    요즘이야 뭐 돈 때려박으면 다 잘 돌아가죠

    아 물론 datalake는 예외지만요

     

    수제 FinOps???

    그냥 별 생각없이 만든 제목인데 갑자기 수제햄버거가 생각나더라구요
    옛날에 이런 글을 봤던게 생각이 났습니다

    네 그렇습니다. 제가 말하는 수제란 "고급 수제"버거가 아닌 진짜 "수제" 버거인걸 의미합니다
    그냥 날 것(raw) 그 자체로 좋은 Tool을 쓴건 아니고 직접 한땀한땀 찾아서 요금 절감한 경험을 써봤습니다
    그래서 어떤 Tool을 만들고 그런건 아니고 겪었던 사례를 공유하는 내용입니다

     

    FinOps의 어려운점

    일단 좀 본격적으로 얘기하기 전에 좀 찡찡거리자면 어려운 점은 이런게 있네요

    • FinOps를 위한 (좋은/범용적인) Tool이 없다
    • 사용량이 적다고 EC2, RDS 인스턴스를 지울 순 없잖아요. 이중화 버려?
    • 사실 절감하기 위해서 지울 수 있는건 이미 다 지워버려서 replica(slave) 노드가 없...(이중화를 진짜 버렸...)
    • 퇴근할땐 개발용 서버를 stop하라는데 그럼 private IP가 바뀌어서 매일 출근할때마다 방화벽 신청을 다시 해야되는 상황이 생깁니다
    • 멀티 클라우드 / 하이브리드 클라우드를 지원하는 통합 View가 없다
    • 개발은 다른 사람이 했는데 비용 절감은 내가 해야되는... ㅠ
    • 담당하는 계정의 아키텍처에 대한 이해도가 필요
    • AWS는 cross account를 지원하기 때문에 연결되는 모든 계정에 대한 이해도가 어느정도는 필요
    • 무조건 고정적으로 시간당 과금되는 비용을 줄일 수 없는 서비스가 존재(RDS, NAT 등)
    • 돈 아끼면 제 통장으로 들어오나요?

    뭐 더 있지만 너무 찡찡거리면 듣는 사람도 짜증나니 여기까지만 하겠습니다
    그리고 가장 유명한 백종원 선생님의 말씀이 있죠

    주인이 아닌데 주인의식을 어떻게...?

    저도 뭐 일개 월급쟁이라 이렇게까지 아껴야되나 싶기도 한데
    서비스에 대한 주인의식이 없으면 FinOps는 쉽지 않죠

    암튼 FinOps에 대해선 각설하고 제가 겪었던 내용에 대해서 얘기해보겠습니다

     

    Lambda 사례

    1. 테이블 추천 기능에 대한 대략적인 배경

    제가 주로 하던 업무 중 하나가 datahub와 같은 사내용 메타데이터 조회 서비스 개발/운영이었습니다
    저희 그룹은 주로 hive를 사용하는데 관리하는 테이블이 워낙 많다보니 담당자 아니면 이 테이블이 어디에 조인되는지는 잘모릅니다
    문제는 hive는 RDB가 아니라서 PK(기본키), FK(외래키) 같은걸 관리할 수 없죠...
    물론 조인되는 테이블을 view table로 관리해서 잘 쓰면 되는거 아니냐?라고 한다면
    이미 관리해야되는 테이블이 30만개가 넘는데 view table을 추가하면 관리 포인트만 늘어나겠죠?
    그리고 원본 테이블에 변경이 있으면 그때마다 view table이 잘되는지도 봐야하는데
    이런걸 모두 관리하기가 쉽지 않습니다
    (data lineage로 한두개여야죠...)

    이렇다보니 보통 분석팀마다 알아서 자주쓰는 테이블 관계에 대한 족보를 만들어두긴하던데
    그래도 결국 신규 테이블이 생기면 이걸 어떻게 쓸 수 있냐란 문의가 많이 옵니다
    (이 테이블의 어떤 컬럼을 어떤 테이블과 조인을 해야되는냐? 파티션 레벨의 data lineage는 뭐냐? 등)

    그래서 메타데이터를 조회한 테이블과 조인하기 좋은 테이블을 추천해주는 기능이나
    메타데이터 검색시 사용한 키워드에 대한 연관 키워드를 추천해주는 기능을 제공하고 있습니다

    2. 테이블 추천 기능 동작 방식(서비스/리소스 구조)

    추천 모델 제공 방식

    • flow 설명

    API는 Django로 개발해서 zappa를 사용해서 API Gateway + Lambda 구조로 배포하고
    Frontend(React)는 API Gateway를 통해서 API를 호출합니다

    다만 테이블추천/연관키워드은 API에서 직접 제공하지 않고
    Embedding기반 추천 Model은 별도의 Lambda 함수에 배포해서
    API용 Lambda에서 모델용 Lambda를 invoke로 호출합니다

    굳이 Lambda 함수를 나눈 이유는
    django는 메모리 256MB면 충분하지만
    모델은 메모리를 약 8배인 2GB를 사용하거든요
    비용을 절감하기 위해서 함수를 분리해서 사용했습니다

    • cold start / warm-up

    다만 이렇게 API나 모델을 Lambda에 올려서 사용할 경우엔
    Lambda는 cold start문제가 있어서 자주 사용하지 않거나 트래픽이 몰리면 응답속도가 느려지는 이슈가 있습니다
    이런 부분을 보안 하기 위해서 CloudWatch Event로 1분마다 해당 Lambda를 호출해서
    Lambda container가 계속 유지되게 끔 처리하고 있습니다
    (이게 warm-up 방식이죠)

    • 왜 자네는 캐시를 소중히 하지 않지?

    이렇게 하지 않는다면 최초로 추천모델 Lambda가 호출되면
    S3에서 추천모델용 데이터를 load해서 실행 가능한 상태까지 만들어지는데 약 1~2분이 소요됩니다
    그럼 유저 입장에서는 추천 테이블 목록이나 연관 검색어를 보기까지 1~2분이 소요가 되는거죠

    물론 정석처럼 redis나 DB에 캐시로 쌓아서 쓰면 좋긴하겠지만
    안그래도 사내용 서비스라 DAU가 100명도 안되는데
    DB에 캐시를 쌓는 비용이 warm-up보다 비싸서 이런 형태를 유지하고 있습니다
    (ElastiCache가 얼만데 감히...)

    3. 추천 모델 비용 계산

    그럼 위에서 추천 모델을 warm-up하는데 발생하는 비용은 얼마냐?

    • Lambda 설정

    embedding model이 동작하는 Lambda는 메모리를 2GB로 설정해서 쓰고 있습니다

    • warm-up 유지에 대한 duration time

    warm-up을 위해서 Lambda를 호출했을때 1분마다 평균 10초씩 소요됩니다
    Lambda Container가 유지되면 1~2초 내로 끝나지만
    새로 뜨게 된다면 약 1~2분 정도 소요가 되서
    대충 계산해보니 1분마다 평균 10초씩 쓰는셈이었습니다

    이건 당연히 매우매우 보수적으로 잡은 시간입니다
    돈은 늘 최소가 아닌 최대치로 잡아야되거든요
    (돈이 남으면 괜찮지만 연말에 부족하면 서비스 중단해야되는 상황도...)

    • 한달 예상 비용

    대충 계산해보면 아래 정도됩니다

    시간: (GB/초당 $0.0000133334) x 10초 x 2GB x 4320회(24 x 60 x 30) = $1.15200576
    요청: 1백만 건당 $0.20이지만 4320회 호출하므로 대충 $0.1

    한달 예상 비용: 약 $1.2

    추천 모델을 한달동안 warm-up으로 설정해서 유지하는데 $1.2면 됩니다!
    물론 API에서 추가적으로 얼마나 호출하느냐에 따라서 더 과금되긴할텐데 2배해봤자 $2.4죠 ㅎㅎ

    메모리 2GiB짜리 EC2(=t4g.small)가 시간당 $0.0208이고
    한달(750시간)동안 켜두기만 해도 $15.60이니
    EC2보단 Lambda에 모델을 유지하는게 훨씬 저렴하죠!
    물론 안정성은 떨어지겠지만 "추천" 기능이기 때문에 안되도 그만이거든요 ㅎ
    (라곤 얘기했지만 숨만 쉬어도 API서버보다 돈을 더 많이 먹는 기능...)

    다만 빠른 응답속도를 위해서 일부는 provisioned concurrency를 설정해서 쓰긴하는데
    사실 가격차이가 그렇게 크진 않습니다 ㅎ

    4. 그런데 말입니다

    실제로 해당 기능을 배포해서 잘 서비스하고 있었는데
    올해 2월에 비용탐색기로 보니깐 Lambda 서비스에 대한 비용이 말도 안되게 많이 나오고 있었습니다
    평소엔 $100 내외로 비용이 발생했는데
    2월엔 거의 4배인 $400가 발생했던것이죠

    누군가는 트래픽이 늘어서 행복한 고민이라고 하겠지만
    위에 예상 비용을 보셔서 아시겠지만 사실상 나올 수 없는 비용입니다

    5. 누구인가?

    비용탐색기 사용량 유형별 화면

    1~3월 비용을 사용량 유형으로 보니 위와 같이 비용이 발생하고 있었습니다
    나머진 변동이 없고 순수하게 Lambda 실행시간이 급격하게 늘어났던 것이죠

    원래 매달 $50였던게 2월엔 $345까지 늘어났습니다
    비용을 시간으로 역산해보면 Lambda의 실행시간(duration time)아래와 같았습니다

    https://aws.amazon.com/ko/lambda/pricing/

    실행시간: 25,874,870s = 431,247min = 7,187h

    이건 아무리 생각해봐도 말이 안되는 수치였습니다
    한달은 720h 밖에 안되는데
    그럼 못해도 Lambda가 24시간동안 10개가 동시에 계속 실행되고 있다는 뜻이죠

    warm-up때문이라고 해도 운영용으로 warm-up을 적용하고 있는 모델이 10개가 되진 않았습니다
    아 물론 개발용 모델이 있긴했지만 테스트할때 써보는정도라
    자주 실행은 안했고 warm-up도 적용 안되어있었습니다

    그리고 사실 메인 기능인 API서버의 실행시간은 아래와 같았습니다

    391,546s = 6,525min = 108h

    네 그렇죠... 주객이 전도됐습니다

     

    6. Lambda 함수별 실행시간 확인

    그래서 아래와 같이 CloudWatch에서 실행시간을 함수별로 뽑아봤습니다

    CloudWatch Duration Time Metric

    그랬더니 매일 같이 돌아가고 실제로 사용하고 있는 Batch, API용 Lambda는 적절한 수준인것에 비해서
    추천 모델, 연관키워드 모델의 실행시간은 정말 말도 안되는 수치로 동작하고 있었습니다
    추천 모델만 해도 9,221,309초였고 기타 다른 모델도 비슷한 시간만큼 동작하고 있었습니다

    실행시간: 9,221,309s = 153,688min = 2,561h

    위에서 언급한것처럼 한달은 720h인데
    Lambda의 실행시간은 1달 동안 약 3달치에 달하는 시간으로 계속 동작하고 있었습니다
    warm-up을 위해서 매번 1분마다 1분씩 동작했다쳐도 3배 많은 수치죠

     

    7. 그런데 말입니다22

    왜 모든 모델 함수는 3배지...?라는 의문이 생기더군요
    위에서 본 함수별 실행시간을 보면 모델용 Lambda만 모두 비슷한 시간대로 실행이 되고 있었습니다
    그것도 한달치 실행시간이 한달의 3배였죠
    왜 하필 3배지...?

     

    8. 범인은 바로 너!!!!

    전에 언급했던것처럼 warm-up을 위해서 CloudWatch Event로 1분마다 Lambda를 호출한다고 했었죠?
    그런데 Lambda를 기본으로 생성하면 비동기식 호출의 설정값은 아래와 같습니다

    누가 맘대로 2번 더 실행하래?

    네 그렇습니다...
    1분마다 실행되고 에러가 나서 재시도를 2번까지 했던 것이죠
    이건 모델 작업하신 분에게 리소스에 대한 권한을 부여하고 확인하지 못했던 부분이었죠
    알아서 잘했겠거니하고 맡겼는데 사실은 모니터링이 안되고 있었던 겁니다
    이걸 1달이 지나서야 비용으로 찍히고 알았던것이죠

    하지만 왜 잘 돌고 있었던게 갑자기 에러가 나고 있었을까요?

     

    9. 에러의 원인

    모델을 생성하기 위한 데이터는 현재 S3에 넣어두고 있습니다
    그래서 대부분의 분석가분들은 S3에 있는 데이터를 가지고 올때 이런식으로 가져옵니다
    pandas를 쓰면 아래처럼 1줄이면 되거든요!

    외쳐 갓 pandas!!!!

    그런데 Lambda는 2GB로 세팅되어 있습니다
    여기서 가지고오는 S3의 용량이 2GB가 넘어가면 어떻게 될까요...?
    OOM이 발생하여 Lambda는 죽어버립니다

    그래서 Lambda는 매번 1분마다 1~2분정도 S3파일을 load하다가 죽고 이걸 2번 더 재시도했던 것이죠
    이래서 대충 3배의 값이 발생했던 겁니다

     

    10. 근데 그럼 왜 이제까지 발견을 못했느냐?

    이게 참 슬픈게 제가 장애 모니터링을 잘 안하기도 했지만
    실제로도 서비스에서는 문제가 없었습니다

    왜냐면 warm-up으로 계속 유지되고 있던 녀석은 S3에서 데이터를 load할 일이 없고
    그러니 데이터도 옛날 버전으로 계속 돌고 있었거든요
    그 와중에 추가로 배포한 적도 없어서 안되는지도 몰랐던 것이죠...

    그리고 에러나던 녀석들은 warm-up과 API가 같이 호출되서
    그 순간 Lambda concurrency 가 발생해서 새로 container를 띄려고 했던 녀석들이라
    warm-up은 비동기로 처리되어서 재수 좋게 이런 상태를 유지하고 있던 것이죠

    그래서 해당 부분에 대해서 모니터링을 안하고도 있었지만 실제로도 장애는 없었습니다
    물론 내부적으로 에러가 계속 나고 있었죠

    그리고 유저는 어쩌다가 그냥 추천/연관키워드 기능이 안되도 별로 관심도 없었구요...
    유용할거라 생각하면 만들어지만 고객의 needs는 그게 아니었던 것이죠

    사실 위 내용엔 없지만 Lambda timeout을 15분으로 잡고 사용하던것도 1+2번씩 실행되서 45배로 실행된것도 있긴 했습니다

     

    11. 조치는?

    이쁘지는 않지만 Lambda에 대한 위처럼 CloudWatch Dashboard / Alarm을 구성했습니다
    알림은 예상치보다 높으면 slack으로 warning 알림이 오도록요
    (Duration Time, Function Error Count)

    그리고 S3에 쌓는 데이터의 range도 무제한으로 쌓는게 아니고 최근 6개월로 고정하는것으로 완료했습니다
    이제 한번 세팅해놨으니 한 6개월은 안정적으로 가겠죠...?(제발)

     

    결론

    덕분(?)에 매달 불필요하게 발생하던 $300 이상을 절감할 수 있었습니다
    엄청 큰 돈은 아니지만 그 상태를 유지했었다면 할당 받았던 연간 예산의 10~20% 수준으로 더 쓸뻔했거든요
    (대기업이지만 가난한 팀입니다 ㅠ)

    그리고 하다보니 발견하게된 또 다른 문제가 있었는데...
    이건 다음 포스팅으로 넘기도록 하겠습니다!

    반응형
    Comments