Skip to content

Compiling Go from Windows to Linux to run on AWS Lambda

As a cloud architect, I am currently working with several AWS Serverless Services.  One of these is an amazing platform called Lambda.  I won’t get into describing in details of what it is, but in general, it allows you to run code without having to setup any servers, and is pretty awesome at responding to and handling events.  For a better and more detailed description on this, go here.  In addition to using AWS Lambda, I am also using a programming language called Go, also known as GoLang, which you can read more about here.  At time of this writing, there is no native support for GoLang on AWS Lambda, however using a JavaScript wrapper, it is possible to run a GoLang binary.

There are more than a few examples of running GoLang inside of AWS Lambda, however the one I chose to use is located here.  It was relatively easy to get an example working, with a few caveats that I will outline below.  The first issue was getting a version of the binary that will run on Linux with an amd64 chip architecture.  This is the operating system and chip architecture that AWS Lambda currently runs on.  You see, I am using Windows to develop my code on.  I know a lot of techies would just say “switch over to Mac” or use Ubuntu to develop on, but I am just use to windows, work must faster with it, and prefer it, especially with Windows 10. I did use a pretty bad a$$ (and expensive) Mac for a few months, but ended up going back “to the dark side”.

 

Anyway, in order to get my GoLang code to run on AWS Lambda, I had to follow 2 additional steps prior to compiling my code.  I had to set the desired operating system and the desired chip architecture.  This is known as “Cross Compiling” and has changed substantially (for the better) over the life of GoLang.  I am only going to cover how to do this with versions 1.5.x and later because it’s just so much easier now.

  • Basically I had to set these 2 values:
    set GOOS=linux
    set GOARCH=amd64
  • I then called build to create a binary that would be compatible to run on AWS Lambda:
    go build -v main.go

You’ll notice that i included a -v parameter.  This was done to print to the console a list of all dependencies that were included for my binary to run independently.  I won’t include a copy of the GoLang code or the JavasScript code because you can find an example in the second link I provided above.  Kudos to Jason Mooberry for providing the framework code so I did not have to attempt to write it myself.

In addition to the pre-compile steps, I also was shortly delayed in getting it to work because I forgot to include the “childprocess” JavaScript module.  To do this i needed a “node_modeles” folder in the root and then needed to include the childprocess module itself in a sub folder.  Any referenced Node.JS module must be included, as AWS Lambda will not know what to do with your code without them.  The last piece of the Javascript puzzle is to make sure you include the main file index.js  or whatever you decide to call it, however your lambda function will need to be configured accordingly based on the file name and the handler name.

The last and final delay I ran into was trying to get the lambda function to run at all.  I kept running into permissions exceptions within lambda when it would try to invoke the GoLang module.  Here is the error messag i was receiving:

START Request:
2016-03-20T18:26:07.405Z	74xiip7892mhm26b	{ [Error: spawn ENOENT] code: 'ENOENT', errno: 'ENOENT', syscall: 'spawn' }
2016-03-20T18:26:07.464Z	74xiip7892mhm26b	{ [Error: spawn ENOENT] code: 'ENOENT', errno: 'ENOENT', syscall: 'spawn' }
2016-03-20T18:26:07.485Z	74xiip7892mhm26b	{ [Error: spawn ENOENT] code: 'ENOENT', errno: 'ENOENT', syscall: 'spawn' }
2016-03-20T18:26:07.505Z	74xiip7892mhm26b	{ [Error: spawn ENOENT] code: 'ENOENT', errno: 'ENOENT', syscall: 'spawn' }
END Request:	
Process exited before completing request

After quite a bit of research and testing, I found that I needed to set some execution permissions on the binary.  I was not able to do this in windows, as it seems there is no way that I could find a way to set execute permissions on the file.  So, I setup Virtual Box for windows, and installed the latest version of the 64bit Ubuntu client which you can download here.  Once this was done, I copied over all of the files and folders mentioned above into my Ubuntu instance.  There is a linux command known as “chmod” which can be used to set permissions, and you can find more about this here.  I was able to achieve the same thing simply by right clicking the binary, selecting properties, and then going into the permissions tab.  Under “Others Access” I selected Read and Write, and then also made sure the Execute check box was checked, and then closed the properties window, compressed the node_modules folder, the index.js file, and the main GoLang binary all into one zip file.  Lastly I then copied it back over to my windows machine and was on my way back into the normal process of creating a lambda function from a zip file and was excited to see it work and log to AWS Cloud Watch on the first run.

One last word of caution, I had originally installed GoLang on my Ubuntu instance, had to setup SSH first, and compile the code there.  However since I do not use Ubuntu enough, I was able to achieve the same thing much faster by just doing what I outline above in windows and avoid having to fix a few other problems I was having with my Ubuntu install.  In case it matters I am using Windows 10 64bit.  In the end, I guess the last comment I can add, is that I really really really can’t wait until GoLang is natively supported in AWS Lambda !

Written for ServerlessArchitecture.com
Written by Jeff Mangan

Leave a Reply