Personal CI server on Amazon EC2

Personal projects may not always have a critical requirement for automation as part of the build process, but in a world where even static sites often want some sort of build step, getting things done without this type of tool can be painful at times. While many managed solutions exist for this purpose, the range of choices can be daunting, it can be hard to find a service which matches your needs, and these services can be expensive.

If the idea of managing your own solution is more appealing, getting up and running with Jenkins on EC2 is worth investigation. Jenkins gives you flexibility as your jobs can run more or less whatever commands you choose, and the costs stay low as you can easily stick with a very small EC2 instance.

This post walks through the following:

If you haven’t done so already, go ahead and sign up for an AWS account.

Setting up new EC2 instance

Head to the AWS EC2 panel, and hit ‘Launch new instance’ to start. This will take you through the configuration flow for your new instance.

You may have your own preferences for how this is configured, so feel free to use your own. Personally I selected a Ubuntu 14.04 SSD t2.small instance, sticking with defaults, with the following exceptions:

  • Configure Instance: Disable the 'auto-assign public IP’ option. These auto-assigned IP addresses are not persistent through reboots, so we’ll assign an elastic IP to the instance instead below.
  • Create a new IAM role for this new instance. I set up a policy allowing this role to perform actions in a specific S3 bucket (which hosts a static site), you’ll want to do similar for any resources on AWS which the instance needs to access.
  • Tag Instance: It’s useful to have a descriptive tag for the instance, such as; Key: Purpose, Value: CI.
  • Configure Security Group: At this stage it can be useful to whitelist your own access to the instance by setting up 2 rules: allow SSH on port 22, and TCP on port 8080 from 'My IP’. We go through security setup in more detail below.
  • Key pair: Rather than sharing keys around, create a new key pair for this instance.

Configuring EC2 for ssh access

Before we do anything else, we need to setup an Elastic IP in order to access our instance.

Under the NETWORK & SECURITY section in the sidebar, click Elastic IPs, select 'Allocate New Address’, and finally confirm.

Now make sure the IP is selected, and choose 'Associate Address’ from the Actions dropdown. Select your instance, hit Associate, and you’re done.

Next let’s make sure we can SSH into our instance. First of all we need to make sure our new private key is not publicly viewable:

1
$ chmod 400 /path/to/new_key.pem

Now we should be able to access the instance:

1
2
$ ssh -i /path/to/new_key.pem ubuntu@elastic.ip.address
Welcome to Ubuntu 14.04.4 LTS...

Next up we should secure the server. This is a little beyond the scope of this article, but this is a great primer.

Setup Jenkins user

It’s best not to use the root user for everything, so let’s go ahead and setup a jenkins user:

1
2
3
4
5
6
7
$ useradd jenkins # create jenkins user
$ mkdir /home/jenkins # create jenkins home directory
$ mkdir /home/jenkins/.ssh # create jenkins ssh keys directory
$ chmod 700 /home/jenkins/.ssh # set owner read/write/execute for .ssh/
$ usermod -s /bin/bash jenkins # set jenkins shell to bash
$ usermod -aG sudo jenkins # add jenkins to sudo group
$ chown jenkins:jenkins /home/jenkins -R # set jenkins as owner of above

Whitelist Instance Access

Let’s configure the security group to allow Github to access the instance. Head to 'Security Groups’, and hit 'Create Security Group’. Set both the inbound and outbound lists like so:

Github security group

Now head back to your instance, and select Actions->Networking->'Change Security Groups’, assigning the newly created group to your instance.

Installing Jenkins

Now that the instance is up and running, and allowing access to ourselves as well as Github, we can go ahead and install Jenkins.

Install Java

1
2
3
$ sudo add-apt-repository ppa:webupd8team/java
$ sudo apt-get update
$ sudo apt-get install oracle-java8-installer

Check this worked with:

1
2
$ java -version
java version "1.8.0_101"...

Install Jenkins

These instructions are slightly tweaked from the official ones here to better handle the additional apt source.

Add the Jenkins key:

1
2
3
$ wget -q -O - http://pkg.jenkins.io/debian/jenkins.io.key | \
> sudo apt-key add -
OK

Add Jenkins source. We’ll create a new .list file in /etc/apt/sources.list.d/ as changes to sources.list won’t persist through a rebundle:

1
$ sudo vim /etc/apt/sources.list.d/jenkins.list

Add deb http://pkg.jenkins.io/debian binary/ to this file, and save.

Update package index, and install:

1
2
$ sudo apt-get update
$ sudo apt-get install jenkins

Run Jenkins setup wizard

Once this is complete, we can use the setup wizard by hitting [elastic IP]:8080 in our browser.

Configuring Nginx

Let’s configure nginx to act as a reverse proxy, so we don’t have to specify port 8080 when accessing Jenkins.

1
2
$ sudo apt-get update
$ sudo apt-get install nginx

Add an HTTP inbound rule allowing traffic from your location on port 80, and you should see the nginx welcome by hitting the IP without a port specified.

Now let’s configure nginx to proxy port 80 to 8080 for Jenkins:

1
$ sudo vim /etc/nginx/sites-available/jenkins

Entering:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server {
  listen 80;

  server_name your-ip-or-url;

  access_log /var/log/nginx/jenkins/access.log;
  error_log /var/log/nginx/jenkins/error.log info;

  location / {
    proxy_pass http://localhost:8080;
    proxy_redirect default;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

Prepare nginx files/folders:

1
2
3
4
5
$ sudo mkdir -p /var/log/nginx/jenkins/ # create the logs dir
$ sudo rm /etc/nginx/sites-enabled/default # disable default site
$ sudo ln -s /etc/nginx/sites-available/jenkins \
>   /etc/nginx/sites-enabled/ # enable jenkins site
$ sudo service nginx restart # restart nginx

Hit your instance again now, and you should see Jenkins. Finally, we should remove the rule allowing connections through port 8080 from the security group.

Next steps

Congratulations! You have now successfully configured Jenkins to run on your own personal EC2 instance. This guide should have got you most of the way, for extra credit you might want to look into the following:

  • Allow ssh/http access only via VPN. Unless you have a static IP, you’ll need to update your security group to let yourself in whenever it changes. A VPN is a good way to solve this without opening up your instance to the whole internet.
  • Configure Jenkins to run a job on push to a Github repo’s master branch.
  • Configure DNS for your instance.