Cronjobs com Docker

Oi Pessoal,

Hoje vamos trazer para vocês um forma de resolver algo que é bem recorrente e comum em ambientes de produção, e que talvez acabe de 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 \o/.

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 mais poder 🙂 ), 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, dentre 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, nativamente não.

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, então…

Ok Cristiano, e como o Docker pode me ajudar?

Aeooooo mais um convertido, vamos ver como funciona na prática?

A primeira coisa que faremos é criar os diretório 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 né, você já deve ter lido este post 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 ai já contém tudo que é necessário, obvio 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 minimo 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 sistema 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 será persistido.

 

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 😉

 

Obrigado e grande abraço!