Desenvolvimento

24 out, 2017

Cronjobs com Docker

Publicidade

Oi pessoal!

Hoje vamos trazer para vocês uma forma de resolver algo que é bem recorrente e comum em ambientes de produção, e que talvez acabe se tornando uma porta de entrada para o uso do Docker em maior escala. Dessa vez, você entenderá como é possível realizar o agendamento e execução de script utilizando a crontab dentro de containers Docker.

Para quem ainda não sabe, dentro de ambiente like *unix é possível realizar o agendamento de scripts utilizando uma ferramenta chamada Crontab (para quem é do ambiente Microsoft, é o mesmo que o task scheduler, mas com um poder maior). Através dela, você define o horário de execução para os scripts, usuário que será utilizado para a execução do mesmo, e claro, qual script deve ser executado. Pois bem, sabendo disso, é possível utilizar o Docker para que a execução desses scripts seja realizada dentro de containers.

Mas afinal, quais as vantagens disso?

Bem, a execução de script via crontab pode gerar alguns desafios, principalmente se não há um controle rigoroso de quais scripts estão sendo agendados. Entre os desafios, podemos destacar:

1 – Uso excessivo de recursos: Pode acontecer de algum script ter algum erro e fazer com que haja uso excessivo de recursos (memória, cpu) durante a sua execução, e acredite, isso é mais comum do que imagina. Existe formas de contornar isso, no entanto, não nativamente.

2 – Segurança na execução: Você pode definir qual usuário executará um determinado script, isso funciona muito bem dentro da crontab. No entanto, caso você não especifique, o usuário utilizado para a execução do script será o mesmo que inicializou o serviço da cron, e geralmente este serviço inicializa com o usuário root.

Ok, Cristiano, e como o Docker pode me ajudar?

Mais um convertido! Vamos ver como funciona na prática?

A primeira coisa que faremos é criar os diretórios necessários. Para isso, defina um diretório de trabalho, e nele, crie uma pasta chamada “cron” e outra chamada “scripts”. Em seguida, vamos buildar uma imagem Docker apenas com o que precisamos para realizar a execução dos scripts. Como imagem base, vamos utilizar uma do NodeJS Alpine, lembrando que, neste caso, nossa cron executará um script escrito em node. Você deve adaptar a imagem de acordo com a sua necessidade (se seu script é em PHP, então a imagem deve ser PHP, e assim por diante. Veja como ficou nosso Dockerfile:

FROM node:8-alpine
RUN apk update && apk add tzdata &&\ 
    cp /usr/share/zoneinfo/America/Sao_Paulo /etc/localtime &&\ 
    echo "America/Sao_Paulo" > /etc/timezone &&\ 
    apk del tzdata && rm -rf /var/cache/apk/*
CMD chown root:root /etc/crontabs/root && /usr/sbin/crond -f

Não preciso explicar este Dockerfile, você já deve ter lido este artigo, né? Salve-o dentro da pasta “cron”.

Bem, agora precisamos montar o resto do ambiente, para este lab, vamos criar um arquivo chamado cron1 com o seguinte conteúdo:

0 7,19 * * * /usr/local/bin/node /home/mundodocker/hello.js >> /var/log/cronteste/hello.log 2>&1

Neste caso, o script será executado as 7h e as 19h todos os dias, salve-o dentro da pasta “cron” também.

Veja que neste caso, o script que deverá ser executado é o /home/mundodocker/hello.js que contém algo simples em node:

console.log("Hello world");

Salve-o dentro da pasta “scripts”.

Ok, agora estamos quase prontos – pelo menos tudo que precisamos está pronto. Se quisermos, podemos utilizar essa estrutura, pois já contém tudo que é necessário. Óbvio que isso não basta, visto que a intenção é deixar tudo automatizado. Para isso, é necessário que você tenha o docker-compose instalado. Com isso, conseguimos automatizar 100% do ambiente. Como você já sabe, para utilizarmos o docker-compose, precisamos de um arquivo no formato yaml com as definições de nosso ambiente, veja como ficou nosso docker-compose.yml:

version: "3"
services:
    cron:
        build: cron
        container_name: cronteste
        volumes:
            - /var/log/cronteste:/var/log/cronteste
            - ./cron/cron1:/etc/crontabs/root
            - ./sripts:/home/mundodocker

Para validar, você pode executar o comando:

docker-compose up

Com isso ele fará o build da imagem e em seguida iniciará um container com esse agendamento. Além das opções acima, você pode, por exemplo, limitar os recursos desse container, inclusive definir um valor mínimo para o mesmo. Outra possibilidade é iniciar este container com um usuário especifico. Dessa forma, você não terá problemas com aquela execução com usuário root.

Validou? Tudo certo? Agora vamos fazer com que esse mesmo comando seja executado na inicialização do servidor, visto que, ele ficará no somente enquanto o servidor estiver ligado, e obviamente os agendamentos serão perdidos. Não queremos isso, certo?

Pois bem, em sistemas onde você tem o systemd (Ubuntu, RedHat, Fedora, CentOS), você deverá criar um novo service. Para isso, crie dentro de: “/lib/systemd/system/” um arquivo com nome de: docker-cron.service com o seguinte conteúdo:

[Unit]
Description=Docker Cron
Requires=docker.service
After=docker.service

[Service]
Restart=always
ExecStart=/usr/local/bin/docker-compose -f /home/mundodocker/docker-compose.yml up
ExecStop=/usr/local/bin/docker-compose -f /home/mundodocker/docker-compose.yml stop

[Install]
WantedBy=default.target

Criado o arquivo, basta fazer com que o systemctl releia o arquivo e adicione o serviço a sua base:

systemctl daemon-reload

Agora é simples, vamos adicionar este serviço ao startup do servidor:

systemctl enable docker-infra

Dessa forma você pode administrar da mesma forma que um serviço, ou seja, posso iniciar e parar esse agendamento a qualquer momento, e mesmo que o servidor seja reiniciado, o agendamento persistirá.

Ok, agora sim temos tudo 100% automático. Não precisamos nos preocupar com mais nada, a não ser é claro, em estender isso a outros tipos de agendamento (se for este o seu caso é claro).

Bom, por hora era isso. Ficou com dúvida? Tem algo a contribuir? Por favor deixe nos comentários e vamos melhorando juntos.