IaC(Infratstructure as Code, 코드로 관리하는 인프라)란 애플리케이션을 구동하기 위한 인프라를 코드로 구축하는 것을 뜻한다. 인프라의 크기가 갈수록 커짐에 따라 웹에서 제공하는 콘솔로만 인프라를 관리하기가 힘들어지고, 협업을 하는 상황이라면 누가 어느 부분을 건드렸는지 확인하기가 힘들어진다. IaC는 인프라를 코드로 관리하고, git 등으로 버전 관리를 할 수 있기 때문에 이러한 문제를 해결해줄 수 있다. 대표적인 IaC 도구로는 Terraform, Cloudformation 등이 있는데, 이번 프로젝트에서는 Cloudformation을 이용해서 내가 만든 서버리스 크롤러 앱을 인프라에 띄워보았다.
cloudformation stack을 생성하기 위해서는 템플릿이 필요한데, 템플릿은 내가 구축하고 싶은 인프라의 구성을 코드로 작성한 파일이다. yaml 혹은 json의 형식을 사용할 수 있다.
cloudformation.yml
AWSTemplateFormatVersion: 2010-09-09
Parameters: # (1)
LambdaCodeBucket:
Type: String
Description: S3 bucket in which custom lambda code is stored
LambdaCodeKey:
Type: String
Description: Jar file name in which custom lambda code is stored
Default: deliSHAs_crawler-1.0-SNAPSHOT-all.jar
Lambdahandler:
Type: String
Description: Python file name which is packed inside the zip file
Default: Crawler::handleRequest
JdbcUrl:
Type: String
Description: Jdbc url which lambda function would use
JdbcPassword:
Type: String
Description: Jdbc password for db connection
Resources: # (2)
LambdaIAMRole: # (3)
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 's3:*'
Resource: '*'
- Effect: Allow
Action:
- 'logs:CreateLogGroup'
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource: 'arn:aws:logs:*:*:*'
CustomResourceLambdaFunction: # (4)
Type: 'AWS::Lambda::Function'
Properties:
Handler: !Ref Lambdahandler
Role: !GetAtt LambdaIAMRole.Arn # (4-1)
FunctionName: 'deliSHAs_crawler_test'
Code: # (4-2)
S3Bucket: !Ref LambdaCodeBucket
S3Key: !Ref LambdaCodeKey
Runtime: java11 # (4-3)
Timeout: 60
Environment: # (4-4)
Variables:
BASE_CRAWL_URL: 'http://snuco.snu.ac.kr/ko/foodmenu'
JDBC_DRIVER: 'com.mysql.cj.jdbc.Driver'
JDBC_PASSWORD: !Ref JdbcPassword
JDBC_URL: !Ref JdbcUrl
JDBC_USERNAME: 'root'
ScheduledRule: # (5)
Type: AWS::Events::Rule
Properties:
Description: "Scheduled Rule"
ScheduleExpression: "rate(10 minutes)"
State: "DISABLED"
Targets:
- Arn: !GetAtt CustomResourceLambdaFunction.Arn
Id: "TargetFunctionV1"
PermissionForEventsToInvokeLambda: # (6)
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt CustomResourceLambdaFunction.Arn
Action: "lambda:InvokeFunction"
Principal: "events.amazonaws.com"
SourceArn: !GetAtt ScheduledRule.Arn
(1) stack 생성 시 외부에서 주입받을 파라미터들의 목록을 정의한다. 여기에 선언한 파라미터들을 .travis.yml 에서 주입해주면 된다.
(2) 이 템플릿 파일을 통해 구성할 AWS Resource의 목록을 나열한다. 여기서는 아래와 같이 총 4개의 리소스를 정의한다.
(3) lambda function을 만들 때는 이 function을 실행할 역할을 지정해주어야 한다. lambda function 코드를 S3로부터 가져와야 하므로 이 역할에 S3에 관한 권한을 할당해주고, 로그 기록을 위해 로그 관련 권한도 할당해주었다.
(4) 우리가 배포할 AWS lambda 리소스에 대한 정의이다.
(4-1) 2-1에서 만든 Role의 arn을 적용해준다.
(4-2) function 코드를 어디서 받아올 것인지 설정한다. 여기서는 S3에서 코드를 가져오도록 했고, bucket 이름과 빌드 파일의 이름은 파라미터화해놓은 것을 사용한다.
(4-3) 함수가 실행될 런타임 환경을 정의한다. 나는 함수를 kotlin으로 작성했기 때문에 java11을 선택했다.
(4-4) 함수가 실행되는 환경에서의 환경변수를 정의한다. 바로 적어줄 수 있는 것들은 native string으로 적어주었고, 나머지는 파라미터로 받아오도록 했다.
(5) lambda를 주기적으로 trigger할 cloudwatch event를 rule과 함께 생성하기 위한 리소스이다. ScheduledExpression 필드에서 rate expression 혹은 cron expression을 사용할 수 있다.
(6) 이 리소스는 타 AWS 리소스 혹은 AWS 계정에 해당 함수를 사용할 권한을 부여한다. ScheduledRule 리소스에 CustomResourceLambdaFunction 함수를 invoke할 권한을 부여해주었다.
aws-cli 등의 도구로 cloudformation stack을 만들면 아래와 같이 AWS 콘솔에서 stack이 만들어진 것을 확인할 수 있다.
그리고 이 stack에 정의된 내용대로 lambda 함수가 잘 만들어지는 것도 확인할 수 있다.