• בלוג
  • צעדים ראשונים עם localstack

צעדים ראשונים עם localstack

01/02/2025

לוקאל סטאק https://github.com/localstack/localstack הוא פרויקט שמטרתו הפעלת גירסה מקומית של AWS אצלכם על המחשב בתוך קונטיינר. הם מכסים עשרות סרביסים של AWS בחשבון החינמי, ויש גם חשבון בתשלום שכולל יותר יכולות לחלק מהסרביסים ויותר סרביסים. מה שאהבתי במיוחד בעבודה עם local stack זה שהקוד לא צריך להשתנות - הממשק שלהם זהה לממשק של AWS מלבד פקודת קונפיגורציה אחת שמגדירה את ה endpoint שלהם במקום של AWS.

בואו נראה איך להתקין ולעבוד עם סרביס S3 של לוקאל סטאק דרך שלושה סקריפטים.

1. התקנת הכלים

את לוקאל סטאק אפשר להתקין מדף ההתקנה שלהם כאן:

https://docs.localstack.cloud/getting-started/installation/

אחרי ההתקנה הייתי צריך להוסיף את התיקיה שלהם לתיקיות הקבצים של Docker Desktop מתוך ההגדרות של דוקר, וגם להתקין סקריפט שעוטף את aws באמצעות הפקודה:

pip install awscli-local

הפעלתי את local stack עם:

$ localstack start -d

ואחרי זה הרצתי את פקודת הסטטוס כדי לראות את הסרביסים שקיבלתי:

$ localstack status services
┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┓
┃ Service                  ┃ Status      ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━┩
│ acm                      │ ✔ available │
│ apigateway               │ ✔ available │
│ cloudformation           │ ✔ available │
│ cloudwatch               │ ✔ available │
│ config                   │ ✔ available │
│ dynamodb                 │ ✔ available │
│ dynamodbstreams          │ ✔ available │
│ ec2                      │ ✔ available │
│ es                       │ ✔ available │
│ events                   │ ✔ available │
│ firehose                 │ ✔ available │
│ iam                      │ ✔ available │
│ kinesis                  │ ✔ available │
│ kms                      │ ✔ available │
│ lambda                   │ ✔ available │
│ logs                     │ ✔ available │
│ opensearch               │ ✔ available │
│ redshift                 │ ✔ available │
│ resource-groups          │ ✔ available │
│ resourcegroupstaggingapi │ ✔ available │
│ route53                  │ ✔ available │
│ route53resolver          │ ✔ available │
│ s3                       │ ✔ running   │
│ s3control                │ ✔ available │
│ scheduler                │ ✔ available │
│ secretsmanager           │ ✔ available │
│ ses                      │ ✔ available │
│ sns                      │ ✔ available │
│ sqs                      │ ✔ available │
│ ssm                      │ ✔ available │
│ stepfunctions            │ ✔ available │
│ sts                      │ ✔ available │
│ support                  │ ✔ available │
│ swf                      │ ✔ available │
│ transcribe               │ ✔ available │
└──────────────────────────┴─────────────┘

2. עבודה עם S3

נמשיך לכמה סקריפטים ב Ruby שעובדים עם S3 בשביל הדוגמה. בכולם השורה הכי חשובה היא:

Aws.config.update(
  endpoint:  'http://s3.localhost.localstack.cloud:4566'
  region: 'us-east-2',
  force_path_style: true
)

הסקריפט הראשון יוצר bucket חדש על s3 שרץ על local stack:

require "aws-sdk-s3"

class BucketCreateWrapper
  attr_reader :bucket

  def initialize(bucket)
    @bucket = bucket
  end

  def create?(region)
    @bucket.create(create_bucket_configuration: { location_constraint: region })
    true
  rescue Aws::Errors::ServiceError => e
    puts "Couldn't create bucket. Here's why: #{e.message}"
    false
  end

  def location
    if @bucket.nil?
      "None. You must create a bucket before you can get its location!"
    else
      @bucket.client.get_bucket_location(bucket: @bucket.name).location_constraint
    end
  rescue Aws::Errors::ServiceError => e
    "Couldn't get the location of #{@bucket.name}. Here's why: #{e.message}"
  end
end

def run_demo
  region = "us-east-2"
  Aws.config.update(
    endpoint:  'http://s3.localhost.localstack.cloud:4566', # update with localstack endpoint
    region: region,
    force_path_style: true, # Enable 'force_path_style' => true, if bucket name is non DNS compliant
  )
  bucket_name = "test-bucket"
  wrapper = BucketCreateWrapper.new(Aws::S3::Bucket.new(bucket_name))
  return unless wrapper.create?(region)

  puts "Created bucket #{wrapper.bucket.name}."
  puts "Your bucket's region is: #{wrapper.location}"
end

run_demo if $PROGRAM_NAME == __FILE__

סקריפט שני כותב קובץ לאותו bucket:

require 'aws-sdk-s3'

region = "us-east-2"

Aws.config.update(
  endpoint:  'http://s3.localhost.localstack.cloud:4566', # update with localstack endpoint
  region: region,
  force_path_style: true, # Enable 'force_path_style' => true, if bucket name is non DNS compliant
)

s3_client = Aws::S3::Client.new

bucket_name = 'test-bucket'
object_key = 'demo.txt'
file_content = "This is a demo file uploaded via Ruby to my LocalStack S3.\n"

begin
  response = s3_client.put_object(
    bucket: bucket_name,
    key: object_key,
    body: file_content
  )
  puts "File '#{object_key}' uploaded successfully to '#{bucket_name}'!"
rescue Aws::S3::Errors::ServiceError => e
  puts "Error uploading file: #{e.message}"
end

וסקריפט שלישי עדיין ברובי קורא את הקובץ ומדפיס את תוכנו:

require 'aws-sdk-s3'

Aws.config.update(
  endpoint:  'http://s3.localhost.localstack.cloud:4566', # update with localstack endpoint
  region: 'us-east-2',
  force_path_style: true # Enable 'force_path_style' => true, if bucket name is non DNS complian
)


s3_client = Aws::S3::Client.new

bucket_name = 'test-bucket'
object_key  = 'demo.txt'

begin
  response = s3_client.get_object(bucket: bucket_name, key: object_key)

  file_contents = response.body.read

  puts "Contents of '#{object_key}' in bucket '#{bucket_name}':\n\n"
  puts file_contents
rescue Aws::S3::Errors::NoSuchKey => e
  puts "File '#{object_key}' not found: #{e.message}"
rescue Aws::S3::Errors::ServiceError => e
  puts "Error retrieving file: #{e.message}"
end

לסיום אפשר למחוק את הבאקט עם הסקריפט פייתון שהתקנו קודם בהפעלת:

$ awslocal s3 rb s3://test-bucket --force

סך הכל הפיתוח עם local stack מאפשר עבודה באותו ממשק כמו AWS אבל מקומית לגמרי. ברור שבסיום הפיתוח צריך לבדוק הכל על AWS ובטח יהיו בעיות ועוד צריך לדבר על הרשאות והכל נכון. אבל למי שרוצה להתחיל פיתוח שיגיע ל AWS אבל בינתיים רק לראות שדברים עובדים אצלנו על המחשב לוקאל סטאק יכול לתת פיתרון מצוין ומהיר.