Categoria: PT-BR

Ansible + Windows: Rodando Ansible a partir do Windows com Docker

Apesar de ser em python, e python rodar tranquilamente em Windows, rodar o Ansible dentro do Windows não é suportado, já diz claramente a doc.

Mas eu estou agora com um ambiente multiplataforma, com um bando de gente rodando Windows, e eu quero que o Ansible seja uma opção pra eles, inclusive rodando de sua própria máquina. E agora?

Bom, dá pra rodar dentro de uma VM, claro. Mas há outras opções também. Eu explorei 3 delas:

  • Cygwin
  • O novíssimo Bash for Ubuntu on Windows 10
  • Um container docker linux dentro do Windows.

Cygwin

No fim das contas, o cygwin NÃO é uma opção viável. Eu tentei, não perca o seu tempo também. Já diz, novamente, a doc:

Cygwin is not supported, so please do not ask questions about Ansible running from Cygwin.

Eu fui cabeça-dura e comprovei que não funciona, tomando erros malucos na cabeça. Vi vários relatos de que na versão 1.9 esses erros não acontecem. Mas uma thread recente na lista ansible-users me convenceu. Nela o “Windows Guy” da Ansible/RedHat dá mais detalhes:

“Ansible is most decidedly *not* supported or tested under cygwin (or any mechanism under Windows except a Linux VM, currently). Cygwin’s fork() syscall is unstable (even by their own admission), so you *will* run into issues and heisenbugs, even more so under 2.0+ where we’re making extensive use of fork() behavior that’s more likely to break under Cygwin.

Ansible *may* at some point be supported under the new Bash/Ubuntu on Windows stuff in Win10 (because their fork() is actually functional), but even there things are not working 100% yet.

As the “Windows Guy” at Ansible, nothing would make me happier than to have full Windows support for running the controller on my favorite OS, but there are major technical hurdles to doing so reliably, which is the reason we don’t.

Please don’t run the Ansible controller for anything real on Windows. Please. 🙂

Love,

Matt Davis
Ansible Core Engineering (The Windows Guy)”

Desisti. Vamos para a alternativa que na verdade eu tentei primeiro:

Bash on Ubuntu on Windows 10

Eu sou um linux guy, e um que ama o apt-get. No badalado Aniversary Update lançado em agosto (neste 2016), foi lançado o impensável feature dentro do próprio Windows, que permite habilitar um bash no Windows, que roda nativo (ou algo próximo disso) através do Windows Subsystem for Linux – WSL (em 2000 e poucos isso seria motivo de gargalhada). Não é só um bash, é um pequeno ambiente Ubuntu completo. Com apt-get.

E eu dei apt-get install ansible (acho que eu usei até o ppa de ansible), e lá foi o apt-get fazendo o seu trabalho, e funcionou, instalou. E uma lágrima de felicidade e incredulidade escorreu no meu rosto. Isso foi em casa.

Fui testar no trabalho, e bateu o reality check. O apt-get não funcionava direito. Acontece que o anti-vírus/anti-malware/firewall utilizado na empresa interfere no WSL, aparentemente isso está acontecendo com vários fabricantes de soluções de segurança. Única solução que eu achei até agora foi desabilitar o firewall da solução. Sem ele, o ansible pareceu funcionar bem ali, apesar do Matt Davis no comentário acima falar em alguns problemas nesse cenário. Mas infelizmente desabilitar esse firewall não é uma solução decente para a adoção que eu busco nesse ambiente. E agora? Vai ter que ser VM, pensei.

Mas no meio de uma discussão sobre docker, me surgiu uma ideia.

Docker to the rescue

Vamos à principal ideia deste post.

O Rafael Gomes (@gomex) tem uma palestra muito interessante sobre “Docker como super comandos” (tem uma gravação dela aqui). O caso dele é muito mais elaborado, mas me inspirou nessa solução. Simplificando drasticamente a ideia dele, é utilizar docker não só para subir um serviço, mas para encapsular um ou mais comandos em um processo, com todas as dependências necessárias. Aproveitando assim várias propriedades interessantes trazidas pelo Docker: empacotamento, distribuição fácil, versionamento, descartabilidade (inventei a palavra, talvez).

Looks very nice. E vou levar de certa forma essa ideia a um extremo: um container que encapsula vários comandos potencialmente interativos que para completar precisam ler arquivos no meu host, fora do container.

Eu usei para isso o Docker Toolboox, que usa docker-machine: ele levanta uma VM linux no virtualbox que é o host docker, e esse host é acessado pelo cliente docker rodando no Windows (dentro de um shell bash.exe que ele instala, usando MinGW). Oh… wait, na real então eu não me escapei da VM :-o. É verdade, não tem muita escapatória.  Mas a forma de utilização vai ficar bem mais interessante do que a VM pura ou até do que uma via vagrant.

O novo Docker for Windows também é uma opção, mais interessante até, mas ele requer o Hyper-V, o que quebra o VirtualBox, que eu utilizo para outras coisas também. Espero testar com ele também em breve.

Meu roteiro rápido para rodar o ansible no Windows, então:

  1. Instale o Docker Toolbox no Windows.
  2. Faça pull da minha imagem dgmorales/ansible no DockerHub.
  3. Configure alguns aliases no bash que o docker-toolbox utiliza.
  4. Profit!

Quanto ao passo 1, o link tá ali em cima, siga as instruções. O passo 2 é só um docker pull dgmorales/ansible:

dockerpull2

 

(nessa saída aí, eu já tinha a imagem baixada)

A criação dessa imagem não tem lá muito mistério. É só instalar o ansible e algumas dependências necessárias (como a pywinrm). Eu não mostro o Dockerfile dela, pois resolvi criá-la usando o ansible-container, e isso vai ser assunto para um próximo post.

Agora aos aliases… aqui a gente mascara uma série chata de opções que precisamos passar ao docker. Usando o Docker Toolbox, eles podem ser colocados no ~/.bash_profile. São 2 aliases:

$ cat .bash_profile
alias ansible='docker run -it --rm -w /code -v `pwd`:/code \
   --entrypoint /usr/bin/ansible dgmorales/ansible'
alias ansible-playbook='docker run -it --rm -w /code -v `pwd`:/code \
   dgmorales/ansible'

Já vamos explicar eles melhor, mas antes…

Ver pra crer

Antes de continuar, só deixa eu mostrar que funciona. Nesse caso eu estou rodando um playbook que instala o ansible em uma máquina linux (é um container com ansible que eu gerei usando ansible(-container), instalando o ansible, então são 2 níveis de meta).

ansible_run_windows2

(nessa saída, eu já havia executado o playbook antes, então na verdade ele não fez nada, só conferiu e deu OK pra tudo.)

Explicando

Olhando os aliases podemos entender melhor como funciona (a diferença entre os dois é apenas o –entrypoint). Os parâmetros aí são a chave da mágica:

docker run -it –rm -w /code -v `pwd`:/code –entrypoint /usr/bin/ansible dgmorales/ansible

  • -it: roda o container em modo interativo. É necessário porque em alguns casos o Ansible pode solicitar input (por exemplo com o –ask-pass, ou para aceitar um ssh fingerprint na primeira conexão).
  • –rm: faz com que o container seja automaticamente destruído após terminar sua execução. Isso é vital, pois caso contrário cada execução do ansible vai deixar armazenado mais um container que nunca mais será reusado.
  • -v `pwd`:/code: mapeia o diretório atual no windows para o diretório /code dentro do container.
  • -w /code: define o diretório atual para o comando sendo executado. O diretório atual fora do container foi mapeado para /code.
  • –enrypoint /usr/bin/ansible: a definição do container indica o comando a ser executado como /usr/bin/ansible-playbook. Para rodar o /usr/bin/ansible eu não preciso de outra imagem, eu só preciso dar um override no entrypoint.

Detalhe IMPORTANTE:  é preciso rodar o ansible no windows estando na da raiz do repositório ansible, para que ele mapeie todo o repositório (e apenas o repositório) para o /code e encontre tudo no lugar certo.

Depois eu me dei conta que falta uma coisinha ainda (pelo menos) aí: mapear mais um local com as chaves ssh. No caso eu loguei com usuário e senha. É um ajuste que farei.

Concluindo

Vejamos essa solução com docker. Eu usei pouco ela ainda, mas ela tem algum potencial. Talvez até em um linux eventualmente, pois além de me possibilitar a execução do ansible no windows de uma forma quase transparente, ela me traz algumas outras vantagens:

  • Se o usuário já tiver o docker instalado, a instalação é quase só um docker pull.
  • O processo de instalação do ansible em si, daonde pegar (source, ppa, rpm, pacote xyz), e dependências (pywinrm, python-kerberos, e outras assim), ficam transparentes para o usuário.
  • Adicione aí alguma configuração do ansible, temos o controle sobre a configuração recomendada, que está dentro do container.
  • Todos vão rodar os “pacotes” de ansible que foram testados e “aprovados” antes por pessoas mais experientes com  ele.
  • Qualquer atualização nos fatores acima é fácil de distribuir, é só gerar uma nova versão da imagem e as pessoas fazerem um novo pull.
  • Não interfere com nada instalado na máquina, e felizmente os anti-vírus e firewalls parecem deixá-lo em paz também.

Onde estou, é um dos nossos objetivos que todas as pessoas da TI ganhem alguma familiaridade com docker, e que também tenham o ansible como ferramenta para seu uso, com o mínimo de entraves. Então a proposta encaixa bastante bem 😀

Se alguém quiser testar, aproveite, e me conte como foi. Docker para rodar comandos, e não serviços. Guarde esse conceito, ele é poderoso.

Anúncios
Ansible + Windows: o básico, NTLM, DSC e mais.

Ansible + Windows: o básico, NTLM, DSC e mais.

Eu passei os últimos 6 anos trabalhando muito pouco quase nada com Windows. Meu coração está com o linux, mas eu não sou dado a fanatismos, e agora me encontro novamente trabalhando em um ambiente multiplataforma, com uso forte de Windows. Hora de aprender novos truques e tirar a poeira das minhas adormecidas Windows skills.

Parte disso está sendo testar como está o suporte de Windows no Ansible. Puppet, Salt (e acho que Chef também) tem um suporte mais avançado e/ou maduro a Windows, mas o Ansible tem avançado bastante recentemente. Nem tudo está muito bem documentado e difundido ainda, e nessa jornada eu descobri algumas jóias um pouco escondidas. Então, let’s blog.

Mantendo a filosofia agentless, o Ansible acessa o Windows remotamente usando recursos de acesso remoto do Powershell (Powershell Remoting). Para quem não sabe (eu não sabia), é possível abrir um console ou rodar comandos remotamente usando os comandos Enter-PSession e Invoke-Command no Powershell. Assim meio que parecido com um ssh, mas com uma sintaxe esquisita :D. Esse acesso remoto pode ser implementado de mais de uma maneira, usando RPC, WMI ou WS-Management, este último provido pelo serviço Windows Remote Management (WinRM). E é o WinRM que o Ansible utiliza.

Para isso tudo funcionar, algumas coisas precisam ser preparadas. Há scripts prontos para ajudar, mas vejamos uma lista:

  • Instalar o ansible, dãã.
  • Instalar a pywinrm na máquina que vai rodar o ansible
  • Habilitar o Powershell Remoting no Windows, se já não estiver (o procedimento depende da versão do Windows)
  • Habilitar o listener SSL no serviço WinRM do Windows (e gerar um certificado auto-assinado ou utilizar um “quente”).
  • Configurar o ansible para conectar usando o winrm e se autenticar …
    • … usando Kerberos …
    • … ou usando NTLM.
    • Nota: Pelo que investiguei eu aaacho que talvez seja possível se autenticar com base no certificado SSL do cliente (na ansible control machine), mas eu não testei nem achei nenhum documento sobre isso relacionado ao ansible.

Antes de botar na massa, só mais um comentário. O cenário normal e suportado é rodar o ansible em uma estação linux/unix. Para rodar o ansible a partir de uma estação Windows, a coisa complica. Cygwin não serve, Bash for Ubuntu no Windows 10 talvez, e eu já testei com sucesso uma solução usando docker. Mas isso é o assunto de outro post 😀

Mãos à obra

Leia a documentação oficial sobre Windows Support. Sério, leia, não é longa. Depois volte aqui e veja o meu resumo e o que funcionou pra mim.

(é, eu sei que você não vai ler, mas eu avisei)

Na máquina que roda o ansible (a control machine):

pip install "pywinrm>=0.1.1

Eu geralmente prefiro instalar o pacote da distro quando disponível, mas a versão pode acabar sendo antiga e não suportar alguma coisa, por exemplo a autenticação NTLM que eu explico abaixo. Teste.

No Windows, rode este script mencionado na documentação. Ele tem alguns parâmetros que você pode ou não precisar. Veja a seção Windows System Prep na documentação. Eu li e entendi o script. Ele gera um certificado auto assinado, configura um listener SSL no WinRM na porta 5986 (5985 é a padrão sem SSL), e libera essa porta no firewall para as redes privadas.

Dependendo da versão do Windows, o trabalho pode ser maior:

PowerShell 3.0 or higher is needed for most provided Ansible modules for Windows, and is also required to run the above setup script. Note that PowerShell 3.0 is only supported on Windows 7 SP1, Windows Server 2008 SP1, and later releases of Windows.

Eles sugerem um script para fazer o upgrade do powershell, veja na doc.

No meu ambiente, estou fazendo essa preparação do windows já na imagem base de Windows que estou gerando (go packer!). Daí quando a máquina nasce já está pronta para acesso via ansible.

Configurando

Precisamos passar para o ansible alguns parâmetros para ele saber como conectar nas máquinas windows. Eu faço isso associando elas a um grupo “windows” no inventário e aí criando este arquivo de vars:

$ cat group_vars/windows 
---
ansible_port: 5986
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore

Com tudo que já falamos, acho que é bastante auto-explicativo. Claro que é necessário ter as credenciais também. Dá para fornecê-las iterativamente, se for seu caso, com os parâmetros normais -u e –ask-pass, ou manter em outro arquivo de variáveis criptografado com ansible vault:

$ ansible-vault edit group_vars/ansiblelab 
Vault password: 
# opens in my $EDITOR:
---
ansible_user: myuser
ansible_password: mypassword

Bom, com isso, já dá para acessar a máquina utilizando o usuário da base local do Windows. Agora vamos ao AD…

Autenticando Windows com o Active Directory

Para autenticar no Windows, usando uma conta do AD (ao invés de uma conta local do servidor), o procedimento normal descrito na documentação do Ansible é utilizar Kerberos, tendo inclusive que colocar a control machine no domínio.

Arrrghh, que dor só de ouvir. E imagina se eu tiver mais de um domínio não relacionado (sem relação de confiança entre eles), vou ter que ter uma control machine para cada domínio?

Felizmente, não é bem assim. Para começar, é algo meio recente, mas dá para utilizar NTLM, que me parece uma alternativa muito mais simples e flexível.

Autenticando com NTLM

Basta adicionar essa opção aqui junto daquelas outras lá em cima:

ansible_winrm_transport: ntlm

Além disso, é preciso informar o domínio ao passar o usuário:

ansible_user: myuser@ANSIBLELAB.LOCAL
ansible_password: mypassword

Pronto, só isso! Note que eu usei o formato user@FULL.DNS.DOMAIN em vez do mais comum SHORTDOMAIN\user. Esse foi o jeito de funcionar da mesma com ntlm ou Kerberos, meu próximo teste.

Autenticando com Kerberos

Eu não sei se meu ambiente de teste (só nele eu testei com Kerberos) não representou bem a realidade, mas eu não precisei adicionar a control machine no domínio. No entanto, tem vários detalhes importantes:

  •  Obviamente, a control machine precisa enxergar perfeitamente o domínio DNS associado ao AD em questão.
  • O relógio precisa estar sincronizado, isso é básico para qualquer ambiente Kerberos.
  • O registro reverso no DNS precisa estar correto. Isso me mordeu, demorei a me dar conta. Confesso que agora não tenho certeza se só os registros dos servidores windows ou da control machine também. Eu acertei todos eles.
  • O inventário precisa utilizar o nome do servidor Windows, e não o IP.

O método NTLM não precisa dessas coisas, mas cabe notar que são todas boas práticas a serem seguidas de qualquer forma.

Pacotes:

yum -y install python-devel krb5-devel gcc krb5-libs krb5-workstation
pip install kerberos requests_kerberos

Eu criei um domínio de teste, chamado ANSIBLELAB (ansiblelab.local), promovendo a Domain Controller o meu host de teste ansiblelab-w02. Não vou entrar em detalhes aqui sobre como fazer isso no Windows.  Mas a configuração do Kerberos na minha control machine ficou assim:

# cat /etc/krb5.conf 
includedir /var/lib/sss/pubconf/krb5.include.d/ 
[logging] 
 default = FILE:/var/log/krb5libs.log 
 kdc = FILE:/var/log/krb5kdc.log 
 admin_server = FILE:/var/log/kadmind.log 
 
[libdefaults] 
 dns_lookup_realm = false 
 ticket_lifetime = 24h 
 renew_lifetime = 7d 
 forwardable = true 
 rdns = false 
 default_realm = ANSIBLELAB.LOCAL 
 default_ccache_name = KEYRING:persistent:%{uid} 
 
[realms] 
ANSIBLELAB.LOCAL = { 
  kdc = ansiblelab-w02.ansiblelab.local 
  kdc = ansiblelab-w02.ansiblelab.local 
} 
 
[domain_realm] 
.ansiblelab.local = ANSIBLELAB.LOCAL

E nas minhas variáveis do ansible, eu setei para a WinRM usar kerberos:

ansible_winrm_transport: kerberos

Para que o ansible possa se autenticar via Kerberos, eu preciso rodar o kinit (para obter o TGT). Eu criei um usuário configurado como admin das máquinas windows, para testar.

kinit -U myuser@ANSIBLELAB.LOCAL

Ele pede a senha, e pronto, depois é só conectar com o ansible informando apenas o usuário e domínio (igual ao mostrado acima com o NTLM), não precisa mais colocar a senha.

Testando

Para começar, aquele teste com módulo ping do ansible, sempre útil para testar a conectividade básica antes de mais nada. Só que no caso do windows, vocês deve usar o módulo win_ping:

ansible_win_ping

Se por acaso der um erro tal como “ssl: HTTPSConnectionPool(host=’w2′, port=5986): Max retries exceeded with url: /wsman (Caused by ConnectTimeoutError …”, então o listener SSL não foi habilitado na porta 5986 (veja lá em cima onde eu cito o script powershell que habilita isso).

Agora vamos rodar um playbook simples. Eu resolvi testar subir um virtual host no IIS, copiando os arquivos do site em um zip para o servidor. Veja aqui o playbook:

Destacando as principais tarefas nele:

  • Instala o IIS (win_feature, linha 8)
  • Cria o Website/VirtualHost (win_iis_website, linha 25)
  • Abre a porta do novo website no firewall (win_firewall_rule, linha 31)
  • Copia um arquivo zip com o conteúdo do site para um diretório de staging (win_copy, linha 45)
  • Descompacta o arquivo zip no diretório do site (win_unzip, linha 49)

Algumas outras tarefas menos interessantes estão ali também, para criar os diretórios envolvidos e para subir um arquivo index.html para o site default, em geral usando o win_file.

A execução fica assim:

ansible_iis2

 

 

Essa não foi a minha primeira execução, então algumas tarefas no início já estavam feitas (como a instalação do IIS), e ele não precisou fazer nada, marcando apenas como OK. O resto não tinha sido feito ainda e então ele executou e marcou como changed.

Esses dois warnings aparecem devido a algum bug inócuo introduzido recentemente. Nothing to worry about, eu achei dito na lista no Google Groups.

Mas tem um outro bug mais chato escondido aí. Ao executar novamente o playbook, a task do firewall me deu erro:

ansible_iis_error

The rule exists but has different values. Arrgh. A vida não é um moranguinho, definitivamente. Há um issue aberto. Uma “solução” é setar force=true neste módulo, mas daí ele executa toda vez, marcando sempre como changed. Indesejável, para dizer o mínimo.

Módulos para Windows, e DSC

A lista de módulos para Windows já é grandinha, e tem crescido a cada versão. Vimos alguns no exemplo acima. A maioria é específico, com prefixo win_, mas alguns poucos funcionam tanto para linux quanto para windows, como o módulo script, que pode fazer upload e executar scripts powershell, bash, python, whatever. Tirado da doc:

Note:: There are a few other Ansible modules that don’t start with “win” that also function with Windows, including “fetch”, “slurp”, “raw”, and “setup” (which is how fact gathering works).

Além disso, existem outros ainda não oficiais, e os mais interessantes que eu achei foram os que linkei abaixo relativos ao DSC. Eu não testei eles ainda, mas é preciso comentar sobre eles pois o potencial é gigante.

O que é DSC? Desired State Configuration (DSC) é um conjunto de extensões ao Powershell para uma gerência de configuração de forma declarativa, nativa do Windows. Existe já um caminhão de resources DSC para realizar as mais variadas tarefas. E estão no GitHub. Tem módulo Puppet para  DSC.

A partir do Powershell 5.0, é possível utilizar o DSC de forma mais isolada e pontual. O que permitiu um tal de trondhindenes criar um módulo ansible para executar DSC, win_dsc. Mas isso não é o melhor. Ele criou um gerador automático de módulos DSC pra ansible, com o qual ele conseguiu converter todo aquele caminhão módulos DSC já prontos, para módulos DSC ansible.

São 176 (!) módulos. Eu ainda não sei se eles funcionam muito bem (testarei, não precisei deles ainda), mas caso positivo, o suporte do Ansible a Windows está catapultado a outro patamar.

Considerações finais e próximos passos

É preciso lembrar que nem tudo é uma beleza. O ansible não é tão maduro quanto caras mais antigos como Puppet ou Chef, e o suporte dele a Windows é especialmente novo. Haverão pedras no caminho. Eu não fiz muita coisa séria ainda e já encontrei algumas, como o bug do win_firewall_rule acima. Sinceramente, acho que qualquer destas alternativas ainda vai dar uma dor de cabeça extra ao lidar com Windows. Mas o cenário está evoluindo e o ansible é uma alternativa muito interessante nele.

Coisas que estou testando e que podem virar posts logo logo:

  • Instalar o novo Docker for Windows com containers nativos do Windows 2016, usando Ansible.
  • Examinar as alternativas (i.e. gambiarras) disponíveis para rodar o Ansible a partir de uma estação Windows.
  • Examinar alguns problemas que tive executando alguns módulos no Windows Server Core.

Outras ideias interessantes a serem exploradas:

  • O Active Directory tem um recurso de geração e distribuição automática de certificados (Auto Enrollment) que eu acredito ser uma boa solução para evitar os certificados auto-assinados. A geração “em massa” de certificados auto-assinados é uma nojeira, e um risco de segurança. Com certificados de uma CA confiada a segurança iria ficar muito mais séria.
  • Dado o passo acima, testar a autenticação via certificado SSL do cliente fica mais fácil. Pelo que vi superficialmente, acho que o serviço WinRM suporta isso e a pywinrm também.
  • Obter as credenciais de acesso ao Windows via HashiCorp Vault, ou talvez armazená-las no Ansible Tower ou quem sabe do Rundeck.
  • Testar aqueles módulos DSC mencionados acima, ver se funcionam bem.

Se alguém lendo isso tiver alguma experiência com os itens acima, please compartilhe 😀

Criando pacotes facilmente com o FPM

Criando pacotes facilmente com o FPM

Uma tarefa bastante comum nessa vida de sysadmin linux é criar um pacote deb/rpm/whatever a partir do zero. Pode ser um software que não tem ainda empacotamento para a nossa plataforma, pode ser um software próprio nosso, um script ou um conjunto de scripts.

(Observação: quando o problema é reempacotar/recompilar algo que já existe como pacote, a história é outra. Daí eu sempre tento usar, e ajustar se necessário, o empacotamento que já existe. Em distros baseadas em debian, ao menos, isso costuma ser bem fácil.)

Isso vira uma tarefa mais comum ainda quando usamos ferramentas como Puppet, Ansible, etc., onde utilizar um pacote para instalação de um software qualquer fica muito mais natural, prático e “limpo”, do que ter que rodar configure/make/make-install ou ficar descompactando tgzs/zip através delas.

Eu já fiz pacotes deb from scratch e até já mergulhei também alguma longínqua vez  nos meandros de pacotes RPM. Funciona, e não é lá muito difícil, mas esses processos são meio overkill quando o que eu preciso é pouco mais do que um tar/zip versionado com algumas dependências simples, e quiçá um pre/post install script. Ainda mais se for para consumo interno apenas.

Quem é das antigas vai lembrar do checkinstall. Muito usei ele. E me incomodei com ele. Mas hoje em dia para esse tipo de empacotamento leve, eu só uso o FPM:

https://github.com/jordansissel/fpm

Vantagens do FPM:

  • É fácil, mas muuuuito fácil.
  • Suporta pacotes DEB e RPM, entre outros formatos de saída.
  • … e de entrada também (Python pip -> deb|rpm em um comando, exemplo mais abaixo)
  • Tem um monte de opções para especificar dependências, pre/post scripts, e mais.
  • Mais moderno, mais recursos, mais bem mantido e menos bugs que o checkinstall.

Instalando

Um pouco ironicamente, o FPM é instalado via (ruby) gem, e não por um pacote. Ele precisa do pacote ruby-dev, gcc e make (ruby-devel no Fedora/CentOS/RHEL):

apt-get install ruby-dev make gcc
gem install fpm

Criando pacotes

Vamos começar  por um caso super simples, converter um tar em um pacote. É um caso real: em um projeto para um cliente da Propus era desejado converter o tar.xz da distribuição binária do RabbitMQ em um pacote RPM. Baixado o tar.xz, basta:

# fpm -s tar -t rpm -C rabbitmq_server-3.6.2 --prefix /opt/rabbitmq \
   -n rabbitmq-custom -v 3.6.2 rabbitmq-server-generic-unix-3.6.2.tar.xz

no value for epoch is set, defaulting to nil {:level=>:warn}
no value for epoch is set, defaulting to nil {:level=>:warn}
Created package {:path=>"rabbitmq-custom-3.6.2-1.x86_64.rpm"}

Temos aí:

  • -s: o formato de origem (tar). Ele cuida da descompactação xz/gz automaticamente, não é preciso especificar nada.
  • -t: o formato de destino (rpm).
  • -C: (chdir) diretório daonde serão coletados os arquivos para o pacote. Nesse caso, rabbitmq_server-3.6.2 é o dir raiz presente no tar, mas eu só quero no pacote o que está dentro dele.
  • –prefix: o prefixo dos arquivos dentro do pacote.
  • -n: o nome do pacote.
  • -v: a versão.
  • o argumento final é a origem propriamente dita (o arquivo tar.xz)

Python

Com o FPM também é incrivelmente fácil gerar um pacote deb/rpm a partir de um pacote do PyPI (Python Package Index, aqueles instalados com o pip):

fpm -s python -t deb rt
fpm -s python -t deb -v 0.1.6 businesstime

Pronto, gerados pacotes deb da biblioteca rt (API wrapper do RequestTracker) e da versão 0.1.6 da biblioteca businesstime. Interessante que ele converte até as dependências do pacote no PyPI:

dpkg -I python-rt_1.0.9_all.deb |grep Depends
 Depends: python-requests, python-six

Makefile

Mas o que eu mais uso, disparado, é o FPM junto com um make install. Geralmente eu tenho um Makefile para qualquer projetinho que eu queria gerar um pacote. Já tenho um modelo pronto, que segue abaixo.

Nele eu defino vários metadados como descrição, versão, autor, arquitetura, etc (linhas 22 a 28) que serão passados ao FPM (linha 33). Também já tenho nele um exemplo de chamada para pacote RPM e DEB, definindo dependências e scripts de pós-instalação (a partir das linhas 62 e 79).

O vital no caso do make install é usar a variável $(DESTDIR) como prefixo de todos os caminhos utilizados no install. Ao rodar o make install para usar com o FPM, defino esse DESTDIR para um diretório temporário TMPINSTALLDIR (ver linha 73), e o mesmo diretório é passado ao FPM como o parâmetro -C. Os argumentos finais do comando fpm são os (sub)diretórios que foram criados pelo install dentro do TMPINSTALLDIR. No caso do exemplo, etc, usr e var (ver linha 83).

Eu geralmente tenho ainda um target publish no Makefile, que já faz o upload do pacote para o repositório APT privado, configurado usando o reprepro. Mas isso seria assunto para outro post :D.

Concluindo

Existem muitas formas de gerar pacotes DEB e RPM, mas usar o FPM é disparado a mais fácil, prática e flexível que eu conheço. Empacote!

Puppet, Ansible, Salt: Maturidade, Simplicidade e Flexibilidade

Puppet, Ansible, Salt: Maturidade, Simplicidade e Flexibilidade

Nesta semana (dia 7/4) eu apresentei no Meetup DevOps Carioca esta palestra sobre Puppet, Ansible e Salt, apresentando e comparando eles. Foi muito legal, e o pessoal curtiu bastante.

(Os encontros deste meetup aliás estão sendo muito bons, e estão acontecendo mensalmente. Parabéns ao Luiz Henrique Coelho e ao Stefan Teixeira, que tem organizado eles, e a todos envolvidos também.)

Os slides estão disponíveis aqui: dgmorales.info/talks/cm-pas

No espaço limitado de uma palestra, eu tentei apresentar o básico sobre cada um: suas diferenças de abordagem, sua arquitetura e seus recursos, ofertas comerciais e alternativas open source a elas. Além disso mostrei um pouco de código, com dois exemplos simples em cada ferramenta, e ao final adicionei ainda um glossário mapeando os conceitos equivalentes em cada um e os diferentes nomes que eles usam.

Para apoiar e expandir tudo isso eu estou montando um ambiente sandbox no vagrant com os 3 protagonistas aqui e outras ferramentas/dashboards open source associados, com exemplos de código, simples e mais elaborados, em cada um deles. Ainda está na sua infância, mas já tem alguns exemplos e eu devo incrementar bastante ele nas próximas semanas:

https://github.com/dgmorales/vagrant-cfgmgmt-sandbox

 

O nome que dei a palestra vem das características que eu considero mais definidoras de cada um:

Puppet: Maturidade

É o líder de mercado, uma excelente ferramenta open source, um excelente produto comercial, muito bem documentado, com uma comunidade muito bem organizada (também no Brasil), e buscando ativamente melhorar seus pontos mais fracos.

Ansible: Simplicidade

Descobri que está até na introdução da documentação do Ansible:

Ansible’s main goals are simplicity and ease-of-use.

E é visível isso. O Ansible é disparado o mais fácil de começar a usar, o mais fácil de entender rapidamente, o mais fácil de aplicar para pequenas coisas, possivelmente isoladas, aos poucos. E o valor disso não pode ser subestimado, ele é imenso. Pode ser determinante para introduzir automação de tarefas e infraestrutura como código em uma equipe que ainda não está neste caminho, que talvez tenha muita resistência ou dificuldades, por n motivos diferentes.

Salt: Flexibilidade

E o Salt também assume com orgulho sua vocação para flexibilidade:

Every management tool and their Git forks claim to be flexible, but when we say flexible at SaltStack, we mean entire-management-approach flexible.

https://docs.saltstack.com/en/getstarted/flexibility.html

Push, Pull, com agente, sem agente via SSH, gerenciando estados ou executando comandos remotos, o Salt parece abraçar tudo.

Próximos capítulos

A palestra já está inscrita no FISL17, e estou na torcida para que seja aprovada. Até lá devo incrementar mais a minha sandbox e quem sabe dar uma refinada nos slides. Mãos à obra!

Entendendo o Systemd

Algum tempo atrás eu tive que fazer meu primeiro “script” de inicialização no systemd, e aproveitei para aprender mais sobre ele. Para os desavisados, o systemd é um novo PID 1, o primeiro processo levantado pelo kernel do sistema operacional. Um substituto para os veneráveis init e seus scripts de inicialização estilo SysV, ou para o mais moderno Upstart da Canonical. Mas ele não para por aí, e ele é um bicho bastante diferente dos seus antecessores.

A polêmica

O systemd faz muita coisa. Além de iniciar e supervisionar serviços, ele monta sistemas de arquivos, executa tarefas periódicas (substituindo o cron), gerencia logins e sessões, entre outras tantas coisas. O que leva a muitas discussões sobre o systemd não estar respeitando a filosofia unix de ferramentas e daemons fazerem uma coisa e fazê-la bem, sobre ele ser inchado (bloated) demais, e ser muito invasivo. Não ajudam muito as atitudes e a postura complicada dos desenvolvedores do systemd que já foi criticada por caras como o próprio Linus e o Theodore Ts’o. O resultado inclui posts apocalípticos, longas réplicas de defesa, sites anti-systemd, e até um fork do Debian motivado por ele. A página do systemd na Wikipedia traz vários links sobre a a polêmica.

E apesar de tudo isso, o systemd já se tornou o padrão no RedHat/CentOS 7, no Ubuntu 15.04, no Debian 8 (jessie) e na maioria das outras distribuições. Então é bom se entender com ele.

Primeiro script (aliás, não é um script)

É um unit configuration file, e ele que substitui o script velho de guerra que antes ficava no /etc/init.d. Services são uma das diferentes units que o systemd pode gerenciar. Um mount é outro tipo de unit, por exemplo.

O systemd procura arquivos de units em vários locais, e em geral o melhor local para criarmos um personalizado é em /etc/systemd/system/. Aqueles instalados por pacotes ficam em /lib/systemd/system/. Um man systemd.unit mostra todos os caminhos utilizados. Eis um exemplo simples do daemon do rsync:

$ cat /lib/systemd/system/rsyncd.service
[Unit]
Description=fast remote file copy program daemon
ConditionPathExists=/etc/rsyncd.conf

[Service]
ExecStart=/usr/bin/rsync --daemon --no-detach

[Install]
WantedBy=multi-user.target

Analisando algumas das diretivas:

  • O ConditionPathExists define que o serviço não será iniciado se o rsync.conf não existir. Nesse caso, o serviço não falha, ele apenas não é iniciado.
  • O ExecStart define o comando que inicia o daemon. Detalhe para a opção –no-detach, que evita que o processo vá para background. Esse tipo de opção é comumente utilizada com o systemd e outros supervisores de processos, pois dessa forma é mais fácil o supervisor saber qual o estado atual do processo.
  • O WantedBy cumpre nesse caso a função do runlevel no SysV init, mas ele é mais flexível. Targets do systemd são agrupamentos de units quaisquer. O multi-user.target especifica o conjunto de units (em sua maioria, serviços) que compõe o boot regular multi-usuário da máquina (como o velho runlevel 2 no Debian ou 3 nos RedHat).

Para manipular os serviços, usamos o utilitário systemctl. Para habilitar o serviço acima no boot, por exemplo, rodamos:

$ sudo systemctl enable rsyncd.service
ln -s '/usr/lib/systemd/system/rsyncd.service' \ 
  '/etc/systemd/system/multi-user.target.wants/rsyncd.service'

Note que ele cria um link simbólico no diretório /etc/systemd/system/multi-user.target.wants (sim, o target é na verdade um diretório de symlinks).

Iniciar e parar o serviço é meio óbvio:

$ sudo systemctl start rsyncd.service
$ sudo systemctl stop rsyncd

Note que especificar o sufixo .service  é opcional, se não houver ambiguidade. Podem existir diferentes tipos de units do systemd com o mesmo nome, por exemplo rsyncd.service e o rsyncd.socket, e dependendo do comando systemctl os dois podem ser válidos, necessitando então especificar o sufixo.  Eu acho recomendável colocar o sufixo sempre para não ter surpresas, e em geral temos o tab completion para nos ajudar.

O comando de status do systemd é bem mais útil do que o do UpStart ou do que geralmente são os status de scripts SysV:

[root@centos7 ~]# systemctl status rsyncd.service 
rsyncd.service - fast remote file copy program daemon
 Loaded: loaded (/usr/lib/systemd/system/rsyncd.service; enabled)
 Active: active (running) since Thu 2016-03-03 13:31:36 UTC; 1s ago
 Main PID: 5331 (rsync)
 CGroup: /system.slice/rsyncd.service
 └─5331 /usr/bin/rsync --daemon --no-detach

Mar 03 13:31:36 centos7.local systemd[1]: Starting fast remote file copy program daemon...
Mar 03 13:31:36 centos7.local systemd[1]: Started fast remote file copy program daemon.
Mar 03 13:31:36 centos7.local rsyncd[5331]: rsyncd version 3.0.9 starting, listening on port 873

Temos aí o estado, o PID, a linha de comando executada, e ao final um trecho recente dos logs no journal. Espera… hein? Journal?

Ahh, o Journal

Mas o que diabos é isso? Bom, lembra que systemd se mete em tudo? O journal é ele se metendo no logging do sistema. Ele coleta mensagens de log do kernel, de chamadas ao syslog, saídas de stdout e stderr, e junta tudo num lugar só. Ele pode ser usado em conjunto com um daemon de syslog, ou substituindo ele completamente (os Fedoras mais novos já usam só o journal, por exemplo).

O comando status mostra apenas as últimas linhas do journal. Para ver ele completo, temos o comando journalctl, com a opção -u para filtrar pela unit:

[root@centos7 ~]# journalctl -u rsyncd.service
-- Logs begin at Thu 2016-03-03 13:07:47 UTC, end at Thu 2016-03-03 13:31:36 UTC. --
Mar 03 13:21:58 centos7.local systemd[1]: Starting fast remote file copy program daemon...
Mar 03 13:21:58 centos7.local systemd[1]: Started fast remote file copy program daemon.
Mar 03 13:21:58 centos7.local rsyncd[5316]: rsyncd version 3.0.9 starting, listening on port 873
Mar 03 13:22:04 centos7.local systemd[1]: Stopping fast remote file copy program daemon...
Mar 03 13:22:04 centos7.local systemd[1]: rsyncd.service: main process exited, code=exited, status=20/n/a
Mar 03 13:22:04 centos7.local systemd[1]: Stopped fast remote file copy program daemon.
Mar 03 13:22:04 centos7.local systemd[1]: Unit rsyncd.service entered failed state.
Mar 03 13:31:36 centos7.local systemd[1]: Starting fast remote file copy program daemon...
Mar 03 13:31:36 centos7.local systemd[1]: Started fast remote file copy program daemon.
Mar 03 13:31:36 centos7.local rsyncd[5331]: rsyncd version 3.0.9 starting, listening on port 873

O journalctl tem zilhões de opções, permitindo vários tipos de filtros por data, severidade, serviço, etc. Por exemplo:

  • -b mostra todas mensagens do último boot.
  • -f mostra os logs continuamente, como se fosse um tail -f nos arquivos de log.
  • -p <sev> filtra por severidade. -p err, por exemplo, mostra os logs de severidade error ou maior.
  • -u <unitname> filtra pelo nome de uma unit/serviço, como visto acima.
  • –since e –until definem limites de data. Valores como yesterday, 11min ago, ou 2012-11-23 11:12:13, são todos válidos.

Sugiro este post para uma introdução mais completa ao journal.

Elaborando mais um pouco

Vamos a mais alguns exemplos de unit config files. Digamos que eu queira executar um comando arbitrário durante o boot, que não é um serviço, vai executar uma vez e só. Um exemplo disso é desabilitar o Transparent Huge Pages (THP), coisa que caras como o Redis e o MongoDB recomendam fazer:

[Unit]
Description=Disable Transparent Huge Pages

[Service]
Type=oneshot
ExecStart=/bin/sh -c "/usr/bin/echo never | tee /sys/kernel/mm/transparent_hugepage/enabled"
ExecStart=/bin/sh -c "/usr/bin/echo never | tee /sys/kernel/mm/transparent_hugepage/defrag"

[Install]
WantedBy=multi-user.target

Observe o type=oneshot (executa o comando e acabou), e que eu estou passando vários comandos usando várias diretivas ExecStart. Prático.

Agora vejamos o arquivo para um serviço redis, que eu quero que seja iniciado só depois que o THP tenha sido desabilitado. Claro que o pacote do redis possivelmente já virá com um script systemd, mas digamos que seja uma instância adicional do redis. E digamos ainda que queremos rodar essa instância como um usuário específico, que é fornecido via NIS. Segue:

[Unit]
Description=Redis persistent key-value database
# user redisabc needs NIS (ypbind.service)
After=network.target ypbind.service disable-transparent-hugepages.service

[Service]
ExecStart=/usr/bin/redis-server /etc/redis-abc.conf --daemonize no
User=redisabc
Group=redisabc

[Install]
WantedBy=multi-user.target
  • O After define algumas dependências para o serviço redis: a rede, o daemon do NIS (ypbind) e o nosso “serviço” anterior que desabilita o THP.
  • Note o –daemonize no na linha de execução, cumprindo o mesmo papel do –no-detach no exemplo do rsync.
  • User e Group … bem, já deu pra entender.

 

Indo (muito) além

Já deu para ver o básico, mas foi só uma arranhada no monstro que é o systemd. Deixo aqui mais algumas referências finais, com destaque para a série de posts “The systemd for Administrators”:

A página principal do systemd tem vários links interessantes (e de lá que eu tirei a lista acima).

Outro must read: Why systemd?

E para fechar, mais uma polêmica envolvendo o moço, dessa vez com o menina-dos-olhos-do-mundo-cloud, o Docker: System vs Docker.

Ok, agora já temos leitura para uma semana, praticamente 😉