Using s3cmd to make interactaction with Amazon S3 easier, including simple backups

We use Amazon Web Services quite a bit here. We not only use it to host most of our clients’ applications, but also for backups. We like to use S3 to store our backups as it is reliable, secure and very cheap. S3 stands for Amazon’s Simple Storage Service, it is more or less a limitless place to store data. You can mount S3 as a network hard drive but it’s main use is to store objects, or data, that you can retrieve at a low cost. It has 99.999999999% durability, so you most likely won’t lose anything, but even if you do, we use produce multiple backups for every object.

One thing we’ve noticed is that some people have issues interacting with S3, so here are a few things to help you out. First, if you are just looking to browse your S3 you can do so via your AWS Console or I like to use S3Fox. However, when you are looking to write some scripts or access it from the command line it can be difficult if you don’t use some pre-built tools. The best one we’ve found is s3cmd.

s3cmd allows you to list, update, create, delete objects and buckets in your S3. It’s really easy to install. Depending on your distribution of linux you can most likely get it from your package manager. Once you’ve done that you can configure it easily via ‘s3cmd –configure’. You’ll just need access credentials from your AWS account. Once you’ve set it up lets go through some useful commands.

To list your available buckets:

$ s3cmd ls
2013-03-08 19:45  s3://setfive-db

To create a bucket:

$ s3cmd mb s3://setfive-test
Bucket 's3://setfive-test/' created

To list the contents of a bucket:

$ s3cmd ls s3://setfive-db
2013-04-02 07:18 440877587   s3://setfive-db/backup-2013-04-02.sql.gz
2013-04-03 07:19 441482415   s3://setfive-db/backup-2013-04-03.sql.gz
2013-04-04 07:19 441527737   s3://setfive-db/backup-2013-04-04.sql.gz
2013-04-05 07:18 442114096   s3://setfive-db/backup-2013-04-05.sql.gz

To put a file in the bucket it is very easy, just run (ie move tester-1.jpg to the bucket):

$ s3cmd put tester-1.jpg s3://setfive-db/tester-1.jpg
tester-1.jpg -> s3://setfive-db/tester-1.jpg  [1 of 1]
 6312 of 6312   100% in    0s    33.98 kB/s  done

To delete the file you can run:

$ s3cmd del s3://setfive-db/tester-1.jpg
File s3://setfive-db/tester-1.jpg deleted

These are the basics. Probably the most common uses that we see are doing backups of data from a server to S3. An example of a bash script for this is as follows:

#!/bin/bash
SQL_FILE="$(date +%m-%d-%y).sql"
mysqldump -ubackupuser -p'backupuser-password' --all-databases > "$SQL_FILE"
RC = $?
if [[ $RC != 0 ]] ; then
        echo "Failure, mysqldump failed to dump"
        exit $RC;
fi

gzip "$SQL_FILE"
RC = $?
if [[ $RC != 0 ]] ; then
        echo "Failure, gzip failed to zip!"
        exit $RC;
fi

s3cmd put $SQL_FILE.gz s3://mybackup-db/$SQL_FILE.gz

RC = $?
if [[ $RC != 0 ]] ; then
        echo "Failure, s3cmd failed to put file in bucket"
        exit $RC;
fi


rm $SQL_FILE.gz

In this script it will just output the the console any errors. As you are most likely not running this by hand every day you’d want to change the “echo” statements to be mail commands or another way to alert administrators of an error on the backup. If you want to backup more than once a day all you need to change is the way the SQL_FILE variable is named to include hours for example.

This is a very simple backup script for MySQL. One thing that it doesn’t do is remove any old files, there is no reason for this to happen in the script. Amazon now has object lifecycles which allows you to automatically expire files in a bucket that are older than 60 days for example.

One thing that many people forget to do when they are making backups is to make sure that they actually work. We highly suggest that you once a month have a script which will check that whatever you are backing up is valid. This means if you are backing up a database that it checks to make sure that the database will reimport and that the data is valid (ie a row that should always exist does). The worst thing is finding out when you need a backup that your backup failed ages ago and you have no valid ones.

Make sure that your backups are not deleted quicker than it would take you to discover a problem. For example, if you only check your blog once a week, don’t have your backups delete after 5 days as you may discover a problem too late and your backups will also have the problem. Storage is cheap, keep backups for a long time.

Hope s3cmd makes your life easier and if you have any questions leave us a comment below!

Recruiters - Before You Call, Do a Little Research

As some of you may know we right now are http://www.setfive.com/careers">hiring a mid-level engineer for our team. We’ve noticed in the past few weeks quite the influx of recruiters calling us trying to fill the position. As a company we’ve never used a recruiter in the past, its not that we’ve been closed minded to it, it’s just that we never have had a good experience with one for multiple reasons.  We’re paying the recruiter part a fee for finding us these great people, so they should be doing a little work on their end too.

With a recruiter we expect that the applicant has been pre-screened so that they match what we’re looking for roughly.  Half the time we have anyone call us they don’t even know what type of company we are, come on at least visit our webpage.  I don’t want to have to explain that we are a PHP shop with a heavy Symfony influence, you should already know that.  Of course, once we mention PHP and that we’re looking for a mid level person, the recruiter always has someone that we need to talk to.  This is the best fit for us.

This brings me to my second pet peeve, non-technical recruiters doing technical recruiting.  Now the recruiter know’s we want PHP developers, so they filter their resumes by PHP.  Often the next question is oh are you using Apache? Tomcat? IIS? Node?  For the most part, what does this have to do with it, but no we aren’t primarily using java or a javascript web server.  Often it is clear the recruiter who insists they’ve personally screened the person has no clue what they are talking about, they just are trying to match keywords to a resume.

Third, stop pushing to get me to come to your office to interview candidates I have no idea who they are.  Often on these calls after they’ve learened who we are and what we want, they want me to jump on a call or come into their office to do interviews with their perfect match candidates.  Everyone is busy, I want to see some resumes before going into these first round interviews, otherwise they could be a total waste of both our time.

Lastly, we’re a consulting firm, this means we have clients.  I can’t tell you how many times a recruiter doesn’t look at our clients list and then proceeds to give us people who still work for our clients.  A heads up, most of our contracts do not allow us to hire directly from a client while we are engaged with them (some even for a period there after).  Nevertheless, if the client ever saw us and thought we’d were stealing or aggressively recruiting their employees we can kiss that relationship good bye.

What do I want from a recruiter?  First, I want you to have some technical knowledge, at least know what groups of technologies go together and that LAMP is not a word but an acronym.  Second, take 5-10 minutes, look at our website, projects, blog, and clients make sure whomever you are telling us is a great fit actually has a good chance of being a good fit.  Third, send me a resume, remove all the contact information if you’re worried about us going direct to them, before trying to push me to either jump on a phone interview or come to your office.

Finally, if I’ve said no thank you we’re fine for now, do not continue to email and call me saying that you do have a better candidate.

This may come off as a bit of a rant, but really I hope some recruiters read this and understand that we would be happy to look at your candidates if you’ve put a little effort into making sure they are actually a good fit.

 

Symfony2 Cache Clear in Prod Fails? Using JMSDiExtraBundle?

Just a quick one out there as I saw a bunch of posts trying to get around the following error:

 $ php app/console cache:clear --env=prod
Clearing the cache for the prod environment with debug true
PHP Fatal error:  Cannot redeclare class EnhancedProxy_19abbe87dfe07b1a52563cac33dd823f08c79506\__CG__\Setfive\XXBundle\Controller\AwesomeController in /home/sf/sf/app/cache/pro_/jms_diextra/proxies/Setfive-XXBundle-Controller-AwesomeController.php on line 46

The error of the occurs on the cache warmup part of the clearing. After looking around people referenced a bunch of different solutions, the most popular being to do a –no-warmup. However, we wanted to try to fix the problem rather than just avoid warming the cache.

The solution fairly quick and really easy. This is using 2.1.X of Symfony and 1.3.* of the JMSDiExtraBundle. Before we had in our AppKernel.php the following:

<?php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Symfony\Bundle\SecurityBundle\SecurityBundle(),
            new Symfony\Bundle\TwigBundle\TwigBundle(),
            new Symfony\Bundle\MonologBundle\MonologBundle(),
            new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
            new Symfony\Bundle\AsseticBundle\AsseticBundle(),
            new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
            new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(),
            new JMS\DiExtraBundle\JMSDiExtraBundle($this),
            new JMS\AopBundle\JMSAopBundle(),
            ....


        );

        if (in_array($this->getEnvironment(), array('dev', 'test'))) {
            $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
            $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
            $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
        }

        return $bundles;
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
    }
}

I noticed in one of the docs for the JMSDiExtraBundle it has the JMSAopBundle in the list BEFORE JMSDiExtraBundle. We tried moving this up and had success, so the final one looked like:

<?php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Symfony\Bundle\SecurityBundle\SecurityBundle(),
            new Symfony\Bundle\TwigBundle\TwigBundle(),
            new Symfony\Bundle\MonologBundle\MonologBundle(),
            new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
            new Symfony\Bundle\AsseticBundle\AsseticBundle(),
            new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
            new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(),
            new JMS\AopBundle\JMSAopBundle(),
            new JMS\DiExtraBundle\JMSDiExtraBundle($this),
...
        );

        if (in_array($this->getEnvironment(), array('dev', 'test'))) {
            $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
            $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
            $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
        }

        return $bundles;
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
    }
}

Hope this saves someone some time.

Amazon AWS EC2 LAMP Quickstart Guide - 5 steps in 10 minutes

We’ve heard some people are having a few small issues with getting AWS up and running. I’ve whipped up a quick guide to get you up and running, for FREE, on AWS within a few minutes. Let’s get started.

1.) Sign Up with Amazon AWS

First you need to get signed up on Amazon in order to use their accounts. Head on over to http://aws.amazon.com/ to create an account. Creating an account is 100% free, even though they do ask for your credit card. Click on the ‘Get started for free’ button in the middle of the page. From there you’ll be taken through a quick registration.

2.) Launch Your Instance

There are are tons of different instances you can choose from. For this tutorial we’ll just give you a simple Ubuntu 12 image. Click https://console.aws.amazon.com/ec2/home?region=us-east-1#launchAmi=ami-3bec7952 this will take you to the launch instance screen:

choose-instance

Click on “Continue”. On this screen for now just make sure that the Instance type (top right of screen) is “T1 Micro…”. This is their free tier. You get 720 hours of run time for free on it. The other options on this screen allow you to customize the number of instances and their location, but for now just click “Continue”. Selection_002This screen is the advanced options screen where you can select some extra options such as the kernel and monitoring for the instance. The defaults here are fine, so just click “Continue”. Selection_003This screen will let you configure the storage for your instance, again the defaults are fine just click “Continue”. Selection_004This screen you can put different tags on your instance. If you have a ton of instances it can be helpful to tag them, however as this is your first and only instance, no need to do anything other than click “Continue”.

Selection_005

This screen is important. You are going to setup your SSH keys to access the server here. Amazon does not launch the server with passwords, instead is uses https://wiki.archlinux.org/index.php/SSH_KeysSSH Keys. These let you identify with the server without having to specify a password. Read up on them, their really helpful.

You’ll want to click “Create a new Key Pair”. Amazon does not currently let you upload your own public ssh key, you must use one stored on your account.“ Enter whatever name you want for the pair and click “Create & Download your Key Pair”. This will download a file to your computer.

Selection_006

You’ll be automatically advanced to the next screen when is has downloaded. Here you’ll configure which security group you want the server to be in. A security group is pretty much just a set of firewall settings. Use the “default” group. Click “Continue.”

Selection_007

It’ll allow you to review your settings and you can click “Launch”

Selection_008

3.) Connecting to your Instance

Your instance is now being launched. You’ll see a “pending” on the screen under state until it is fully up and running.

Selection_009

Once it is running the state will change to “running”. Click on the server. At the bottom of the screen you’ll see information about the server. At the top of it under the top line “EC2 Instance ….” there is a URL. This is your servers public DNS record. You’ll use this to connect. Selection_010Before you can connect to your server you need to update your default security group to allow SSH. On the left side of the window click on “Security Groups”. Click default. In the bottom pane click “Inbound”. Select “SSH” in the dropdown for “Create a new rule:”. Click the “Add rule” button. Then do the same but for “HTTP”. At this point click “Apply Rule Changes”. If you do not do this, it will NOT save your updates. Selection_012Now open your terminal. Navigate to where you downloaded the file from earlier. Now it is time to SSH into your server. You may encounter a permission error, if you do run the chmod command from the gist below.

daum@daum ~ $ ssh ubuntu@ec2-23-22-148-92.compute-1.amazonaws.com -i test.pem 
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for 'test.pem' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
bad permissions: ignore key: test.pem
Permission denied (publickey).
daum@daum ~ $ chmod 0600 test.pem 
daum@daum ~ $ ssh ubuntu@ec2-23-22-148-92.compute-1.amazonaws.com -i test.pem 
Welcome to Ubuntu 12.04.1 LTS (GNU/Linux 3.2.0-36-virtual i686)

Congratulations, you’re now on your own server!

4.) Installing the Basics

Now that you are on your server you need to install the LAMP stack. The next steps we’ll do is have you become the super user, run apt-get and install the LAMP software. apt-get is a package/software manager.

// Become the super user.
ubuntu@ip-10-202-91-158:~$ sudo -s

// Update apt-get so that it has the most recent information
root@ip-10-202-91-158:~# apt-get update
Ign http://us-east-1.ec2.archive.ubuntu.com precise InRelease
...

// We now tell it to install the lamp server software BE SURE TO INCLUDE THE "^"
root@ip-10-202-91-158:~# apt-get install lamp-server^
...
3 upgraded, 27 newly installed, 0 to remove and 58 not upgraded.
Need to get 43.4 MB of archives.
After this operation, 115 MB of additional disk space will be used.
Do you want to continue [Y/n]? y

// Enter your MySQL password when prompted.

5.) View Your LAMP Server

You’ve now setup MySQL, Apache2, and PHP. You can verify Apache is running by going to your public DNS in your browser. You should see the following screen.

Selection_013

Congratulations!

You now have a fully functional LAMP web server. To modify the files that are being served you’ll need to go to the webroot on the filesystem at “/var/www”.

Don’t forget to turn your instance off, as once your free tier runs out they will charge you. When you turn off your instance you will not be able to recover anything on it, so make sure if you have any files you want to keep you download them first.

Congrats on launching a LAMP server on AWS. Good luck and let us know if we can help you out on AWS or your next project!

Want to learn how to do other things on AWS? Leave us a comment and we’ll do our best to help out!

Symfony2 and Ordering of Relations and Collection Form Fields

Recently I was working on a project where I kept finding myself ordering a relation over and over by other than something than ID order (ie id= 1,2,3,4,5). For example, I always wanted my relation to be ordered by the ‘name’ field, rather than the ID or order it was inserted into the DB. Let’s take this schema as an example:

CREATE  TABLE IF NOT EXISTS `post` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(32) NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB


CREATE  TABLE IF NOT EXISTS `post_attachment` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(32) NULL ,
  `url` VARCHAR(32) NULL ,
  `post_id` INT NOT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_post_attachment_post_idx` (`post_id` ASC) ,
  CONSTRAINT `fk_post_attachment_post`
    FOREIGN KEY (`post_id` )
    REFERENCES `post` (`id` )
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB

The issue is each time I attempted:

<?php
$attachments = $post->getPostAttachments();
foreach($attachments as $attachment)
{
     echo $attachment->getId().' '.$attachment->getName()."\n";
}

//Output
// 1 d name
// 2 c name
// 3 a name
// 4 b name

I wanted the output to be in alphabetical order for example. To make this the default for that relation you can add the following annotation to your ‘Post’ entity:

<?php
//MyBundle/Entity/Post.php
...
class Post
{
    ...
    /**
     * @ORM\OneToMany(targetEntity=PostAttachment",mappedBy="post")
     * @ORM\OrderBy({"name"="ASC"})
     */
     private $post_attachments;
    ...


}

Now if you do “$post->getPostAttachments()” they’ll be automatically in order. The ‘@ORM\OrderBy’ column takes care of the ordering automatically. You can specify as many columns on the relation as you’d like there. In addition, this will make it so that all form collections on post with post_attachments are also ordered by name, rather than ID. This affects the relation call every time. If you are only looking into having it some of the time, look into using the repository to do the ordering for those calls.