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 blog --label 'traefik.port=80' --label traefik.frontend.rule="Host:blog.mundodocker.com.br;" --network net wordpress

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!

Entusiasta Open Source, seu principal foco é ir atrás de ideias novas e torna-las realidade através de soluções simples e eficientes, o menos é mais, e o dividir é multiplicar.

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!

Entusiasta Open Source, seu principal foco é ir atrás de ideias novas e torna-las realidade através de soluções simples e eficientes, o menos é mais, e o dividir é multiplicar.

Coleta de métricas no Docker Swarm

Oi Pessoal!

Queremos trazer para vocês hoje uma solução para coleta e visualização de métricas para o Docker Swarm. Um dos maiores desafios, principalmente para as equipes de operações, é saber quanto está sendo utilizado por cada container, OK, existem soluções na engine do Docker, como é o caso do docker stats, mas e quando você tem um diversos hosts, e eles estão em cluster e não tem definido quais containers estão em cada host, complicou né?

A intenção com este post é mostrar como é possível ter, de forma fácil, todas as informações de consumo de seu cluster, utilizando soluções simples, e que a maioria de vocês já deve ter visto. Para este lab vamos usar o Cadvisor, Influxdb e Grafana, vamos entender melhor onde cada um irá atuar neste ambiente.

Cadvisor

Como já vimos aqui no blog, o Cadvisor é uma ferramenta desenvolvida justamente para realizar a coleta de recursos de containers/aplicações dentro de um servidor. É possível estender seu uso através de integração com a API que essa ferramenta disponibiliza, e enviar os dados coletados para diversos backends, em nosso lab vamos utilizar o InfluxDB, que você verá logo abaixo o que é e como funciona.

 

InfluxDB

Para quem não conhece, o InfluxDB faz parte de um conjunto de soluções da empresa InfluxData, que tem seus produtos voltadas para analise, monitoramento e armazenamento de informações cronológicas. O InfluxDB é conhecido como um data series database, que serve justamente para armazenar dados em ordem cronológica.

 

Grafana

O Grafana talvez seja uma das mais completas ferramentas para criação e exibição de gráficos, e uma ferramenta extremamente flexível e adaptável, tendo como input diversos tipos de backend, o que facilita ainda mais o nosso lab 😉 . Você pode inclusive ter mais datasources e montar o seu dashboard unindo todas as essas informações, bacana né?

 

Ok, teoria é muito bonita, mas… vamos praticar?

Primeiramente você deve ter em execução o seu cluster, caso não tenha feito isso ainda, faça agora, caso tenha dúvidas, veja este post, onde mostramos na prática como fazer isso 🙂 . Certo agora vamos criar os serviços de coleta para o nosso cluster, para isso, você pode executar o seguinte passo-a-passo:

  •  Crie uma rede do tipo overlay para que seja possível a comunicação entra as ferramentas, e que essa comunicação seja realizada de forma isolada:
$ docker network create -d overlay --attachable coleta
  • Em um dos dos hosts (de preferência o menos utilizado), crie o servidor de Influx, que será utilizado como backend para as informações coletadas:
$ docker run -d --restart=always -v /data:/var/lib/influxdb --network coleta --name influx influxdb
  • Devemos criar a database dentro do influx para que seja possível salvar as informações:
$ docker exec -it influx influx -execute 'CREATE DATABASE cadvisor'
  • Ok, agora basta criar o serviço de coleta no cluster:
$ docker service create --name agentes --network coleta --mode global --mount type=bind,source=/,destination=/rootfs,readonly=true --mount type=bind,source=/var/run,destination=/var/run  --mount type=bind,source=/sys,destination=/sys,readonly=true  --mount type=bind,source=/var/lib/docker,destination=/var/lib/docker,readonly=true google/cadvisor -logtostderr -docker_only -storage_driver=influxdb -storage_driver_db=cadvisor -storage_driver_host=influx:8086

Se tudo correu bem, você deve terá criado um serviço do tipo global, ou seja, cada host de seu cluster terá um container desse tipo (Cadvisor), e todos eles enviarão os dados para o banco influx. Para ter certeza de que tudo correu bem, você pode executar o comando:

$ docker service ls

Dessa forma você visualizará os serviços que estão em execução, e terá ter certeza de que os containers iniciaram em todos os nós, o retorno do comando deverá ser algo parecido com isso:

$ docker service ls
ID           NAME    MODE   REPLICAS IMAGE                  PORTS
5ras37n3iyu2 agentes global 2/2      google/cadvisor:latest

Viu, tudo certo 🙂 Mas ainda não acabou, continuando:

  • A última etapa é subir nosso serviço de Grafana para podermos visualizar as informações que foram coletadas pelo Cadvisor, e armazenadas no InfluxDB, execute:
$ docker run -d --restart=always -v /data:/var/lib/grafana --network coleta --name grafana -p 80:3000 grafana/grafana

Simples né? Agora temos que acessar a interface do Grafana para podemos configurar e termos os gráficos que desejamos, acesse: http://ip-do-host/login, você visualizará a interface de acesso dele, como ilustrado abaixo:

Os dados de acesso default do Grafana são:

User: admin
Password: admin

Estando dentro da interface do Grafana, vá até “Source” e adicione uma nova fonte de dados, nela informe “InfluxDB”, conforme imagem abaixo:

Após clique em “save & test” para validar o acesso ao seu InfluxDB. Se tudo ocorrer conforme o planejado, você terá sucesso nessa adição, e em seguida deverá montar o gráfico baseado nas informações que estão no banco, para isso existem duas formas, uma mais complexa onde você precisará montar as querys e depois criar o gráfico com essas querys, ou importar um template pronto para isso, e neste caso eu agradeço ao Hanzel Jesheen, pela contribuição a comunidade, em ter criado o template e disponibilizado em seu github 🙂 . Você pode baixar este arquivo, salvar em seu computador e depois ir até a área de Dashboard de seu Grafana e clicar em import, informe o caminho de onde salvou o template, escolha o source criado anteriormente e depois clique em “import”, conforme imagem abaixo:

Parabéns! Você acaba de montar seu sistema de coleta e visualização de recursos para todo o seu cluster! Indo até Dashboards – Cadvisor você terá uma visualização parecida com esta:

Bacana não?

A partir desse protótipo você pode evoluir seu ambiente para atender a sua demanda, em nosso lab a coleta é realizada pelo Cadvisor e enviada ao InfluxDB a cada 60 segundos, isso quer dizer que o gráfico tem um delay de 1 minuto entre a coleta e a visualização, para alguns isso é problema, para outros não. Outro ponto que você deve se atentar é: Quantos mais containers você tiver, mais poluído ficará o seu gráfico, isso por motivos óbvios, então talvez você precisa alterar o layout do dashboard. Ahhh não esqueça de trocar a senha do usuário admin do Grafana 😉

Por hoje era isso gente, esperamos que isso ajude vocês, e continuaremos com alguns posts mais técnicos trazendo alguns soluções ou implementações legais utilizando Docker. Nos ajude divulgando o blog, e tendo dúvida nos avise!

Grande abraço!

Entusiasta Open Source, seu principal foco é ir atrás de ideias novas e torna-las realidade através de soluções simples e eficientes, o menos é mais, e o dividir é multiplicar.

Cluster Mongo em Docker

Opa!

Hoje o post será mais prático, e queremos trazer para vocês uma forma de criar um cluster de MongoDB utilizando Docker. O próprio Docker tem um tutorial de como você pode criar a sua imagem de mongo e executá-lo dentro do Docker, para quem não viu ainda, segue o link. Para quem não conhece, MongoDB é um tipo de banco de dados orientado a documentos ou seja, utiliza o conceito de dados e documentos auto contidos e auto descritivos, e isso implica que o documento em si já define como ele deve ser apresentado e qual é o significado dos dados armazenados na sua estrutura.

O MongoDB também é chamado de banco de dados NoSql, mas isso é outro assunto 🙂 . O que veremos neste artigo é como você pode montar um cluster de instâncias do mongo utilizando como backend containers Docker. Isso é possível pois o existe na “caixa” do mongo algumas formas de se construir um cluster, uma delas é utilizando o método de replica set, que consiste em ter instâncias  para onde a informação será replicada, e dessa forma garantir a persistência das informações, abaixo segue uma imagem ilustrativa de como isso funciona:

Dessa forma garantimos a persistência e tolerância a falha dos dados que estão sendo armazenados. Neste post mostraremos como utilizar essa abordagem, não entraremos em detalhes mais fundos do Mongo pois não é este o objetivo.

Vamos lá?

Antes de tudo, é recomendável que os servidores possam ser acessíveis via nome, caso não seja possível, você pode adicionar nos hosts os endereços para que isso seja temporariamente possível.

Em seguida precisamos criar o diretório no host onde será persistido os dados do banco:

$ mkdir /opt/mongo
$ mkdir /opt/mongo/data $ cd /opt/mongo

É importante lembrar que essas pastas devem estar criadas em todos os nós, para que dessa forma seja possível persistir os dados em todos os nós.

Agora você precisa gerar uma chave que será utilizada para realizar a sincronia entre as replicas, e garantir que os dados além de trafegar de forma segura, estejam replicados entre todos os nós, essa chave deve ser copiada para todos os nós também.

$ openssl rand -base64 741 > mongodb-keyfile $ chmod 600 mongodb-keyfile $ chown 999 mongodb-keyfile

Configurado este arquivo, precisamos subir um container para realizar algumas tarefas administrativas, para depois subir o cluster propriamente dito:

$ docker run --name mongo -v /opt/mongo/data:/data/db -v /opt/mongo:/opt/keyfile --hostname="node1" -p 27017:27017 -d mongo:2.6.5 --smallfiles

Vamos as configurações agora:

$ docker exec -it mongo /bin/bash
[email protected]:/# mongo
> use admin
db.createUser( {
     user: "admin",
     pwd: "SENHA_DE_ADMIN",
     roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
   });

db.createUser( {
     user: "root",
     pwd: "SENHA_DE_ROOT",
     roles: [ { role: "root", db: "admin" } ]
   });
> exit
[email protected]:/# exit

Remova o container para que possamos agora subir o ambiente todo:

$ docker stop mongo
$ docker rm mongo

Agora sim vamos subir o ambiente:

$ docker run --name mongo -v /etc/hosts:/etc/hosts -v /opt/mongo/data:/data/db -v /opt/mongo:/opt/keyfile --hostname="node1" --add-host node1.db:${node1} --add-host node2.db:${node2} --add-host node3.db:${node3} -p 27017:27017 -d mongo:2.6.5 --smallfiles --keyFile /opt/keyfile/mongodb-keyfile --replSet "rs0"

Acesse este container para inicializar o cluster:

$ docker exec -it mongo /bin/bash
[email protected]:/# mongo
> use admin
> db.auth("root", "SENHA_DE_ROOT");
> rs.initiate()
{
         "info2" : "no configuration explicitly specified -- making one",
         "me" : "node1.db:27017",
         "info" : "Config now saved locally.  Should come online in about a minute.",
         "ok" : 1
}

> rs0:PRIMARY> rs.conf()
{
        "_id" : "rs0",
        "version" : 1,r
        "members" : [
              {
                  "_id" : 0,
                  "host" : "node1.db:27017"
              }
        ]
}

Nos demais nós, suba os containers da seguinte forma:

Servidor 2

$ docker run --name mongo -v /etc/hosts:/etc/hosts -v /opt/mongo/data:/data/db -v /opt/mongo:/opt/keyfile --hostname="node2" -p 27017:27017 -d mongo:2.6.5 --smallfiles --keyFile /opt/keyfile/mongodb-keyfile --replSet "rs0"

Servidor 3

$ docker run --name mongo -v /etc/hosts:/etc/hosts -v /opt/mongo/data:/data/db -v /opt/mongo:/opt/keyfile --hostname="node3" -p 27017:27017 -d mongo:2.6.5 --smallfiles --keyFile /opt/keyfile/mongodb-keyfile --replSet "rs0"

Volte até o servidor 1, acesse o container de mongo e execute os comandos:

$ docker exec -it mongo /bin/bash
[email protected]:/# mongo
> use admin
> db.auth("root", "SENHA_DE_ROOT");
> rs0:PRIMARY> rs.add("node2.db") 
> rs0:PRIMARY> rs.add("node3.db") 
> rs0:PRIMARY> rs.status()

O status devem ser algo parecido com este retorno:

rs0:PRIMARY> rs.status()
{
 "set" : "rs0",
 "date" : ISODate("2017-05-16T15:43:30Z"),
 "myState" : 1,
 "members" : [
 {
 "_id" : 0,
 "name" : "node1:27017",
 "health" : 1,
 "state" : 1,
 "stateStr" : "PRIMARY",
 "uptime" : 578713,
 "optime" : Timestamp(1494371417, 1),
 "optimeDate" : ISODate("2017-05-09T23:10:17Z"),
 "electionTime" : Timestamp(1494371963, 1),
 "electionDate" : ISODate("2017-05-09T23:19:23Z"),
 "self" : true
 },
 {
 "_id" : 1,
 "name" : "node2.db:27017",
 "health" : 1,
 "state" : 2,
 "stateStr" : "SECONDARY",
 "uptime" : 577454,
 "optime" : Timestamp(1494371417, 1),
 "optimeDate" : ISODate("2017-05-09T23:10:17Z"),
 "lastHeartbeat" : ISODate("2017-05-16T15:43:29Z"),
 "lastHeartbeatRecv" : ISODate("2017-05-16T15:43:29Z"),
 "pingMs" : 0,
 "syncingTo" : "node1:27017"
 },
 {
 "_id" : 2,
 "name" : "node3.db:27017",
 "health" : 1,
 "state" : 2,
 "stateStr" : "SECONDARY",
 "uptime" : 577354,
 "optime" : Timestamp(1494371417, 1),
 "optimeDate" : ISODate("2017-05-09T23:10:17Z"),
 "lastHeartbeat" : ISODate("2017-05-16T15:43:29Z"),
 "lastHeartbeatRecv" : ISODate("2017-05-16T15:43:29Z"),
 "pingMs" : 0,
 "syncingTo" : "node1:27017"
 }
 ],
 "ok" : 1
}
rs0:PRIMARY>

Caso tenha ocorrido algum erro na sincronização das informações, você poderá visualizar através deste retorno, e claro terá informações para realizar a correção do caso. Existem muitas maneiras de realizar esse tipo de configuração de cluster, algumas mais complexas e outras mais simples, em nosso teste esta foi a forma onde tivemos mais objetividade e assertividade na configuração, baseado nessas informações você pode montar/adaptar os passos que atendem a sua demanda.

Um ponto muito importante nesse ambiente é a forma como ocorre a recuperação em caso de desastre, pois você pode parar o master (primário) e você poderá ver através do rs.status(); o comportamento do cluster elegendo um novo master, ou seja, dessa forma seu cluster ficará quase que 100% a prova de falhas ;).

Esperamos ter ajudado, e se tiver alguma dificuldade ou dúvida com relação a este ambiente por favor entre em contato conosco para que possamos ajudar o/. E como sempre, nos ajude divulgando o blog, isso é muito importante para nós.

Grande abraço!

Entusiasta Open Source, seu principal foco é ir atrás de ideias novas e torna-las realidade através de soluções simples e eficientes, o menos é mais, e o dividir é multiplicar.

Docker Swarm na Prática

Oi Pessoal!

Fizemos um vídeo mostrando na prática como é possível criar um cluster de Swarm e gerenciar seus serviços com ele, veja também como você pode portar sua stack do Docker Compose para dentro do cluster via o Docker Stack. Veja:

Este é apenas um vídeo de lab, lembramos que você deve se ater a alguns outros pontos nesse ambiente antes de colocar em produção, OK? 😉

Contamos com a ajuda de vocês para divulgação do blog, grande abraço!

Entusiasta Open Source, seu principal foco é ir atrás de ideias novas e torna-las realidade através de soluções simples e eficientes, o menos é mais, e o dividir é multiplicar.

Serviços Windows em Docker

Oi Pessoal,

Já vimos em alguns posts, por exemplo este, este e este, como é possível ter seu ambiente Windows em Docker, ou Docker para Windows. Pois bem, a intenção hoje é nos aprofundarmos mais em como é possível portar um ambiente já existente para dentro de containers no Windows, ou seja, veremos como migrar sua aplicação .net que atualmente está em uma máquina virtual para um container, isso de forma bem simples e rápida.

 

Do início

Antes de tudo, precisamos nos situarmos sobre o uso de algumas ferramentas, e aqui entra a parte fundamental desse processo, que é entender como funciona o Image2Docker, para quem não o conhece, este é um módulo do powershell que possibilita a criação de arquivo(s) Dockerfile(s) a partir de um VHD, VHDx ou ainda uma imagem WIM. Você não precisa estar com a máquina virtual ligada para realizar esse processo, o Image2Docker inspeciona o disco offline coletando informações sobre o que você tem instalado ali, e claro, ele serve apenas para ambiente Windows, então ele já possui uma tabela pré-determinada de aplicações que devem ser verificadas.

Após finalizado o scan, ele monta um Dockerfile baseado na imagem do Windows Server Core (microsoft/windowsservercore), é claro que ele não portará tudo de seu ambiente, por exemplo, se você tem instalado uma base MySQL na VM Windows que escaneou, essa instalação não será portada para o Dockerfile, e consequentemente terá que ser portada manualmente depois. Aqui está uma lista de softwares/aplicações que o Image2Docker busca, e que são suportados por ele:

  • IIS e aplicações ASP.NET
  • MSMQ
  • DNS
  • DHCP
  • Apache
  • SQL Server

Ou Seja, se dentro de seu VHD você possuir algum desses softwares instalados, o Image2Docker gerará o Dockerfile baseado nisso.

image2docker

 

Instalação

Há duas formas de instalação, Você pode instalar o módulo nativo do Image2Docker, o processo é simples:

Install-Module Image2Docker
Import-Module Image2Docker

Neste caso, se você não tiver todas as dependências instaladas, ele fará essa instalação de forma automática (caso você aprove é claro).
Outro método é você utilizar a última versão diretamente do repositório, neste caso você terá que instalar algumas coisas antes, como por exemplo:

Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201
Install-Module -Name Pester,PSScriptAnalyzer,PowerShellGet

Feito isso, basta agora realizar o download do módulo e importa-lo:

mkdir docker
cd docker
git clone https://github.com/sixeyed/communitytools-image2docker-win.git
cd communitytools-image2docker-win
Import-Module .\Image2Docker.psm1

É muito importante ressaltar que ele foi desenvolvido para rodar em versões superiores a 5.0 do Poweshell, então se atende a esse detalhe.

 

Convertendo

Vamos começar as poucos, digamos que você tenha uma máquina virtual com IIS instalado com algumas aplicações e quer porta-la para container Docker, é possível? Sim, o Image2Docker suporta VMs em Windows 2016, 2012, 2008 e 2003, independente da tecnologia utilizada, seja ela ASP.NET WebForms, ASP.NET MVC, ASP.NET WebApi.

Para você rodar Image2Docker, precisará informar alguns parâmetros, dentre eles:

  • ImagePath – Localização do VHD, VHDx ou WIM
  • Artifact – Que tipo de serviço você quer portar para um Dockerfile, deve ser um dos itens da listagem acima.
  • ArtifactParam – Parametro que deve ser utilizado apenas se o artefato for IIS, pois é possível especificar apenas um site a ser portado.
  • OutputPath – Localização de onde você salvará o Dockerfile

Chega de papo, vamos ao trabalho, para converter uma VM com IIS e todos os seus sites para um único Dockerfile, basta executar:

ConvertTo-Dockerfile -ImagePath C:\VMS\win-2016-iis.vhd -Artifact IIS -Verbose -OutputPath c:\docker\iis

A saída desse comando deve ser algo como isso aqui:

VERBOSE: IIS service is present on the system
VERBOSE: ASP.NET is present on the system
VERBOSE: Finished discovering IIS artifact
VERBOSE: Generating Dockerfile based on discovered artifacts in
:C:\Users\mundodocker\AppData\Local\Temp\287653115-6dbb-40e8-b88a-c0142922d954-mount
VERBOSE: Generating result for IIS component
VERBOSE: Copying IIS configuration files
VERBOSE: Writing instruction to install IIS
VERBOSE: Writing instruction to install ASP.NET
VERBOSE: Copying website files from
C:\Users\mundodocker\AppData\Local\Temp\287653115-6dbb-40e8-b88a-c0142922d954-mount\websites\aspnet-mvc to
C:\docker\iis
VERBOSE: Writing instruction to copy files for aspnet-mvc site
VERBOSE: Writing instruction to create site aspnet-mvc
VERBOSE: Writing instruction to expose port for site aspnet-mvc

Ao final da execução do comando, ele gerará um Dockerfile dentro da pasta c:\docker\iis com o seguinte conteúdo:

# Instalar a feature do IIS
RUN Add-WindowsFeature Web-server, NET-Framework-45-ASPNET, Web-Asp-Net45
RUN Enable-WindowsOptionalFeature -Online -FeatureName IIS-ApplicationDevelopment,IIS-ASPNET45,IIS-BasicAuthentication...

# Adicionar o site mvc no IIS
COPY aspnet-mvc /websites/aspnet-mvc
RUN New-Website -Name 'aspnet-mvc' -PhysicalPath "C:\websites\aspnet-mvc" -Port 8081 -Force
EXPOSE 8081

# Adicionar o site webapi no IIS
COPY aspnet-webapi /websites/aspnet-webapi
RUN New-Website -Name 'aspnet-webapi' -PhysicalPath "C:\websites\aspnet-webapi" -Port 8082 -Force
EXPOSE 8082

Lembrando, no exemplo acima nós portamos todos os sites do IIS para um único Dockerfile, é possível especificar apenas um site, para isso:

ConvertTo-Dockerfile -ImagePath C:\VMS\win-2016-iis.vhd -Artifact IIS -ArtifactParam aspnet-webforms -Verbose -OutputPath c:\docker\iis

Dessa forma, apenas o site chamado: aspnet-webforms será portado para um Dockerfile.

 

Converti, e agora?

Tendo gerado o Dockerfile, basta você seguir o fluxo normal de criação de imagem Docker, por exemplo:

docker build -t mundodocker/site-webforms .

Dessa forma você criará uma imagem chamada: mundodocker/site-webforms e poderá utiliza-la da mesma forma como as demais, apenas lembrando que essa imagem fará a criação do site e copiará os arquivos do site que havia na VM para dentro do container, vamos rodar:

docker run -d -p 8080:8083 mundodocker/site-webforms

Basta agora, você acessar o endereço de sua aplicação, para isso você terá que saber o ip do container criado, então execute:

docker inspect --format '{{ .NetworkSettings.Networks.nat.IPAddress }}' ID_DO_CONTAINER

Agora sim, basta acessar o ip que esse comando retornará, na porta 8080, algo assim: http://172.28.192.4:8080 e você poderá visualizar a aplicação que você tinha em um VM, agora dentro de um container 🙂

 

Dica final

Imagine que você tenha diversos sites dentro do mesmo IIS, e queira portar cada um para um container diferente, qual seria o método normal? Rodar o comando N vezes até ter todos portados, certo? Pois bem, você pode automatizar isso, criar um script em powershell que faça essa “mágica” sem muito esforço. O script pode ser parecido como este:

$sites = @("aspnet-mvc", "aspnet-webapi", "aspnet-webforms", "static")
foreach ($site in $sites) { 
    ConvertTo-Dockerfile -ImagePath C:\VMS\win-2016-iis.vhd -Artifact IIS -ArtifactParam $site -Verbose -OutputPath "c:\docker\$website" -Force
    cd "c:\docker\$site"
    docker build -t "mundodocker/$site" .
}

Dessa forma ele gerará tanto o Dockerfile para cada site assim como as imagens para cada um.

 

Gostaram? Ficaram com dúvidas? Deixe nos comentários ou no nosso fórum, que tanto nós quanto nossos leitores poderão ajudar. E como sempre, ajude divulgando o blog.

Grande abraço!

Entusiasta Open Source, seu principal foco é ir atrás de ideias novas e torna-las realidade através de soluções simples e eficientes, o menos é mais, e o dividir é multiplicar.

Desenvolvimento com Docker

Oi Pessoal,

A intenção hoje é trazer para vocês um conteúdo mais voltado para as equipes de desenvolvimento, ilustrando como é possível automatizar alguns pontos do seu trabalho utilizando Docker e como ele tornará as equipes mais eficientes naquilo que precisam ser: Entrega de resultado, óbvio.

Neste exemplo abordaremos um pouco sobre como é fácil montar um ambiente de desenvolvimento local utilizando nginx, php e mysql, este será, é claro, o primeiro passo em seu caminho para utilizar docker no seu dia-a-dia. Bom, chega de conversa, mãos a obra :).

Obviamente, nesse post não abordaremos como você deve instalar o Docker, para isso temos esse post que vai lhe ajudar muito, veja também que vamos usar nesse ambiente o docker compose, e claro, levamos em consideração que você está utilizando Docker em seu host de desenvolvimento, seja ele Windows, Linux ou Mac, e não no servidor de produção.

A receita

Ok, atendo a esses requisitos, e agora Cristiano, o que faço?

Primeiramente você deve criar, dentro de seu diretório de trabalho um arquivo para que o Docker Compose possa dar inicio a sua stack, e como você viu no post sobre o docker compose, o formato dele é de um arquivo do tipo Yaml, vamos utilizar a versão 2 do Docker Compose, que trás algumas melhorias, mas que difere um pouco na sintaxe, veja o exemplo que vamos usar nesse post:

# Utilizando sintaxe da versão 2:
version: '2'

volumes:
 database_data:
 driver: local

services:
###########################
# Container Web (Nginx)
###########################
 nginx:
 image: nginx:latest
 ports:
 - 8080:80
 volumes:
 - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
 volumes_from:
 - php

###########################
# Container PHP
###########################
 php:
 build: ./php/
 expose:
 - 9000
 volumes:
 - .:/var/www/html

###########################
# Container de banco de dados (MySQL)
###########################
 mysql:
 image: mysql:latest
 expose:
 - 3306
 volumes:
 - database_data:/var/lib/mysql
 environment:
 MYSQL_ROOT_PASSWORD: senha
 MYSQL_DATABASE: projeto
 MYSQL_USER: projeto
 MYSQL_PASSWORD: projeto

A grande diferença entre a versão 1 e 2 do Docker Compose está relacionado a utilização de algumas tags especiais, entre elas: services e volumes. Essas tags obviamente foram adicionadas por um bom motivo, e esse motivo é você poder ter múltiplos serviços dentro de uma stack, mas ter a flexibilidade de escalonar ou modificar atributos de um único serviço dentro de sua stack, sem precisar mexer em toda ela, isso é muito bom, não?

Outro motivo está relacionado ao uso de volumes para a persistência de dados, você não quer perder todo seu trabalho se seu container for acidentalmente removido, certo? E uma das grandes vantagens nesse caso é que você pode especificar um driver para volume que seja persistente não apenas em seu host, mas distribuído em um cluster de volume, explicamos um pouco sobre isso aqui e aqui, em nosso exemplo vamos usar local mesmo.

 

Inside

Note que nas linhas que referem-se ao container PHP, não há referência para imagem, o motivo é simples, faremos o build da imagem no mesmo momento de subir a stack, ou seja, quando rodarmos o docker-compose up -d o docker realizará o build do Dockerfile que encontra-se dentro da pasta php e utilizará a imagem gerada por esse build para iniciar o container php.

Mas e o que tem nesse Dockerfile? Calma, não íamos deixar isso de lado, veja abaixo como é esse Dockerfile:

FROM php:7.0-fpm 
RUN docker-php-ext-install pdo_mysql \ 
&& docker-php-ext-install json

Em nosso lab, usaremos como base a imagem do php 7.0 em fpm, e baseado nisso instalaremos algumas extensões, para que posteriormente possamos utilizá-las. Note que não é algo muito elaborado, mas poderia, caso você tenha essa necessidade. Na imagem em questão, há um binário responsável pela instalação das extensões, que é o docker-php-ext-install, ele realiza o download e instalação da extensão e sua ativação no php.ini global.

Note também que definimos expor a porta 9000 do container php para que o container do serviço web possa acessa-lo e assim processar as requisições. O arquivo de configuração do servidor web deve ser assim:

server {

 # Set the port to listen on and the server name
 listen 80 default_server;

 # Set the document root of the project
 root /var/www/html/public;

 # Set the directory index files
 index index.php;

 # Specify the default character set
 charset utf-8;

 # Setup the default location configuration
 location / {
 try_files $uri $uri/ /index.php;
 }

 # Specify the details of favicon.ico
 location = /favicon.ico { access_log off; log_not_found off; }

 # Specify the details of robots.txt
 location = /robots.txt { access_log off; log_not_found off; }

 # Specify the logging configuration
 access_log /var/log/nginx/access.log;
 error_log /var/log/nginx/error.log;

 sendfile off;

 client_max_body_size 100m;

 # Specify what happens when PHP files are requested
 location ~ \.php$ {
 fastcgi_split_path_info ^(.+\.php)(/.+)$;
 fastcgi_pass php:9000;
 fastcgi_index index.php;
 include fastcgi_params;
 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
 fastcgi_param APPLICATION_ENV development;
 fastcgi_intercept_errors off;
 fastcgi_buffer_size 16k;
 fastcgi_buffers 4 16k;
 }

 # Specify what happens what .ht files are requested
 location ~ /\.ht {
 deny all;
 }
}

Veja que na opção: fastcgi_pass definimos como php:9000 ou seja, o nome do container na porta que expomos no docker-compose. Lembrando que você pode ter acesso a stack completa baixando esse exemplo de nosso repositório no gitub.

Para o Container de banco de dados, utilizamos a imagem oficial do MySQL, definimos apenas os dados de acesso e nome do banco de dados que gostaríamos de criar, e claro definimos um volume onde serão persistidos os dados desse banco.

Agora finalmente basta você subir a sua stack, para isso:

docker-compose up -d

Depois do docker compose realizar todo o processo de build da sua stack, basta você acessar o ambiente web pelo endereço: http://localhost:8080 e você terá como retorno a pagina inicial do seu site (que em nosso teste é apenas um phpinfo).

Próximos passos

 Bom, agora você só precisar criar :), quando você ficar alguma modificação na pasta onde está o projeto, elas serão refletidas no site, ou seja, modificando o seu index.php será alterado no site que está rodando nos container, isso porque mapeamos a pasta local como sendo a public do servidor web/php. O mais interessante dessa abordagem é poder movimentar esse ambiente para onde quiser, imagem que isso tudo faz parte de seu código versionado no git, basta chegar em basta e rodar um git clone do seu projeto (ou pull) e você terá o mesmo ambiente de desenvolvimento.

Gostou? Não gostou? Tem dúvida? Deixa nos comentários que vamos conversando! E como sempre, nos ajude divulgando o blog.

Grande abraço!

Entusiasta Open Source, seu principal foco é ir atrás de ideias novas e torna-las realidade através de soluções simples e eficientes, o menos é mais, e o dividir é multiplicar.