OpenShift, Cron, and Naked Domains

As an experiment, I migrated my website over to OpenShift yesterday. I've been hosting a pastebin there already, and have found the service to be both straightforward and flexible; it was time to put it to a more thorough test.

In the process, I ran into a number of interesting issues, some of which took quite some time to resolve; this post is both to help inform other potential users of the service, as well as act as a reminder to myself.

Cron

OpenShift offers a Cron cartridge, which I was excited to try out.1

The basics are quite easy. In your repository's .openshift directory is a cron subdirectory, further divided into minutely, hourly, daily, weekly, and monthly subdirectories. You drop a script you want to run into one of these directories, and push your changes upstream.

The problem is: what if I want a job to run at a specific time daily? or on the quarter hour? or on a specific day of the week?

As it turns out, you can manage all of the above, just not quite as succinctly as you would in a normal crontab. Here, for example, is a script that I run at 5AM daily; I placed it in the hourly directory so that it can test more frequently:

###!/bin/bash
if [ `date +%H` == "05" ]
then
    (
        export PHP=/usr/local/zend/bin/php ;
        cd $OPENSHIFT_REPO_DIR ; 
        $PHP public/index.php phlycomic fetch all ; 
        $PHP public/index.php phlysimplepage cache clear --page=pages/comics 
    )
fi

And here's one that runs on the quarter-hour, placed in the minutely directory:

###!/bin/bash
MINUTES=`date +%M`

for i in "00" "15" "30" "45";do
    if [ "$MINUTES" == "$i" ];then
        (
            export PHP=/usr/local/zend/bin/php ;
            cd $OPENSHIFT_REPO_DIR ;
            $PHP public/index.php githubfeed fetch 
        )
    fi
done

The point is that if you need more specificity, push the script into the next more specific directory, and test against the time of execution.

Naked Domains

Naked domains are domains without a preceding subdomain. In my case, this means "mwop.net", vs. "www.mwop.net".

The problem that cloud hosting presents is that the IP address on which you are hosted can change at any time, for a variety of reasons. As such, you typically cannot use DNS A records to point to your domain; the recommendation is to use a CNAME record that points the domain to a "virtual" domain registered with your cloud hosting provider.

However, most domain registrars and DNS providers do not let you do this for a naked domain, particularly if you also have MX or other records associated with that naked domain.

Some registrars will allow you to forward the A record to a subdomain. I tried this, but had limited success; I personally found that I ended up in an infinite loop situation when doing the DNS lookup.

Another solution is to have a redirect in place for your naked domain to the subdomain, which can then be a CNAME record. Typically, this would require you have a web server under your control with a fixed IP that then simply redirects to the subdomain. Fortunately, there's an easier solution: wwwizer. You simply point your naked domain A record to the wwwizer IP address, and they will do a redirect to your www subdomain.

I implemented www.mwop.nety domain (which is why you'll see "www.mwop.net" in your location bar), and it's been working flawlessly since doing so.

Private repositories

I keep my critical site settings in a private repository, which allows me to version them while keeping the credentials they hold out of the public eye. This means, however, that I need to use GitHub deploy keys on my server in order to retrieve changes.

This was simple enough: I created an ssh subdirectory in my $OPENSHIFT_DATA_DIR directory, and generated a new SSH keypair.

The problem was telling SSH to use this key when fetching changes.

The solution is to use a combination of ssh-agent and ssh-add, and it looks something like this:

###!/bin/bash
ssh-agent `ssh-add $OPENSHIFT_DATA_DIR/ssh/github-key && (
    cd $OPENSHIFT_DATA_DIR/config ; 
    git fetch origin ; 
    git rebase origin/mwop.net.config
)`

After testing the above, I put this in a pre_build script in my OpenShift configuration so that I can autoupdate my private configuration on each build. However, I discovered a new problem: when a build is being done, the ssh-agent is not available, which means the above cannot be executed. I'm still trying to find a solution.

Fin

I'm pretty happy with the move. I don't have to do anything special to automate deployment, and all my cronjobs and deployment scripts are now self-contained in the repository, which makes my site more portable. While a few things could use more documentation, all the pieces are there and discoverable with a small amount of work.

I'll likely give some other PaaS providers a try in the future, but for the moment, I'm quite happy with the functionality and flexibility of OpenShift.

Footnotes
  • 1 Zend Server's JobQueue can also be used as a cron replacement, but I was not keen on exposing the job functionality via HTTP.