Posted by & filed under aws, Deployments, DevOps, Docker, Go, Web development.

The latest version of Golapa comes with some significant changes, namely extending support beyond Google App Engine. While we are still supporting GAE builds using the same codebase, by dockerizing Golapa we’re making it a lot easier to develop locally and deploy almost anywhere Docker is supported.

Launch pages are supposed to be simple, right? A single, very well designed page with awe-inspiring effects as you scroll coupled with an email form and you should be done. At least, until you realize that you want visitors to share your page on Facebook, Twitter, and Google+. And then what are you doing with those email addresses? Why does it take so long to see the thank you page after you submit the form? How about A/B testing your content or knowing which referring sources are providing the most signups? Where are visitors clicking? How well will this scale if you’re lucky enough to get picked up by TechCrunch?

Launch pages aren’t simple.

The latest version of Golapa doesn’t address all of these concerns, but it does provide a starting point that’s easy to develop and test locally, deploy to docker-supported environments like AWS Elastic Beanstalk and CoreOS clusters, and integrate 3rd party Javascript packages like Google Analytics.

Golapa is written in Go, leverages NGiNX to serve static content, and Redis to queue form submissions. It can be deployed on AWS Elastic Beanstalk, a CoreOS cluster, most Docker environments, and Google App Engine.

Getting started on OS X

Requirements: Boot2Docker

– Launch the boot2docker environment

– Clone the github.com/turret-io/golapa repository

> git clone https://github.com/turret-io/golapa
Cloning into 'golapa'...
remote: Counting objects: 300, done.
remote: Compressing objects: 100% (41/41), done.
remote: Total 300 (delta 17), reused 0 (delta 0)
Receiving objects: 100% (300/300), 213.35 KiB | 0 bytes/s, done.
Resolving deltas: 100% (87/87), done.
Checking connectivity... done.
> cd golapa
> ls
LICENSE          golapa                 static
README.md        linux_init.sh          templates
build.sh         osx_deactivate.sh
docker           osx_init.sh

With the repository cloned, let’s look at the contents:

  • osx_init.sh: Initializes build directory and Samba server for compiling project inside a docker container (only needed for OS X)
  • linux_init.sh: Initializes build directory and shared volume for compiling project inside a docker container (non OS X)
  • build.sh: Compiles Go code and creates a docker image containing static files, templates, and Golapa binaries
  • osx_deactivate.sh: Disables Samba server and deletes volume container
  • docker: Contains docker files and supporting files for building containers
  • golapa: Go source files
  • static: Static CSS, Javascript, fonts, and sass files
  • templates: HTML template files and email template

– Initialize the project

> ./osx_init.sh

– Build the project, providing a name for the target image to be created

> ./build.sh my-golapa-image

– Run the container with environment variables set to send an email each time a visitor submits the email form

> docker run -p 8080:80 \
-e EMAIL_HOST="[SMTP HOSTNAME:PORT]" \ 
-e EMAIL_PASSWORD="[SMTP PASSWORD]" \
-e EMAIL_FROM="[FROM EMAIL ADDRESS]" \
-e EMAIL_SSL=[FORCE SSL TRUE|FALSE] \
-e EMAIL_SENDER="[EMAIL SENDER ADDRESS]" \
-e EMAIL_SUBJECT="[EMAIL SUBJECT]" \
-e EMAIL_TO="[EMAIL TO]" \
-e SEND_VIA="email" \
my-golapa-image

What’s running? An NGiNX-powered front-end web server serves static files while passing other requests to the Golapa web server. Redis provides a queue to process successful form submissions (sending email or adding contacts to Turret.IO) and supervisord keeps everything up.

– Find the Boot2Docker IP address

> boot2docker ip

The VM's Host only interface IP address is: 192.168.59.103

– Open a web browser to http://192.168.59.103:8080

– Golapa can be stopped by pressing Control+C in the terminal

– Make changes to the templates, static files, or Go code and rebuild with ./build.sh, then re-run the previous docker command

Getting started on Linux

Requirements: docker

– Clone the github.com/turret-io/golapa repository

> git clone https://github.com/turret-io/golapa
Cloning into 'golapa'...
remote: Counting objects: 300, done.
remote: Compressing objects: 100% (41/41), done.
remote: Total 300 (delta 17), reused 0 (delta 0)
Receiving objects: 100% (300/300), 213.35 KiB | 0 bytes/s, done.
Resolving deltas: 100% (87/87), done.
Checking connectivity... done.
> cd golapa

– Initialize the project

> ./linux_init.sh

– Build the project, providing a name for the target image to be created

> ./build.sh my-golapa-image

– Run the container with environment variables set to send an email each time a visitor submits the email form. Replace 80:80 with [LOCAL PORT]:80 to bind the docker container to a different port.

> docker run -p 80:80 \
-e EMAIL_HOST="[SMTP HOSTNAME:PORT]" \ 
-e EMAIL_PASSWORD="[SMTP PASSWORD]" \
-e EMAIL_FROM="[FROM EMAIL ADDRESS]" \
-e EMAIL_SSL=[FORCE SSL TRUE|FALSE] \
-e EMAIL_SENDER="[EMAIL SENDER ADDRESS]" \
-e EMAIL_SUBJECT="[EMAIL SUBJECT]" \
-e EMAIL_TO="[EMAIL TO]" \
-e SEND_VIA="email" \
my-golapa-image

– Open a web browser to http://localhost:[LOCAL PORT]

– Golapa can be stopped by pressing Control+C in the terminal

– Make changes to the templates, static files, or Go code and rebuild with ./build.sh, then re-run the previous docker command

Deploying

Golapa can be deployed to virtually any docker-supported environments that support setting environment variables.

Currently, deploying to docker-supported environments is easiest if you have your images in a central location. To keep things simple for this article, we’ll be using Docker Hub which provides a single private repository for free.

1. Login to http://hub.docker.com

2. Click the Add Repository button and choose “Repository”

3. Provide a name for the repository (i.e. my-golapa-image) and optionally provide a description

4. Choose “Private” for the “Repository Type”

5. Click “Add Repository”

6. Push your image to the repository (you will be prompted to login)

> docker push my-golapa-image

Deploying to AWS Elastic Beanstalk

1. Create a dockercfg file with your email address and password to provide access to your Docker Hub account (this is created for you when you first run docker push and is located at ~/.dockercfg)

2. Upload the dockercfg file to an S3 bucket that is not publicly accessible

3. Create a Dockerrun.aws.json file for directing Elastic Beanstalk to your dockercfg file and hosted Docker image:

{
  "AWSEBDockerrunVersion": "1",
  "Authentication": {
    "Bucket": "YOUR.S3.BUCKET",
    "Key": "DOCKERCFG_FILE"
  },
  "Image": {
    "Name": "[NAMESPACE]/my-golapa-image",
    "Update": "true"
  },
  "Ports": [
    {
      "ContainerPort": "80"
    }
  ],
  "Logging": "/var/log/nginx"
}

4. Login to the AWS Elastic Beanstalk console

5. Click “Create New Application” and enter an application name (i.e. my-golapa-project) and click “Next”

6. Choose the “Web Server” environment tier, “Docker” predefined configuration, and “Load balancing, autoscaling” environment type, then click “Next”

7. Choose “Upload your own” application and select your Dockerrun.aws.json file you just created, then click “Next”

8. Update the environment name and URL to your choosing, then click “Next”

9. Optionally, choose to create create the environment inside a VPC (if selected, another step will added to the wizard) and click “Next”

10. Choose an instance type (micro instances are the cheapest), keypair, and adjust any other options as needed, then take note of the instance profile

11. Open a new tab to the AWS IAM console and select “Roles”

12. Click the role name that matches the instance profile from the wizard

13. Click “Attach Role Policy” and create a policy that provides access to the dockercfg file you uploaded to S3 — example:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::YOUR.S3.BUCKET/DOCKERCFG_FILE"
      ]
    }
  ]
}

14. After adding the policy, close the IAM tab and return to the Elastic Beanstalk console

15. Click “Next” and provide any desired environment tags, then click “Next” to enter the final review section of the wizard

16. Review your configuration settings and click “Launch”

Monitor the creation of the environment for any errors.

After the environment has launched without errors, click “Configuration” and then modify the “Software Configuration” section with the Golapa environment variables you require.

Re-deploying new versions after you’ve made changes to the Go code, static content, or templates is simple:

(make changes...)
> ./build.sh [IMAGE NAME]
(test your changes locally)
> docker push [IMAGE NAME]

Then deploy to Elastic Beanstalk by clicking “Upload and deploy”, choosing “All Versions”, selecting the version to re-deploy, clicking “Deploy”, selecting the environment, and finally, clicking “Deploy” once again.

Adjust your autoscaling preferences as needed.

Remember: your EC2 instances are billed by the hour and S3 also has storage fees. To avoid excess usage fees, destroy the Elastic Beanstalk environment that we created and remove the file uploaded to S3 once you’re no longer using the service.

Deploying to a CoreOS cluster

If you need help launching a CoreOS cluster, see https://coreos.com/docs/#running-coreos

1. Before launching your cluster, add a write_files key to your cloud-config file that will provide authentication for Docker Hub. You can get the auth and email values from your ~/.dockercfg file.

write_files:
    - path: /home/core/.dockercfg
      owner: core:core
      permissions: 0644
      content: |
        {
          "https://index.docker.io/v1/": {
            "auth": "[DOCKER HUB AUTH]",
            "email": "[DOCKER HUB EMAIL]"
          }
        }

2. Launch a cluster containing at least 3 machines

3. Create a systemd unit for Golapa named golapa.1.service, replacing [NAMESPACE]/my-golapa-image with the appropriate Docker Hub repository and updating the environment variables per your configuration:

[Unit]
Description=Golapa service
After=docker.service 
Requires=docker.service 

[Service]
User=core
ExecStartPre=-/usr/bin/docker kill golapa
ExecStartPre=-/usr/bin/docker rm golapa
ExecStartPre=/usr/bin/docker pull [NAMESPACE]/my-golapa-image
ExecStart=/usr/bin/docker run --name golapa -p 80:80 \
-e EMAIL_HOST="[SMTP HOSTNAME:PORT]" \ 
-e EMAIL_PASSWORD="[SMTP PASSWORD]" \
-e EMAIL_FROM="[FROM EMAIL ADDRESS]" \
-e EMAIL_SSL=[FORCE SSL TRUE|FALSE] \
-e EMAIL_SENDER="[EMAIL SENDER ADDRESS]" \
-e EMAIL_SUBJECT="[EMAIL SUBJECT]" \
-e EMAIL_TO="[EMAIL TO]" \
-e SEND_VIA="email" \
[NAMESPACE]/my-golapa-image
ExecStop=-/usr/bin/docker rm -f golapa

[X-Fleet]
X-Conflicts=golapa.*.service

4. Duplicate golapa.1.service for each machine, changing the filename each time:

> cp golapa.1.service golapa.2.service
> cp golapa.1.service golapa.3.service

Note: the X-Conflicts=golapa.*.service prevents any two golapa.x.service units from running on the same machine, creating a highly available service when a load balancer is added.

5. Start all three services using fleet:

> fleetctl --tunnel=[MACHINE IP ADDRESS]:22 start golapa.*

Note: Using the –tunnel parameter allows us to remotely send unit files to the cluster over an SSH tunnel. Alternatively, you can copy the unit files to a single machine on the cluster, SSH into that machine, and run fleetctl without the –tunnel parameter.

6. Check the status of the launched services. This may take some time as each machine pulls the requires images from the repository.

> fleetctl --tunnel=[MACHINE IP ADDRESS]:22 list-units
UNIT			STATE		LOAD	ACTIVE		SUB		DESC		MACHINE
golapa.1.service	launched	loaded	activating	start-pre	Golapa service	6fbef5ae.../10.0.0.6
golapa.2.service	launched	loaded	activating	start-pre	Golapa service	30eb769f.../10.0.0.4
golapa.3.service	launched	loaded	activating	start-pre	Golapa service	e1dc6d30.../10.0.0.5

(... SUB will change to running when the unit has successfully started ...)

> fleetctl --tunnel=[MACHINE IP ADDRESS]:22 list-units
UNIT			STATE		LOAD	ACTIVE	SUB	DESC		MACHINE
golapa.1.service	launched	loaded	active	running	Golapa service	6fbef5ae.../10.0.0.6
golapa.2.service	launched	loaded	active	running	Golapa service	30eb769f.../10.0.0.4
golapa.3.service	launched	loaded	active	running	Golapa service	e1dc6d30.../10.0.0.5

8. Setup a load balancer to distribute the load between the three machines for a highly available service (this process differs depending on your provider, i.e. AWS Elastic Load Balancing, Custom HAProxy setup, etc.).

Re-deploying new versions after you’ve made changes to the Go code, static content, or templates is simple:

(make changes...)
> ./build.sh [IMAGE NAME]
(test your changes locally)
> docker push [IMAGE NAME]

Then deploy by restarting each CoreOS machine in the cluster, or by destroying and re-starting each unit file: fleetctl --tunnel=[MACHINE IP ADDRESS]:22 destroy golapa.* && sleep 5 && fleetctl --tunnel=[MACHINE IP ADDRESS]:22 start golapa.*

Enjoy!

Sign up for Turret.IO – the only email marketing platform specifically for developers

2 Responses to “Golapa v2: Dockerized Launch Page Written in Go”

    • tim

      No demo at the moment, but if you have Docker installed running the container locally (without any modifications) does provide a basic example.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *