xuni - EventBridge - lambda - RDS 연동하기 (1)
이게 효율적인지는 모르겠으나 구상한대로 하려다보니까 이렇게 되어버렸습니다. 그리고 AWS 리소스 중에 어떤 것을 기본적으로 알고 있어야 하는지 배울 수 있었던 시간이었습니다. 그리고 오늘로 처음에 기획했던 프로젝트의 아키텍처를 완성했습니다.
포스팅에선 아키텍처 하단 Event Bridge - lambda - RDS 연동과 관련된 내용을 담겠습니다. 연결할 때 마주했던 문제들을 위주로 포스팅할 계획입니다. 1편에서는 1, 2를 2편에서는 3을 다루겠습니다.
목차
1. Event Bridge - lambda 연결
- 크론표현식 시간 설정 팁
2. lambda - Spring Boot 연결
- spring cloud function adapter 버전 이슈
- 람다 제한 시간 및 메모리 설정
- 스프링 부트 내 Function 이 2개 이상일 경우
3. lambda - RDS 연결
- RDS와 연결할 VPC 구성하기
- lambda IAM Roles 설정
Event Bridge - lambda 연결
둘을 연결하는 방법을 찾다보면 한 가지 방법만 있는건 아닙니다. 저는 lambda 트리거를 이용해서 두 리소스를 연결했습니다. 이게 제일 간단한 것 같습니다. lambda 리소스를 만드는 과정은 AWS 람다로 서버리스 서비스 만들기 (2) AWS lambda 적용 포스팅으로 대체하겠습니다.
이벤트 브릿지를 통해 스케줄러를 구현하면 아래와 같은 장점이 존재합니다. 다양한 장점이 존재하지만 Retry policy, monitor 기능은 정말 유용한 것 같습니다.
이제 그럼 lambda에 event bridge를 연결해봅시다. lambda 함수를 만들게 되면 아래와 같은 이미지를 볼 수 있습니다.
(1) 트리거 추가 페이지로 이동합니다.
(2) 트리거로 사용할 소스를 선택합니다.
저는 EventBridge를 선택했습니다. Serverless 아키텍처를 검색해보면 EventBridge 자리에 API 게이트웨이가 들어가는 경우가 많더군요. 다음에 API 게이트웨이에 대해서도 찾아봐야겠습니다.
크론표현식 시간 설정 팁
규칙을 생성하기 전에 AWS 크론표현식 시간 설정과 관련된 팁에 대해 조금 알아보겠습니다. 제가 아직 람다를 잘 모르기 때문일수도 있습니다(허허) 람다는 타임존이 UTC 기준입니다. 그래서 타임스탬는 한국 시간 기준으로 나오지만 메시지(서버에 찍히는 로그)는 - 9시간해서 나오게 됩니다. cron 표현식에서 시간(hour)을 고려해야 한다면 참고하시면 됩니다.
이제 규칙을 생성해봅시다.
(3) 새 규칙을 생성합니다.
저는 스케줄 이벤트가 필요하니 예약 표현식을 선택했습니다. cron 표현식으로 매 분 마다 람다가 호출되도록 만들었습니다. 크론표현식은 천하통일이 되지 않은 것 같아요. 스프링에서 사용했을때랑 조금 다르더라구요. AWS 크론 표현식 문서링크를 남기겠습니다. Schedule Expressions for Rules
EventBridge에서 직접 규칙을 만들 수도 있지만 이 경우에는 빨간색 하이라이트 한 함수 호출을 위해 필요한 권한을 직접 설정해야해서 조금 더 번거롭습니다. 트리거 추가 버튼을 누르면 트리거가 생성됩니다.
이제 설정한 시간마다 람다가 호출되는지 확인해보겠습니다. 확인은 AWS cloud watch 모니터링 기능을 통해 확인해보겠습니다.
아래와 같이 로그 스트림이 저장됩니다. 가장 상단에 실행된 이벤트의 로그를 확인해보겠습니다.
스프링 부트를 이용해봤다면 친근한 그림이 보실 수 있습니다. 서버가 실행되고 있다는 것을 확인할 수 있습니다.
lambda - Spring Boot 연결
처음에 혼자해보다가 시간을 보냈고 그 다음에는 공식 문서 그대로 할려다가 시간을 보냈어요... 미숙한 구글링, 번역 실력으로 해석한거니 틀린 내용이 있다면 지적 부탁드릴게요. 저는 Spring Cloud Function 공식 문서를 참고하여 lambda와 Spring Boot를 연결했습니다. 환경변수, 핸들러와 같은 기본 설정은 AWS 람다로 서버리스 서비스 만들기 (2) AWS lambda 적용을 참고해주시길 바랍니다.
원활한 연결을 위해 2가지만 설명하겠습니다.
spring cloud function adapter 버전 이슈
implementation 'org.springframework.cloud:spring-cloud-function-adapter-aws:3.x.x'
3.x에서는 람다 함수를 처리하기 위해 SpringBootHandler 클래스가 존재합니다. SpringBootHandler를 통해 lambda가 핸들러를 인지하는데요. 문제는 Cannot convert event stream 에러를 마주할 수 있다는 것입니다.
Cannot convert event stream: java.lang.IllegalStateException
java.lang.IllegalStateException: Cannot convert event stream
at org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler.convertStream
(SpringBootStreamHandler.java:87)
at org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler.handleRequest
(SpringBootStreamHandler.java:54)
이와 관련된 이슈는 https://github.com/spring-cloud/spring-cloud-function/issues/906에서 확인하실 수 있습니다.
답변을 요약하면 'spring cloud function adapter 4.x 사용해라.' 입니다. AWS lambda와 연결하기 위해서는 해당 인터페이스인 RequestStreamHandler를 구현해야 합니다. 3.x 까지는 구현체가 SpringBootHandler 였지만 4.x 버전으로 업그레이드 되면서 구현체가 Functionlnvoker로 변경되었습니다.
아래와 같이 상속만 받고 빈 클래스로 내비두면 됩니다.
public class LambdaHandler extends FunctionInvoker {
}
이후 상속받은 클래스를 AWS lambda 함수 페이즈로 와서 핸들러로 설정하면 됩니다.
람다 제한 시간 및 메모리 설정
lambda 함수 구성 탭 - 일반 구성에 들어가면 아래와 같은 정보를 볼 수 있습니다.
OOM은 둘째치고 제한 시간은 조정을 해야 합니다. Java는 코드 편집을 지원하는 언어가 아니기 때문에 Maybe 컴파일 언어기 때문에? 우선 느립니다. 컴파일하고 서버띄우고 15초 내로 어떤 Task를 수행하기 어려울 것 같습니다. 제한 시간을 조정하지 않는다면 아래처럼 Task timed out이 걸리게됩니다.
메모리, 스토리지, 제한 시간의 범위는 위 이미지를 참고해주세요. SnapStart는 캐싱을 통해 람다 호출 시간을 단축시켜준다고 합니다. 단, 호출이 잦지 않은 함수는 성능 향상을 보장하지 않는다고 합니다. 위 2가지 사항을 고려한다면 lambda - spring boot 연결은 어렵지 않으실 것 같습니다.
스프링 부트 내 Function 이 2개 이상일 경우
동일한 프로젝트에 등록해야하는 lambda가 2개 일 수 있습니다. 이 경우 람다 함수로 등록할 클래스를 명시하지 않으면 AWS에서 람다 함수를 등록하는 BeanFactoryAwareFunctionRegistry가 어떤 람다 함수를 등록해야할지 알 수 없습니다.
properties or yml 설정 파일에 등록할 람다 함수를 등록해야 합니다.
spring.cloud.function.definition={Function Bean 이름}
더 상세한 내용은 제가 진행하고 있는 lambda 프로젝트 wiki에서 확인하실 수 있습니다.