Stars

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.)

Typical EB Golang deployment

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:

  • Make a new temporary folder
  • Compile your binary for amd64/linux: GOARCH=amd64 GOOS=linux go build -o myTempFolder/application
  • Zip it up (current working folder being myTempFolder) zip MyEBApp.zip application
  • Deploy the zip file using the AWS EB console

Deploying Revel

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.

  • Run revel clean
  • Run 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
    
  • And the resulting zip file should be deployable in your AWS EB console.

main.go

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.

.ebextensions

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.

Chris Hulbert

(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



 Subscribe via RSS