Buat Lambda Python untuk Menghemat Tagihan Cloud Anda dengan Terraform
Untuk menggunakan cloud dengan baik, Anda harus berbicara bahasa cloud. Dan bahasa cloud adalah kode.
Jika ingin mengotomatiskan pengelolaan sumber daya dalam skala besar, kita perlu menggunakan kode, seperti Terraform, untuk menyederhanakan pembuatan dan pengelolaan sumber daya. Kami harus memprioritaskan penggunaan tanpa server, seperti Lambdas, agar biaya tetap rendah. Dan kita harus bisa menggunakan bahasa pemrograman, seperti Python, untuk mengubah sumber daya yang berjalan secara dinamis.
Dalam proyek ini, kita akan melihat cara menggunakan Terraform, Lambdas, dan Python untuk membuat tugas berulang yang melindungi kita dari biaya yang tidak perlu pada tagihan cloud kita. Kami akan menggunakan Terraform untuk membuat Lambda yang menjalankan skrip Python yang akan menghentikan semua instance dengan tag tertentu, dan kami akan menjadwalkan Lambda tersebut untuk berjalan setiap hari.
Yang terbaik dari semuanya, ini akan dilakukan sepenuhnya dalam kode. Jadi mari kita mulai.
Prasyarat
- Pemahaman Menengah tentang Terraform — Saya akan menjelaskan template saya, tetapi Anda harus mengetahui cara kerja Sumber Daya dan Modul untuk mendapatkan hasil maksimal dari panduan ini.
- Pemahaman Pemula tentang Python — Kami akan menggunakan Kondisional, Untuk Loop, dan Daftar dalam skrip Lambda.
- Sedikit pengetahuan tentang Boto3 — Saya akan menggunakan AWS SDK for Python ( Boto3) di seluruh Lambda Script.
Setup terdiri dari beberapa file dan folder:
providers.tf: File ini menentukan penyedia AWS, yang diperlukan agar Terraform dapat membuat dan mengelola sumber daya AWS.main.tf: File ini mengilustrasikan sumber daya untuk proyek, termasuk modul untuk mengelola izin IAM dan modul lain untuk membuat fungsi Lambda.iam: Modul ini menentukan sumber daya IAM yang diperlukan untuk fungsi Lambda, termasuk peran IAM dengan izin untuk memulai dan menghentikan instans EC2 dan kebijakan IAM yang dapat dikaitkan ke peran tersebut.lambda: Modul ini mendefinisikan fungsi Lambda, termasuk nama, kode, dan peran IAM serta lampiran kebijakan yang ditentukan dalamiamModul.python: file yang berisi script yang akan digunakan oleh Modulelambda.
Providers.tf
Menggunakan us-east-1 untuk proyek ini, tetapi jangan ragu untuk mengubah wilayah tersebut tergantung di mana Anda berada.
# -- root/variables.tf --
#Declare the AWS provider
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
# Configure the AWS Provider
provider "aws" {
region = "us-east-1"
}
# -- root/main.tf --
#Defines variable I wll use to name the Lambda I'm creating
locals {
lambda_name = "stop-dev-instances"
}
#Module containing IAM permissions used by Lambda
module "iam" {
source = "./iam"
}
#Defines a data resource of type "archive_file" named "zip_the_python_code".
data "archive_file" "zip_the_python_code" {
type = "zip"
# Creates a zip archive file by combining the contents of the "python" directory and saving it to the specified output path
source_dir = "${path.module}/python/"
# The output path for the zip file is being set to a file
# Named after the value of the "lambda_name" local variable in the "python" directory
output_path = "${path.module}/python/${local.lambda_name}.zip"
}
#Module creating the actual Lambda
module "lambda" {
source = "./lambda"
lambda_name = local.lambda_name
#The filename of the zip file containing the code for the lambda function
filename = "${path.module}/python/${local.lambda_name}.zip"
#The IAM role that the lambda function should assume
lambda_role_arn = module.iam.lambda_role_arn
#The IAM policy attached to that role
role_policy_attachment = module.iam.role_policy_attachment
}
Dalam Modul ini, kami akan memberi Lambda izin yang diperlukan untuk menghentikan dan memulai penggunaan EC2. Untuk melakukannya, kami harus mengaktifkan Lambda untuk menjalankan peran IAM dengan izin yang sesuai.
iam/main.tf
# -- iam/main.tf --
#Policy for Lambda to assume IAM role
resource "aws_iam_role" "lambda_role" {
name = "assume-lambda-role"
#References a file from the iam directory that has the Lambda's assume role IAM policy
assume_role_policy = file("${path.module}/iam_role.txt")
}
#Policy for the IAM role to use
resource "aws_iam_policy" "iam_policy_for_lambda_role" {
name = "aws-policy-for-assume_role"
description = "AWS IAM Policy for managing assume-lambda-role"
#References a file from the iam directory that has the IAM policy for the role assumed
policy = file("${path.module}/iam_policy.txt")
}
#Attach the IAM policy to the role
resource "aws_iam_role_policy_attachment" "role_policy_attachment" {
role = aws_iam_role.lambda_role.name
policy_arn = aws_iam_policy.iam_policy_for_lambda_role.arn
}
Kebijakan yang memungkinkan Lambda mengambil peran IAM:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
Kebijakan yang mengizinkan Peran IAM untuk menemukan dan menghentikan instans EC2:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:StopInstances"
],
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "arn:aws:logs:us-east-1:235447109042:*"
},
{
"Sid": "VisualEditor2",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:us-east-1:235447109042:log-group:/aws/lambda/createEnvInstances:*"
}
]
}
Nilai-nilai ini akan diteruskan di Modul Lambda melalui variabel di main.tf di Modul Root.
#-- iam/outputs.tf --
output "lambda_role_arn" {
value = aws_iam_role.lambda_role.arn
}
output "role_policy_attachment" {
value = aws_iam_role_policy_attachment.role_policy_attachment
}
Modul ini menyerap kebijakan IAM yang dibuat sebelumnya dan skrip Python, yang akan kami tinjau setelah ini, untuk menghentikan semua instans EC2 yang diberi label Dev. Kami juga akan menggunakan CloudWatch untuk menjadwalkan Lambda agar berjalan setiap hari.
main.tf
# -- lambda/main.tf --
#Create Lambda function that implements Python code
#This function will stop the instances labeled Dev
resource "aws_lambda_function" "lambda_function" {
#Uses the Python file that is zipped in main.tf
filename = var.filename
function_name = "stop-Dev-instances"
#Attach IAM role to Lambda
role = var.lambda_role_arn
handler = "lambda_function.lambda_handler"
runtime = "python3.8"
timeout = 60
#Wait until IAM Policy is attached to IAM role before creating
depends_on = [var.role_policy_attachment]
}
# Create the daily stop schedule
resource "aws_cloudwatch_event_rule" "every_day" {
name = "daily"
schedule_expression = "rate(1 day)"
}
# Allow CloudWatch to invoke stop_dev_lambda Function
resource "aws_lambda_permission" "allow_cloudwatch_to_invoke" {
function_name = aws_lambda_function.lambda_function.function_name
statement_id = "CloudWatchInvoke"
action = "lambda:InvokeFunction"
#Uses the daily CloudWatch stop schedule we just created
source_arn = aws_cloudwatch_event_rule.every_day.arn
principal = "events.amazonaws.com"
}
# Set the stop_dev_lambda to perform when the every_day is triggered
resource "aws_cloudwatch_event_target" "invoke_lambda" {
rule = aws_cloudwatch_event_rule.every_day.name
arn = aws_lambda_function.lambda_function.arn
depends_on = [aws_cloudwatch_event_rule.every_day, aws_lambda_function.lambda_function]
}
Deklarasikan variabel yang akan diteruskan dari Modul Root ke Modul Lambda:
# -- lambda_name/variables.tf --
variable "filename" {
type = string
}
variable "lambda_name" {
type = string
}
variable "lambda_role_arn" {
type = string
}
variable "role_policy_attachment" {}
Jika Anda ingat, di Modul Root, kami memiliki kode di bawah ini untuk membuat file zip yang nantinya akan kami gunakan sebagai nilai filenamedi Modul Lambda kami:
#Defines a data resource of type "archive_file" named "zip_the_python_code".
data "archive_file" "zip_the_python_code" {
type = "zip"
# Creates a zip archive file by combining the contents of the "python" directory and saving it to the specified output path
source_dir = "${path.module}/python/"
# The output path for the zip file is being set to a file
# Named after the value of the "lambda_name" local variable in the "python" directory
output_path = "${path.module}/python/${local.lambda_name}.zip"
}
import json
def lambda_handler(event, context):
import logging
import boto3
#make logging executable in Lambda and locally
if len(logging.getLogger().handlers) > 0:
# the Lambda environment pre-configures a handler logging to stderr. If a handler is already configured,
# `.basicConfig` does not execute. Thus we set the level directly.
# Reference: https://stackoverflow.com/questions/37703609/using-python-logging-with-aws-lambda
logging.getLogger().setLevel(logging.INFO)
else:
logging.basicConfig(level=logging.INFO)
#Set the boto3 client to modify us-east-1 resources
#change this if your region isn't us-east-1
ec2_client=boto3.client("ec2", region_name='us-east-1')
#capture all the instance reservations in us-east-1
list_instances=ec2_client.describe_instances()
reservations=list_instances["Reservations"]
#declare list that will collect ids of instances to stop
stop_list=[]
#iterate through instance reservations
for r in reservations:
instances=r["Instances"]
#iterate through instances within the reservation
for i in instances:
instance_id = i['InstanceId']
#determine whether instance is running AND has a Environment tagged as Dev
#add matching instance_ids to stop_list
if i['State']['Name'] == 'running':
tags=i['Tags']
to_stop=False
for t in tags:
if t['Key']=='Environment' and t['Value']=='Dev':
to_stop=True
if to_stop:
logging.info(f"{instance_id} is being stopped", )
stop_list.append(instance_id)
else:
logging.info(f"{instance_id} is not tagged as Environment:Dev and will not be stopped")
else:
logging.info(f"{instance_id} is not running and will not be stopped")
logging.info(f"Stop List: {stop_list}")
#stop all instance_ids on the stop_list
if(len(stop_list)>0):
result=ec2_client.stop_instances(InstanceIds=stop_list)
#log the outcome of running ec2_clinet.stop_instances
logging.info(f"Result: {result}")
else:
#log that the list is empty if there are no Dev instances to stop
logging.info("Stop List is empty. Nothing to stop")
Saatnya Mendaftar
Verifikasi bahwa Anda telah membuat semua folder dan file di bawah ini di direktori:
Setelah menyelesaikan direktori, saatnya menerapkan perubahan Anda di terminal. Anda harus mengetahui perintah ini jika Anda pernah menggunakan Terraform sebelumnya, tetapi jika tidak, ini dia:
#make sure the directory has been initialized
terraform init
#validate that the configuration files are syntatically valid and internally consistent
terraform validate
#see what changes Terraform will make when you run your template
terraform plan
#deploy your infrastructure
terraform apply
Ke Lambda ini, navigasikan ke menu Lambda di Konsol AWS Anda. Anda akan melihat Fungsi dengan namanya:
Klik ke Lambda dan klik Test :
Pertahankan semua default dan gunakan apa pun yang Anda inginkan untuk Nama Acara apa pun yang Anda inginkan:
Saat menekan Tes lagi, Anda akan melihat hasil Eksekusi . Di bagian Log Fungsi , Anda akan melihat ini:
Karena kami tidak Devmenjalankan instance apa pun, ini adalah hasil yang diharapkan.
Untuk menguji Devinstans yang sedang berjalan, Anda dapat menjalankan instans dengan Environment DevTag secara manual atau menggunakan skrip Terraform di bawah untuk membuat beberapa instans pengujian dengan cepat:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "test" {
count = 3
ami = "ami-0093a6022697a73aa"
instance_type = "t2.micro"
tags = {
Name = "Dev-${count.index}"
Environment = "Dev"
}
}
Sekarang kembali ke Lambda dan jalankan Test lagi.
Kali ini di hasil Eksekusi, kita akan melihat bahwa Daftar Berhenti berisi tiga Instance ID yang ditandai dengan Dev Environmenttag:
Dan jika kita melihat Konsol EC2, Instans telah dihentikan:
Membungkus
Seperti yang telah Anda lihat di demo ini, dengan menggunakan Lambda, Python, dan Terraform secara bersamaan, Anda dapat membuat proses otomatis yang merespons kejadian, mengeksekusi kode yang ditulis dengan Python, dan mengelola sumber daya infrastruktur dengan cara yang andal dan hemat biaya. Kami melakukan tugas yang relatif sederhana untuk menghentikan instans pada jadwal harian. Namun, Anda dapat membayangkan bagaimana ketiga alat ini dapat digunakan bersama untuk tugas seperti pemrosesan data, pencadangan, dan pemantauan.
Jika Anda ingin melihat kode yang saya gunakan dalam kursus, repo dapat ditemukan di sini:https://github.com/nickcmiller/tf-stop-instances

![Apa itu Linked List? [Bagian 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































