Going offline with golang, serverless and dynamodb

2020-05-15

First of all, you do not need this! Really, lambda (or other sls platforms) are pretty cheap. Most of the time, development costs are covered with free tier and in the worst case it will cost you less than one beer.

Anyway there is a lot of noise and tools related to offline development so I’ve been curious and tried to setup that crap. The most common use-case is a http service backed with dynamodb so let’s focus on that. Fortunately (or not?), there are tools which do all hard work for you.

serverless-offline

serverless-offline is a serverless plugin which emulates AWS lambda and API gateway on your local machine.

Install the plugin:

$ npm install serverless-offline --save-dev

And the plugin to the plugins section in your serverless.yml:

plugins:
  - serverless-offline

That’s it. After executing sls offline start command, the service is available on http://localhost:3000/dev.

Oh wait! Requests to the one of configured endpoints says my runtime is not supported.

offline: Failure: Unsupported runtime
Error: Unsupported runtime

Well, golang runtime is supported only via docker-runner which is toggled with --useDocker flag.

So sls offline start --useDocker spins the runtime shit in docker container and I should be good now, shouldn’t I? Let’s move to the dynamodb part.

note: at the time of writing I am using the plugin version v6.1.4

serverless-dynamodb-local

I am following documentation and installing the plugin:

$ npm install --save [email protected]

Update the plugins section:

plugins:
  - serverless-offline
  - serverless-dynamodb-local

Install dynamodb local and start it:

$ sls plugin install -n serverless-dynamodb-local
$ sls dynamodb install
$ sls dynamodb start

Mocked dynamodb will run on localhost and port 8000 so it’s necessary to update client configuration in the service code:

var db = dynamodb.New(session.New(&aws.Config{
	Region: aws.String("localhost"),
	Endpoint: aws.String("http://localhost:8000"),
}))

Note that in AWS dynamo won’t run on localhost therefore the db must have different configuration in prod. I use IS_OFFLINE environment variable to distinguish between offline and prod deployment.

provider:
  name: aws
  runtime: go1.x
  environment:
    IS_OFFLINE: ${env:IS_OFFLINE}
var db *dynamodb.DynamoDB

func init() {
	if os.Getenv("IS_OFFLINE") {
		db = ...
	} else {
		db = ...
	}
}

Start the application sls offline start --useDocker do some requests and tadaaa … application is not working because it’s not able to connect to localhost:8000, the port where dynamodb is running.

$ netstat -ntlp | grep 8000
tcp6    0    0    :::8000    :::*    LISTEN    172892/java

The reason is that the runtime is on different network since the default network mode for docker is bridge mode. After inspecting the runtime container or digging in the plugin’s codebase I’ve found a host mapping for a docker0 bridge gateway IP which is bound to host.docker.internal hostname.

So let’s fix the endpoint in the AWS config:

	Endpoint: aws.String("http://host.docker.internal:8000"),

Restart the service and voila … API gateway with dynamodb emulator are working now and local development setup is finally ready. Enjoy!