O documento discute práticas de engenharia de software aplicadas à infraestrutura, incluindo:
1) Testes automatizados de infraestrutura para garantir qualidade e permitir mudanças contínuas;
2) Ferramentas como Chef, InSpec e Test Kitchen para definir infraestrutura como código e testar provisionamento;
3) Exemplos de como escrever testes de unidade para Chef recipes e testes de integração/aceitação para infraestrutura.
2. Agenda
• Infraestrutura como código
• Ferramentas de configuração de servidores
• Práticas de engenharia de software
aplicadas à infraestrutura
• Testando sua infraestrutura
2
4. Infraestrutura como código
• Definição e gerenciamento de infraestrutura através de
código-fonte
• Objetivos:
• Consistência
• Reprodutibilidade
• Confiança
• Facilidade de manutenção e crescimento
4
6. Desafios
• Quando má empregada, pode causar
problemas em uma escala muito maior:
• Crescimento rápido e desordenado
• Flutuações nas configurações
• Servidores "floco de neve"
6
18. Qualidade através de código
• Qualidade não é uma prática separada
• Seu código de infraestrutura deve:
• Ser fácil de compreender
• Ser simples de modificar
• Fornecer feedback rápido quando problemas
acontecem
18
19. Qualidade através de código
• Algumas práticas que nos dão mais confiança:
• Sistema de controle de versão
• Testes automatizados
• Integração contínua
• Entrega contínua
19
22. Testes automatizados
• Habilitam mudanças contínuas em um
sistema
• Garantem que o trabalho pode ser feito
rapidamente
• Identificam erros assim que possível
22
25. Escrevendo um cookbook com
testes
• O que você precisa?
• Um editor de texto
• Chef
• Ruby
• Conexão com a internet*
• Vagrant (Virtualbox)
25
26. Escrevendo um cookbook com
testes
• A idéia é escrevermos um cookbook que
instala o Apache em um servidor
• Vamos seguir um fluxo guiado por testes
26
33. $ bundle install
Fetching gem metadata from https://rubygems.org/
Fetching version metadata from https://rubygems.org/
Fetching dependency metadata from https://rubygems.org/
Resolving dependencies.......
...
Bundle complete! 4 Gemfile dependencies, 77 gems now installed.
Use `bundle show [gemname]` to see where a bundled gem is installed.
33
34. $ bundle exec rspec test/unit
No examples found.
Finished in 0.00029 seconds (files took 0.51493 seconds to load)
0 examples, 0 failures
34
36. test/unit/apache_spec.rb
1 require 'chefspec'
2 require 'chefspec/berkshelf'
3
4 describe 'example::apache' do
5 let(:chef_run) do
6 ChefSpec::SoloRunner.converge(described_recipe)
7 end
8
9 it 'installs Apache server package' do
10 expect(chef_run).to install_package('apache2')
11 end
12 end
36
37. $ bundle exec rspec test/unit
===========================================================================
Recipe Compile Error
===========================================================================
Chef::Exceptions::RecipeNotFound
--------------------------------
could not find recipe apache for cookbook example
...
Finished in 0.84264 seconds (files took 2.6 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/unit/apache_spec.rb:9 # example::apache installs Apache server
package
37
39. $ bundle exec rspec test/unit
.
Finished in 0.82665 seconds (files took 2.57 seconds to load)
1 example, 0 failures
39
40. test/unit/apache_spec.rb
1 require 'chefspec'
2 require 'chefspec/berkshelf'
3
4 describe 'example::apache' do
5 let(:chef_run) do
6 ChefSpec::SoloRunner.converge(described_recipe)
7 end
8
9 it 'installs Apache server package' do
10 expect(chef_run).to install_package('apache2')
11 end
12
13 it 'starts Apache server' do
14 expect(chef_run).to start_service('apache2')
15 end
16 end
40
41. $ bundle exec rspec test/unit
.F
Failures:
1) example::apache starts Apache server
Failure/Error: expect(chef_run).to start_service('apache2')
expected "service[apache2]" with action :start to be in Chef run.
Other service resources:
# ./spec/unit/apache_spec.rb:14:in `block (2 levels) in <top
(required)>'
Finished in 0.84234 seconds (files took 1.46 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/unit/apache_spec.rb:13 # example::apache starts Apache server
41
42. recipes/apache.rb
1 # encoding: utf-8
2 package 'apache2' do
3 action :install
4 end
5
6 service 'apache2' do
7 action :start
8 end
42
43. $ bundle exec rspec test/unit
..
Finished in 0.85119 seconds (files took 1.63 seconds to load)
2 examples, 0 failures
43
45. recipes/apache.rb
1 # encoding: utf-8
2 case node['platform']
3 when 'ubuntu'
4 include_recipe('apt')
5
6 package 'apache2' do
7 action :install
8 end
9
10 service 'apache2' do
11 action :start
12 end
13 when 'centos'
14 package 'httpd' do
15 action :install
16 end
17
18 service 'httpd' do
19 action :start
20 end
21 end
45
46. test/unit/apache_spec.rb
1 require 'chefspec'
2 require 'chefspec/berkshelf'
3
4 describe 'example::apache' do
5 context 'when applying recipe to Ubuntu' do
6 let(:chef_run) do
7 runner = ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '14.04')
8 runner.converge(described_recipe)
9 end
10
11 it 'includes apt recipe' do
12 expect(chef_run).to include_recipe('apt')
13 end
14
15 it 'installs Apache server package' do
16 expect(chef_run).to install_package('apache2')
17 end
18
19 it 'starts Apache server' do
20 expect(chef_run).to start_service('apache2')
21 end
22 end
46
47. test/unit/apache_spec.rb
23
24 context 'when applying recipe to CentOS' do
25 let(:chef_run) do
26 runner = ChefSpec::SoloRunner.new(platform: 'centos', version: '7.1.1503')
27 runner.converge(described_recipe)
28 end
29
30 it 'installs Apache server package' do
31 expect(chef_run).to install_package('httpd')
32 end
33
34 it 'starts Apache server' do
35 expect(chef_run).to start_service('httpd')
36 end
37 end
38 end
47
48. $ bundle exec rspec test/unit
.....
Finished in 1.47 seconds (files took 3.07 seconds to load)
5 examples, 0 failures
48
53. 1 file '/etc/login_service.yml'
2 owner ourapp
3 group ourapp
4 end
1 describe 'creating the configuration file for login_service' do
2 it 'gives the file the right attributes' do
3 expect(chef_run).to create_template('/etc/login_service.yml').with(
4 user: 'ourapp',
5 group: 'ourapp'
6 )
7 end
8 end
53
56. InSpec
1 describe package('apache2') do
2 it { should be_installed }
3 end
1 describe file('/etc/ssh') do
2 it { should be_directory }
3 end
1 describe command('env') do
2 its('stdout') { should_not match(/^MYSQL_PASS=/) }
3 end
56
57. Test Kitchen
• Ferramenta de integração para desenvolver
e testar seu código de infraestrutura
• https://github.com/test-kitchen/test-kitchen
57
60. 1 # encoding: utf-8
2 if os[:family] == 'ubuntu'
3 package_name = 'apache2'
4 else
5 package_name = 'httpd'
6 end
7
8 describe package(package_name) do
9 it { should be_installed }
10 end
11
12 describe service(package_name) do
13 it { should be_installed }
14 it { should be_running }
15 end
16
17 describe port(80) do
18 it { should be_listening }
19 end
test/integration/default/
apache_spec.rb
60
67. -----> Creating <default-centos-71>...
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'bento/centos-7.1'...
...
-----> Converging <default-centos-71>...
Preparing files for transfer
...
Converging 2 resources
...
Recipe: example::apache
* yum_package[httpd] action install
- install version 2.4.6-40.el7.centos.1 of package httpd
* service[httpd] action start
- start service service[httpd]
...
Target: ssh://vagrant@127.0.0.1:2201
✔ System Package httpd should be installed
✔ Service httpd should be installed
✔ Port 80 should be listening
Summary: 3 successful 0 failures 0 skipped
67
68. -----> Creating <default-ubuntu-1404>...
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'bento/ubuntu-14.04'...
...
-----> Converging <default-ubuntu-1404>...
Preparing files for transfer
...
Converging 2 resources
...
Recipe: example::apache
* apt_package[apache2] action install
- install version 2.4.7-1ubuntu4.10 of package apache2
* service[apache2] action start (up to date)
...
Target: ssh://vagrant@127.0.0.1:2201
✔ System Package apache2 should be installed
✔ Service apache2 should be installed
✔ Port 80 should be listening
Summary: 3 successful 0 failures 0 skipped
68
75. Instalando uma nuvem
OpenStack
• Cada componente é desenvolvido de
maneira isolada
• A cada mudança aceita, os componentes
são integrados para garantir que continuam
funcionando corretamente
• https://github.com/openstack/tempest
75
76. Instalando uma nuvem
OpenStack
• Duas vezes por dia, um pacote era gerado
contendo uma versão potencial para
produção com todas as mudanças das
últimas x horas
76
77. Instalando uma nuvem
OpenStack
Preparar o
ambiente
Executar um
script de
instalação
Executar
a suite de
testes de integração
Reportar a
saída por email
e chat
77
78. Instalando uma nuvem
OpenStack
Preparar o
ambiente
Executar um
script de
instalação
Executar
a suite de
testes de integração
Reportar a
saída por email
e chat
~20 minutos
~40 minutos
~30 minutos
~20 segundos
78