CI/CD Con Jenkins DSL y Capistrano
<3
Pablo Fredrikson
- DevOps Manager @ invisionapp
- Nerd
- 10 años con Linux




@pablokbs
Devs
- Escribían el código
- Probaban el código
- Probaban todo en su entorno local
Ops
- Compilaban el código
- Empaquetaban el código
- Desplegaban el código
- Probaban todo en producción
En la prehistoria:




- 15 desarrolladores
- 5 servicios
- 5 entornos
- 10 cookbooks
En el comienzo
El horror
- Entornos empezaron a crecer
- Más gente empezaba a usar Jenkins
- Más gente cometía errores
- Jenkins se hacía más lento
- Jefes se quejaban
- Caos, muerte y destrucción
El ecosistema
- Código para las aplicaciones
- Código para crear entornos de desarrollo (docker)
- Código para crear la infraestructura y configurar servicios externos (terraform)
- Código para configurar servidores y mantenerlos (chef)
- Jenkins se mantenía a mano

Job DSL Plugin

- Groovy
- Wiki con documentación
- Spec tests
- Soporte para muchos plugins
Job DSL Lang

Job DSL Plugin

job('MiTrabajo') {
scm {
github("git@github.com:pablokbs/myapp.git")
}
triggers {
scm('*/15 * * * *')
}
steps {
shell('deploy.sh')
}
}Ejemplo simple
Job DSL Plugin

import groovy.json.jsonSlurper
def project = 'pablokbs/myapp'
def github = 'https://api.github.com'
def api = new URL("${github}/repos/${project}/branches")
def branches = new JsonSlurper().parse(api.newReader())
branches.each {
def branchName = it.name
job {
name "${project}-${branchName}".replaceAll('/','-')
scm {
git("git://github.com/${project}.git", branchName)
}
steps {
shell("deploy.sh ${project} ${branchName}")
}
}
}Ejemplo más complejo



Job DSL Plugin

Integración github


Muuuuuuchos trabajos

1225 trabajos
Desventajas
- Nuevo lenguaje, no es muy fácil
- Se puede complicar
- No tiene soporte para el 100% de los plugins
1200 trabajos son muchos trabajos



jenkins-master:~# docker -H tcp://127.0.0.1:3241 ps | wc -l
64
1200 trabajos son muchos trabajos
Capistrano
Capistrano
├── current -> /var/www/myapp/releases/20150120114500/
├── releases/
│ ├── 20150080072500
│ ├── 20150090083000
│ ├── 20150100093500
│ ├── 20150110104000
│ └── 20150120114500
├── revisions.log
└── shared/
└── myapp.conf
└── temp/
└── plugins/
└── uploads/Capistrano
# basic details
set :application, 'app_name'
set :deploy_user, 'jenkins'
# setup repo details
set :scm, :git
set :repo_url, 'git@github.com:pablokbs/myapp.git'
# how many old releases do we want to keep
set :keep_releases, 5
# files we want symlinking to specific entries in shared.
set :linked_files, %w{config/database.yml}
# dirs we want symlinking to shared
set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets}
namespace :deploy do
on roles(:web) do |host|
execute "sudo service nginx reload"
info "Nginx reloaded on #{host.hostname}"
end
end
Capistrano
$ cap production deploy$ cap production deploy:rollbackRunning /usr/bin/env mkdir -p /var/myapp/releases/20160421215335
Running tar xf /var/myapp/shared/129.tgz -C /var/myapp/releases/20160421215335
Running /usr/bin/env echo "129.tgz" >> REVISION
Running /usr/bin/env mkdir -p /var/myapp/releases/20160421215335
Running /usr/bin/env ln -s /var/myapp/shared/.env /var/myapp/releases/20160421215335/.env
Running /usr/bin/env ln -s /var/myapp/releases/20160421215335 /var/myapp/releases/current
Running sudo service myapp restart
Running echo "Branch master deployed as release 20160421215335 by jenkins" >> revisions.logCapistrano
s3://yourbucket/somedirectory/
|- 201506011200.zip
|- 201506011500.zip
...
|- 201506020100.zip
`- 201506030100.zipgem 'capistrano-elb-mgmt'
gem 'capistrano-s3_archive'Plugins
Capistrano
gem 'chef'Plugins
# Grab the servers
q = "roles:#{fetch(:application)} \
AND roles:#{fetch(:chef_role)} \
AND chef_environment:#{fetch(:environment)}"
servers = query.search(
:node,
q,
:filter_result =>
{
:hostname => ["hostname"],
:ip_address => ["ipaddress"],
:roles => ["roles"]
}
).firstCapistrano
gem 'dogapi'Plugins

Flow
- PR en Github dispara el test en Jenkins
- Merge
- Se dispara el trabajo en Jenkins (compilación)
- Jenkins envia el zip a S3 y dispara deploys
- Capistrano busca en chef por los nodos y ejecuta el deploy
- Capistrano baja el zip de S3 y crea el release en cada nodo en paralelo
- Se quita el nodo del balanceador
- Se reinician los servicios
- Se agrega el nodo al balanceador

Resultados

Gracias
¿Preguntas?
@pablokbs


@invisionapp
slides.com/pablokbs
ci/cd jenkins y cap
By Pablo Fredrikson
ci/cd jenkins y cap
Charla para Flisol Mendoza 2016
- 884