4 min read

How to develop a Lambda Function in the local environment with GO using Localstack?

How to develop a Lambda Function in the local environment with GO using Localstack?

Most developers who use AWS stack have the same problem in their local development environment. The problem is we couldn't use the AWS Services in the local environment, so we couldn't develop comfortably. At this point, I want you to meet Localstack.

Before starting, I'm using macOS Silicon(M1) right now. And the examples and commands I will use are suitable for M1.

What is Localstack?

Localstack is a fully functional local cloud stack that has the most popular services that we need. That has enterprise and community editions. But don't worry; the community edition includes the most important APIs for mocking the cloud services.

How do I use Localstack?

Most users use Docker. Also, you could install it in your system if you want. In this post, I'll use Docker.

So, What do we need?

  • Docker Desktop
  • NodeJS
  • Go
  • AWS CLI

If you don't have Docker Desktop still, please follow this link.

You could install NodeJS to your macOS system with the following brew command.

$ brew install node

Now, we need the following things.

$ brew install awscli
$ brew install go

Okay, Let's create a scenario.

We will make a basic integration between Lambda and SQS. We'll send a message to SQS, and then SQS'll trigger Lambda. Here's the schema;

Let's make a Golang environment

$ mkdir golang-localstack
$ cd golang-localstack
$ go mod init golang-localstack
$ touch main.go

Install the AWS Lambda Package

$ go get github.com/aws/aws-lambda-go/lambdacontext@v1.27.0

The "go.mod" file would be like the following;

module golang-localstack

go 1.17

require github.com/aws/aws-lambda-go v1.27.0 // indirect

Finally, make a basic Go function.

package main

import (
   "context"
   "encoding/json"
   "github.com/aws/aws-lambda-go/events"
   "github.com/aws/aws-lambda-go/lambda"
   "github.com/aws/aws-lambda-go/lambdacontext"
   "log"
)

func handler(ctx context.Context, event events.SQSEvent) {
   lc, _ := lambdacontext.FromContext(ctx)
   log.Print(lc.Identity.CognitoIdentityPoolID)
   eventJson, _ := json.MarshalIndent(event, "", "  ")
   log.Printf("EVENT: %s", eventJson)
}

func main() {
   lambda.Start(handler)
}

After all, there is only one thing left. It's a deployment Go function to Localstack.

To deployment, We'll use Serverless Framework in the project.

Let's create a simple serverless.yml file. First, we have to install the following packages.

$ npm install -g serverless
$ npm install --save serverless-deployment-bucket
$ npm install --save serverless-localstack

The serverless.yml file must be like the following thing.

service: localstack

plugins:
  - serverless-localstack
  - serverless-deployment-bucket

provider:
  name: aws
  runtime: go1.x
  stage: ${opt:stage, 'local'}
  region: us-east-1
  deploymentBucket:
    name: ${self:service}-${opt:stage}-deployment-bucket

custom:
  localstack:
    debug: true
    stages:
      - local

package:
  exclude:
    - ./**
  include:
    - ./bin/**

resources:
  Resources:
    QueueName:
      Type: "AWS::SQS::Queue"
      Properties:
        QueueName: "QueueName"

functions:
  consumer:
    handler: bin/app
    events:
      - sqs:
          arn:
            Fn::GetAtt:
              - QueueName
              - Arn

Here's what we've done so far

  • Install tools
  • Create a Go function
  • Install Serverless File

Next up, we'll create a Localstack environment

We need a docker-compose.yml file to build a Localstack. Here's a simple docker-compose.yml file.

version: "3.8"

services:
  localstack:
    image: localstack/localstack:latest
    network_mode: bridge
    privileged: true
    ports:
      - "127.0.0.1:53:53"
      - "127.0.0.1:53:53/udp"
      - "127.0.0.1:443:443"
      - "127.0.0.1:4510-4530:4510-4530"
      - "127.0.0.1:4566:4566"
      - "127.0.0.1:4571:4571"
    environment:
      - LOCALSTACK_HOSTNAME=localhost
      - HOST_TMP_FOLDER=/tmp/localstack
      - DOCKER_HOST=unix:///var/run/docker.sock
      - DEFAULT_REGION=us-east-1
      - LAMBDA_EXECUTOR=docker
      - DEBUG=0
    volumes:
      - "${TMPDIR:-/tmp}/localstack:/tmp/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"

And, run.

$ docker-compose up -d

Now, We build a Go App. GOOS must be linux and GOARCH must be amd64. Because Localstack doesn't support the others.

$ GOOS=linux GOARCH=amd64 go build -o bin/app .

Now, We can deploy the app to Localstack.

$ serverless deploy --stage local

And, here we are...

Well, How could we send a message to the SQS service?

There are many ways to send a message to SQS. You could use AWS SDK in any language if you want. I'll use AWS-CLI at the moment.

First, We need an SQS Queue in Localstack. Let's list the queues.

$ aws --endpoint-url=http://localhost:4566 sqs list-queues

And output;

{
    "QueueUrls": [
        "http://localhost:4566/000000000000/QueueName"
    ]
}

Perfect, We have a queue. Now We can send a message.

$ aws --endpoint-url=http://localhost:4566 sqs send-message --queue-url http://localhost:4566/000000000000/QueueName --message-body 'Localstack & Golang !!'

And output;

{
    "MD5OfMessageBody": "972f9fcb6f04fa1d1376584503edbe32",
    "MessageId": "5f5b56c0-5d12-45ad-df6d-4b7e467760dc"
}

Let's take a look at the log groups;

The following code will list the log groups;

$ aws --endpoint-url http://localhost:4566 logs describe-log-groups

Here we are. Our log group name is "/aws/lambda/localstack-local consumer"

{
    "logGroups": [
        {
            "logGroupName": "/aws/lambda/localstack-local-consumer",
            "creationTime": 1638126446317,
            "metricFilterCount": 0,
            "arn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/lambda/localstack-local-consumer",
            "storedBytes": 43626
        }
    ]
}

Let's tail the logs.

$ aws --endpoint-url http://localhost:4566 logs tail /aws/lambda/localstack-local-consumer

via GIPHY

Cheers!