Recebendo Alarmes Do AWS Diretamente No Slack

| Comments

Antes de entrar na configuração dos serviços, talvez seja necessário apresentar o Slack, visto que muitos ainda não conhecem ou utilizam esta poderosa e versátil ferramenta de comunicação instantânea para times.

Slack é uma plataforma para comunicação entre times que desejam um ambiente mais dinâmico e ágil. Diferentemente de muitas plataformas de chat disponíveis, como o Google Hangouts, o Slack nos permite criar canais distintos com membros distintos de um mesmo time fazendo parte daquele canal específico. Não, não estou falando de chat em grupo, mas sim canais específicos que permitem integrações com serviços distintos, como receber notificações sobre commits feitos em um repositório ou branch específico no github, notificações de tickets abertos em ferramentas como o Jira, por exemplo, etc. O Slack é completamente programável e escalável, o que nos permite ter inúmeras funcionalidas.

Provavelmente não seja necessário apresentar o AWS, ou Amazon Web Services, visto que já está no mercado desde 2006, no entanto cabe um resumo para os que não estão familiarizados com o mesmo (embora o público alvo deste post seja quem já possui alguma familiaridade com AWS).

Aws ou Amazon Web Services é uma plataforma de serviços em nuvem segura, oferecendo poder computacional, armazentamento de banco de dados, distribuição de conteúdo e outras funcionalidades.

Por que eu deveria ter alarmes e notificações do AWS em um serviço de chat como o Slack quando já recebo estas notificações por email?

É verdade que o uso mais comum para envio de alarmes e notificações do AWS costuma ser via email, no entanto fica fácil identificar alguns problemas com este método. O principal e mais recorrente que vejo é o caso de as notificações caírem em um email específico visto por poucas pessoas (na maioria das vezes) ou nem visto sequer, pois geralmente as pessoas ficam cansadas de olhar notificações e ter sua caixa de entrada entupida com eles portanto criam filtros que jogam os emails de notificação para um diretório que dificilmente será checado.

Outro problema comum com esta prática é a demora até que alguém leia a notificação no meio de tantos outros na pasta ou filtro criado e, muitas vezes, quando se vê a notificação, o problema já está aguardando uma solução há horas.

Deixando claro, não estou defendendo a ideia de abolir as notificações por email. Eu mesmo utilizo ambos, afinal o email continua bastante eficiente para fins de armazenamento e checagem histórica, por exemplo.

Uma vez que nos dias atuais os times de TI estão cada vez mais unificados e dinâmicos, buscando incorporar uma mentalidade DevOps e Agile, a comunicação rápida e eficiente se torna um fator primordial para o sucesso de qualquer projeto. Ter um local centralizado para conversar com os demais membros do time, trocar arquivos, detalhes de projetos, receber notificações de commits, prazos, tickets, documentação e, por que não, notificações de monitoramento e alarmes, torna-se essencial.

Vamos então entender como funcionaria uma solução para enviar as notificações e alarmes do AWS para o Slack.

O que utilizaremos:

  1. No Slack:
    • Um plugin ou Slack App chamado Incoming WebHooks
    • O nome de um canal para envio das notificações
  2. No AWS:
    • Serviço SNS Topic
    • Serviço CloudWatch
    • Serviço Lambda Function

Vamos lá…

Slack

Vamos começar escolhendo o canal no Slack no qual desejo receber a minha notificação ou alarme: #devops

Estou supondo que você já utiliza o Slack e já possui um time criado no mesmo. Caso ainda não, crie um time no Slack seguindo os passos descritos no site oficial antes de seguir em frente… ;]

O próximo passo é configurar a integração instalando o Plugin ou Slack App Incoming WebHooks. Para isto, acesse a página de apps de seu time no Slack: https://SEUTIME.slack.com/apps

Pesquise por Incoming WebHooks e você terá apenas um resultado, portanto clique sem medo.

Clique no pequeno lápis que se encontrará no canto direito para editar as configurações do Incoming WebHook. Os únicos campos que precisaremos editar neste momento são os seguintes: * Post to Channel – Aqui indicarei o meu canal: #devops * Customize Name – Aqui indicarei um nome qualquer: AWS-Alerts

Importante: Repare que nesta página de configurações ele lhe passará uma entrada ou URL com o código para o seu WebHook. Esta informação estará listada em Webhook URL e será algo como: *https://hooks.slack.com/services/T434P71A4/U4G3JUG13/kPjvXY4Kd8wPm4TvrEqhN6Dv*. Copie esta informação em algum local de fácil acesso pois precisaremos desta URL para a configuração que faremos a seguir no AWS.

Salve suas configurações e vamos configurar os serviços do AWS para que nosso WebHook possa receber as informações devidamente.

Amazon Web Services

Se você já possui alguma familiaridade com o AWS, sabe que existem duas formas principais para administração e gerenciamento de nossos serviços: Pela interface web de gerenciamento (GUI) OU pela linha de comandos através da AWS CLI Tool que se comunica com a API do AWS. Este procedimento, assim como praticamente todos os outros, pode ser realizado por ambos os meios.

Se você também gosta de automação, provavelmente prefere utilizar a CLI, no entanto irei listar aqui o procedimento em ambos os meios.

Passo 1: Criando um SNS Topic para receber os alarmes

1.1 – Pela Interface Web de Gerenciamento (GUI)

  • A partir da Dashboard principal, clique ou busque pelo serviço SNS;
  • Crie um novo SNS Topic:
    • No menu da lateral esquerda, clique em Topics;
    • Clique em Create new topic;
    • Preencha os campos Name (obrigatório) e Display Name (opcional) para o seu tópico. Para este exemplo utilizarei aws-slack-alerts como Name e aws-slack como Display Name; (O Display Name só é necessário em caso de você também desejar enviar notificações por SMS)
    • Clique em Create Topic
  • Agora você já deve ser capaz de ver seu SNS Topic na lista.

1.2 – Pela AWS CLI Tool

Estou assumindo que se você optou por utilizar este método, é porque já possui sua CLI configurada e autenticando em sua conta do AWS com sua chave. Caso você não saiba do que estou falando, sugiro que siga a documentação oficial para isto.

  • Pela CLI tool, digite o seguinte comando, indicando a região na qual você deseja criar seu tópico e o nome desejado:
1
2
3
aws sns create-topic
    --region us-west-1
    --name aws-slack-alerts
  • IMPORTANTE: Você receberá um identificador (TopicArn) para este alarme. Você precisará dele no passo seguinte.
  • Caso queira ter certeza, você pode listar seus tópicos utilizando:
1
aws sns list-topics

Passo 2: Criando um Alarme no serviço CloudWatch

2.1 – Pela Interface Web de Gerenciamento (GUI)

  • A partir da Dashboard principal, clique ou busque pelo serviço CloudWatch;
  • Crie um novo Alarme:
    • Clique em Alarms;
    • Clique no botão Create Alarm;
    • Escolha a categoria do alarme desejado. Para este exemplo utilizarei ELB Metric > Per-LB Metrics (Dentre as várias categorias disponíveis, esta se refere à Load Balancers);
    • Selecione a métrica exata desejada. No caso deste exemplo, preciso selecionar a métrica e o Load Balancer desejado. Ao escolher a métrica e o alvo (em meu caso um Load Balancer) clique em Next. Neste exemplo eu escolhi a métrica HTTPCode_Backend_5XX (para monitorar 500 errors) e um Load Balancer chamado LB-GuySpyV3;
    • O próximo passo é definir um nome e uma descrição para este Alarme, bem como definir as triggers e períodos de monitoramento. Neste exemplo utilizei o nome LB-GuySpyV3-ELB_500 para meu alarme; (Não entrarei em detalhes quanto ao uso das triggers, visto que para cada tipo ou categoria de métrica, as triggers serão diferentes, bem como o cenário de seu ambiente e nível de criticidade. Em resumo, se você deseja monitorar o uso de CPU de um determinado servidor, a trigger seria o gatilho que ativaria o alarme, por exemplo: Só quero ser alarmado se o uso de CPU neste servidor ou instância for >= 90% e assim permanecer por pelo menos 60 segundos, ou por dois períodos seguidos de 60seg.)
    • Na seção Actions da configuração do Alarme defina o State e indique que a notificação deverá ser enviada (Send notification to) para o SNS Topic que criamos anteriormente. Para este exemplo optei por State is ALARM e decidi enviar as notificações para aws-slack-alerts, sendo este o SNS Topic que criei no início;
    • Finalize clicando em Create Alarm.

2.2 – Pela AWS CLI Tool

Novamente… Estou assumindo que se você optou por utilizar este método, é porque já possui sua CLI configurada e autenticando em sua conta do AWS com sua chave. Caso você não saiba do que estou falando, sugiro que siga a documentação oficial para isto.

  • Pela CLI tool, digite o seguinte comando, indicando os atributos abaixo:
    • region (Região);
    • alarm-name (Nome do alarme);
    • alarm-description (Descrição do alarme);
    • alarm-actions (Definir a ação do alarme – Apontar para o TopicArn do SNS Topic que criamos anteriormente);
    • metric-name (Nome da Métrica desejada);
    • namespace AWS/ELB —statistic (Estatística desejada para aquela métrica, neste caso utilizarei Sum (Soma) ao invés de Average (Média));
    • dimensions (O alvo desta métrica de monitoramento, no nosso caso um Load Balancer);
    • period e evaluation-periods (Períodos desejados para a trigger);
    • threshold (O valor desejado: Neste exemplo estou colocando o valor como 1, portanto receberei o alarme caso seja >= 1. Sim, eu sei que receberei o alarme a cada minuto, mas estou fazendo isto de propósito para recebermos a notificação a fim de teste. Nunca utilize um threshold desses em produção. :p);
    • comparison-operator (Operador de comparação desejado, neste caso >=);
1
2
3
4
5
6
7
8
9
10
11
12
aws cloudwatch put-metric-alarm --region us-west-1
    --alarm-name "LB-GuySpyV3-ELB_500"
    --alarm-description "Sends 500-errors to Slack"
    --actions-enabled
    --alarm-actions "TheTopicArn from last step"
    --metric-name "HTTPCode_Backend_5XX"
    --namespace AWS/ELB --statistic "Sum"
    --dimensions "Name=LoadBalancerName,Value=LB-GuySpyV3"
    --period 60
    --evaluation-periods 60
    --threshold 1
    --comparison-operator "GreaterThanOrEqualToThreshold"

Passo 3: Criando uma Função Lambda como Assinante (Subscriber) do nosso SNS Topic

3.1 – Pela Interface Web de Gerenciamento (GUI)

  • A partir da Dashboard principal, clique ou busque pelo serviço Lambda;
  • Crie uma Nova Função Lambda:
    • Clique em Create a Lambda Function;
    • Na tela Select Blueprint clique na opção cloudwatch-alarm-to-slack; (Você poderá precisar buscar por esta opção)
    • O próximo passo será a tela Configure Triggers. Selecione o SNS Topic que foi criado anteriormente (aws-slack-alerts neste exemplo) e marque a opção Enable Trigger e clique em Next;
    • Em Configure Function dê um Nome e uma Descrição para a função e escolha Node.js.4.3 como Runtime;
    • No campo Lambda Function Code cole o seguinte código: Disponível no github
      • (Você deverá setar os valores das variáveis CHANNEL e PATH, onde CHANNEL é o canal do Slack para o qual você deseja mandar as notificações e PATH é a URL de seu WebHook, recebida quando configuramos o Incoming WebHook no Slack)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
var https = require('https');
var util = require('util');

var CHANNEL = "#devops";
var PATH = "/services/T434P71A4/U4G3JUG13/kPjvXY4Kd8wPm4TvrEqhN6Dv";

exports.handler = function(event, context) {
    console.log(JSON.stringify(event, null, 2));
    console.log('From SNS:', event.Records[0].Sns.Message);

    var postData = {
        "channel": CHANNEL,
        "username": "AWS SNS",
        "text": "*" + event.Records[0].Sns.Subject + "*",
        "icon_emoji": ":aws:"
    };

    var message = event.Records[0].Sns.Message;
    var severity = "good";

    var dangerMessages = [
        " but with errors",
        " to RED",
        "During an aborted deployment",
        "Failed to deploy application",
        "Failed to deploy configuration",
        "has a dependent object",
        "is not authorized to perform",
        "Pending to Degraded",
        "Stack deletion failed",
        "Unsuccessful command execution",
        "You do not have permission",
        "Your quota allows for 0 more running instance"];

    var warningMessages = [
        " aborted operation.",
        " to YELLOW",
        "Adding instance ",
        "Degraded to Info",
        "Deleting SNS topic",
        "is currently running under desired capacity",
        "Ok to Info",
        "Ok to Warning",
        "Pending Initialization",
        "Removed instance ",
        "Rollback of environment"
        ];

    for(var dangerMessagesItem in dangerMessages) {
        if (message.indexOf(dangerMessages[dangerMessagesItem]) != -1) {
            severity = "danger";
            break;
        }
    }

    // Only check for warning messages if necessary
    if (severity == "good") {
        for(var warningMessagesItem in warningMessages) {
            if (message.indexOf(warningMessages[warningMessagesItem]) != -1) {
                severity = "warning";
                break;
            }
        }
    }

    postData.attachments = [
        {
            "color": severity,
            "text": message
        }
    ];

    var options = {
        method: 'POST',
        hostname: 'hooks.slack.com',
        port: 443,
        path: PATH
    };

    var req = https.request(options, function(res) {
      res.setEncoding('utf8');
      res.on('data', function (chunk) {
        context.done(null, postData);
      });
    });

    req.on('error', function(e) {
      console.log('problem with request: ' + e.message);
    });

    req.write(util.format("%j", postData));
    req.end();
};
  • O Handler deverá ser o default index.handler;
    • Para role selecione Create a custom role; (Isto será necessário apenas para a sua primeira função)
    • Na tela seguinte selecione lambda_basic_execution como IAM role e deixe o Policy Name com seu valor default. O AWS irá criar uma política de segurança padrão que nos dará os privilégios necessários. Clique em Allow;
    • Certifique-se de que o valor para VPC na seção Advanced Settings seja No VPC; Clique em Next, reveja suas configurações e clique em Create Function;
  • Aguarde seu alarme acontecer e receba a notificação no Slack. :D

O resultado em seu Slack será algo assim…

Parabéns, você já está recebendo suas notificações via Slack. Basta criar outros alarmes no AWS utilizando a mesma Lambda Function e o mesmo SNS Topic.

Happy Hacking!

Docker - Uma Alternativa Elegante Para Containers No Linux

| Comments

Antes de falar sobre o Docker, é importante que se entenda o conceito de um container, portanto vamos começar do básico. A imagem acima, logomarca do Docker, deixa claro o que é um container. A baleia, representando um navio, carregando diversos caixotes ou containers ilustra o conceito físico de um container. Nada mais do que um enorme caixote que possui o intuito de isolar algo. Quando um grande navio transporta mercadorias de um porto para outro, ele costuma trazer diversos containers separando estas mercadorias, de forma que as coisas não fiquem misturadas e bagunçadas. A forma de separação vai depender dos critérios de organização utilizados pela embarcação, seja por proprietário, seja por categoria de produtos, etc. De qualquer forma, embora cada container possua seus elementos próprios, todos os containers compartilham alguns recursos básicos, como por exemplo a embarcação, que é o meio de transporte para todos os containers ali contidos.

Da mesma forma se dá no mundo dos computadores, onde o conceito de containers surgiu para separar e isolar alguns recursos e aplicações, otimizando os recursos que servem como base e que podem ser utilizados de forma compartilhada, como por exemplo o kernel do Sistema Operacional. De certa forma isto nos faz lembrar um pouco da virtualização, onde cada máquina virtual compartilha os recursos da máquina física, no entanto existe uma diferença clara no contexto de containers, visto que em um cenário de virtualização você precisará possuir um SO instalado na máquina física, com seu kernel e todos os seus recursos, e um SO instalado em sua máquina virtual, também com seu kernel e todos os seus recursos. Quando falamos em containers, imagine que você só precisará do kernel, bem como vários outros recursos, na máquina que será a hospedeira do container (a embarcação).

Containers Linux surgiram como uma tecnologia chave para empacotamento e entrega de aplicativos, combinando a leveza do isolamento de aplicativos com a flexibilidade de métodos de deploy baseados em imagens.

Uma das formas mais simples de se imaginar a vantagem da utilização de containers é imaginar que você possui uma empresa que hospeda servidores de aplicações para seus clientes. Se um novo cliente surge querendo hospedar a aplicação dele, você subirá uma nova máquina virtual, o que inclui todo um novo sistema operacional, enquanto que em uma solução baseada em containers você poderá ter apenas a sua máquina com um único kernel Linux provendo as priorizações de recursos (CPU, memória, I/O, rede, etc.) sem a necessidade de dar boot em um novo sistema operacional (máquinia virtual) na qual rodará a aplicação deste cliente.

Dizem que uma imagem vale mais que mil palavras…

Na imagem acima temos o cenário convencional com a utilização de Máquinas Virtuais. Em suma, temos um host físico, com seu respectivo SO e kernel. Acima deles temos a camada de virtualização ou HyperVisor, enquanto que acima desta teremos as máquinas virtuais, com seus respectivos SOs (cada um com seu kernel) instalados. No caso temos 3 VMs, com 3 SOs (cada um com seu kernel). Na camada acima encontramos o que realmente é necessário para o app do cliente funcionar, que são as bibliotecas e os binários. Por fim, o App do cliente em si.

Vejamos como fica o cenário com a utilização de containers, docker neste caso…

No cenário com o Docker percebemos que a camada de SO das VMs sumiu, visto que ela não é mais necessária. Ao invés de Máquinas Virtuais, agora nós temos 3 containers, onde cada container roda os binários e bibliotecas de um SO, porém se aproveitando do kernel já existente no Host.

Com este grau de modularização nós ganhamos maior flexibilidade e agilidade no deploy de ambientes e aplicações.

Uma das vantagens da utilização do Docker é a existência de um repositório de imagens prontas que ficam disponibilizadas livremente para quem desejar utilizar. Seja uma imagem pronta de um container com CentOS, Ubuntu, etc.. Já existem centenas e centenas de imagens prontas para uso, sendo esta uma base de compartilhamento comunitário, mas…

Vamos ao que interessa…

Nos exemplos a seguir, estou utilizando o Ubuntu Server 15.04, visto que estou atualmente realizando uma POC de VPS com um novo host, portanto aproveitarei para fazer disto uma parte de meus testes nesta VPS. Sinta-se livre para utilizar sua máquina física com Ubuntu, com Debian, ou mesmo uma máquina virtual, caso não goste de realizar testes em sua máquina física, o resultado será o mesmo. Para que tudo funcione como esperamos, só existem 2 pré-requisitos a serem atendidos:

1- O kernel do Linux que será utilizado deve ser igual ou superior ao 3.8;

2- Caso você esteja realizando os testes em uma VM, seria interessante que sua máquina física tivesse comunicação com a VM. Isso pode ser testado com um ping da máquina física para a VM. No caso de sua máquina virtual ser instalada com interface gráfica, esta comunicação não será necessária, pois o único momento em que utilizaremos isto será para abrir um navegador e fazer um teste de acesso ao endereço da máquina virtual.

Vamos lá. Para ter a certeza de que você atende o pré-requisito de kernel, utilize o comando “uname -r”:

1
2
 kalib@cloudcaverna:~$ uname -r
 3.19.0-25-generic

Estou com o kernel 3.19, portanto superior ao kernel 3.8 que é o pré-requisito mínimo. Vamos em frente.

Primeiramente, vamos instalar o Docker. Seja lá qual for sua distribuição Linux, digite o comando: (O comando deve ser executado com o usuário root ou com o comando sudo!)

1
 # curl -sSL https://get.docker.com | sh

Ele baixará e executará um script de instalação, no meu caso do Ubuntu ele irá instalar um repositório e em seguida instalará o docker. O próximo passo será iniciar o serviço do docker:

1
2
 # /etc/init.d/docker start
 [ ok ] Starting docker (via systemctl): docker.service.

O docker possui uma série de scripts/comandos próprios para facilitar a sua administração, como por exemplo um script de ps, para que possamos ter a certeza de que ele está rodando e, além disso, saber se existem containers em execução, da mesma forma que faríamos com o ps do linux para ver os processos em andamento.

1
2
3
 # docker ps

 CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

Podemos ver que o docker está rodando, no entanto nenhum container está em execução. Na verdade, não temos nenhum container criado, portanto obviamente não poderia estar em execução.

Além do ps, podemos utilizar o script images para ver quais imagens de containers já possuímos para uso:

1
2
3
 # docker images

 REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE

Da mesma forma, não temos ainda nenhuma imagem baixada para uso.

Uma vez que estamos falando de containers, conforme dito anteriormente, a ideia é isolar ao máximo e otimizar o que precisamos para este container, portanto precisamos informar o processo que desejamos iniciar no container em questão.

Vamos criar um container do Ubuntu, por exemplo, na versão 15.04, lançada em Abril deste ano, e vamos iniciar juntamente com ele o processo /bin/bash. O comando utilizado será: docker run -i -t ubuntu:15.04 /bin/bash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 # docker run -i -t ubuntu:15.04 /bin/bash

 Unable to find image 'ubuntu:15.04' locally
 15.04: Pulling from library/ubuntu

 6e6a100fa147: Pull complete
 13c0c663a321: Pull complete
 2bd276ed39d5: Pull complete
 013f3d01d247: Already exists
 library/ubuntu:15.04: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.

 Digest: sha256:b2d4940544e515d4bc62b2a9ad3e6137b3e1e0937a41fdc1f0f30d12935e5b09
 Status: Downloaded newer image for ubuntu:15.04

 root@d70562e7533c:/#

É importante reparar que na primeira linha de execução ele me trouxe um alerta informando que não foi possível encontrar a imagem “ubuntu:15.04” localmente. Como disse acima, não temos ainda nenhuma imagem baixada, portanto ele não encontrou localmente e foi baixar diretamente no repositório de imagens do docker.

O procedimento foi extremamente rápido, certo? Acredite ou não, você já possui um container Ubuntu rodando em sua máquina. ;] Ainda não acredita? Repare novamente no seu prompt de comandos, veja que logo que ele finalizou o processo ele lhe deixou em um prompt “estranho”. No caso do meu exemplo acima, perceba que ao concluir o processo ele me deixou com o prompt assim:

1
 root@d70562e7533c:/#

Não, minha máquina não se chama d70562e7533c. Tenho certeza de que a sua também não se chama.. seja lá qual for a combinação de caracteres que lhe foi apresentada no prompt. Na verdade, sempre que iniciamos um container, o comportamento default é que você já é logado nele. Em suma, seu container com ubuntu 15.04 já foi criado e você já está logado nele, e esta combinação de caracteres estranha é o ID que foi dado ao seu container.

Ainda não acredita? Bom, você pode, por exemplo, dar um cat /etc/issue, para ver que você está de fato rodando um ubuntu 15.04. Claro, no meu caso não haverá diferença, pois minha máquina que está rodando o docker também é ubuntu 15.04.

1
2
3
4
5
 root@d70562e7533c:/# cat /etc/issue

 Ubuntu 15.04 \n \l

 root@d70562e7533c:/#

O*utro teste, seria rodar ps -ef** no container. Você verá que não existe nenhum processo rodando. Aliás, haverá apenas 1 processo (além do próprio ps), que foi o processo indicado na criação: /bin/bash. Desta forma você terá a certeza de que não está no prompt de sua máquina mesmo, visto que na sua certamente existem dezenas ou centenas de processos rodando, do kernel, do usuário, etc.

1
2
3
4
5
6
7
 root@d70562e7533c:/# ps -ef

 UID        PID  PPID  C STIME TTY          TIME CMD
 root         1     0  0 13:36 ?        00:00:00 /bin/bash
 root         9     1  0 13:44 ?        00:00:00 ps -ef

 root@d70562e7533c:/#

Da mesma forma você poderá experimentar outros comandos para testar (caso ainda não esteja acreditando que de forma tão “oi, simples assim” você já está com seu container pronto): ls, apt-get update, etc.. Tudo funcionando como se fosse uma máquina real, ou virtual, no entanto sem um kernel, visto que ela está utilizando o kernel da máquina host.

Agora que temos a certeza de que estamos em nosso container, você pode sair do container e voltar para sua máquina host. Para isso você precisará pressionar as teclas Ctrl + P + Q. Desta forma, você verá que seu prompt voltará para seu host enquanto que seu container continuará rodando, você apenas saiu do prompt do mesmo.

1
2
3
 root@d70562e7533c:/# root@cloudcaverna:~#

 root@cloudcaverna:~#

Vamos verificar novamente o ps do docker, visto que da última vez ele estava vazio. Desta vez ele nos mostrará um processo em execução, que no caso é o container que criamos.

1
2
3
4
 root@cloudcaverna:~# docker ps

 CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
 d70562e7533c        ubuntu:15.04        "/bin/bash"         15 minutes ago      Up 15 minutes                           modest_khorana

No retorno podemos ver o ID do nosso container, o nome da imagem que ele utiliza, o comando em execução, o tempo desde sua criação, o seu status, portas e nome.

O container ID poderá ser utilizado para voltar para nosso container através do comando docker attach <container-id>: (Após digitar o comando, pressione novamente Enter para liberar o prompt)

1
2
3
 root@cloudcaverna:~# docker attach d70562e7533c

 root@d70562e7533c:/#

No exemplo anterior nós utilizamos a combinação Ctrl + P + Q para sair do container mantendo-o rodando. Vamos experimentar utilizar desta vez Ctrl + D. Desta forma você não apenas está saindo mas também desligando o container. Execute novamente a lista de processos/containers do docker para ver:

1
2
3
 root@cloudcaverna:~# docker ps

 CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

No entanto, é importante lembrar que a imagem do Ubuntu 15.04 que baixamos, continua disponível para o caso de precisarmos criar novos containers Ubuntu 15.04:

1
2
3
4
 root@cloudcaverna:~# docker images

 REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
 ubuntu              15.04               013f3d01d247        17 hours ago        131.4 MB

Simples… Mas vamos fazer algo mais próximo do mundo real, afinal um container apenas com o Ubuntu rodando, ou qualquer outra que seja a distribuição escolhida, não tem muita utilidade, portanto vamos criar um container rodando um servidor web, para o caso de querermos hospedar um site ou aplicalção web neste container. Por ser mais leve e simples, vou utilizar o nginx no exemplo. Vamos utilizar o comando docker run -i -t -p 8080:80 ubuntu:15.04 /bin/bash

No comando acima estamos dizendo que queremos criar um novo container ubuntu 15.04 rodando o /bin/bash. Desta vez temos um parâmetro que não utilizamos anteriormente. O p serve para indicar a utilização de portas. Quando utilizamos p 8080:80 estamos dizendo que vamos utilizar a porta 80 no container e que ela estará mapeada na porta 8080 do nosso host ou hospedeiro. Ou seja, quando instalarmos o Nginx no ubuntu do container, você poderá através de seu host abrir o navegador e acessar o seu endereço ip ou nome de host (caso você possua resolução de nomes funcionando) na porta 8080.

1
2
3
 root@cloudcaverna:~# docker run -i -t -p 8080:80 ubuntu:15.04 /bin/bash

 root@08abb8611700:/#

O processo foi bem mais rápido desta vez, visto que já tínhamos a imagem do container ubuntu 15.04, portanto não tivemos a necessidade de baixar outra imagem. O seu container já está criado e você já está logado no prompt do mesmo, conforme pode ver através do ID do container que apareceu em seu prompt.

Vamos agora instalar o servidor web nginx para realizarmos o nosso teste. Para isso vamos atualizar os repositórios e em seguida instalar o pacote: apt-get update && apt-get install -y nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 root@08abb8611700:/# apt-get update && apt-get install -y nginx

 Ign http://archive.ubuntu.com vivid InRelease
 Ign http://archive.ubuntu.com vivid-updates InRelease
 Get:1 http://archive.ubuntu.com vivid/main Sources [1358 kB]
 Get:2 http://archive.ubuntu.com vivid/restricted Sources [7100 B]
 ...
 Building dependency tree       
 Reading state information... Done
 The following extra packages will be installed:
  fontconfig-config fonts-dejavu-core geoip-database init-system-helpers libexpat1 libfontconfig1 libfreetype6 libgd3 libgeoip1 libicu52
  libjbig0 libjpeg-turbo8 libjpeg8 libpng12-0 libssl1.0.0 libtiff5 libvpx1 libx11-6 libx11-data libxau6 libxcb1 libxdmcp6 libxml2 libxpm4
  libxslt1.1 nginx-common nginx-core sgml-base ucf xml-core
 Suggested packages:
  libgd-tools geoip-bin fcgiwrap nginx-doc ssl-cert sgml-base-doc debhelper
 The following NEW packages will be installed:
  fontconfig-config fonts-dejavu-core geoip-database init-system-helpers libexpat1 libfontconfig1 libfreetype6 libgd3 libgeoip1 libicu52
  libjbig0 libjpeg-turbo8 libjpeg8 libpng12-0 libssl1.0.0 libtiff5 libvpx1 libx11-6 libx11-data libxau6 libxcb1 libxdmcp6 libxml2 libxpm4
  libxslt1.1 nginx nginx-common nginx-core sgml-base ucf xml-core
 0 upgraded, 31 newly installed, 0 to remove and 0 not upgraded.
 Need to get 14.0 MB of archives.
 After this operation, 53.3 MB of additional disk space will be used.
 ...
 Get:1 http://archive.ubuntu.com/ubuntu/ vivid/main libexpat1 amd64 2.1.0-6ubuntu1 [70.6 kB]
 Processing triggers for systemd (219-7ubuntu6) ...

 root@08abb8611700:/#

Cortei bastante a saída visto ser desnecessária, mas uma vez que o nginx esteja instalado, vamos iniciá-lo: /etc/init.d/nginx start. Em seguida, vamos utilizar o ps para ver os processos que estão rodando no nosso container:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 root@08abb8611700:/# /etc/init.d/nginx start

 root@08abb8611700:/# ps -ef

 UID        PID  PPID  C STIME TTY          TIME CMD
 root         1     0  0 14:10 ?        00:00:00 /bin/bash
 root       609     1  0 14:19 ?        00:00:00 nginx: master process /usr/sbin/nginx
 www-data   610   609  0 14:19 ?        00:00:00 nginx: worker process
 www-data   611   609  0 14:19 ?        00:00:00 nginx: worker process
 www-data   612   609  0 14:19 ?        00:00:00 nginx: worker process
 www-data   613   609  0 14:19 ?        00:00:00 nginx: worker process
 root       614     1  0 14:19 ?        00:00:00 ps -ef

 root@08abb8611700:/#

Feito isto, o serviço está rodando e já pode ser testado. Em seu host você poderá abrir o navegador e acessar o endereço local de seu host, com a porta 8080, visto que foi esta que definimos inicialmente para mapear a porta 80 do container: localhost:8080

Caso esteja utilizando uma máquina virtual para fazer seus testes, você terá duas possibilidades: 1- Caso sua máquina virtual possua alguma interface gráfica instalada, você poderá abrir o navegador da própria VM e acessar o mesmo endereço de localhost com a porta 8080; 2- Caso sua VM não esteja com nenhum ambiente gráfico instalado, você poderá utilizar aplicações de CLI para testar (ex: lynx, curl, etc.) ou usar o navegador da máquina que serve de host para sua VM, levando em conta que você fez o teste descrito no início para ter certeza de que sua máquina física consegue se comunicar com sua VM. Neste caso, em sua máqunina física você acessará o endereço de sua vm no navegador, com a porta 8080.

Resultado? O Nginx em nosso container rodando perfeitamente.

Da mesma forma feita anteriormente, podemos sair de nosso container com a combinação de teclas Ctrl + P + Q e verificar os processos/containers do docker em execução:

1
2
3
4
5
6
7
8
 root@08abb8611700:/# root@cloudcaverna:~#

 root@cloudcaverna:~# docker ps

 CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
 08abb8611700        ubuntu:15.04        "/bin/bash"         19 minutes ago      Up 19 minutes       0.0.0.0:8080->80/tcp   jolly_hawking

 root@cloudcaverna:~#

É importante salientar que quando saímos do container com a combinação Ctrl + P + Q nós não estamos fechando o container, pois ele continua rodando, conforme pode ser visto com docker ps. No entanto, quando saímos do nosso antigo container com a combinação Ctrl + D, nós percebemos que ele finalizou de vez o container. Além de finalizar, ele excluiu o nosso container, visto que o mesmo não foi salvo, ou “comittado”. Se sairmos deste container no qual instalamos o nginx utilizando Ctrl + D, nós estaremos descartando tudo o que foi feito nele. Para poder finalizar o seu container sem perdê-lo, ou seja, mantendo o container salvo e tudo o que foi instalado/configurado nele, nós precisamos sair do container com a combinação Ctrl + P + Q, conforme fizemos acima, e em seguida realizar um commit deste container. Estaremos então criando uma imagem com o atual estado do container. Desta forma, se posteriormente precisarmos subir um novo container que rode o SO ubuntu 15.04 e que também possua o nginx, poderemos criar um novo container a partir desta imagem em poucos segundos. O commit se dá da seguinte forma: docker commit <container-id> <nome-que-vc-desejar>

Lembrando que o id do container pode ser conseguido através do comando docker ps e que o nome-que-vc-desejar será o nome utilizado para identificar esta sua máquina/imagem.

1
2
3
4
5
 root@cloudcaverna:~# docker commit 08abb8611700 cloudcaverna/ubuntu-nginx:1.0

 b0922bc8f41295cadadbd131c075e29288b52e8bb2d9546cb7c0327eb95fe7dc

 root@cloudcaverna:~#

Utilizei 1.0 ao final para ter uma ideia de versionamento, visto que o docker nos permite trabalhar desta forma. É uma questão de organização.

Em seguida você pode verificar sua imagem criada através do script images do docker, bem como o seu container ainda rodando através do script ps:

1
2
3
4
5
6
7
8
9
10
11
12
 root@cloudcaverna:~# docker images

 REPOSITORY                  TAG                 IMAGE ID            CREATED              VIRTUAL SIZE
 cloudcaverna/ubuntu-nginx   1.0                 b0922bc8f412        About a minute ago   204.6 MB
 ubuntu                      15.04               013f3d01d247        18 hours ago         131.4 MB

 root@cloudcaverna:~# docker ps

 CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
 08abb8611700        ubuntu:15.04        "/bin/bash"         49 minutes ago      Up 49 minutes       0.0.0.0:8080->80/tcp   jolly_hawking

 root@cloudcaverna:~#

Vamos agora criar um novo container. Aqui você pode escolher como prosseguir. Eu criarei um container com a distribuição Arch Linux rodando, mas você pode seguir e criar outra com o Ubuntu caso deseje, então vejamos as opções:

Opção 1 – Caso escolha criar este segundo container como Arch Linux

1
2
3
4
5
6
7
8
9
10
11
 root@cloudcaverna:~# docker run -i -t -p 6660:80 base/archlinux /bin/bash

 Unable to find image 'base/archlinux:latest' locally
 latest: Pulling from base/archlinux

 b31c6c1462e6: Pull complete
 b97e110c94d9: Already exists
 Digest: sha256:7905fad7578b9852999935fb0ba9c32fe16cece9e4d1d742a34f55ce9cebdfd1
 Status: Downloaded newer image for base/archlinux:latest

 [root@be266bf7e5a3 /]#

Opção 2 – Caso deseje criar um novo container Ubuntu aproveitando a imagem que já está pronta e com nginx já instalado

1
 docker run -i -t -p 6660:80 <nome-que-vc-deu-para-sua-imagem>

Como eu dei o nome de cloudcaverna/ubuntu-nginx, eu utilizaria docker run -i -t -p 6660:80 cloudcaverna/ubuntu-nginx.

Desta forma você não precisará sequer instalar o nginx novamente neste container, visto que você utilizou como base uma imagem que já possuía o nginx instalado, restando apenas a você testar no navegador novamente o endereço porém trocando a porta para 6660.

Como eu resolvi seguir em frente com um container totalmente novo, baseado no Arch Linux, vou dar um cat /etc/issue para ver que realmente estou em um ambiente com a distribuição Arch Linux e vou instalar o nginx nele com o pacman, visto que este é o gerenciador de pacotes do Arch:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 [root@be266bf7e5a3 /]# cat /etc/issue

 Arch Linux \r (\l)

 [root@be266bf7e5a3 /]# pacman -Sy nginx

 :: Synchronizing package databases...
 core                                                        121.2 KiB   203K/s 00:01 [#################################################] 100%
 extra                                                      1773.8 KiB   644K/s 00:03 [#################################################] 100%
 community                                                     2.7 MiB   936K/s 00:03 [#################################################] 100%
 resolving dependencies...
 looking for inter-conflicts...

 Packages (1): nginx-1.8.0-1

 Total Download Size:    0.34 MiB
 Total Installed Size:   0.98 MiB

 :: Proceed with installation? [Y/n]
 :: Retrieving packages ...
 nginx-1.8.0-1-x86_64                                        349.5 KiB   266K/s 00:01  [#################################################] 100%
 (1/1) installing nginx                                                                [ #################################################] 100%

 [root@be266bf7e5a3 /]#

Agora que estou com o nginx rodando também neste container do Arch Linux, vou sair do container pressionando a combinação Ctrl + P + Q e em seguida rodar o script de ps do docker:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 [root@be266bf7e5a3 /]# root@cloudcaverna:~#

 root@cloudcaverna:~# docker ps

 CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
 be266bf7e5a3        base/archlinux      "/bin/bash"         10 minutes ago      Up 10 minutes       0.0.0.0:6660->80/tcp   cocky_hoover
 08abb8611700        ubuntu:15.04        "/bin/bash"         About an hour ago   Up About an hour    0.0.0.0:8080->80/tcp   jolly_hawking

 root@cloudcaverna:~#
 ```

 **D**esta vez eu possuo dois containers criados, sendo um com o ubuntu 15:04 e outro com o Arch Linux. Agora vou realizar o commit do meu container Arch Linux com nginx, para não perder esta imagem. Em seguida, executarei o script **images** para ver as imagens que já possuo:

 ```
  root@cloudcaverna:~# docker commit be266bf7e5a3 cloudcaverna/archlinux-nginx:1.0

 f4ea14bf23c47466fb256fff9e3ab32ca85fb0256a05007ef4972ad7ff5f2aa9

 root@cloudcaverna:~# docker images

 REPOSITORY                     TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
 cloudcaverna/archlinux-nginx   1.0                 f4ea14bf23c4        5 seconds ago       285.9 MB
 cloudcaverna/ubuntu-nginx      1.0                 b0922bc8f412        20 minutes ago      204.6 MB
 ubuntu                         15.04               013f3d01d247        18 hours ago        131.4 MB
 base/archlinux                 latest              b97e110c94d9        8 weeks ago         278.8 MB

 root@cloudcaverna:~#

Agora vou testar no navegador o nginx deste meu segundo container, o qual defini que utilizaria a porta 6660 do meu host:

Repare que acessei ambos os endereços, tanto o da porta 8080, o qual está apresentando o nginx do meu primeiro container, quanto o da porta 6660, que apresenta o nginx do meu segundo container. Ambos funcionando em paralelo, com ambientes distintos, sendo um Ubuntu e o outro Arch Linux, porém ambos compartilham o mesmo kernel, que é o do meu host.

O próprio script ps do docker lhe informa as portas que estão sendo utilizadas para cada container, caso você esteja em dúvida:

1
2
3
4
5
6
7
 root@cloudcaverna:~# docker ps

 CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
 be266bf7e5a3        base/archlinux      "/bin/bash"         26 minutes ago      Up 26 minutes       0.0.0.0:6660->80/tcp   cocky_hoover
 08abb8611700        ubuntu:15.04        "/bin/bash"         About an hour ago   Up About an hour    0.0.0.0:8080->80/tcp   jolly_hawking

 root@cloudcaverna:~#

Da mesma forma, você poderá fazer toda e qualquer operação que você faria em uma máquina qualquer, como por exemplo monitorar os logs de acesso do nginx. Basta se conectar em algum dos dois containers com docker attach <container-id> e em seguida abrir o arquivo de log do nginx com o tail: tail -f /var/log/nginx/access.log. Com o log rodando, pode acessar novamente no navegador o endereço com a porta que está mapeada para este container e você verá os logs do seu acesso.

Anteriormente nós vimos que com a combinação Ctrl + P + Q eu consigo sair do container porém ele permanecerá rodando. Com Ctrl + D eu finalizava o container de vez. Mas, supondo que eu queira parar temporariamente o container, posso utilizar o script stop do container inserindo o id do container que desejo parar:

1
2
3
4
 root@cloudcaverna:~# docker stop be266bf7e5a3
 be266bf7e5a3

 root@cloudcaverna:~#

Existe ainda uma forma de ver o que foi alterado no container desde sua criação. É literalmente uma espécie de “diff”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 root@cloudcaverna:~# docker diff 08abb8611700
 C /.wh..wh.plnk
 A /.wh..wh.plnk/93.1190648
 C /bin
 A /bin/running-in-container
 A /core
 C /etc
 C /etc/default
 A /etc/default/nginx
 A /etc/fonts
 A /etc/fonts/conf.avail
 A /etc/fonts/conf.avail/10-antialias.conf
 A /etc/fonts/conf.avail/10-autohint.conf
 A /etc/fonts/conf.avail/10-hinting-full.conf
 A /etc/fonts/conf.avail/10-hinting-medium.conf
 A /etc/fonts/conf.avail/10-hinting-slight.conf
 A /etc/fonts/conf.avail/10-hinting.conf
 A /etc/fonts/conf.avail/10-no-sub-pixel.conf
 A /etc/fonts/conf.avail/10-scale-bitmap-fonts.conf
 A /etc/fonts/conf.avail/10-sub-pixel-bgr.conf
 A /etc/fonts/conf.avail/10-sub-pixel-rgb.conf
 A /etc/fonts/conf.avail/10-sub-pixel-vbgr.conf
 A /etc/fonts/conf.avail/10-sub-pixel-vrgb.conf
 A /etc/fonts/conf.avail/10-unhinted.conf
 A /etc/fonts/conf.avail/11-lcdfilter-default.conf
 A /etc/fonts/conf.avail/11-lcdfilter-legacy.conf
 A /etc/fonts/conf.avail/11-lcdfilter-light.conf
 ...
 ...

A saída é extensa, portanto cortei aqui mesmo, mas você terá basicamente todo o diff do que foi alterado desde a criação do container.

Resumidamente, esta é a função do docker. Com criatividade e disposição se faz muita coisa com ele. Não é a toa que os big players de mercado já estão utilizando bastante esta ferramenta para soluções de container, como por exemplo: Amazon, Apcera, Cisco, CoreOS, Datera, VMWare, Verizon Labs, Red Hat, Google, RackSpace, Oracle, IBM, Intel, Microsoft, HP, etc.

Lembrando que o endereço de repositórios com as imagens já existentes e disponibilizadas gratuitamente é https://hub.docker.com

Até a próxima…

Happy Hacking!

Assista Ao Documentário Sobre Aaron Swartz: O Menino Da Internet

| Comments

Informação é poder. Mas, como todo poder, há aqueles que querem mantê-la para si mesmos. O patrimônio cultural e científico do mundo, publicado ao longo dos séculos em livros e revistas, está cada vez mais szendo digitalizado e trancado por um punhado de corporações privadas. Enquanto isso, aqueles que foram bloqueados não estão em pé de braços cruzados. Eles estão bisbilhotando em buracos e escalando cercas libertando as informações trancadas pelos editores e compartilhando com seus amigos. Mas toda essa ação acontece no escuro, escondida no subterrâneo. É chamado de roubo ou pirataria, como se compartilhar uma riqueza de conhecimentos fosse o equivalente de saquear um navio e matar sua tripulação. Mas compartilhar não é imoral - é um imperativo moral. Só os cegos pela ganância se recusam a deixar um amigo fazer uma cópia. Não há justiça em seguir leis injustas. É hora de virmos para a luz e, na grande tradição de desobediência civil, declararmos a nossa oposição a este roubo privado da cultura pública.

Este é apenas um trecho do manifesto assinado por Aaron, entitulado “Manifesto do guerrilheiro ao acesso livre”.

Recentemente foi lançado um documentário de Brian Knappenberger sobre Aaron Swartz, o qual foi considerado um dos principais nomes da internet e luta pela liberdade de acesso ao conhecimento dos últimos anos.

Para quem ainda não sabe do que se trata, Aaron Swartz foi um dos criadores do RSS bem como do famoso site de notícias e debates Reddit.Após alguns anos lutando e enfrentando a justiça americana Aaron cometeu suicídio.

Aaron estava sendo condenado a cerca de 50 anos de prisão, bem como a pagar um montante superior a U$ 4 milhões em multas por querer tornar públicos os artigos acadêmicos e científicos que eram mantidos na base do JSTOR, o qual vendia o acesso a estes artigos que, na visão de Aaron, deveriam ser de domínio e acesso público.

Aaron sempre defendeu e lutou para que o conhecimento fosse de livre acesso a todos. Lutou tanto que acabou sendo covardemente perseguido e pressionado pelo governo americano, o que acabou por lhe deixar exausto, psicologicamente e financeiramente, ao ponto de ele desistir da luta ao invés de simplesmente “assumir” que estava errado e aceitar os “acordos” que lhe foram oferecidos pela justiça americana.

Seu crime? A curiosidade. A fome por conhecimento. Também a vontade de expor este conhecimento a todos que o desejassem.

Para quem por algum motivo ainda não conhece o recurso de legendas ou caption do YoutTube, caso a legenda do filme não apareça automaticamente, basta clicar no botão de legendas/caption, habilitando-o, conforme apresentado na imagem abaixo:

Link para o vídeo: https://www.youtube.com/watch?v=2uj1EeiuK5U

Abraços,