LocalStack의 AWS의 요소를 로컬또는 온프레미스기반 구축을할수 있으며

로컬또는 개발환경에서  테스트 서버로 이용될수 있습니다.


Docker DeskTop과 유사하게 AWS Stack들을 데스크탑환경에 탑재해 클라우드환경을 내품안에 둘수 있습니다. Pro버전은 유료버전인것으로 보이며~ 해당버전은 DockerStack을 이용하거나 AWS Cloud를 이용할수 있습니다. 

개발환경에서 클라우드 Only가 아닌 선택지가 있다란 의미이며 개발환경의 비용을 고려, 하이브리드 채택할수 있습니다. 

  • Kafka / ELasiticSearch(ElasticCache)를 도커로 구성해서 작동시키는것은 그렇게 어려운일이 아닙니다. 이 경우 도커스택을 이용할수 있습니다.
  • Glue/Athena/SageMaker와 같은 AI와 관련된 분석처리에 파이프라인 테스트를 한다고하면 클라우드 사용비용이 높을수 있습니다. 이경우 LocalStack Pro를 이용하는경우 테스트비용을 줄일수 있습니다.
    • AWS의 빅데이터및 AI는 정액이 아닌 일반적으로 사용량 기준 비용이 청구되며~ 사용량과 상관없이 개인이 스크립트를 잘못 구성해 작동노드가 수십대의 노드로 자동 스케일아웃되버리면 수백만원이 청구될수도 있습니다. ( 필자의 뼈아픈 사례 )
      • 작동에 오버헤드가 걸리는경우 자동 스케일아웃 트리거는 항상 조심해서 사용해야합니다. -개발환경에서 이러한 설정이 있다고하면 오토스케일아웃 자체를 테스트하는것이아니면 OFF하는것을 권장합니다.


  • 유료유무와 OpenStack이 지원하는 AWS 구성을 살펴볼수 있습니다.


여기서는 AWS의 리모트 디스크인 S3를 로컬에서 셋업하여 유닛테스트와함께 Stream처리가 가능한 S3 활용방법도 시도해보겠습니다.

도커 LocalStack 구성및 로컬구동



version: '3'
services:
  localstack:
    image: localstack/localstack:3.0.2
    ports:
          - "4567:4566"
    environment:
      - SERVICES=s3:4566
      - HOSTNAME=localstack
      - HOSTNAME_EXTERNAL=localstack
      - DEFAULT_REGION=us-east-1      
  aws-cli:
    image: amazon/aws-cli
    depends_on:
      - localstack
    volumes:
      - ./testbucket:/aws      
      - /opt/localstack:/tmp/localstack-s3-storage
      - "/var/run/docker.sock:/var/run/docker.sock"
    environment:
      - AWS_ACCESS_KEY_ID=test
      - AWS_SECRET_ACCESS_KEY=test
      - AWS_DEFAULT_REGION=us-east-2
    entrypoint: /bin/sh -c
    command: >
      "
        touch testData.csv
        touch testDataOne.csv
        touch testDataTwo.csv
        touch testDataThree.csv
        aws --endpoint-url=http://localstack:4566 s3api create-bucket --bucket my-bucket --region us-east-1
        aws --endpoint-url=http://localstack:4566 s3 cp testData.csv s3://my-bucket//testData.csv
        aws --endpoint-url=http://localstack:4566 s3 cp testDataOne.csv s3://my-bucket//testDataTwo.csv
        aws --endpoint-url=http://localstack:4566 s3 cp testDataTwo.csv s3://my-bucket//testDataTwo.csv
        aws --endpoint-url=http://localstack:4566 s3 cp testDataThree.csv s3://my-bucket//testDataThree.csv
      "
             


  • docker-compose up -d 로 구동가능


2024-01-10 11:24:19 
2024-01-10 11:24:19 LocalStack version: 3.0.2
2024-01-10 11:24:19 LocalStack build date: 2023-11-29
2024-01-10 11:24:19 LocalStack build git hash: 60518e11
2024-01-10 11:24:19 
2024-01-10 11:24:21 2024-01-10T02:24:21.008  WARN --- [  MainThread] localstack.deprecations    : DEFAULT_REGION is deprecated (since 0.12.7) and will be removed in upcoming releases of LocalStack! LocalStack now has full multi-region support. This option has no effect. Please remove it from your configuration.
2024-01-10 11:24:21 2024-01-10T02:24:21.008  WARN --- [  MainThread] localstack.deprecations    : HOSTNAME_EXTERNAL is deprecated (since 2.0.0) and will be removed in upcoming releases of LocalStack! This configuration will be migrated to LOCALSTACK_HOST
2024-01-10 11:24:21 2024-01-10T02:24:21.898  INFO --- [-functhread4] hypercorn.error            : Running on https://0.0.0.0:4566 (CTRL + C to quit)
2024-01-10 11:24:21 2024-01-10T02:24:21.898  INFO --- [-functhread4] hypercorn.error            : Running on https://0.0.0.0:4566 (CTRL + C to quit)
2024-01-10 11:24:21 2024-01-10T02:24:21.942  INFO --- [  MainThread] localstack.utils.bootstrap : Execution of "start_runtime_components" took 908.67ms
2024-01-10 11:24:21 Ready.
2024-01-10 11:24:32 2024-01-10T02:24:32.335  INFO --- [   asgi_gw_0] localstack.utils.bootstrap : Execution of "_load_service_plugin" took 1108.37ms
2024-01-10 11:24:32 2024-01-10T02:24:32.336  INFO --- [   asgi_gw_0] localstack.utils.bootstrap : Execution of "require" took 1109.21ms
2024-01-10 11:24:32 2024-01-10T02:24:32.889  INFO --- [   asgi_gw_0] localstack.request.aws     : AWS s3.CreateBucket => 200
2024-01-10 11:24:36 2024-01-10T02:24:36.327  INFO --- [   asgi_gw_1] localstack.request.aws     : AWS s3.PutObject => 200
2024-01-10 11:24:38 2024-01-10T02:24:38.670  INFO --- [   asgi_gw_0] localstack.request.aws     : AWS s3.PutObject => 200
2024-01-10 11:24:39 2024-01-10T02:24:39.754  INFO --- [   asgi_gw_1] localstack.request.aws     : AWS s3.PutObject => 200
2024-01-10 11:24:40 2024-01-10T02:24:40.944  INFO --- [   asgi_gw_0] localstack.request.aws     : AWS s3.PutObject => 200



AWS CLI를 통한 S3제어 명령어


LocalStack을 로컬에 탑재함으로  로컬개발에 필요한 AWS요소를 시뮬레이션할수 있습니다.

각 언어에서 S3를 제어할수 있는 라이브러리를 이용해 S3를 다루는방법을 깊이있게 알필요가 없을수도 있지만

AWS CLI명령어를 로컬에서 이용할수 있으며 로우레벨의 s3api를 이용함으로 작동방식을 이해할수 있으며

다양한 유닛테스트를 시도해볼수 있습니다.


more api : https://docs.aws.amazon.com/cli/latest/reference/s3api/


LocalStack구동시 자동으로 추가한 파일확인을 다음명령을 통해 할수 있으며

네이티브한 방식을 먼저 이용해봄으로 작동모델을 파일시스템과 비교하여 이해할수 있습니다.

root@DESKTOP-K1IEP1L:/mnt/c/Code/Webnori/java-labs/infra# aws --endpoint-url=http://localhost:4567 s3 ls s3://my-bucket//
2024-01-10 11:53:32          0 testData.csv
2024-01-10 11:53:34          0 testDataThree.csv
2024-01-10 11:53:34          0 testDataTwo.csv



단순하게 MockServer가 아닌 시뮬레이션이되며 도커에서 작동하기때문에 작동로그(INFO)를 확인할수 있습니다.


로컬기반 유닛테스트



S3를 로컬에서 클라우드요소없이 스트림처리 유닛테스트를 할수 있으며

uploadSource
        .throttle(processCouuntPerSec, Duration.ofSeconds(1))
        .runForeach(pair -> {
            ByteString byteString = pair.first();
            long index = pair.second();
            String dynamicFileKey = fileKey + index; // fileKey에 인덱스를 추가하여 고유한 파일 이름 생성

            Source.single(byteString)
                    .runWith(S3.multipartUpload(bucketName, dynamicFileKey)
                            .withAttributes(S3Attributes.settings(s3Settings)), materializer)
                    .thenAccept(result -> {
                        LocalTime now = LocalTime.now();
                        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
                        String formatedNow = now.format(formatter);
                        System.out.println(formatedNow + " Upload complete: " + result.location());
                        greetActor.tell("hello", null);
                    })
                    .exceptionally(throwable -> {
                        System.err.println("Upload failed: " + throwable.getMessage());
                        return null;
                    });
        }, materializer);
유닛 테스트 결과
Try Upload
Wait for UploadCompleted
23:33:50 Upload complete: http://my-bucket.s3.localhost.localstack.cloud:4566/example.txt11
23:33:50 Upload complete: http://my-bucket.s3.localhost.localstack.cloud:4566/example.txt10
23:33:50 Upload complete: http://my-bucket.s3.localhost.localstack.cloud:4566/example.txt12
23:33:51 Upload complete: http://my-bucket.s3.localhost.localstack.cloud:4566/example.txt13
23:33:52 Upload complete: http://my-bucket.s3.localhost.localstack.cloud:4566/example.txt14
23:33:53 Upload complete: http://my-bucket.s3.localhost.localstack.cloud:4566/example.txt15
23:33:54 Upload complete: http://my-bucket.s3.localhost.localstack.cloud:4566/example.txt16
23:33:55 Upload complete: http://my-bucket.s3.localhost.localstack.cloud:4566/example.txt17
23:33:56 Upload complete: http://my-bucket.s3.localhost.localstack.cloud:4566/example.txt18
23:33:57 Upload complete: http://my-bucket.s3.localhost.localstack.cloud:4566/example.txt19
Try Download
23:33:57 Downloaded: example.txt19
23:33:57 Downloaded: example.txt10
23:33:57 Downloaded: example.txt17
23:33:57 Downloaded: example.txt11
23:33:57 Downloaded: example.txt12
23:33:57 Downloaded: example.txt14
23:33:57 Downloaded: example.txt16
23:33:57 Downloaded: example.txt18
23:33:57 Downloaded: example.txt15
23:33:57 Downloaded: example.txt13
  • AkkaStream의 조절기를 활용 업로드의 TPS를 조절하고~ 업로드가 모두완료후 다운로드를 이어서 진행할수 있습니다.


유닛테스트 전체 코드는 다음링크를 통해 확인할수 있으며 ReactiveStream의 기능을 활용하여 S3의 스트림을 다양한 오픈스트림과 연결 시킬수 있으며

비동기 수신검증및 TPS측정을 지원함으로 복잡도가 높은 다양한 처리를 로컬에서 시도할수 있습니다.


여기서 연구되는 상호연결가능한 Stack(Kafka,S3,Nosql....) 들은 모두 일관된 ReactiveStream의 인터페이스를 준수하고 있음으로 상호연결이 가능합니다.

시간단위 마감기능 같은경우 배치처리가 유용할수도 있으며 Stream처리가 배치처리보다 항상 완벽한것은 아닙니다.

단지 리얼타임에 가까운 데이터처리가 필요할때 한가지 방법으로 채택할수 있습니다.

  • stream 예 : 데이터의 흐름을 스트림으로 함수형프로그래밍의 특징을 이용해 Flow를 구성할수 있으며 backpresure를 지원합니다. ( 최종 소비가 느린경우 생산 속도를 조절 )
    • reactive stream case : 생산을 구독하여 흐름이 끊기지 않고 최종 소비자에게 흘러감
      • s3 → kafka → nosql 
      • kafka → s3 : 

응용편 - S3를 채택하는 Data아키텍처 구성을 모두 로컬화



S3를 활용하는 몇몇 데이터 아키텍처들



우리의 개발환경이 조금씩  클라우드 요소를 이용하고 전환하기 시작하면서 로컬에서 모두 작동하는 방법을 잃어버리게 되었습니다.

오늘날의 공통 단점으로 지적되고 있는 모놀리식 개발방식이였을때 로컬에서 백/프론트/배치처리 모두 디버깅을 하는 방법을 알고 있었지만

이 역시 서버리스를 활용하면서 잃어버린 스킬이 되었습니다.

이 활동의 최종목적은 여러 데이터플랫폼에 언급되는  AWS-S3원격 스토리지를 로컬에서 이용가능하게 함으로

클라우드 종속없는  OpenStack(Kafka/Spark/Nosql)들을 로컬 개발환경을 구성하는것 그 자체입니다.


클라우드의 비용이 관리된다고 하면 서버리스를 기본 개발환경으로 시작할수도 있습니다.

값비싼 맥북개발장비가 아니라~ 크롬OS처럼 개발장비를 라이트하게 하여 서버리스 클라우드 First 와 같은 흥미로운 개발환경도 분명 발전해 나갈것입니다.


오늘날의 개발환경은 도커를 이용해 오픈스택 서버를 구동하는것이 그렇게 어려운것이 아니기때문에

모놀리식에서 가능했던 로컬개발및 디버깅방식을 복원해야 하는 도전과제가 있을수 있으며 그것을 Local First~ 라 부르겠습니다.


클라우드에 제공되는 메니지드가 사실은 오픈소스의 엔진을 채택하고 있으며 대응하는 스택을 도커에서 찾아서 로컬 first로 모두 구성할수가 있습니다.

아래는 Aws 메니지드 서비스와 대응될수 있는 오픈스택입니다.

  • Athena - Spark
  • S3 - LocalStack
  • Redshift - Postgres
  • Lambda - NoteBook Code
  • OpenSearch - ElasticSearch



운영의 경우 이것을 모두 직접 관리하는 형태인 PasS가아닌 온프레미스의 경우 SE의 전문적인 한땀한땀 리소스가 투입되어야할수 있으며,  안정적인 환경 유지하기 어려울수 있습니다.

하지만 클라우드 100%가 아닌~  Local First 에서 우리의 개발이 시작되었기에 우리에게 다음과같은 선택지가 생겼습니다.

  • 오픈스택 요소중 클라우드 메니지드 PasS를 채택해서 서버운영 리소스를 줄임
  • 과도한 메니지드 Pass 서비스인경우 온프레미스로 부분 구동
  • AWS / Azure / GCP 중 가성비및 핵심 Pass가 존재하는 클라우드를 채택
    • 클라우드를 혼합했을때는 망 데이터 전송비(ISP)가 발생하기 때문에 한가지 클라우드내에서 클러스터를 구성하는것이 추천됩니다.


다음링크를 통해 도커로 구동한 유용한 OpenStack을 정리하고 있으며 StandAlone으로 구동가능합니다.

참고링크

Next 

  • AWS LocalStack - Lambda
    • Lambda는 서버리스에서 코드작동을 지원하는 AWS의 구성요소이며 , LocalStack을 활용해 로컬및 온프레미스 개발환경에서 이용할수 있습니다.
    • ETL은 추출(Extract), 변환(Transform), 로드(Load)를 의미하며 DataLake의 부분요소인  ETL 함수를 작동시킬때 활용될수 있습니다.
  • Reactive Stream의 기원 - 개발스택의 기원찾기 놀이를 하다보면 연관된 좋은 스택도 함께 찾게됩니다.




  • No labels

1 Comment

  1. Anonymous

    좋은 글 감사합니다 ^^

Write a comment…