인생은 고통의 연속

serverless proxy 서버 구축 본문

아키텍쳐/Cloud

serverless proxy 서버 구축

gnidoc 2021. 2. 4. 04:45
    반응형

    기존에 운영 중인 환경에서 proxy 서버를 구축했던 경험을 서술했다

     

    도입배경

    일단 원래 인프라 구조는 이러했다

    대충 인프라 현황

    온프레미스쪽 서버에 MSTR이란 제품으로 간단한 데이터 분석을 제공하고 있었는데
    (mstr : www.microstrategy.com/ko)

     

    비즈니스 분석 및 모바일 솔루션

    MicroStrategy의 비즈니스 분석 및 모바일 플랫폼은 기업이 분석 및 모바일 앱을 구축 및 배포하여 비즈니스를 혁신할 수 있도록 지원합니다.

    www.microstrategy.com

    여러가지 이유로 이 MSTR에서 사용할 DB로 redshift를 사용했다

    당연히, 그냥 Public 망으로 IP뚫어서 쓰면 트래픽에 대한 어마어마한 비용을 내야하므로 전용선을 써야했고

    다행히 다른팀에서 관리 중인 Direct Connect가 구축된게 있어서 이걸 물려서 사용했다

    그러니 개념상 위와 같지만 실제로 AWS 콘솔에서 적용된 모습은 다음과 같았다

    Direct Connect를 쓰지만 담당부서에서 Virtual Private Gateway로 연결해줌

    왜냐면 직접 Direct Connect를 물려주지 않고 방화벽과 연결된 네트워크로 연결을 해줬기 때문이다

    더보기

    이게 Direct Connect의 가장 큰 문제가 AWS로 계정 상관없이 막 업로드가 가능해서

    개인이 계정을 생성해서 IDC에 있는 정보를 막 올려버릴 수 있는 문제가 있고

    AWS에 있는 리소스는 보안그룹이나 NetworkACL 등을 통해서 IDC쪽 접근을 막을 수 있는데

    Direct Connect로 연결된 계정에서 IDC로 접근하는건 막을 수가 없다...

    암튼 그래서 잘?쓰고 있었다

     

    그런데 문제는 뭐냐,

    기존에 구축했던 Private VPC에 할당된 IP가 매우 적었는데

    이유는 Private VPC와 연결된 Virtual Private Gateway는 라우팅을 위해서 서로 IP가 충돌되면 안되고

    온프레미스로 구축되어있던 IDC의 경우, 기본적으로 모두 Public IP를 사용했기 때문에

    사실상 AWS에서는 Private VPC의 IP는 사설IP이겠지만,

    IDC입장에서는 Private VPC도 실제 IP를 할당한다는 개념으로 봐야하기 때문에

    Private VPC에 할당할 수 있는 IP의 수가 매우 적었다

    즉, Private VPC에 IP를 늘릴 수 없는 상태이고 이미 운영 중인 시스템이 돌고 있다

     

    그런데 이제 이 MSTR이란 제품을 활용해서 새로운 프로젝트 도입이 필요했는데

    MSTR의 API를 사용할 필요가 있었다

    그냥 단순히 생각하면 저 Private VPC에서 서버 띄워서 운영하면 되지만

    위에서 언급되었듯 IP가 부족하고 이 API 접근 아니면 굳이 저 Private VPC에 접근할 필요가 없었다

     

    해결방안1

    그래서 처음엔 아래처럼 Public Subnet을 따로 만들어서 NAT를 통해서 MSTR API에 접근하려고 생각했다

    새로운 프로젝트는 NAT로 통신

    문제는 뭐냐... 바로 Private VPC이다

    보안상 redshift가 외부에 노출되면 안되므로 해당 VPC에 인터넷 게이트웨이를 쓸 수 없었다

    그럼 만약에 Public Subnet에 있는 EC2에 문제가 생겨서 직접 ssh로 접근해야되는 상황이 오면 어떻게 해야되냐...

    Gateway 서버 -> Bastion 서버 -> New Project 서버

    IDC쪽에 별도로 Gateway용 서버를 만들어서 Private subnet에 있는 Bastion을 거쳐서 새로운 프로젝트 EC2에 접근해야되는 거지같은 구조로 접속을 해야된다

    물론 AWS에서 session manager라는 기능으로 인터넷이 안되는 서버도 웹에서 접근할 수 있게 제공은 해준다

    (session manager : aws.amazon.com/ko/blogs/korea/new-session-manager/)

     

    AWS Systems Manager Session Manager, EC2 인스턴스 쉘 접근을 위한 신규 기능 | Amazon Web Services

    지금은 기업 IT 관리자에 매우 흥미로운 시기입니다. 개발자는 코드로서의 인프라를 통해 프로그래밍 가능한 인프라 관리를 제공하고 있습니다. 한편, 레거시 시스템은 여전히 수작업으로 또는

    aws.amazon.com

    하지만 장애라는게 SSM agent가 죽는 상황도 언제나 발생할 수 있기 때문에

    이게 안되는 상황이라면 결국 위의 방식으로 접근해야된다

    그리고 더 중요한건, 네트워크단에서 개발/스테이징/운영을 분리할 수가 없다는 것이다

    사람은 분명 실수하기 때문에 네트워크단에서 아예 분리를 시켜줘야 안심할 수 있는데

    하나의 VPC로 관리하기에는 위험부담이 컸고

    VPC 내에서 계속 subnet을 추가하면서 관리하기에는 보안그룹이나 라우팅 테이블 등을 관리하기 너무 귀찮았다
    (테라폼으로 관리하는것도 테라폼을 테스트해볼 환경이 결국 같은 VPC라는거 아닌가! 결국 실수할 수 있다)

    결국, 접근성과 환경분리 이 2개의 문제는 타협할 수 없고 해결할 수 없는 문제였다

    그래서 일단 개발환경 분리를 위해서 개발/운영 VPC를 2개 추가했다

    VPC는 돈이 안드니깐 ㅎㅎ...

     

    해결방안2

    어차피 개발환경만 완성되면 운영은 복붙하면 되니 개발환경을 먼저 고민해봤는데

    먼저 VPC Transit Gateway에서 Direct Connect 를 지원하기에 이걸 고민했다

    aws.amazon.com/ko/blogs/korea/use-aws-transit-gateway-direct-connect-to-centralize-and-streamline-your-network-connectivity/

     

    AWS Transit Gateway에 대한 Direct Connect 지원 정식 출시 | Amazon Web Services

    작년에 AWS Transit Gateway를 사용하여 네트워크 아키텍처를 간소화하는 방법을 보여드렸습니다. 당시에 다음과 같은 내용을 말씀드린 바 있습니다. VPC, Active Directory, 공유 서비스 등의 리소스가 여

    aws.amazon.com

    하지만 뭐가 문제냐...

    위에서 말했듯 내 계정은 Direct Connect로 연결이 안되어있다

    Virtual Pirvate Gateway로 연결되어 있기 때문에 위의 방법을 쓸 순 없었다

     

    해결방안3

    생각해보니깐 어차피 VPC끼리 연결이고 개발/운영VPC만 추가되면 VPC가 추가될일은 없으니깐

    좀 귀찮아도 그냥 VPC Peering 쓰면 되는거 아닌가?라는 생각이 들어서 아래처럼 구축을 해봤다

    (해보고 되면 VPC Transit Gateway로 운영하면 되니깐 일단 구축이 쉬운 Peering으로 테스트)

    VPC Peering

    오~ 개꿀이네하고 EC2를 띄워서 MSTR API에 접근이 되는지 테스트 해봤다

    안된다 ^^ㅣ발

    아니 당연히 되야하는거 아닌가?

    내가 라우팅테이블을 잘못했나도 보고 이것저것해봐도 개발용 VPC에서 MSTR API에 접근이 되지 않는다

    느낌이 뭔가 또 Limit 같은게 있을거 같아서 문서를 보니 이런게 있었다

    지원되지 않는 VPC Peering 구성 : docs.aws.amazon.com/ko_kr/vpc/latest/peering/invalid-peering-configurations.html#transitive-peering

     

    지원되지 않는 VPC 피어링 구성 - Amazon Virtual Private Cloud

    지원되지 않는 VPC 피어링 구성 다음 VPC 피어링 연결 구성은 지원되지 않습니다. 경우에 따라 피어링 연결 대신 전송 게이트웨이 연결을 사용할 수 있습니다. 자세한 내용은 Amazon VPC Transit Gateways

    docs.aws.amazon.com

    하하하...

    그렇다 네트워크 전이(Network Transitive)를 철저하게 지원하지 않는다

    사례를 보니 나같은 놈들이 당연히 있을까봐 일부러 막아둔 듯하다

    전이가 지원되면 위의 그림처럼 VPC B에 있는 데이터를 외부로 유출가능해진다

    생각해보니 이렇게 관리되어야 네트워크 Flow 파악에 좀 더 유리할거 같긴한데

    막상 필요한 상황이 되니 하... 아쉽네

     

    해결방안4

    여기서 생각해볼 수 있는건 MSTR API에 직접 접근은 안되지만 개발용 VPC와 Private VPC는 서로 통신이 가능하다는 점이다

    그래서 VPC Peering된 상태에서 아래와 같이 Private VPC에서 nginx로 Proxy 서버를 운영하는게 Best인데

    nginx proxy 서버 추가

    당연히 nginx proxy 서버를 운영하는건 EC2를 별도로 띄운다는 것이고

    Auto scaling을 건다고 해도 기본적으로 HA구성을 해야되기 때문에 최소 2대가 필요하다

    proxy서버는 네트워크 트래픽말고는 다른 리소스가 거의 발생하지 않으므로

    2 vCPU, 1GiB짜리인 t3.micro로 1년 운영한다고 했을때 최소 $234이 발생한다
    (시간당 $0.013 * 2대 * 750시간 * 12달 = $234)

    솔직히 이 신규프로젝트에서 얼마나 저 MSTR을 호출할지도 모르고

    아무리 많이 호출해봤자 1초에 1건이 안나올게 뻔한 상황이라

    내 돈은 아니지만 이 돈이 너무 아까웠다

     

    해결방안5

    사실 VPC Peering도 거슬리고 nginx proxy 서버용 ami 관리하는것도 귀찮을거 같았다

    어차피 쓸 API 몇개 안되서 뭘 쓸지 뻔하고 하루에 100번도 호출 안될텐데

    굳이 저렇게 어렵게 가야되나라는 생각이 들었다

    그래서 혹시나 싶어서 AWS에서 제공하는 Proxy 관련 서비스가 있나 찾아봤는데 없었다...

     

    그런데 생각해보니 트래픽이 거의 없는 서비스는 serverless 가 유리한데

    그냥 직접 API Gateway + Lambda로 API를 대신 호출해주는걸 만들면 되지않을까?라는 생각이 들었고

    아래와 같이 생각을 고쳐먹었다

    하핫 난 천재야

    VPC Peering은 Private Endpoint 타입의 API Gateway로 대체하고

    docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/apigateway-private-apis.html

     

    Amazon API Gateway에서 프라이빗 API 생성 - Amazon API Gateway

    프라이빗 DNS를 활성화된 상태로 두는 것이 바람직합니다. 프라이빗 DNS를 활성화하지 않으면 퍼블릭 DNS를 통해서만 API에 액세스할 수 있습니다.

    docs.aws.amazon.com

    nginx proxy 서버는 그냥 Private subnet에 Lambda를 할당했다

    Lambda는 그냥 MSTR API를 호출하도록 requests 를 써서 아래처럼 짰다

    import requests
    import json
    
    URL = 'MSTRURL'
    
    def lambda_handler(event, context):
        print('event = ', json.dumps(event))
        
        url = URL + event.get('path')
        method = event.get('httpMethod')
        headers = event.get('headers')
        query_string_parameters = event.get('queryStringParameters')
        body = event.get('body')
        
        res = requests.request(method, headers=headers,
                               url=url, params=query_string_parameters, data=body)
        print('response body = ', res.text)
        print('response headers = ', res.headers)
    
        return {
            'statusCode': res.status_code,
            'body': res.text,
            'headers': dict(res.headers)
        }

    혹시 API호출하다가 장애날 수도 있으니 로그를 위해서 event와 reponse 값을 찍었다

    근데 MSTR API의 경우, 로그인 토큰값을 header에 넣어서 주기 때문에

    Lambda에서 굳이 return할때 headers도 넣어줘야했다

    테스트해본 결과, 매우 만족스럽게 잘동작했고

    신규프로젝트는 저 private endpoint로 호출하면 되서 매우 간단해졌다

    그리고 Lambda에서 오류가 나면 cloud watch에서 SNS + Slack으로 알림을 보내도록 설정해서

    장애에 대한 대응도 유연하게 할 수 있게 되었다

     

    최종

    전체적인 구조

    최종적인 구조는 위와 같이 구성했고 현재 프로젝트는 진행 중에 있다

    그리고 내가 만든 저 API Gateway 는 내부적으로 serverless proxy 서버로 부르고 있다

     

    결론적으로

    API Gateway와 Lambda는 알아서 auto scaling되고 cloud watch를 통해서 로그나 장애알림을 확인할 수 있고

    개발/운영 VPC를 분리가능하며

    기존에 redshift를 운영하고 있는 Private VPC를 건들지 않고 IP부족에 대한 이슈도 해결할 수 있었다

     

    뭐 물론 Lambda를 통하기 때문에 일반 nginx proxy랑은 다르게 느리기야 하겠지만

    진행할 프로젝트는 API의 응답속도가 ms단위로 중요한 서비스가 아니기 때문에 가능했다

     

    만들어놓고보면 뿌듯하긴 한데

    이 전용선이랑 통신할일이 생기면 진짜 너무 골치아픈게 많은듯하다

    API통신이야 이렇게 해결했는데 나중에 mysql이나 git 서버에 연결해야되면....

    그건 나중에 고민해보는걸로... ㅋㅋ

    반응형
    Comments