Deploying Ruby app using AWS Codedeploy to EC2: How to do it with Bundler!
Tags: ruby, bundler, codedeploy, aws
Published: 2019-07-10 16:43:20

It's late at night, after a week of struggle I managed to configure my deployment properly. The purpose of this short article is to show you some problems that I came across with dependencies in my Ruby app during deployment with Codedeploy, and solutions to them.

Prerequisites: Basic knowledge of AWS Codedeploy configuration (If you went through the documentation you will do just fine).

## From the beginning...

I have my Ruby app. It is using Bundler to install gem dependencies. I put it on AWS EC2 and it works just great.
Note: On my EC2 I am using Amazon Linux AMI 2018.03.0 (HVM), SSD Volume Type, since it comes with Ruby and some CLI tools installed out of the box.
There is just one thing missing to make me happy - I would like to deploy my application when a particular branch is updated on my bitbucket repository.
Let's assume I have VM running and codedeploy-agent installed on it.
I recommend [this tutorial](https://hackernoon.com/deploy-to-ec2-with-aws-codedeploy-from-bitbucket-pipelines-4f403e96d50c) to set up deployment pipelines for your bitbucket repository.
There is one thing not covered in the tutorial though: installing app dependencies with Bundler.

## The problem #1

So what's the big deal?
I can use `gem install bundler` and then add `bundle install` in my scripts ran in my AppSpec deployment hooks, right?
{% gist https://gist.github.com/mknycha/1262a21b878fa660ac6f773a01a1cbf0/b53f8aeb3d9cf3fa0c2953407646dc4a39f86970 file=appspec.yml %}
{% gist https://gist.github.com/mknycha/1262a21b878fa660ac6f773a01a1cbf0/b53f8aeb3d9cf3fa0c2953407646dc4a39f86970 file=bundle_install %}
{% gist https://gist.github.com/mknycha/1262a21b878fa660ac6f773a01a1cbf0/b53f8aeb3d9cf3fa0c2953407646dc4a39f86970 file=install_dependencies %}
{% gist https://gist.github.com/mknycha/1262a21b878fa660ac6f773a01a1cbf0/b53f8aeb3d9cf3fa0c2953407646dc4a39f86970 file=Gemfile %}
(App code is not really important here)
But I get an error when I deploy my code:
```
[2019-07-07 10:01:40.378] [d-0N2XZ4E6A]LifecycleEvent - AfterInstall
[2019-07-07 10:01:40.379] [d-0N2XZ4E6A]Script - scripts/bundle_install
[2019-07-07 10:01:40.391] [d-0N2XZ4E6A][stderr]/opt/codedeploy-agent/deployment-root/d0556b49-b646-4579-9d8e-20fca9397a2e/d-0N2XZ4E6A/deployment-archive/scripts/bundle_install: line 4: bundle: command not found
[2019-07-07 10:03:58.992] [d-8Y4VW7E6A]LifecycleEvent - ApplicationStop
[2019-07-07 10:03:58.992] [d-8Y4VW7E6A]Script - scripts/stop_server
```
It looks like **bundle binary is not found**.

## Solution

So let's just install the binary somewhere in the environment PATH:
{% gist https://gist.github.com/mknycha/1262a21b878fa660ac6f773a01a1cbf0/e94277b99230c64e13a3d592112fb0ea36f39dd4 file=install_dependencies %}

## Problem #2
All right, so will it work now?
```
2019-07-07 18:00:20 [stdout]Fetching json 2.2.0
2019-07-07 18:00:20 [stdout]Installing json 2.2.0 with native extensions
2019-07-07 18:00:20 [stderr]Gem::Ext::BuildError: ERROR: Failed to build gem native extension.
2019-07-07 18:00:20 [stderr]
2019-07-07 18:00:20 [stderr]current directory:
2019-07-07 18:00:20 [stderr]/home/ec2-user/vendor/bundle/ruby/2.4/gems/json-2.2.0/ext/json/ext/generator
2019-07-07 18:00:20 [stderr]/usr/bin/ruby2.4 -r ./siteconf20190707-26032-sw7ot0.rb extconf.rb
2019-07-07 18:00:20 [stderr]mkmf.rb can't find header files for ruby at /usr/share/ruby/include/ruby.h
2019-07-07 18:00:20 [stderr]
2019-07-07 18:00:20 [stderr]extconf failed, exit code 1
2019-07-07 18:00:20 [stderr]
2019-07-07 18:00:20 [stderr]Gem files will remain installed in
2019-07-07 18:00:20 [stderr]/home/ec2-user/vendor/bundle/ruby/2.4/gems/json-2.2.0 for inspection.
2019-07-07 18:00:20 [stderr]Results logged to
2019-07-07 18:00:20 [stderr]/home/ec2-user/vendor/bundle/ruby/2.4/extensions/x86_64-linux/2.4/json-2.2.0/gem_make.out
2019-07-07 18:00:20 [stderr]
2019-07-07 18:00:20 [stderr]An error occurred while installing json (2.2.0), and Bundler cannot continue.
2019-07-07 18:00:20 [stderr]Make sure that `gem install json -v '2.2.0' --source 'https://rubygems.org/'`
2019-07-07 18:00:20 [stderr]succeeds before bundling.
2019-07-07 18:00:20 [stderr]
2019-07-07 18:00:20 [stderr]In Gemfile:
2019-07-07 18:00:20 [stderr]  json
```
Not really. Funny thing is that some gems got installed, while some did not - in my case it's `json` **gem that cannot be build**.

## Solution

I needed to install another two things to make it work - ruby development kit and c++ garbage collector
{% gist https://gist.github.com/mknycha/1262a21b878fa660ac6f773a01a1cbf0/128b5e2a900abb77de73c3b5b9e0b0ef56146653 file=install_dependencies %}

## Why putting it all in hooks scripts?

Good question. Why not just connect to EC2 instance and install all this stuff by hand?
Because it will not work. I have not found a precise cause, but I have found out that the environment under which deployment is made (at least before copying/replacing files of the working app) is different than the one we have access to when connecting to our VM.

I hope you found the above information useful. If you did, please like, share or leave a comment.

That's my first post, so any constructive feedback will be appreciated :)