Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewnimmo/470e0b6e8b76d6fa96577866cdfb1f8c to your computer and use it in GitHub Desktop.
Save andrewnimmo/470e0b6e8b76d6fa96577866cdfb1f8c to your computer and use it in GitHub Desktop.
herokuish build+deploy using LXC

Introduction

Heroku differs from traditional build & deploy by having the concept of "buildpacks", which are a generic method to identify, compile and package builds. The traditional method here would be to have hardcoded build commands, in a Makefile for instance. Buildpacks generally make custom build+deploy steps uneccessary, although one can specify custom buildpacks if necessary.

When notifying Heroku that a deploy is needed (usually by a Github webook), Heroku will run through all supported buildpacks until one can identify your app, which is then compiled and packaged by the buildpack. The output of this process is referred to as a "slug", which contains your app and all dependencies. For instance, a Python app would have the virtualenv packaged inside it. Heroku automatically deploys slugs that are built successfully.

This document describes a way to use LXC and existing third-party tools to reproduce this setup.

You need to have a server running that accepts webhooks and can take action based on them - DeadCI is assumed below. A webhook and container must be configured for every app that you wish to build, for security reasons - Heroku supplies a dashboard and a command-line tool which automates this initial setup.

Install LXC, create a new "builder" container

sudo apt-get install lxc
sudo lxc-create \
    -t download -n builder -- -d ubuntu -r trusty -a amd64

Start up the container and initialize it

sudo lxc-start -n builder --daemon
sudo lxc-wait -n builder -s RUNNING

Now you can attach to the container:

sudo lxc-attach -n builder

Inside the container, install "cedarish", which installs a heroku-like set of packages:

sudo apt-get update
sudo apt-get -y install git make
git clone https://github.com/progrium/cedarish.git
pushd cedarish
cedar14/cedar-14.sh 
popd

Now you can install Herokuish, which manages buildpacks, builds and slugs ("slugs" are deployment artifacts):

wget "https://github.com/gliderlabs/herokuish/releases/download/v0.3.0/herokuish_0.3.0_linux_x86_64.tgz"
tar zxf herokuish_0.3.0_linux_x86_64.tgz
chmod +x ./herokuish

Your container is set up and ready for builds!

If you want to isolate each app to its own container, just copy the "builder" one you just made:

sudo lxc-stop -n builder
sudo lxc-clone -o builder -n socorro-collector_builder

Running builds

To initiate a build, run something like this script from the host (triggered by DeadCI via a Github webhook in this case):

#!/usr/bin/env bash

sudo lxc-start -n ${DEADCI_REPO}_builder
sudo lxc-attach -n ${DEADCI_REPO}_builder -- \
    /usr/bin/git clone https://${DEADCI_DOMAIN}/${DEADCI_OWNER}/${DEADCI_REPO}.git /app
sudo lxc-attach -n ${DEADCI_REPO}_builder -- \
    /bin/bash -c "cd /app && git checkout ${DEADCI_COMMIT}"
sudo lxc-attach -n ${DEADCI_REPO}_builder -- \
    /usr/bin/env USER=ubuntu /home/ubuntu/herokuish buildpack install
sudo lxc-attach -n ${DEADCI_REPO}_builder -- \
    /usr/bin/env USER=ubuntu /home/ubuntu/herokuish buildpack build
sudo lxc-attach -n ${DEADCI_REPO}_builder -- \
    /home/ubuntu/herokuish slug generate
sudo lxc-attach -n ${DEADCI_REPO}_builder -- \
    /home/ubuntu/herokuish slug export > ${DEADCI_REPO}_slug.tgz
sudo lxc-stop -n ${DEADCI_REPO}_builder

The slug will be in ./*_slug.tgz on the host.

If you want to get the slug onto the host, you can just pull it out of the container's filesystem in /var/lib/lxc/socorro-collector_builder/rootfs/home/ubuntu/slug.tgz

Deploying slugs

To deploy the app on a different host (EC2, LXC container, etc), first set up the host:

sudo mkdir /app
sudo apt-get install python-pip curl
sudo pip install honcho

Unpack the app and run it:

sudo su - ubuntu
tar -C /app -zxf slug.tgz
cd /app
. .profile.d/python.sh 
export PORT=8000
/usr/local/bin/honcho start

The app should now be running on specified port:

curl -F 'ProductName=Firefox' -F 'Version=1.0' localhost:8000/submit

Note that the user is "ubuntu" because this is what we specified way back when we did the herokuish buildpack build. If you want to change the user or the deployment location, you need to do this back in the build step - this uses python virtualenv which are not relocatable.

See https://help.ubuntu.com/lts/serverguide/lxc.html for more info on using LXC, info on other tools are in their respective git repos.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment