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
Tom
Demo? Example?
tim
No demo at the moment, but if you have Docker installed running the container locally (without any modifications) does provide a basic example.