티스토리 뷰

반응형

 

Spring Cloud Function(Spring Boot) 프로젝트를 AWS Lambda에 배포하는 방법에 대해 기록해보겠습니다.

1. 프로젝트 gradle 설정 샘플

ext {
    set('springCloudVersion', "2020.0.2")
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

dependencies {
    implementation 'org.springframework.cloud:spring-cloud-function-adapter-aws'
    implementation 'com.amazonaws:aws-lambda-java-core:1.2.1'
    implementation 'com.amazonaws:aws-lambda-java-events:3.8.0'
}
  • Spring Cloud Function을 위한 의존성, AWS에 Lamda에 배포하기 위한 의존성, AWS Lambda 이벤트를 다루기 위한 의존성이 필요합니다.

2. 예제 코드

  • 예제로 사용할 코드는 name과 age를 입력값으로 넣어주고, age값에 따라 성인인지 아닌지를 판단하는 간단한 코드입니다. 
  • 사용되는 클래스는 스프링 컨테이너에서 다뤄야 하므로 모두 Bean으로 등록되어야 합니다.
  • 사용되는 클래스는 Funtion, Consumer, Supplier와 같은 함수형 인터페이스를 구현해야합니다. 
@Component // 하나의 함수로써 노출되는 스프링 Bean 객체
public class AdultJudge implements Function<UserRequest, AdultJudgeResponse> {
    @Override
    public AdultJudgeResponse apply(UserRequest request) {
        String name = request.getName();

        if (request.getAge() > 20) {
            return new AdultJudgeResponse(name + "은(는) 성인입니다.");
        }

        return new AdultJudgeResponse(name + "은(는) 성인이 아닙니다.");
    }
}
//요청 DTO
public class UserRequest {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
//응답 DTO
public class AdultJudgeResponse {
    private String result;

    public AdultJudgeResponse(String result) {
        this.result = result;
    }

    public String getResult() {
        return result;
    }
}
  • 만약 @Component 애노테이션 없이 특정 패키지의 클래스들을 Function으로써 스캔을 하고 싶은 경우 properties에 아래와 같이 패키지명을 설정합니다.
spring.cloud.function.scan.packages=me.siyoon.springbootlambdatest.functions

3. 로컬환경에서 테스트하기

  • 로컬에서 테스트 하기 위해서는 spring-cloud-starter-function-web 의존 설정이 추가로 필요합니다.
dependencies {
    ...
    // 로컬테스트를 위한 의존성 설정. aws로 deploy시에는 필요없다.
    implementation 'org.springframework.cloud:spring-cloud-starter-function-web'
}

요청 & 응답

POST http://localhost:8080/adultJudge
Content-Type: application/json

{
  "name": "siyoon",
  "age": 13
}
POST http://localhost:8080/adultJudge

HTTP/1.1 200 
scf-func-name: adultJudge
host: localhost:8080
Content-Type: application/json

{
  "result": "siyoon은(는) 성인이 아닙니다."
}
  • 요청시에 엔드포인트는 Function의 Bean 이름으로 설정합니다.

4. AWS에 배포하기

AWS Handlers 생성

  • AWS Lamda가 Spring Cloud Function 프로젝트를 다루기 위해서 Spring Cloud Function AWS에서 제공하는 핸들러를 상속받은 클래스가 필요합니다.
import org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler;

public class LambdaHandlers extends SpringBootStreamHandler {

}
  • 해당 클래스는 (HTTP 프로토콜과 같은) 요청정보를 AWS Lamda에서 다룰 수 있도록 하고 프로젝트에 접근할 수 있도록 도와줍니다. 별도로 구현한 사항이 없어 보이지만 상속받은 SpringBootStreamHandler를 살펴보면 InputStream과 OutputStream을 입력받고 적절한 핸들러를 찾아서 실행하는 handlerRequest 메서드를 확인할 수 있습니다.

zip 파일로 만들기 위한 task 추가

  • 프로젝트를 zip파일로 압축하기 위한 task를 추가합니다. (jar파일로 만든 경우에 이상하게 잘 안되어서 zip 파일로 만들겠습니다.)
task buildZip(type: Zip) {
    from compileJava
    from processResources
    into('lib') {
        from configurations.runtimeClasspath
    }
}
  • gradle buildZip 를 실행하면 ./build/distributions 디렉토리에 생성된 zip 파일을 확인 할 수 있습니다.

AWS Lambda Function 생성하기

  • AWS Lambda 대시보드에서 새로운 Function을 생성합니다.

  • 생성된 Function에 deploy할 프로젝트를 업로드합니다. (Code source - Upload from)
  • 간단한 프로젝트이지만 10MB를 초과하여 S3에 업로드 한 후, 해당 주소를 사용해서 업로드 하였습니다. (플로그인을 사용하여서 파일 사이즈를 줄이는 방법도 있지만 이번 포스팅에서는 다루지 않습니다.)

  • 프로젝트내의 Handler 클래스 위치를 입력합니다. (Runtime settings - Edit)

  • 환경변수에 FUNCTION_NAME과 MAIN_CLASS를 입력합니다. (Configurtaion - Environment variables - Edit)

  • FUNCTION_NAME은 원하는 Bean 이름을, MAIN_CLASS에는 main 메서드가 위치한 클래스명을 입력합니다.

AWS Lambda Function 테스트하기

  • Test 탭에서 Json 형식의 입력값을 설정한후 Test를 진행해보겠습니다.

  • 응답결과를 확인할 수 있습니다. 다만 초기에 요청은 Spring 컨테이너가 실행되는 시간까지 합쳐져서 거의 10초정도의 시간이 소요되었고, 2번째 요청부터는 1ms로 빠르게 진행되었습니다. 5분이상 요청이 없을 경우 다시 Spring 컨테이너를 실행해야 해서 이런 경우에는 실사용하기 어려울 수 있습니다. (Spring Native가 안정화 되면 이런 문제를 해결 할 수 있을 것으로 기대됩니다.)
  • 만약 테스트가 진행되지 않는다면 Lamda의 동시성(Concurrency) 설정을 확인해보세요.
  • 다음 포스팅에서는 API Gateway를 트리거로 등록하여 실제 HTTP 요청과 AWS Lambda를 매핑해보도록 하겠습니다.

 

참고

반응형
댓글