LocalStack kullanarak AWS uygulamalarını yerelde geliştirmek

Deniz İrgin
Codefiction
Published in
13 min readAug 14, 2019
LocalStack github sayfasından alınmış logoyu ve başlığı içeren bir ekran görüntüsü.

Bu yazının başlığını atarken oldukça zorlandım. Türkçe makale yazarken yaşanan en büyük zorluklardan biri de sanırım yabancı terimlerin Türkçeye çevirilirken anlamını kaybetmeden çevrilmesi.

Yerel derken elbette ki kast ettiğim yerli ve milli AWS geliştirmek değil. AWS’ye bağlanma ihtiyacı duymadan kendi bilgisayarınızda AWS uygulamları geliştirmek.

AWS bugün en yaygın kullanılan cloud (bulut) platformlarından birisi. Günümüzde cloud platformları sadece sanal makine hizmeti sağlamıyorlar. Örneğin AWS’nin S3, Kinesis, DynamoDB, SQS, Lambda, ElasticSearch vb. gibi bir sürü hizmeti var.

Zaman içerisinde kullandığınız dilden bağımsız olarak AWS SDK’sı code base’inize girmeye başlıyor. Veya bazı uygulamları sunucusuz mimari yaklaşımıyla, AWS Lambda kullanarak geliştirmeyi tercih edebiliyorsunuz.

Bu kadar çok cloud hizmeti kullanmaya başladığınızda yaşadığınız sıkıntılardan birisi de test konusu. Özellikle integration testlerini geliştirmek daha da zorlaşıyor. Yaygın kullanılan best practice’lerden biri canlı sisteminizden ayrı bir AWS test hesabı açıp, bu tip işleri orada yapmak. Fakat bunun da bir takım zorlukları var.

  • Yerelde debug yapmak çok daha kolay, cloud platformlar çoğu hizmetleri için böyle bir olanak sağlamıyor.
  • Uygulamalarınızı cloud’a göndermek vakit alan bir süreç. Özellikle ilk başlarda düzgün çalışmasından emin olmak için yaptığınız deneme yanılmalar yüzünden sık sık bu işlemi tekrarlamanız gerekiyor.
  • Cloud’da yaptığınız her şey ücrete tabi :)

Peki ihtiyacınız olan cloud hizmetlerini yerel olarak çalıştırmak harika olmaz mıydı? İşte LocalStack tam bu noktada yardımımıza koşuyor.

LocalStack aslen Atlassian tarafından geliştirilmesine başlanan bir proje, şuan bağımsız olarak başka bir grup tarafından geliştiriliyor. LocalStack, AWS cloud hizmetlerini doğrudan kendi bilgisayarınızda emüle (taklit) etmenize olanak sağlayan, testing/mocking işlerini oldukça kolaylaştıran bir framework. Şuan için maalesef sadece AWS’i destekliyor.

Sadece testing/mocking işlemlerini değil, Infrastructure as code gibi yaklaşımları kullanıyorsanız da oldukça yararlı olacaktır. Örneğin biz Armut.com’da terraform scriptlerimizi önce LocalStack üzerine koşturuyoruz ve denemeler yapıyoruz. Aynı şekilde AWS Lambda deployment işlemleri için Serverless framework’ünü kullanıyoruz. İsmi maalesef biraz talihsiz :).

Serverless AWS Lambda, Azure Functions ve Google Cloud Functions gibi sunucusuz mimari yapılarına kolay deployment yapmınıza imkan sağlayan bir framework ve bu konuda defacto standart haline gelmiş durumda. Bir plugin yardımıyla LocalStack ile beraber de kullanabiliyorsunuz. Makalenin ileryen kısımlarında bir örnek uygulamayı Serverless kullanarak LocalStack’e deploy edeceğiz.

LocalStack yerelde AWS hizmetlerini test etmek için geliştirmiş ilk framework değil. Önceden zaten kinesalite, dynalite, moto ve lambci/docker-lambda gibi open-source projeler de vardı. LocalStack aslında bunları CloudFormation desteği ve AWS Rest API’sine uyumlu olarak daha derli toplu bir araya getiren bir framework. Dolayısıyla desteklenen bütün hizmetler için AWS CLI komutlarını ve çeşitli diller için geliştirmiş olan resmi AWS SDK’larını da LocalStack ile beraber kullanabiliyorsunuz.

Evet sanırım yeterince LocalStack övdük! Genelde çeşitli kavramları detaylı bir şekilde açıkladığım veya kendi tecrüblerimi aktardığım makaleler yazıyorum. Ama bu makale, biraz daha pratik ve kod içeriği olan bir makale olacak. Zaten LocalStack ile ilgili teorik olarak daha fazla da anlatılabilecek birşey yok :)

Kurulum ve Ayarları

LocalStack’in pyhton, pip ve Docker bağımlığı var. Bunlar bilgisayarınızda yüklüyse pip kullanarak kurulum işlemini gerçekleştirebilirsiniz.

pip install localstack

Ama bu komutu çalıştırdığınız anda bilgisayarınıza onlarca yeni paketin yüklendiğini ve bir sürü konfigürasyon yapıldığını göreceksiniz. Ayrıca artık 2019’un yarısını yediğimiz bu günlerde elimizde Docker gibi bir imkan varken bence böyle şeyler yapmamıza gerek yok.

Dolayısıyla ben Docker kullanın derim. Çok kolay ve anlaşılır bir docker-compose dosyası var. Ben de makalenin geri kalanında docker üzerinden gideceğim.

LocalStack’in github sayfasındaki docker-compose dosyası Window bilgisayarlarda çalışmıyor. Sebebi de bind mounts özelliğinin Windows’da çalışmaması. Zaten Docker’ın kendisi de artık volumes özelliğinin kullanılmasını tavsiye ediyor. Dolayısıyla yukarıda verdiğim docker-compose.yml dosyası hem Windows hem de Linux bilgisayarlarda çalışıcaktır.

Yukarıdaki yml dosyasında dikkat çekmek istediğim ve benim de döküman okumadığımdan düşe kalka öğrendiğim üzerinde durmak istediğim bir kaç nokta var.

`privileged: true` özelliğini kullanmamızın sebebi, LocalStack container olarak ayağa kalktığı zaman, host makinenin Docker’ına erişmesine izin vermek. Çünkü bir Lambda function deploy ettiğimiz zaman LocakStack’in kendisi de function’ları container olarak kaldırıyor. Burada lambci/docker-lambda docker image’larından yararlanmışlar. Dediğim gibi LocalStack’in en iyi özelliği daha önce AWS hizmetlerinde yerelde çalıştırmak için yapılmış bir çok iyi projeyi bünyesinde toplamış olması. Yine lambda ile alakalı olarak `LAMBDA_EXECUTOR=docker-reuse` özelliği. Bu özellik lambda function’ın LocalStack tarafından nasıl çalıştırılacağını belirliyor. docker, docker-reuse ve local olmak üzere üç seçenek var, docker ve docker-reuse’un farkı function’a gelen her istekte ayrı container veya aynı container’ın ayağa kaldırılması. Gerçek hayata en yakını docker kullanılması ama bence docker-reuse kullanmasınızda da bir sakınca yok. Local ise hiç ayrı container kullanmadan LocalStack’in içerisinde çalıştırılması. Bu izolasyon ve depedency sorunlarına sebep olacağından tavsiye edilmiyor. Docker kullanın arkadaşlar pişman olmayın :)

`image: localstack/localstack:0.10.1.2` bu özellik kritik. Eğer LocalStack image’ına versiyon vermezseniz sürekli latest versiyonunu indirir. LocalStack daha geliştirme aşamasında olan bir framework ve Docker repository’lerine sürekli yeni versiyon çıkıyorlar ve bunlardan bazıları stabil olmuyor. Dün çalışan image’ın ertesi gün çalışmaması gibi sorunlarla karşılaştıktan sonra versiyonları sabitlemenin iyi bir fikir olduğuna karar verdim. O yüzden her zaman latest yerine etiketli versiyonları kullanmanızı tavsiye ederim.

ports:
- '4567-4585:4567-4585'
- '4590-4593:4590-4593'
- '${PORT_WEB_UI-8080}:${PORT_WEB_UI-8080}'

LocalStack desteklediği her AWS hizmetini bir port üzerinden dışarıya açıyor, örneğin S3'ye http://localhost:4572 üzerinden veya DynamoDB’ye http://localhost:4569 üzerinden ulaşabilirsiniz. Hem AWS CLI’ı hem de SDK’ları kullanırken bu adreslere yönlendirmeniz gerekiyor.

localhost:8080 üzerinden yapmasalar da olurmuş dediğiniz kötü bir web arayüz sunuyorlar. Buradan LocalStack üzerinde yarattığınız AWS resource’larını görebilirsiniz ama büyük umutlarla girmeyin, beklentilerinizi düşük tutun.

Service kısmını `SERVICES=${SERVICES- }` şeklinde bırakırsanız desteklenen bütün AWS hizmetleri ayağa kalkacaktır. Burada yirminin üzerinde hizmetten bahsediyorum ama hepsi kalksa bile çok uzun sürmüyor. Yine de isterseniz asağıdaki gibi bir tanım yaparak sadece istediğiniz hizmetlerin çalışmasını sağlayabilirsiniz.

SERVICES=iam, lambda, dynamodb, apigateway, s3, sns

Benim önerim böyle bir optimizasyona gitmeden önce kısıt vermeden denemelerinizi yapmanız ve sonrasında emin olduktan sonra ilgili hizmetleri belirtmeniz yönünde.

`DEBUG=”1" ` özelliği en büyük yardımcınız olacaktır. Debug özelliğini açtığınız zaman LocalStack oldukça detaylı bir şekilde konsola log basıyor. Herhangi bir resource oluşturmaya çalıştığınızda ilk başlarda bir sürü error görünce panikleyebilirsiniz, ancak bunları çoğu aslında kritik error’ler değil ve satır aralarını okumayı öğrendiğiniz zaman birşeyler çalışmadığında nedenini çok kolay tespit edebiliyorsunuz.

Bunların yanısıra KINESIS_ERROR_PROBABILITY ve DYNAMODB_ERROR_PROBABILITY gibi testing için şirin özellikler de var. Buralara 0.0 ve 1.0 arasında değerler girerek bu Kinesis ve DynamoDB’nin rastgele hatalar vermesini de sağlayabiliyorsunuz.

Bunun gibi daha bir sürü, değiştirip özelleştirebileceğiniz ayarlar var ve bunları github sayfalarında oldukça detaylı olarak anlatmışlar. Başlamadan benim yaptığımı yapmayıp bir göz gezdirmenizi tavsiye ederim.

Bütün bu ayarları yaptıktan sonra docker-compose up yapmanız yeterli :)

LocalStack, AWS SDK’leri, awslocal ve LocalStack.NET

LocalStack.NET logosu

LocalStack’in ağırlıklı olarak Pyhton kullanılarak geliştirildiğinden bahsetmiştik. Aynı ekip LocalStack için localstack-python-client projesini de geliştirmiş. Aslında AWS’nin resmi Python SDK’si olan boto3 etrafına yazılmış bir wrapper. Örneğin Boto3 kullanarak S3'ye ulaşmak için yazdığınız kodun hiç bir ayar gerektirmeden LocalStack’e ulaşmasını sağlamışlar. Aslında basit bir factory olarak düşünebilirsiniz. Basit ama testing sürecinizi oldukça hızlandırıyor.

Ben de benzer amaçla localstack-dotnet-client projesini geliştirdim. Yine resmi .NET SDK’sı olan aws-sdk-net etrafına yazılmış bir wrapper.

LocalStack etrafına .NET için çeşitli tooling kütüphaneleri geliştirmek amacıyla LocalStack.NET adında bir organizasyon altında geliştirmeler yapıyorum. Örneğin vakit bulursam Xunit için otomatik LocalStack container’ı kaldıran bir test runner extension’ı geliştirmeyi de düşünüyorum. Katkı sağlamak isterseniz çok mutlu olurum.

LocalStack’in AWS CLI komutlarıyla tamamen uyumlu olduğundan bahsetmiştim. Örneğin local’imizden LocalStack S3 ye bir dosya kopyalamak istediğimiz zaman aşağıdaki gibi bir komut yazıyoruz.

aws --endpoint-url=http://localhost:4572 s3 cp test.txt s3://mybucket/test2.txt

Gördüğünüz gibi tek fark — endpoint-url=http://localhost:4572 parametresiyle LocalStack’in S3 adresinin belirtilmesi.

Her komutta uzun uzun bunu yazmamamız için yine LocalStack ekibi awscli-local adında bir CLI wrapper geliştirmiş :)

pip install awscli-local

Pyhton paketini yükledikten sonra yukarıdaki komutu aşağıdaki gibi kısaltabiliyorsunuz.

awslocal s3 cp test.txt s3://mybucket/test2.txt

Ben de hem ilgili python paketinin Windows’da çalıştıramadığımdan, hem de daha önce hiç .NET Core Global Tool geliştirme fırsatı elime geçmediğimden, aynı işi yapan localstack-awscli-local adında bir CLI tool’u geliştirdim. İnsanlığın elbetteki ikinci bir wrapper’a ihtiyacı yoktu ama benimle aynı sorunu yaşanlar kullanabilirler.

dotnet tool install --global LocalStack.AwsLocal

Kullanımı awslocal ile tamamen aynı.

LocalStack ve Serverless

LocalStack ile ilgili teorik olarak daha fazla da anlatılabilecek birşey yok.

Dememin üzerinden iki sayfa geçtiğinin farkındayım :) Biraz da Serverless Framework’den bahsetmek istiyorum.

Serverless Framework, sunucusuz mimariyle oluşturduğunuz function’ları, yapılandırmanızı, build ve deploy etmenizi sağlayan açık kaynaklı bir CLI tool’u. Nodejs, go, python, .NET gibi bir çok farklı platforma da desteği var. Aynı şekilde sadece AWS Lambda değil, Azure Functions ve Google Cloud Functions projelerinizde de kullanabiliyorsunuz.

Serverless Framework olmadan da, AWS konsola gidip gerekli resource’ları oluşturup yapılandırabilirsiniz. Projelerin küçük olduğu bir dünyada çok sorun değil ama projeler büyüdükçe ve sayısı arttıkça bu işleri elle yapmak hem maliyetli hem de bakım yapılabilirliği oldukça düşürüyor.

Serverless Framework gerçekten kullanımı çok kolay bir framework, birkaç komutla resource’ları hızla oluşturabilir, build ve deploy edebilirsiniz. Ayrıca serverless.yml dosyalarımızı merkezi bir repository’de saklayabiliriz, böylece süreçleri yazılım ekipleri için de standardize edebiliriz.

Yine LocalStack ekibinin geliştirdiği serverless-localstack eklentisiyle LocalStack ve Serverless Framework’ü bir arada kullanabiliyorsunuz.

Evet artık örnek uygulamamamızı geliştirmek için gerekli olan bütün ön bilgilere sahibiz :)

Profil Oluşturma Uygulaması

Uygulamamız önünde AWS API Gateway olan bir lambda function olacak. İsim, soyisim, email ve profil fotoğrafı gibi bilgileri içeren bir json alıp, fotoğrafı S3’ye, diğer bilgileri de DynamoDB ye kaydedecek.

Uygulamanın mimari şeması

Uygulamamızı .NET Core 2.1 kullanarak geliştireceğiz. Hakkımda “Damarını kesseler Microsoft akacak” denildiği biliyorum. Ama daha önce hem NodeJs hem de Go kullanarak benzer projeler yaptım ve aslında mantık olarak hiç bir fark yok.

Serverless Framework, node tabanlı bir paket, bu nedenle önceden sistemimize kurmamız gerekiyor.

npm install -g serverless

Serverless Framework, hızlı ve kolay bir şekilde lambda projeleri oluşturmanıza yardımcı olacak bir sürü proje şablonu içeriyor.

Bütün listeyi görmek aşağıdaki komutu kullanabilirsiniz:

serverless create –help

Bu komut aşağıdakine benzer bir liste dönecektir.

aws-clojure-gradle
aws-clojurescript-gradle
aws-nodejs
aws-nodejs-typescript
aws-alexa-typescript
aws-nodejs-ecma-script
aws-python
aws-python3
aws-groovy-gradle
aws-java-maven
aws-java-gradle
aws-kotlin-jvm-maven
aws-kotlin-jvm-gradle
aws-kotlin-nodejs-gradle
aws-scala-sbt
aws-csharp
aws-fsharp
aws-go
aws-go-dep
aws-go-mod
aws-ruby
aws-provided

Gördüğünüz üzere bir çok farklı platform için bir çok farlı proje şablonu var. Biz aws-csharp şablonunu kullanacağız.

Müsait bir klasörün içerisinde aşağıdaki komutu çalıştırarak projemizi oluşturalım.

serverless create -t aws-csharp -n profile-lambda-csharp -p profile-lambda-csharp

Serverless, proje yapısını bizim oluşturacak.

aws-csharp.csproj
build.cmd
build.sh
Handler.cs
serverless.yml

build.sh ve build.cmd

Bu iki dosya Serverless Framework’ü ile deploy etmeden önce gerekli olan artifact paketini oluşturma işini yapıyor. Temelde aslında alttaki iki komut çalışmakta.

dotnet restoredotnet lambda package --configuration release --framework netcoreapp2.1 --output-package bin/release/netcoreapp2.1/hello.zip

`dotnet lambda package` AWS’nin bize sağladığı bir CLI tool’u. Aslında yaptığı projeyi derlemek ve lambda deploy için uygun hale getirmek.

Her dil için bunu yapmanız gerekmiyor. Örneğin NodeJs kullanıyorsak Serverless Framework bizim için dev depedency’leri eleyip artifact çıkartıp deploy etme işini otomatik yapıyor.

Windows kullanıcıları build.cmd dosyasını çalıştırabilirler. Veya benim gibi Cmder ve git-bash ikilisini kullanıyorsanız build.sh dosyasını da çalıştırabilirsiniz.

serverless.yml

serverless.yml temelde lambda uygulamanızın konfigürasyonunu ve genel mimarinizde bulunan diğer bileşenlerle nasıl etkileşime gireceğini tanımlayan bir konfigurasyon dosyası.

Lambda uygulamamızı deploy ederken, Serverless Framework bizim için gerekli AWS CloudFormation şablonunu yaratır. AWS dünyasında hemen hemen bütün resource’lar CloudFormation kullanılarak oluşturulur.

Serverless.yml dosyasının dökümantasyonuna bir göz gezdirmenizi tavsiye ederim. Hem ufuk açıcı olacaktır hem de neler yapabildiğini anlamanıza yardımcı olacaktır.

serverless.yml dosyasını aşağıdaki gibi değiştirelim

Aslında üretilmiş dosyanın comment’lerinin temizlenmiş ve serverless-localstack eklentisinin ayarlarının yapılmış hali.

serverless.yml dosyasında dev, stage, prod gibi stage’ler belirleyebiliriz, böylece function’larımızı farklı ortamlara deploy edebiliyoruz. custom: localstack: altında serverless-localstack ile ilgili ayarları görebilirsiniz. Aslında LocalStack’in nereden host edildiğinin adresi ve hangi stage için bu eklentinin devreye gireceğini belirtiyoruz.

custom: localstack:host: değerini LOCAL_STACK_HOST environment variable’a dan alması için değiştirdim, siz Serverless’ı çalıştırken oraya localhost yazabilirsiniz veya ilgili environment variable’a localhost değerini set edebilirsiniz.

custom: stages: alanında da yukarıda bahsettiğim gibi toplamda hangi stage’lerimizin olduğunu belirtiyoruz. Bu bir demo uygulması olduğu için bizim local ve prod olmak üzere iki tane stage’imiz var.

provider: runtime: alanında dotnetcore2.1 olduğunu göreceksiniz. Her ne kadar bu yazı yazılırken en son.NET Core versiyonu 2.2 olsa da AWS Lambda sadece LTS (Long-term support) olan versiyonları destekliyor. .NET Core 2.1 de çok önemli değişiklikler olduğu için o versiyona bir istisna geçtiler ama .NET Core 3.0 çıkana başka bir versiyonu desteklemeyecekler.

provider: region: alanı önemli, buraya girdiğin değerle, LocalStack’in compose dosyasındaki DEFAULT_REGION değerinin aynı olmasına dikkat edin.

provider: functions: alanında lambda function’ımızla ilgili bilgiler var. Aslında burada sadece function’ın ismini ve handler: alanında da function’ın girişini belirtiyoruz.

Yukarıdaki konfigurasyon dosyası, herhangi bir dil veya platform için provider: runtime: ve provider: functions: alanlarında değişiklik yaptığımızda çalışıcaktır. Bu açıdan serverless.yml dosyasını dil agnostik olarak düşünebiliriz.

serverless-localstack ve diğer bağımlılıklar

Serverless Framework’ün node tabanlı bir paket olduğundan bahsetmiştik. Herhangi bir eklendi kullandığımız zaman onu da serverless.yml dosyasının olduğu yere yüklememiz gerekiyor. Dolayısyla serverless-localstack eklentisini kullanmamız için aşağıdaki komutu çalıştırmamız lazım.

npm install serverless-localstack --dev

Böylece .NET projemizin altında bir de node_modules klasörü oluşmuş olacak. Garip durduğunun farkındayım ama diğer diller için de aynı şey yapılmakta.

Serverless Framework çalışırken haliyle AWS_ACCESS_KEY_ID ve AWS_SECRET_ACCESS_KEY environment variable’larına ihtiyaç duyuyor. Biz uygulamamızı LocalStack’de çalıştıracağımızdan dolayı bunları herhangi bir değerle doldurabilirsiniz ama canlıya çıkacağınız zaman geçerli AWS credential’larına ihtiyacınız var.

Hadi çalıştıralım o zaman!

Projenin müsait bir yerine bir docker-compose.yml dosyası oluşturalım ve makalenin başında verdiğim LocalStack yml içeriğini buraya kopyalayalım.

Ayrı bir terminal’de (cmd, powershell, bash vb.) aşağıdaki komutu çalıştıralım.

docker-compose up

Ekranda bir sürü yazının aktığına ve LocalStack’in ayağa kalkma sürecine şahit olacaksınız. Ready yazısını gördüğünüz zaman artık serverless.yml dosyamızı da çalıştırabiliriz.

Ayrı bir terminal’de de aşağıdaki komutu çalıştıralım.

serverless deploy --verbose --stage local

Serverless Framework işini bitirdiğinde aşağıdaki gibi bir sonuç görmeniz gerekiyor

Makelenin başında yazdığım aws cli komutlarıyla function oluştuğunu doğrulayabiliriz.

awslocal lambda list-functions

Bu noktada function’ımız herhangi bir event’e bağlı değil, dolayısıyla lambda function’ımızı AWS CLI kullanarak invoke edebiliriz.

awslocal lambda invoke — function-name profile-local-hello result.log

Komutun ilk kez çalıştırılması biraz uzun sürebilir. LocalStack’in lambda function’larını çalıştırırken container ayağa kaldırdığından bahsetmiştik. Komut ilk çalıştırıldığında LocalStack, lambci/lambda:dotnetcore2.1 docker image’ını indiriyor olacaktır.

Gördüğünüz gibi function’ımız başarılı bir çalıştı. Ayrı bir terminal açıp docker stats komutunu çalıştırdıktan sonra function’ı tekrar invoke ederseniz LocalStack’in ayı bir container kaldırdığını gözlemleyebilirsiniz.

DynamoDB ve S3 bağımlılıklarının yüklenerek örnek uygulamanın tamamlanması

Örnek uygulamamıza başlarken asıl amacımızın DynamoDB ve S3 de içeren bir uygulama geliştirmekti. Hadi biraz daha LocalStack’in sınırlarını zorlayalım :)

Öncelikle ihtiyacımız olan paketleri projemize ekleyelim.

dotnet add package AWSSDK.DynamoDBv2
dotnet add package AWSSDK.S3
dotnet add package Amazon.Lambda.APIGatewayEvents
dotnet add package LocalStack.Client

LocalStack.Client paketini LocalStack’deki AWS resource’larına ulaşmak için kullanacağız. Function’ımızı bir AWS API Gateway’in arkasına alacağımız için Amazon.Lambda.APIGatewayEvents paketine de ihtiyacımız var.

Uygulamamızın çalışması için öncelikle bir S3 bucket ve bir DynamoDB tablosu yaratmamız gerekiyor.

S3 bucket’ımızı yaratalım.

awslocal s3api create-bucket --bucket profile-pictures --region eu-central-1

DynamoDB tablomuzu yaratalım.

awslocal dynamodb create-table --table-name Profiles --attribute-definitions AttributeName=Id,AttributeType=S --key-schema AttributeName=Id,KeyType=HASH --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 --region eu-central-1

Hem S3 ile ilgili hem de DynamoDB ile ilgili CLI komutlarının detayları için AWS dökümantasyonunu inceleyebilirsiniz.

Hem S3 hem de DynamoDB resource’larımızı serverless.yml dosyası içinde de tanımlamamız mümkündü. Ben örneği basit tutmak açısından böyle bir tanım yapmadım. Nasıl yapıldığını öğrenmek için Serverless dökümantasyonunu inceleyebilirsiniz.

chUygulamanın mimari şeması

Yukarıdaki mimariyi kurmamız için Handler sınıfımızda bir takım değişiklikler yapmamız gerekiyor.

private const string AwsAccessKeyId = "test";
private const string AwsAccessKey = "test";
private const string AwsSessionToken = "Token";
private const string RegionName = "eu-central-1";
private const string LocalStackHost = "192.168.1.107";
private const string BucketName = "profile-pictures";
private const string TableName = "Profiles";
private readonly AmazonS3Client _awsS3Client;
private readonly AmazonDynamoDBClient _awsDynamoDbClient;

İhtiyacımız olan değişkenleri tanımlamakla başlayalım. AWS credential’larının bu noktada çok bir önemi yok ama canlıya çıkarken bu değişkenlere geçerli değerler girmemiz lazım veya Lambda function’ımıza gerekli yetkileri verirsek bunları tanımlamaya da gerek yok.

`LocalStackHost` değişkeni çokomelli, LocalStack’in lambda function’ları çalıştırırken ayrı bir container’da ayağa kaldırdığın bahsetmiştik. Yazacağımız yeni function LocalStack içerisindeki S3 ve DynamoDB’ye ulaşacak, normalde kendi bilgisayarımızdan ulaşırken localhost vermemiz yeterli ama function ayrı bir container’da çalışacağından local network adapter’ımızın ip’sini vermemiz gerekiyor. Bunu yapmanın daha iyi yolu Docker network tanımlamak github’daki örnekte inceleyebilirsiniz.

Function’ın constructor’ında AmazonS3Client ve AmazonDynamoDBClient sınıflarının oluşturulduğunu görebilirsiniz. Burada LocalStack.Client kütüphanesini kullandık. Canlıya çıkılırken AmazonS3Client ve AmazonDynamoDBClient sınıflarının yaratılması tabiki farklı olacaktır. Bunun için buraya bir factory veya wrapper koyabilirsiniz. Önemli olan client sınıflarımızın çalıştığımız ortama göre düzgün initialize edilmesi.

CreateProfileAsync metodumuzda artık gelen json’ın deserialize edilmesini, Base64 formatında gelen fotoğrafın decode edilip S3'ye yazıldığını ve diğer bilgilerin DynamoDB’deki tabloya yazıldığını görebilirsiniz.

serverless.yml dosyamızda da bir takım değişiklikler yapmamız gerekiyor.

Yaptığımız değişiklik functions: events: kısmında. Buraya bir http event ekleyip bir path (profile) verdik. Bu tanımlamayı yaptıktan sonra Serverless Framework bizim adımıza bir AWS API Gateway yaratıp bunu function’ımıza bağlayacaktır.

Şimdi yine uygulamamızı build.sh ile build eder ve serverless komutuyla deploy edersek function’ımız hazır olacak.

Serverless komutunu çalıştırdığımızda, yukarıdaki gibi bir çıktı görmemiz gerekiyor. Serverless bu noktada AWS şablonuna uygun bir endpoint bize verecektir, biz uygulamamıza bu endpoint’ten ulaşamayacağız :)

LocalStack’in AWS API Gateway endpoint’lerini çağırmak için şöyle bir şablonu var:

localhost:4567/restapis/{AWS_ID}/local/_user_request_/{PATH}

Bizim burada tek yapmamız gereken {AWS_ID} kısmını serverless komutunun çıktısından aldığımız 442865321A-Z id’siyle, {PATH} kısmını da serverless.yml’daki http event kımsında path olarak tanımladığımız profile ile değiştirmek.

http://localhost:4567/restapis/442865321A-Z/local/_user_request_/profile

Bu aşamadan sonra tek yapmamız gereken bir curl komutuyla ve ya postman gibi bir tool yardımıyla HTTP request’imiz atmak.

curl -X POST \
http://localhost:4567/restapis/442865321A-Z/local/_user_request_/profile \
-H ‘Content-Type: application/json’ \
-H ‘Host: localhost:4567’ \
-H ‘cache-control: no-cache’ \
-d ‘{
“Id”:”2",
“Name”:”Kulübettin",
“Email”:”kulubettin@gmail.com”,
“ProfilePicName”:”my-profile2-pic.jpg”,
“ProfilePicBase64”:”/9j/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wgARCAAlABkDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMFAgT/xAAYAQADAQEAAAAAAAAAAAAAAAACAwQAAf/aAAwDAQACEAMQAAAB3Fl9CzuMThZzaSuEu1cBhTzAqivPCiP/xAAgEAACAgEDBQAAAAAAAAAAAAACAwEEABITIgURFCEk/9oACAEBAAEFAnWUqxfUrCzYW+jSed5Wa1yx8SUDpjDGCKukk3rXo+WIhU1RYyWG36fNbgHysTKWvQMK24z/xAAbEQEAAgIDAAAAAAAAAAAAAAABAAIRIQMQQf/aAAgBAwEBPwHBDkxCiwqesps31//EABsRAAIBBQAAAAAAAAAAAAAAAAABEQIDEDJB/9oACAECAQE/AZY7ZI2+FO2P/8QAJBAAAgIBAwMFAQAAAAAAAAAAAQIAEQMhMUESUWEQEyIykpH/2gAIAQEABj8CofN+1xiApD8VtFrERR5m2L9wqNVudCamC5tA97mYfOkKjhiZ9Gje4GtDxAcbOKOngTqB/U2H9jYiNKqNiQ0tTA3dfT//xAAhEAACAgEEAgMAAAAAAAAAAAABEQAxIRBBYZFR8XGB8P/aAAgBAQABPyEN8NAKdEbA4MU1EafZOksLSUgMDIhBJ4ZPM4EAIDvCeUjJRr51E/QRNgRV2szN7LNmTWhNsE57dLGXHmYHHS7jDlp696H/2gAMAwEAAgADAAAAEKwOsPwf/8QAGhEBAQADAQEAAAAAAAAAAAAAAQARITFBUf/aAAgBAwEBPxA5Eyddi8hqb5RwH1Zv/8QAGBEBAQEBAQAAAAAAAAAAAAAAAQARMYH/2gAIAQIBAT8QezAwNzY/OwzxJf/EACMQAQACAgEDBAMAAAAAAAAAAAERIQAxgUFRcRBhwfGh0fD/2gAIAQEAAT8QSVwwBJ94o5eMkciooeY3zGBeccCMIjSTD4z6/wDrLSShGh6ia3jm1iHa78VOElFCSerPo8FVLIwAsw+PnEOEsGmJl5EyODRjqOj8+mBQYCYQEk92qwz5IPcEutcVlaGkYuuYKJe2f2fzhL4DDBFOJWBBVFBM7XRPthwRUW4IJfLPdc//2Q==”
}’

Request’imizi attıktan sonra AWS CLI komutlarıyla sağlamasını yapalım.

awslocal s3api list-objects --bucket profile-pictures
awslocal dynamodb scan --table-name Profiles

Komutlarının sonuçlarında yarattığımız nesneleri gözlemleyebiliriz.

Projenin tamamına github’dan ulaşabilirsiniz.

Son sözler

Bu yazıda LocalStack kullanarak yerelde AWS hizmetlerini nasıl test edebileceğiniz anlatmaya çalıştım. Örnek projeyi .NET Core kullanarak yazsam da hem Nodejs hem de Go kullanarak benzer uygulamalar geliştirdim. Siz de serverless.yml dosyanızda yapacağınız ufak platform spesifik değişikliklerle istediğiniz dilde benzer uygulamalar geliştirebilirsiniz.

LocalStack hala geliştirilmekte olan bir proje ve her hafta docker hub’larına yeni image upload ediyorlar. İlerleyen zamanlarda giderek daha çok AWS özellikleri ekleyecekler. Biz şuan armut.com’da test süreçlerimiz de LocalStack kullanmaya başladık. Sadece AWS Lambda function’ları değil, Terraform script’lerimizi de LocalStack kullanarak test ediyoruz.

Elbette ki LocalStack’de herşey yolunda gitti diye AWS’de de sorunsuz bir şekilde çalışacak diye bir kaide yok. Ama bizim tecrübelerimiz çok büyük oranda çalıştığı yönünde. İleride diğer cloud platformlarına da destek vereceklerini düşünüyorum.

Umarım sizin için yararlı olmuştur, bir sonraki yazımda görüşünceye dek iyilikler dilerim.

--

--