Choosing a VPS Hosting Provider

I finally got my site up and running. I could have launched it a lot sooner, but I decided to take a detour in the realm of VPS providers. It makes sense that one of my first posts would summarize my findings.

If you’re in the market for a Virtual Private Server (VPS), there’s a good chance you’ve looked at Amazon EC2, Linode, and DigitalOcean. Amazon essentially defined the market. Linone is a longtime darling of the Hacker News crowd. DigitalOcean is the venture capitalist-funded upstart. All are worthy of your business. Here’s my unscientific, unquantified impression of their relative strengths and weaknesses.

Amazon Web Services

Amazon Web Services (AWS) is a smorgasboard of services, of which EC2 is just one. All services are supported by a comprehensive API with client libraries in just about any language you’re likely to care about. The array of services is their greatest advantage and their greatest weakness. Amazon has the tools to build just about anything you can imagine. Way beyond virtual servers, they have globally distributed CDN, hosted NoSQL and SQL servers, persistant virtualized storage (EBS), a way to declare and instantiate all your services (CloudFormation), and authentication scheme to tie it all together. Even that just scratches the surface. Email, SMS, APNS…the list goes on. But where to start? It’s hard to get your head around it all, and it’s easy to get overwhelmed by the information. You have a site to launch; it’s better not to get too distracted.

EC2 has a reputation for being overpriced and slow. But this is all relative to your use case. EBS is blamed for much of the slowness. I can buy EBS-optimized EC2 instances, but then I’m in a $50/month/server scenario, and I’m looking at $10-$20 price range. If I had an infrastructure than could truly take advantage of the flexibility of EBS, and leverage the synergy of complementary AWS services, I’d probably be happy to pay the difference. In short, it feels like it is built for the likes of Netflix, not the small app builder or blogger.

Also, pricing can be hard to estimate. If you’re using Amazon’s pricing estimator, plug in a couple terabytes of outgoing data (the amount you get for free at DigitalOcean and Linode). Your cost just went up a couple of hundred dollars.

Linode

Linode was doing VPS before it was cool. They have a dedicated following who praise their tech support. Their web UI is an eyesore but complete. And if you’re doing it right, you’re setting up your server with the API instead of the web UI.

Linode exposes their virtualization underpinnings more than others. Setting up a server is a multistsep process. Choose a distribution, initialize a disk, apply the image, boot the image. This reflects the reality, but sometimes I wonder if it would be better to present a more abstract interface.

Linode has a unique initialization process. It uses standard scripts (shell scripts, or python, etc., if you image has that installed). They have special syntax to collect variables via web form (so you can enter the MySQL password, for example), and to include (or “source”) other StackScripts. Stackscripts can be shared with the Linode community.

DigitalOcean

DigitalOcean is slick and clean. They have a nice UI which feels simple and much less powerful than Linode. Again, you’re probably using an API instead of the web UI, so it is less important than it might seem.

DigitalOcean supports cloud-init for images that take advantage of it. You can provide a cloud-init configuration file as user data that is available to the image the first time it boots up. It’s no more functional that Linode’s stackscripts, but the configuration file is more delcarative, where the StackScript approach is more procedural. It’s really easy to declare, say, package requirements and let cloud-init take care of the process of installing the package.

DigitalOcean supports associating an SSH public key with your account and have any new image use that public key for login. Linode supports public key insofar as you can add it to a StackScript and put it in the right place, but it was nice to have it build into the deployment process.

If you’re a FreeBSD fan DigitalOcean has images for you.

Conclusion

I decided that I was spending to omuch time reading documentatin with AWS. For the most part I had decided on DigitalOcean, and had built up a nice workflow to setup and manage a wordpress installation. My informal benchmarks didn’t show much of a difference between DigitalOcean and Linode. As it turns out, I don’t have much of a strong preference between the two. If the workflow offered by Linode fits your style better, I have no doubt you’d be pleased.

Easy Network Scripting

I just learned about nc and was so excited I had to write about it. From the man page:

The nc (or netcat) utility is used for just about anything under the sun involving TCP or UDP. It can open TCP connections, send UDP packets, listen on arbitrary TCP and UDP ports, do port scanning, and deal with both IPv4 and IPv6. Unlike telnet(1), nc scripts nicely, and separates error messages onto standard error instead of sending them to standard output, as telnet(1) does with some.

It also communicates with UNIX Domain Sockets easily. I have been using Python for that, but this is easier for simple and one-off tasks.

Better WordPress Management through Fabric

One of my goals in setting up a WordPress site was to automate the deployment, configuration, and maintentance. More specifically, I wanted the following:

  • Provider intependence. I should be albe to easily move my site to another hosting provider.
  • Automanted restore. I couldn’t be in a posistion where restoring from backup meant manually tapping in SQL load statements.

There’s more I want to do long-term, but that’s the bare minimum. The easiest way I found was Fabric. Fabric has a low barrier to entry because it is straigtforwardly build on top of Python functions and the commands on the target machine.

If you need to run apt-get -y install mysql-server on the target host, the Fabric call for that is:

run('apt-get -y install mysql-server')

To make the command available outside the script (e.g., from the command line), just add it in a function:

def install_msql():
    run('apt-get -y install mysql-server')

Then from the shell you can run

$ fab install_mysql

and you computer will connect to the sever and run the apt-get call above.

There’s a little bit more to it in getting your hosts, user, and paswords or keys set up, but it’s all pretty straightforward. It works over SSH so there’s no agent to install on the server.

All your functions can be parameterized in pretty obvious ways. Your backup function might look like

def backup(dbname, dbuser):
    run('mysqldump -u {0} {1} > $HOME/{1}.sql'.format(dbuser, dbname))

There are additional commands to upload and download files, to sync a directory, and other utilities.

There are drawbacks. One is that when I say “obvious,” I mean “obvious if you know Python.” It helps if you’re familiar with installing Python packages and some basic syntax.

Another drawback is the lack of idempotence. I can run apt-get -y install mysql-server multiple times without consequence. But if I run the WordPress cli, wp core install installs wordpress the first time I call it. If I call it again (after the files are already there), it throws an error.

I can live with this for now. I really want the initial setup, which always starts with a clean slate. Extending it modify an existing installation can wait for another day.

Default Arguments in Swift Functions

Default Parameters in Swift

Its the little things that make my life as programmer easier. Like default parameters in Swift. From the The Swift Programming Language (Swift 2.1):

You can define a default value for any parameter in a function by assigning a value to the parameter after that parameter’s type. If a default value is defined, you can omit that parameter when calling the function.

I was working on a swifty implementation of the DigitalOcean API and I came up with a monster function definition, like this:

func createDropletNamed(name: String,
    region: String = "nyc3",
    size:String = "512mb",
    image:String = "ubuntu-14-04-x64",
    keys:[String] = [],
    backups:Bool = false,
    ipv6:Bool = true,
    userData:String = "",
    privateNetworking:Bool = false,
    callback:(NSDictionary?, ErrorType?) -> Void = {_,_ in }) {

In the days of Objecective-C, if I wanted simpler convenience versions of this call, I’d define additional functions which take fewer arguments,[1] and then call the monser function by suppliying some defaults. You see that pattern all the time in Objective-C initializers.

Thanks to the magic of default parameters, that’s not nececcary. Any of the following calls are valid based on the original definition with default arguments:

createDroplenNamed("devserver1")

createDropletNamed("devserver2", size: "1gb")

createDropletNamed("devserver3") { result, error in
    //handle callback
}

The swift manual advises lising arameters with default arguments after paramters that always require arguments. Elsewhere, it recommneds that closures always come in the last position to support the trailing closure syntax. Those two rules together imply that if you have a funciton with default arguments and a closure, the closure must take a default argument. Luckily it’s pretty easy to define an empty closure like {_,_ in }.


  1. Actually, I’d probably define a function that takes an NSDictionary, but then I’d lose auto-completion and type checking.  ↩

Goals for this Site

My goals this blog are to develop a habbit of publishing writing and photographs. The key thing to both these activities is to practice regularly and to publish. This is my place to do that.

I used to have a job writing for a living. I was pretty good at it, and I was generally productive. I also enjoyed it. Now my job is coding, not writing. I always intended to keep writing stuff, but that hasn’t happened. This is an effort to publish a few times a week. I’m sure that in the beginning meeting that goal will mean publishing some mediocre to poor articles. For now, publishing a poor article is a minor improvement over publishing nothing.

Photography is similar to writing in that I always think I’m going to do a lot more than I actually do. My goal for photography to post a few pictures every week or two. These will be new pictures I’ve taken in the previous week, not a dive into my archive. This might include poor photographs taken in my back yard. Again, I consider this a minor improvement over publishing nothing.

For now at least, the goals are personal. I don’t have any goals of readership or income. I doubt I’ll every change that for this particular blog.