본문 바로가기

AWS

Mysql Aurora 네이티브 함수로 - Lambda 호출 ( feat. golang)

1. RDS 클러스터에 람다 역할 추가

  • IAM 에서 람다 최소권한을 줄수 있는 InvokeFunction 권한 정책 생성 
  • 위에서 만든 정책을 IAM 역할을 새로 만들어서 역할에 정책 부여
  • 이후 RDS 클러스터에 IAM 역할 관리에서 위에서 만든 역할을 할당 


2. 람다 함수 생성

  • RDS 와 같은 VPC 안에 동일한 서브넷 할당
  • 보안그룹 생성 혹은 기존에 있던 RDS 에 할당된 보안그룹 재귀로 받아서 연결

3. RDS 클러스터 파라미터 그룹 생성 및 aws_default_lambda_role 역할 부여

  • 파라미터 그룹 Type 에서 DB Cluser Parameter Group 을 선택해 파라미터 그룹 새로 생성

  • 파라미터 설정에 aws_default_lambda_role 값에 
  • 생성한 람다 함수의 ARN role 기입


4. RDS 환경 설정

  • AWS 공식문서 참고
  • SELECT user,host FROM mysql.user; 쿼리문에 아래처럼 설정되어 있는지 확인 


5. 람다 호출 테스트

  • SELECT lambda_async('람다ARN','');

정상적으로 호출되면 빈 JSON이 반환됨


6. 트러블 슈팅

  • Lambda API returned error: Missing IAM Credentials for specified aws_default_lambda_role
    • 1,3 번에 대해 제대로 설정이 안되었을 때 발생
  • Lambda API returned error: Network Connection. curlCode: 28, Timeout was reached
    • 보안그룹 아웃바운드룰 443 포트 열어줘야 함
  • Lambda API returned error: Invalid Request Content. Could not parse request body into json: 
    Could not parse payload into json: 
    Illegal unquoted character ((CTRLCHAR, code 10)): has to be escaped using backslash to be included in string value
    • 인코딩 할때 줄바꿈 이스케이프 문제
    • 아래에서 TO_BASE64()를 사용할때 REPLACE 로 개행제거
SET @data = TO_BASE64(CONCAT(
'{
	"ID": "', NEW.ID,'",
	"NAME": "', NEW.NAME,'",
	"DESCRIPTION": "', NEW.DESCRIPTION,'",		
}'
));

--

SET @data = REPLACE(TO_BASE64(CONCAT(
'{
	"ID": "', NEW.ID,'",
	"NAME": "', NEW.NAME,'",
	"DESCRIPTION": "', NEW.DESCRIPTION,'",		
}'
)),'\n,'');
    • MYSQL 컬럼 데이터 타입이 CHAR 인 경우에 null 이 찍히면 키네시스에 아무런 정보도 담기지 않음
      • lambda 에서 오류도 내 뱉지 않고 키네시스 for 문이 돌아가지도 않아 하나하나 컬럼 테스트 해본결과
      • 고정형 문자열(CHAR) 인 경우 null 말고 해당 길이의 데이터를 기입해야 키네시스가 제대로 돌아감
    • Unknown trigger has an error in its body: ‘Access denied; you need (at least one of) the Invoke Lambda privilege(s) for this operation’
      • 시발 이거 삽질을 몇일동안 한건지 좆같은년들 chatGPT 도 개소리하고 구글링해도 안나옴 ㅡㅡ
      • rds 클러스터 파라미터 그룹에 activate_all_roles_on_login 1 저장
      • 이후 아래 sql 문 실행후 trigger 테스트
# activate_all_roles_on_login 1 로 바꾸고 나서 아래 권한 부여 
-----

SELECT CURRENT_ROLE()

# 오로라 버전 3이상
GRANT AWS_LAMBDA_ACCESS TO 사용할디비계정@'%';

# 오로라 버전 2이상
GRANT INVOKE LAMBDA ON *.* TO 사용할디비계정@'%';

SET ROLE ALL ;
 
FLUSH PRIVILEGES;

------

코드 스니펫 (mysql + golang)

 

MySQL

# 키네시스로 넘기려면 base64 encoding 필요함 
SET @data = TO_BASE64(CONCAT('{"operation":"ping"}'));

# 아래 템플릿에 맞춰서 encoding 한 변수 CONCAT 으로 JSON payload 에 넘겨줌
SELECT lambda_async('람다함수ARN',CONCAT('{
	"Records":[
		{
			"kinesis":{
				"data":"',@data,'"
			}
		}
	]
}'));

# Trigger 

DELIMITER //
CREATE TRIGGER `test_lambda` 
AFTER INSERT ON 
`사용되어질 테이블 이름` FOR EACH ROW 
BEGIN 
	SET @data = TO_BASE64(CONCAT(
'{
	"ID": "', NEW.ID,'",
	"NAME": "', NEW.NAME,'",
	"DESCRIPTION": "', NEW.DESCRIPTION,'",		
}'
));
	SELECT lambda_async('ARN람다함수',CONCAT('{
	"Records":[
		{
			"kinesis":{
				"data":"',REPLACE(@data,'\n',''),'"
			}
		}
	]
}')) INTO @result;
END;
DELIMITER ;

 

패키지 설치

  • go get github.com/aws/aws-lambda-go
  • 아래 코드 zip 파일로 람다에 배포
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	"log"
	"os"
)

func init() {
	log.Println("테스트 시작")
}

type Payload struct {
	Operation string `json:"operation"`
}

func pingTest(ctx context.Context, event events.KinesisEvent) {

	for _, record := range event.Records {
		fmt.Printf("lambda_async 로부터 들어온 데이터", record)

		if record.Kinesis.Data == nil {
			fmt.Println("데이터가 Null")
			continue
		}

		var payload Payload
		if err := json.Unmarshal(record.Kinesis.Data, &payload); err != nil {
			fmt.Println("페이로드 디코딩 실패:", err.Error())
			continue
		}
		fmt.Println(payload)
	}

}

func main() {
	lambda.Start(pingTest)
}

 

'AWS' 카테고리의 다른 글