Để tiết kiệnm chi phí cho môi trường dev, thì bạn có thể tắt Database vào buổi tối và bật lại vào sáng hôm sau, trong bài viết này mình xin chia sẽ cách để làm được điều đó một cách tự động thông qua Lambda và Cloudwatch.
Điều kiện tiên quyết
Có kiến thức cơ bản về python, chủ yếu dùng để debug.
máy tính đã cài sẵn docker, phục vụ cho việc testing local.
Một cụm Aurora, nếu cái này bạn có thể tự tạo nếu muốn làm lab mà không ảnh hưởng đến cụm hiện tại.
máy tính cài sẵn SAM, nếu chưa có thì tham khảo ở đây
Kiến trúc
MÌnh sẽ dùng Cloudwatch để trigger hàm Lambda,
Code python
Stop
import botocore
import boto3
import os
DBClusterIdentifier = os.environ['DB_IDENTIFIER']
Region = os.environ['REGION']
def stopRDS():
rds = boto3.client('rds', region_name=Region)
instances = rds.describe_db_clusters(
DBClusterIdentifier=DBClusterIdentifier)
status = instances.get('DBClusters')[0].get('Status')
if status == 'available':
resp = rds.stop_db_cluster(DBClusterIdentifier=DBClusterIdentifier)
print('Requested to stop rds: ' + str(DBClusterIdentifier))
else:
print('RDS ' + str(DBClusterIdentifier) + ' is ' + str(status))
def lambda_handler(event, context):
stopRDS()
return 'Stopped environment.'
Start
import botocore
import boto3
import os
DBClusterIdentifier = os.environ['DB_IDENTIFIER']
Region = os.environ['REGION']
def startRDS():
rds = boto3.client('rds', region_name=Region)
instances = rds.describe_db_clusters(
DBClusterIdentifier=DBClusterIdentifier)
status = instances.get('DBClusters')[0].get('Status')
if status == 'stopped':
resp = rds.start_db_cluster(DBClusterIdentifier=DBClusterIdentifier)
print('Requested to start rds: ' + str(DBClusterIdentifier))
else:
print('RDS ' + str(DBClusterIdentifier) + ' is ' + str(status))
def lambda_handler(event, context):
startRDS()
return 'started environment.'
Để code có thể hoạt động mình cần hai biến môi trường là DB_IDENTIFIER và REGION
biến DB_IDENTIFIER là tên của cụm DB.
SAM template
Tiêp theo mình sẽ sùng SAM (một phiên bản nâng cấp của Cloudformation) để deploy function này, cùng xem qua phần cấu hình.
Đầu tiên là cấu hình run time, do mình dùng python, nên mình chọn run time là python 3.7, phần này mình sẽ để ở cấu hình global.
Globals:
Function:
MemorySize: 128
Timeout: 3
Runtime: python3.7
Environment:
Variables:
DB_IDENTIFIER: !Ref ClusterName
REGION: !Ref AWS::Region
Về phần cấu hình biến môi trường thì mình sử dụng parameter để truyền vào, tùy thuộc vào tài khoảng của mỗi người mà giá trị này sẽ khác nhau.
Tiếp đến là phần cấu hình của hàm stop
DBShutDownFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: DBShutDownFunction
CodeUri: shut-down/
Handler: stop.lambda_handler
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- rds:StopDBInstance
- rds:DescribeDBClusters
Resource: !Ref ClusterARN
Events:
ScheduleStopDB:
Type: Schedule
Properties:
Schedule: cron(30 23 * * ? *)
Trong phần cấu hình này bao gồm cả phân quyền cho làmbda có thể stop DB instance và event trigger từ Cloudwatch, mình lên lịch là cứ mỗi 11 giờ đêm sẽ tắt cluster.
Cấu hình của hàm start
DBStartUpFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: DBStartUpFunction
CodeUri: start-up/
Handler: start.lambda_handler
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- rds:StopDBInstance
- rds:DescribeDBClusters
Resource: !Ref ClusterARN
Events:
ScheduleStartDB:
Type: Schedule
Properties:
Schedule: cron(0 7 * * ? *)
Cũng tương tự như hàm stop, mình lên lịch sẽ bật DB instance vào mỗi 7 giờ sáng.
File hoàn chỉnh.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Automatic database shut down and start up Lambda functions
Parameters:
ClusterName:
Type: String
ClusterARN:
Type: String
Globals:
Function:
MemorySize: 128
Timeout: 3
Runtime: python3.7
Environment:
Variables:
DB_IDENTIFIER: !Ref ClusterName
REGION: !Ref AWS::Region
Resources:
DBShutDownFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: DBShutDownFunction
CodeUri: shut-down/
Handler: stop.lambda_handler
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- rds:StopDBInstance
- rds:DescribeDBClusters
Resource: !Ref ClusterARN
Events:
ScheduleStopDB:
Type: Schedule
Properties:
Schedule: cron(30 23 * * ? *)
DBStartUpFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: DBStartUpFunction
CodeUri: start-up/
Handler: start.lambda_handler
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- rds:StopDBInstance
- rds:DescribeDBClusters
Resource: !Ref ClusterARN
Events:
ScheduleStartDB:
Type: Schedule
Properties:
Schedule: cron(0 7 * * ? *)
Testing Local
Chuẩn bị thư mục dự án như hình sau
File template dùng để deploy, mặc định khi run SAM command, nó sẻ tự tìm đến file này. File này có nội dung như sau.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
myLambda:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: lhiot-infra-autostartstop-aurora.yaml
Mình dùng command sau để chạy lambda ở local thông qua docker, lưu ý cần thay biến Region bằng tên thực tế, vì khi chạy local biến này không tự gán được.
Command chạy function stop DB.
sam local invoke \
--no-event DBShutDownFunction \
--parameter-overrides \
ParameterKey=ClusterName, ParameterValue=db2-dbauroracluster-1kz8xbu1ru6w4 \
ParameterKey=ClusterARN, ParameterValue=arn:aws:rds:ap-northeast-1:254040661792:cluster:db2-dbauroracluster-1kz8xbu1ru6w4
Command start DB.
sam local invoke \
--no-event DBStartUpFunction \
--parameter-overrides \
ParameterKey=ClusterName, ParameterValue=db2-dbauroracluster-1kz8xbu1ru6w4 \
ParameterKey=ClusterNameARN, ParameterValue=arn:aws:rds:ap-northeast-1:254040661792:cluster:db2-dbauroracluster-1kz8xbu1ru6w4
Deoploy lên môi trường Cloud
Sau khi testing, đãm bảo code đã hoạt động đúng, bây giờ bạn sẽ deploy lên cloud, sử dụng command như sau. Lưu ý bạn cần tạo bucket để lưu code.
sam package --template-file template.yaml --s3-bucket --output-template-file outputTemplate.yaml
sam deploy --template-file outputTemplate.yaml --stack-name --capabilities CAPABILITY_IAM