Categoria: general

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!

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 😉

 

Setting a backup default route for quagga in linux

Sometimes you don’t need to automate something, you just need to find a better setup with your existing tools.

I got some experience with Quagga for the last couple years, and it’s great. It’s seems to be a very very rare event, but I’ve been bitten by a crash in quagga once or twice. And that’s specially bad if that machine receives its default route through quagga and it ends up without any default route.

So I like to set a static backup default route, in case the dynamic one goes away. That’s usually good practice anyway. Just set a static one in linux with larger metric, someone said. But no. Not with Quagga. Quagga does not install a route if there’s already an existing off-quagga route for that destination, even with a different metric. Seems silly, but it’s true.

Workaround: use linux’s multiple routing tables feature. Check the link if you never heard of it. By default, you already have three tables, setup like this:

# ip rule
0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup default

Quagga usually installs routes in the main table, which is the regular one. The ip rules above tell the kernel that if it does not find a route in main, it should look in the default table.  So all you need is:

ip route add default via x.x.x.x table default

Quagga will now install the dynamic default route in table main, because he didn’t find any default route there. But if that route somehow disappears, the one in the default table will kick in to save the day.

service blog start

Well, it’s about time to kick this blog alive.

So this is a blog about automation in the context of system administration. Automation is such a broad term. For example, monitoring is for me a little bit automation. It automates away the need to regularly check things by hand, which is paramount, by the way.

Shell scripting, ssh multiplexers, libraries and tools for automating non-interactive deployments or both text and visual interactive applications are all great tools to have under any sysadmin’s belt, and I’ve been using then for many years. They are still very useful, but recently I added the ultimate automation tool to my belt: a configuration management software. And that’s a role new game, called Infrastructure as Code.

Infrastructure as code is not just about making you type less, tough. It’s about consistency, repeatability, executable documentation, versioning, peer reviewing, testing. Automation is cool, but realizing the benefits from all these things together is what really excites me.

There are quite a few config management tools out there, and I decided to go with Puppet. So far I’m very happy with it, and it will certainly be a frequent topic here.

Update: my friend foscarini pointed out another quite interesting config management alternative.