Revel is a fantastic Go web framework, like Rails for Go. However, deploying isn’t as straightforward as compiling your app and running the binary: you’re supposed to install the Revel command line tool, then use that to launch your app. This goes ‘against the grain’ somewhat versus AWS Elastic Beanstalk, where deployments are typically a matter of putting your binary named application
in a zip file.
So in this post I lay out the technique I use to hammer the square shape that is Revel into the round hole that is Elastic Beanstalk.
(Having said that, it may be possible to configure EB to use the Revel command line launcher, however EB’s documentation isn’t particularly conducive to figuring out how to do that, unfortunately.)
Firstly I’ll recap how EB likes things to work. So here’s how you would deploy the simplest no-frameworks-no-dependencies Go app to EB:
GOARCH=amd64 GOOS=linux go build -o myTempFolder/application
myTempFolder
) zip MyEBApp.zip application
So here’s how I propose compiling your Revel app so that it won’t need the revel
command line tool to start up. This also cross-compiles for amd64/linux which the revel tool does not currently easily support.
Note: You’ll have to replace myOrganisation/myRepo
in the examples below to whatever is appropriate for your git repo.
revel clean
revel build
- this auto-generates app/tmp/* and app/routes/*.Replace the auto-generated app/tmp/main.go
with a customised one that is simpler for EB to launch: (this file will be detailed below)
cp myMain/main.go app/tmp
Build, starting from the new ‘main.go’:
cd app/tmp && GOARCH=amd64 GOOS=linux go build -o ../../tempBuild/application
Copy in your views/config/assets:
mkdir -p tempBuild/src/github.com/myOrganisation/myRepo/app
cp -R app/views tempBuild/src/github.com/myOrganisation/myRepo/app
cp -R conf tempBuild/src/github.com/myOrganisation/myRepo
cp -R public tempBuild/src/github.com/myOrganisation/myRepo
Copy in some revel essentials:
mkdir -p tempBuild/src/github.com/revel/revel
cp -R ~/go/src/github.com/revel/revel/conf \
tempBuild/src/github.com/revel/revel
cp -R ~/go/src/github.com/revel/revel/templates \
tempBuild/src/github.com/revel/revel
Copy in your .ebextensions
folder (this is how you configure EB as needed, which I’ll explain below)
cp -R .ebextensions tempBuild
Zip it all up, such that everything is relative to the tempBuild folder:
cd tempBuild && zip -r "../EBApp.zip" * .ebextensions
My main.go looks like the below. If you choose to remove the support for environment variables and startup logging, you could make it even simpler:
package main
import (
"log"
"os"
"strconv"
"github.com/myOrganisation/myRepo/app/tmp/run"
"github.com/revel/revel"
)
func getEnvWithDefault(key string, def string) string {
e := os.Getenv(key)
if e == "" {
return def
}
return e
}
func main() {
runMode := getEnvWithDefault("RUNMODE", "prod")
port, _ := strconv.Atoi(getEnvWithDefault("PORT", "5000"))
importPath := getEnvWithDefault("IMPORTPATH",
"github.com/myOrganisation/myRepo")
srcPath := getEnvWithDefault("SRCPATH", "src")
log.Println("Main override")
log.Println("Run mode:", runMode)
log.Println("Port:", port)
log.Println("Import path:", importPath)
log.Println("Src path:", srcPath)
log.Println("Starting...")
revel.Init(runMode, importPath, srcPath)
run.Run(port)
}
The above allows you to configure the runmode/port/import path/source path using EB environment variables. Having said that, I do not customise my environment variables at all, I simply use the defaults that are passed as the second parameter to the getEnvWithDefault function in all cases.
Note: To configure environment variables, go to: AWS Console > Services > Elastic Beanstalk > My Application > My Environment > Configuration > Software > Modify > Environment properties. This is a great place to put API keys too, by the way!
And…. well, replacing the main file is a little bit of a ‘code smell’, but it does work well, and is simple, and as a rule of thumb: simple = reliable.
Here are some various EB config files that I recommend:
To make EB pick up the logs from Revel, add the file .ebextensions/revel-logs.config
:
files:
"/opt/elasticbeanstalk/tasks/taillogs.d/my-revel-logs.conf" :
mode: "000755"
owner: root
group: root
content: |
/var/log/revel*
To make EB serve your public files, add .ebextensions/revel-statics.config
:
option_settings:
aws:elasticbeanstalk:container:golang:staticfiles:
/public: src/github.com/myOrganisation/myRepo/public
To get EB to auto-upgrade any incoming HTTP requests to HTTPS, and to get it to proxy through to your Revel binary which is listening on port 5000, add .ebextensions/nginx/conf.d/elasticbeanstalk/00_application.conf
:
location / {
set $redirect 0;
if ($http_x_forwarded_proto != "https") {
set $redirect 1;
}
if ($http_user_agent ~* "ELB-HealthChecker") {
set $redirect 0;
}
if ($redirect = 1) {
return 301 https://$host$request_uri;
}
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Thanks for reading, I hope this helps someone, and have a great week!
Photo by Jules D on Unsplash
Thanks for reading! And if you want to get in touch, I'd love to hear from you: chris.hulbert at gmail.
(Comp Sci, Hons - UTS)
Software Developer (Freelancer / Contractor) in Australia.
I have worked at places such as Google, Cochlear, Assembly Payments, News Corp, Fox Sports, NineMSN, FetchTV, Coles, Woolworths, Trust Bank, and Westpac, among others. If you're looking for help developing an iOS app, drop me a line!
Get in touch:
[email protected]
github.com/chrishulbert
linkedin