Devops phase 2
We made the CRUD app which Creates, Reads, Updates and Deleted the user by api. We made user as resource in rails. Rspec for testing (100% code coverage) with BDD principle and coding etiquettes followed.
Now, we are to build a CI pipeline for the same. I used Gitlab as a Git service and it provides its in built CI pipeline. Make 2 stages for CI pipeline. Use ruby image 2.5
- Test — All the tests commands go here
test:
stage: test
script:
- gem install bundler -v 1.16.3
- bundle install
- bundle exec rake db:migrate
- bundle exec rspec
- Package
pakage:
stage: package
script:
- rm -rf .git
- gem install bundler -v 1.16.3
- bundle install --deployment
artifacts:
name: "$CI_COMMIT_REF_NAME-$CI_JOB_NAME_$-$CI_JOB_STAGE"
paths:
- ./
Follow the documentation for more details of build stages of pipeline here.
The Vagrantfile for running the rails server is given below.
PS: All the secret variables are dummy variables so don’t waste your time on any hacking stuff!
Cool! We could get our server running by this vagrant file which installs dependencies of our rails app.
Now lets talk about chef! A person who makes the dishes based on the recipes you create. Based on Infrastructure as Code principle. Meaning installing ruby 2.5.1 is an instruction which needs a spec too (Using kitchen)! Also to implement DRY principle.
We create the user_api cookbook which has a lot of recipes. Recipes include installing ruby, installing artifact, installing bundler, installing openssl, running server, etc. Its based on the principle that every instruction is code.
Recipe for installing artifact from gitlab.
Integration test for the same.
describe file('/tmp/user_api_artifact.zip') do
it { should exist }
end
describe command('unzip -v') do
its('stdout') { should match(/UnZip 6.00/) }
end
describe file('/srv/user_api/') do
its('type') { should eq :directory }
it { should be_directory }
end
describe file('/srv/user_api/') do
its('owner') { should eq 'vagrant' }
its('group') { should eq 'vagrant' }
end
describe file('/srv/user_api/.bundle/config') do
its('content') { should include "BUNDLE_PATH:" }
end
Recipe for starting the server as service and restarting on failure.
file '/etc/secret_key' do
content 'SECRET_KEY_BASE=SECRET_KEY_FOR_PROD'
mode '0755'
owner 'vagrant'
group 'vagrant'
action :create
end
file '/etc/user_api_runner.sh' do
content "#!/bin/bash\n\nbundle exec rails db:migrate RAILS_ENV=production\nbundle exec rails s -e production"
mode '0755'
owner 'vagrant'
group 'vagrant'
action :create
end
systemd_unit 'user_api.service' do
content({
Unit: {
Description: 'User_api rails service',
},
Service: {
WorkingDirectory: '/srv/user_api',
EnvironmentFile: '/etc/secret_key',
ExecStart: '/etc/user_api_runner.sh'
}
})
action [:create, :enable, :start]
end
It starts the user_api_runner script.
Now, The install commands in vagrant file is replaced by fully automated chef recipes in it. Instead of shell provisioners, we will be using chef as provisioner.
All this automation is for one box!
Our server is up and running. But we need more than one instance of our application running. To distribute the load when the requests increase. We create another box which has a load balancer and balances the load. We are using HAProxy for the same with round robin algorithm to distribute the requests.
Vagrant.configure(2) do |config|
(1..2).each do |i|
config.vm.define "vm#{i}" do |worker|
worker.vm.box = "ubuntu/xenial64"
worker.vm.network "private_network", ip: "192.168.1.#{i}0"
worker.vm.provision :chef_solo do |chef|
chef.add_recipe "rails::ruby"
chef.add_recipe "rails::bundler"
chef.add_recipe "rails::artifact"
chef.add_recipe "rails::openssl"
chef.add_recipe "rails::run_server"
end
end
end
config.vm.define "vm_balancer" do |vm_balancer|
vm_balancer.vm.box = "ubuntu/xenial64"
vm_balancer.vm.network "private_network", ip: "192.168.1.30"
config.vm.network "forwarded_port", guest: 80, host: 8080
vm_balancer.vm.provision :chef_solo do |chef|
chef.add_recipe "load_balancer::install_haproxy"
chef.add_recipe "load_balancer::config_haproxy"
chef.add_recipe "load_balancer::restart_haproxy"
end
end
end
As in above, we have 2 cookbooks. One is load_balancer to install and configure and restart HAProxy (on error) and other is rails to install ruby, bundler, openssl, download artifact from gitlab and then run the server.
In the next part, I will cover doing these things with Kubernetes instead of Vagrant VMs and why? Tune in for the 3rd blog.
You may contact the author at me@abhinavrai.com