My First Vagrant Dev Box

In the past, I’ve done all of my development work on a personal MacBook Pro. This includes interfacing directly with GitHub, developing & building projects locally, deploying manually etc etc.

So, I decided to spin up a Vagrant development machine to move development and custom setups off of my MacBook. This way, if I ever get a new Mac / PC or decide to use more than one computer, I have one common development image I can move around.

This first box I create will have MySQL, Node.js, and Python all included, as well as GitHub support already built-in.

Vagrant Basics

The power of Vagrant can be demonstrated by just two words:

1
vagrant up

When a box is properly configured, typing this into a command prompt will bring up a Vagrant VM and have a dev up and running. Basically, Vagrant is a cutting edge tool allowing for scripting a development environment that can be easily incorporated into any project without requiring much overhead. This means all users working on a project can develop in the same environment with the same versions of software \ operating systems and environment variables, saving massive amounts of time.

Using Salt With Vagrant

Vagrant environments are configured using a provisioning system of which there are many options. The provisioning system comes into play when you first start your Vagrant machine and that is how it knows what software needs to be installed, what services need to be running, and how everything should be configured.

Salt is a server configuration management and remote execution tool designed to automate the management and deployment of servers, similar to Puppet or Chef. For this Vagrant machine, I chose to use Salt.

Nececssary Pre-Reqs

All I needed to have installed was Vagrant v1.1.x or greater.

Initial Setup

To get started, install the Salt plugin for Vagrant, and add the base box.

1
2
vagrant plugin install vagrant-salt
vagrant box add precise64 http://files.vagrantup.com/precise64.box

Creating Vagrantfile

Next, create a Vagrantfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Vagrant.configure("2") do |config|
config.vm.box = "precise64"

## Create a private network, which allows host-only access to the machine
## using a specific IP.
config.vm.network "private_network", ip: "192.168.2.20"

## This is what configuration will be copied to the Vagrant
config.vm.synced_folder "salt/", "/srv/salt/", type: "nfs"
config.bindfs.bind_folder "/srv/salt/", "/srv/salt/"
config.vm.synced_folder ".", "/vagrant/development"

## Configure ssh-key
config.ssh.private_key_path = "~/.ssh/stevus/vagrant/id_rsa"
config.ssh.forward_agent = true

config.vm.provision :salt do |salt|
salt.colorize = true
salt.log_level = "info"
salt.minion_config = "config/minion.conf"
salt.run_highstate = true
salt.verbose = true
end
end

Last, add in some configuration config/minion.conf:

1
2
3
4
5
6
7
8
9
10
11
12
13
master: localhost
file_client: local

file_roots:
base:
- /srv/salt/

mysql.host: 'localhost'
mysql.port: 3306
mysql.user: 'root'
mysql.pass: ''
mysql.db: 'mysql'
mysql.unix_socket: '/var/run/mysqld/mysqld.sock'

Create Vagrant ssh-key

1
2
3
4
5
6
7
8
9
10
$ ssh-keygen -t rsa -b 4096 -C "stevus06@gmail.com"
# Creates a new ssh key, using the provided email as a label
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/you/.ssh/id_rsa): /Users/you/.ssh/stevus/vagrant/id_rsa
Enter passphrase (empty for no passphrase): ****
Enter same passphrase again: ****
Your identification has been saved in /Users/you/.ssh/stevus/vagrant/id_rsa.
Your public key has been saved in /Users/you/.ssh/stevus/vagrant/id_rsa.pub.
The key fingerprint is:
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX stevus06@gmail.com

Adding key to ssh-agent

1
2
3
# start the ssh-agent in the background
eval "$(ssh-agent -s)"
Agent pid 59566
1
2
3
4
ssh-add ~/.ssh/stevus/vagrant/id_rsa

# Check that your key is added
ssh-add -L

Create Salt Files / Folders

Ok, let’s create the configuration file. This is were Salt and its modules will be configured. For this case I’m using the MySQL module so that is what makes up most of this configuration.

These files will basically fire off the individual package installs.

File salt/top.sls

1
2
3
4
5
6
base:
'*':
- common-packages
- mongodb
- mysql
- nvm

File salt/common-packages.sls

1
2
3
4
5
6
7
common_packages:
pkg.installed:
- names:
- git
- vim
- curl
- screen

File salt/mongodb/init.sls

1
2
3
4
5
6
7
mongodb:
pkg:
- installed
service:

- running
- require:
- pkg: mongodb

File salt/mysql/init.sls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
mysql-server:
pkg:
- installed
mysql:

service:
- running
require:

- pkg: mysql-server

python-mysqldb:
pkg:
- installed

database-setup:
mysql_user.present:
- name: testuser
- password: devman
- require:
- pkg: python-mysqldb
- service: mysql

mysql_database.present:
- name: exampledb
- require:
- mysql_user : database-setup

mysql_grants.present:
- grant: all privileges
- database: exampledb.*
- user: testuser
- require:
- mysql_database : database-setup

File salt/nvm/init.sls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
nvm-clone:
git.latest:
- name: https://github.com/creationix/nvm.git
- rev: master
- user: root
- target: /srv/nvm
- always_fetch: False
- force: True
- required_in:
- file: /etc/profile.d/nvm.sh
- file: nvm-dir
- pkg: git

nvm-dir:
file.directory:
- name: /srv/nvm
- user: vagrant
- group: admin
- recurse:
- user
- group
- mode

/etc/profile.d/nvm.sh:

file.managed:
- source: salt://nvm/files/nvm.sh
- mode: 755

nvm-requirements:
pkg.installed:
- names:
- curl

Ready To Use

OK, that should be it. Everything is now configured, and if all goes well, running the following command from the same directory the Vagrantfile is in will fire up the development environment fully provisioned. It may take a while the first time as it has to download a large amount of dependencies for our software.

1
vagrant up

Configure Git

I need to figure out how to have Github automatically recognize me, but for now I guess I can just generate a new SSH key and add it to the known keys for me on Github.

https://help.github.com/articles/generating-ssh-keys/

1
2
3
4
5
6
7
8
9
10
$ ssh-keygen -t rsa -b 4096 -C "stevus06@gmail.com"
# Creates a new ssh key, using the provided email as a label
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/you/.ssh/id_rsa): /Users/you/.ssh/stevus/git/id_rsa
Enter passphrase (empty for no passphrase): ****
Enter same passphrase again: ****
Your identification has been saved in /Users/xxxx/.ssh/stevus/git/id_rsa.
Your public key has been saved in /Users/xxxx/.ssh/stevus/git/id_rsa.pub.
The key fingerprint is:
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX stevus06@gmail.com

Adding key to ssh-agent

1
2
3
# start the ssh-agent in the background
eval "$(ssh-agent -s)"
Agent pid 59566
1
ssh-add ~/.ssh/stevus/git/id_rsa
1
2
3
4
5
6
vagrant@precise64:/vagrant/development/Stevus.Rushmore$ git pull
The authenticity of host 'github.com (SOME_IP)' can't be established.
RSA key fingerprint is XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'github.com,SOME_IP' (RSA) to the list of known hosts.
Already up-to-date.

At this point, pretty much ready to rock and roll. There might be a couple small bugs here and there with config, but for the most part I’m now working completely off of my MacBook and inside an Ubuntu VM.

Welcome

Welcome to the Stevus.com homepage! Since this is the first post of the site, I thought I would give a brief overview on how it is built. Stevus is purely a static-site, meaning that it does not need a database or server-generated code - all files are plain text (HTML/CSS/Javascript). This makes your experience browsing the site super-fast. This is also the first statix=site I have built, so I wanted to document the process and share my experience with all of you.

Stevus.com is built using Hexo, a static-site generator. The thing I like most about using Hexo is all the posts can be written in Markdown. In order to get static files, all I have to do is run a generator command to process the Markdown post files into HTML. This removes any need for databases, special server setups, admin portals or logins, and bloated CMS systems. Also - it saves buttload of time.

Basic Setup

Hexo is built on the Node.js platform, so to get started I created a bare npm project.

1
2
git init
npm init

These commands generated all the git and npm files for my new website (.git folder, package.json etc).

1
2
3
4
5
6
7
8
9
10
{
"name": "stevus.com website",
"version": "1.0.0",
"scripts": {
...
},
"dependencies": {
...
}
}

Also - the Hexo documentation called for me to install Hexo globally, however I want to keep as many modules in the local scope as I can. To do this, I installed Hexo as a local dev module, and made modifications to package.json since Hexo overwrites it upon install.

1
npm install --save hexo-cli

Here is my updated package.json after modifications:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"name": "stevus.com website",
"version": "1.0.0",
"scripts": {
"hexo": "hexo"
}
,

"hexo": {
"version": "3.1.1"
}
,

"dependencies": {
"hexo": "^3.1.0",
"hexo-generator-archive": "^0.1.2",
"hexo-generator-category": "^0.1.2",
"hexo-generator-index": "^0.1.2",
"hexo-generator-tag": "^0.1.1",
"hexo-renderer-ejs": "^0.1.0",
"hexo-renderer-stylus": "^0.3.0",
"hexo-renderer-marked": "^0.2.4",
"hexo-server": "^0.1.2"
}

}

I added the npm script hexo as a shortcut for interacting with the Hexo module. Now, I can run any Hexo command through my project.

1
npm run hexo

As the Hexo documentation states, this created the following file structure:

1
2
3
4
5
6
7
8
.
├── _config.yml
├── package.json
├── scaffolds
├── source
| ├── _drafts
| └── _posts
└── themes

Now that the basic setup is complete, I can generate the static HMTL/CSS/Javascript files:

1
npm run generate

At this point, I had everything I needed to display a static site. All of my content now lives in the public/ folder, so I can just deploy this folder straight to my server, free from any additional server/database configuration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.
├── _config.yml
├── package.json
├── public
| ├── 2015
| ├── archives
| ├── css
| ├── fancybox
| ├── js
| └── index.html
├── scaffolds
├── source
| ├── _drafts
| └── _posts
└── themes

Overall, this process took me about 5-10 minutes to setup, and about 30 seconds to write and publish a new blog post. I’d say that’s a big improvement over any blog platform out there.