Você está na página 1de 62

521

Python - SysAdmin e APIs

www.4linux.com.br
Sumário

1 API 2
SOAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
API REST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Endpoint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Verbo ou Método . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2 cURL 5
Instalação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Utilização . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

3 Postman 7
Instalação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Utilização . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

4 Requisições com Python 10


Módulo Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Instalação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Preparando Ambiente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Criando requisições . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
GET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
POST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
PUT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
DELETE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Exercício . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Objetivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

5 Docker 19
Contêineres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Máquinas Virtuais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
VM vs Contêiner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Instalação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Debian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
CentOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Testando a instalação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Primeiros Passos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Listagem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Imagens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Iniciando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Detalhes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Limpeza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Imagens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Camadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

1
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Criando Aplicações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Portas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Variáveis de ambiente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Volumes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Logs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Dockerfile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Exercício . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Registry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Exercício . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

6 Docker API 32
Unix Socket . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
TCP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Listagem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Exercício . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Criação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Exercício . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

7 Git 36
História . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Clientes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Utilizando o Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Criando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Clonando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Gravando Alterações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Verificando Alterações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Subir alterações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Baixar alterações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Conflitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Trocando de branch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Merge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Pull . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

8 Gerenciadores do Git 40
GitLab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Instalação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Gogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

9 Jenkins 41
História . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Instalação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

10 Preparação 42
Gitlab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Jenkins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

11 Flask 44
Aplicação Simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Blueprints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

12 Jinja 46
Básico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
Variáveis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
Filtros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

2
Blocos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Condições . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Repetições . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

13 LDAP 49
Mas e o banco? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
OpenLDAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Buscar Entradas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Adicionar Usuário . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Apache Directory Studio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
ldap3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

14 Apache 53
Copiar aplicação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Criar WSGI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Configuração do Site . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

15 Logs 54
logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

16 Paramiko 56
Instalação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

17 AWS 58
EC2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
awscli . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
boto3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

3
Capítulo 1

API

API - Application Programming Interface - ou em português, interface de programação de aplicação, é um conjunto de


funções, métodos e padrões desenvolvidas para o acesso de uma determinada aplicação. No geral, uma API pode somente
ser utilizada através de programação.
Um sistema operacional, por exemplo, fornece um conjunto de chamadas que podem ser executados pelos softwares
que querem acesso ao hardware. Já um banco de dados relacional utiliza o SQL como interface de comunicação entre as
aplicações e os dados.

SOAP
SOAP - Simple Object Access Protocol - ou em português, protocolo simples de objeto de acesso é um protocolo que de simples
possui apenas a nomenclatura. O SOAP foi o padrão de comunicação utilizado a algum tempo atrás. Suas mensagens são
trocadas através de XML.
É importante lembrar que o SOAP não é apenas uma definição de troca de mensagens, mas toda a implementação de um
protocolo - diferente do HTTP. O SOAP implementa por exemplo mais segurança, verificação de identidade através de
terceiros, tentativa de reconexão em casos de falha e transações ACID.
Devido a grande maioria das implementações do SOAP utilizar apenas o RPC - Remote Procedure Call - e a relativa
complexidade para pequenas aplicações, o padrão de API REST foi adotado.

XML
1 <?xml version ="1.0" encoding="UTF -8" ?>
2 <root >
3 <id>1</id>
4 <nome >Guido van Rossum </nome >
5 <projetos >
6 <nome >Python </nome >
7 <ano>1989 </ano>
8 </projetos >
9 <projetos >
10 <nome >Computer Programming for Everybody </nome >
11 <ano>1999 </ano>
12 </projetos >
13 <projetos >
14 <nome >Dropbox </nome >
15 <ano>2013 </ano>
16 </projetos >
17 </root >

4
API REST
API REST - Representational State Transfer - é um tipo de API que utiliza endpoints, verbos e status para definir sua
utilização e respostas. APIs REST são largamente utilizadas para comunicação entre diferentes serviços devido sua
natureza genérica.
Geralmente são mais leves pois na maioria das vezes transportam apenas texto em formato JSON, suportam cache e é
relativamente mais fácil de entender.
Muitos serviços possúem APIs REST, como os correios, a wikipedia o imdb e etc. Como a troca de mensagens entre APIs
REST ocorre em formato de texto puro, praticamente qualquer ferramente capaz de fazer uma chamada HTTP pode ser
utilizada para consumir a API, como por exemplo o o comando curl ou o wget.
As linguagens de programação como Python, Perl, PHP, Java, Go e praticamente qualquer outra também podem consultar
APIs REST.
Não se assuste! As APIs REST funcionam de forma muito similar ao que já fazemos em nossos navegadores no dia a dia.
Consultamos URLs, enviamos formulários e etc.

Endpoint
Um endpoint nada mais é do que um endereço de acesso, uma rota. Por exemplo, se tivéssemos um site com o endereço
www.python.example.com e no seguinte endereço www.python.example.com/version obtivéssemos uma resposta
indicando a versão da linguagem, nosso endpoint seria /version.
Podemos fazer uma analogia como o endereço em que um determinado formulário é enviado, por exemplo, ao clicar no
botão de login.

Verbo ou Método
O verbo indica o tipo de ação que pretendemos realizar ao utilizar uma determinada rota. É mais comum encontrarmos a
palávra método em relação a verbo, portanto utilizaremos a palavra método daqui em diante. Existem muitos métodos, mas
os os principais são:
• GET - consulta
• POST - cadastra
• PUT - atualiza
• DELETE - remove
Obs: Os métodos existentes no HTTP 1.0 eram apenas o GET e o POST, porém com a atualização do HTTP para 1.1 dezenas
de outros, incluindo os citados aqui, passaram a existir.

GET
O GET é o mais simples dos métodos, ele realiza uma consulta em um determinado endpoint. Um bom exemplo de um
método GET é o simples acesso a qualquer site.

POST
O POST é o método utilizado quando queremos gravar algo em algum sistema. Por exemplo ao preencher algum formulário
de cadastro de produto ou de identidade em algum site. Os formulários de login - embora não cadastrem o usuário - são
implementados como POST.

PUT
O método PUT é utilizado quando desejamos atualizar algo dentro do sistema, este método já pertence ao HTTP 1.1. Existe
um segundo método chamado PATCH que difere um pouco em relação ao PUT. O PATCH, por convenção, atualiza um
pedaço do recurso em questão, já o PUT atualiza por completo. Não é muito comum encontrar métodos PATCH por aí,
neste caso, por questões de simplicidade, utilizaremos apenas o PUT.

5
DELETE
O DELETE fala por sí só, o utilizamos quando queremos apagar algum recurso no sistema.

Status
O Status é o código que a resposta da chamada retorna. É o status enviado pelo servidor indicando o sucesso, a falha ou
qualquer outra mensagem descrita no protocolo HTTP. Os mais conhecidos são:
• 200 - Tudo Certo
• 201 - Criado
• 301 - Movido Permanentemente
• 302 - Encontrado
• 400 - Requisição Ruim
• 401 - Não Autorizado
• 403 - Proibido
• 404 - Não Encontrado
• 500 - Erro interno
OBS: Apesar do status 401 referir-se a autorização seu verdadeiro uso está nos processos de autenticação, para
autorização utilizamos o status 403.
As mensagems são divididas nos seguintes limites:
• 1xx - Informacional
• 2xx - Sucesso
• 3xx - Redirecionamento
• 4xx - Erro do Cliente
• 5xx - Erro no Servidor
Isso significa que precisamos estar atentos ao status retornado por nossas requisições e tratar as diferentes respostas em
nosso código.
É importante ressaltar que tudo isso é apenas uma convenção, mensagens podem vir em qualquer formato com qualquer
tipo de resposta. Cabe a nós, ao criarmos nossas APIs a respeitar esse padrão e facilitar a utilização por terceiros.

JSON
JSON - JavaSscript Object Notation - é um formato de transporte de dados criado por Douglas Crockford. Seu formato
lembra muito o dicionário do Python, exceto que para definir chaves e valores apenas a aspa - e não o apóstrofo - é permitida.

1 {
2 "id": 1,
3 "nome ": "Guido van Rossum",
4 "projetos ": [
5 {" nome" : "Python", "ano" : 1989} ,
6 {" nome" : "Computer Programming for Everybody", "ano" : 1999} ,
7 {" nome" : "Dropbox", "ano" : 2013}
8 ]
9 }

Obs: Você pode consultar um pouco mais sobre o JSON e seu formato em https://www.json.org/.

6
Capítulo 2

cURL

Conforme foi explicado, APIs REST podem ser consumidas com praticamente qualquer ferramenta capaz de realizar
chamadas HTTP. Existe uma ferramenta de console muito conhecida chamada cURL, apesar de parecer pouco atrativo, o
curl é extremamente poderoso, principalmente para a automação de requisições deste tipo.

Instalação
1 # Debian
2 apt -get install -y curl
3 # CentOS
4 yum install -y curl

Utilização
Com o cURL podemos consultar qualquer página na internet, enviar e receber dados. Dessa mesma forma podemos
consultar APIs REST. Um bom exemplo é utilizar a api https://viacep.com.br/.

1 curl 'viacep.com.br/ws /04101 -300/ json '

O retorno será um JSON com o seguinte conteúdo:

1 {
2 "cep": "04101 -300" ,
3 "logradouro ": "Rua Vergueiro",
4 "complemento ": "de 2771 a 5049 - lado ímpar",
5 "bairro ": "Vila Mariana",
6 "localidade ": "São Paulo",
7 "uf": "SP",
8 "unidade ": "",
9 "ibge ": "3550308" ,
10 "gia": "1004"

Também podemos consultar em XML:

1 curl 'viacep.com.br/ws /04101 -300/ xml'

E o retorno será um XML:

7
1 <?xml version ="1.0" encoding="UTF -8"?>
2 <xmlcep >
3 <cep>04101 -300 </cep>
4 <logradouro >Rua Vergueiro </logradouro >
5 <complemento >de 2771 a 5049 - lado ímpar </complemento >
6 <bairro >Vila Mariana </bairro >
7 <localidade >São Paulo </localidade >
8 <uf>SP</uf>
9 <unidade ></unidade >
10 <ibge >3550308 </ibge >
11 <gia>1004 </gia>
12 </xmlcep >

8
Capítulo 3

Postman

O Postman é uma aplicação utilizada geralmente por pessoas de QA - Q uality Assurance - para realizar testes em rotas de
API REST. Mas é claro, podemos utilizá-lo também durante o desenvolvimento de nossas APIs.
Diferente do cURL o Postman fornece uma interface gráfica, histórico de fácil acesso e as opções disponíveis de forma
bastante intuitiva, facilitando a utilização para pessoas iniciantes.

Instalação
Baixe o Postman em https://www.getpostman.com/downloads/, extraia o conteúdo do arquivo em Downloads, entre no
diretório e execute o programa clicando duas vezes no ícone Postman.
Podemos também fazer o mesmo através do terminal:

1 cd Downloads
2 # Utilizando wget
3 wget https ://dl.pstmn.io/download/latest/linux64 -O postman.tar.gz
4 # Utilizando o cURL
5 curl -L https ://dl.pstmn.io/download/latest/linux64 > postman.tar.gz
6
7 tar -xzvf postman.tar.gz
8 cd Postman
9 ./ Postman

Utilização
O Postman é bastante amigável mas possui muitas opções. Se entendemos os conceitos de uma requisição HTTP sua
interface torna-se bastante sugestiva.

9
Figura 3.1: Tela Login

Esta tela aparece na primeira vez que iniciamos o Postman. Podemos criar uma conta para guardar nossas requisições e
preferências, mas para este momento não precisamos.
1. Utilize a opção para se cadastrar com e-mail e clique na próxima opção pedindo para pular esta etapa.

Figura 3.2: Tela Inicial

Esta é a tela principal, bastante agradável, não?


1. Aqui podemos adicionar novas requisições GET, POST, PUT, DELETE e tantas outras mais.
2. Neste menu lateral ficam o histórico das nossas requisições, que podem ser reutilizadas.

10
Figura 3.3: GET

Ao adicionar uma nova requisição a tela será semelhante para qualquer tipo, com pequenas modificações. Este exemplo
utiliza um GET.
1. Aqui podemos selecionar qual o tipo de requsição queremos utilizar, neste caso um GET.
2. Este é o endereço em que faremos nossas requisições.
3. Este botão dispara a requisição.
4. Ao enviar uma requisição podemos ter acessoa a todos os detalhes nesta barra.
5. O corpo da resposta está dentro desta caixa, e pode ser visualizado de várias formas.

Figura 3.4: POST

1. Aqui podemos selecionar qual o tipo de requsição queremos utilizar, neste caso um POST.
2. Este é o endereço em que faremos nossas requisições.
3. Como este é um POST enviaremos dados:
3.1. Clique em Body.
3.2. Selecione x-www-urlencoded.
4. Preencha os campos necessários para a requisição, neste caso precisamos de uma chave chamada text.
5. Este botão dispara a requisição.
6. Ao enviar uma requisição podemos ter acesso a todos os detalhes nesta barra.
7. O corpo da resposta está dentro desta caixa, e pode ser visualizado de várias formas.

11
Capítulo 4

Requisições com Python

Agora que aprendemos algumas formas de consultar APIs REST estamos prontos para fazer o mesmo através do Python.
Isso significa que poderemos tratar os dados de uma forma melhor e ainda fornecer uma interface para novas consultas ou
realizar consultas mais complexas.
Para facilitar a localização dos arquivos, criaremos um diretório chamado 521 no diretório do nosso usuário:

1 mkdir ~/521

Módulo Requests
O módulo padrão para requisições HTTP - e consequentemente APIs REST - utilizado pelo python chama-se requests.
Apesar de poderoso, sua utilização é bastante simples e talvez isso explique o título “HTTP for Humans” presente na página
do projeto.

Instalação
Para instalar o módulo requests com o gerenciador de pacotes na nossa máquina virtual execute o seguinte:

1 su -
2 apt -get update
3 apt -get install -y python3 -requests

Caso deseje utilizar o virtualenv precisaremos instalar o pip e o próprio pacote pelo sistema, neste caso faça o seguinte:

1 su -c 'apt -get update && apt -get install -y python3 -venv '
2 python3 -m venv 521
3 source 521/ bin/activate
4 pip install requests

OBS: Lembre-se de executar o comando deactivate para sair do ambiente virtual.

Preparando Ambiente
Para testarmos outros métodos além do GET precisaremos de algo mais consistente do que uma API de consulta, e realizar
um POST no dontpad.com não nos permite enviar um JSON, apenas campos de formulário.
Para resolver este problema vamos utilizar o serviço do https://beeceptor.com. O Beeceptor nos fornece uma interface para
criar mocks - simulação - de endpoints e respostas para APIs REST.

12
Acesse o BeeCeptor e vamos criar nossos mocks!

Figura 4.1: Página Inicial

Digite um nome que deseje utilizar para o endereço da API, neste caso escolhemos python.

Figura 4.2: Dashboard

Neste dashboard teremos um histórico de nossas requisições, podemos utilizar o comando curl - veja como este comando é
versátil e utilizado como exemplos nos mais diversos serviços - fornecido como exemplo.
1. Clique em Mocking Rules para criar suas regras.

13
Figura 4.3: Criar Rule

Aqui temos uma listagem de nossos endpoints, clique em Create New Rule para criar um nova rule.

Figura 4.4: POST Rule

1. Em método, selecione a opção POST


2. Defina o endpoint como /usuarios
3. Deixe o tempo de resposta como 1 segundo
4. Modifique o status de retorno para 201 (Created)
5. Modifique o response header para ‘Content-Type: application/json’
6. Modifique o corpo de resposta para ‘{“message” : “Usuário cadastro com sucesso”}’

14
Figura 4.5: Post Rule Criada

Sua nova regra - ou endpoint - aparecerá na lista.

Figura 4.6: Put Rule

1. Em método, selecione a opção PUT, defina o endpoint como /usuarios


2. Deixe o tempo de resposta como 1 segundo e modifique o status de retorno para 200 (OK)
3. Modifique o header para ‘Content-Type: application/json’ e o corpo para ‘{“message” : “Usuário atualizado com sucesso”}’

15
Figura 4.7: Delete Rule

1. Em método, selecione a opção DELETE, defina o endpoint como /usuarios?id=1


2. Deixe o tempo de resposta como 2 segundos e modifique o status de retorno para 200 (OK)
3. Modifique o header para ‘Content-Type: application/json’ e o corpo para ‘{“message” : “Usuário removido com sucesso”}’
Tudo pronto! Vamos começar a programar!

Criando requisições
Vamos criar quatro arquivos, cada um será um exemplo de requisição. Podemos criar pela interface gráfica, mas por
comodidade mostraremos como se faz pelo terminal:

1 mkdir -p ~/521/ rest


2 cd ~/521/ rest
3 touch {get ,post ,put ,delete }.py

GET
O método GET é bastante simples, por conta disso utilizaremos o https://viacep.com.br.
get.py

1 #!/ usr/bin/env python3


2 import requests
3
4 response = requests.get('https :// viacep.com.br/ws /04101 -300/ json/')
5
6 if response.status_code == 200:
7 print(response.json ())

Parece simples, certo?


Como exercício tente resolver estes dois desafios:
1. Receba o CEP do terminal e faça buscas dinâmicas
2. Faça com que a saída exiba apenas o logradouro e o bairro

16
POST
Se o endpoint foi configurado corretamente no BeeCeptor o seguinte comando do curl - ou equivalente no Postman - poderá
consumi-lo:

1 curl -X POST -d '{" nome" : "Paramahansa Yogananda "}' 'https :// python.free.beeceptor.com/usuarios ' -H
'Content -Type: application/json '

OBS: O cabeçalho enviado Content-Type: application/json não é obrigatório, mas vamos utilizá-lo pois a maioria
das APIs REST nos obriga a enviá-lo. O parâmetro -X POST pode ser omitido caso utilizemos o -d.
post.py

1 #!/ usr/bin/env python3


2 import requests
3
4 data = {"nome" : "Paramahansa Yogananda"}
5 response = requests.post('https :// python.free.beeceptor.com/usuarios ', json=data)
6
7 if response.status_code == 201:
8 print(response.json ())

Verifique se o Beeceptor recebeu os seus dados.


Como exercício tente resolver estes dois desafios:
1. Receba o nome do terminal e valide-o, verificando se possui mais de 3 letras.
2. Caso o nome seja vazio ou menor que 3 letras, termine a aplicação.
3. Adicione ao dicionário o campo criacao com a data atual no formato YYYY-mm-dd - utilizar datetime

PUT
O PUT é muito semelhante ao POST, se o Beeceptor foi configurado corretamente, poderemos testá-lo da seguinte forma:

1 curl -X PUT -d '{" nome" : "Paramahansa Yogananda "}' 'https :// python.free.beeceptor.com/usuarios ' -H
'Content -Type: application/json '

put.py

1 #!/ usr/bin/env python3


2 import requests
3
4 data = {"nome" : "Jiddu Krishnamurti"}
5 response = requests.put('https :// python.free.beeceptor.com/usuarios ', json=data)
6
7 if response.status_code == 200:
8 print(response.json ())

DELETE
O DELETE é muito parecido com o método GET, por padrão não possui corpo mas quase sempre carrega um query string
para filtragem:

1 curl -X DELETE https :// python.free.beeceptor.com/usuarios?id=1

delete.py

17
1 #!/ usr/bin/env python3
2 import requests
3
4 response = requests.delete('https :// python.free.beeceptor.com/usuarios?id=1')
5
6 if response.status_code == 200:
7 print(response.json ())

Exercício
Agora que já vimos aquilo que é extritamente necessário para consumir APIs REST, vamos utilizar uma pequena landing page
- escrito em Flask - que pode ser gerenciada via API REST. Esta página aceita todos os métodos e podemos controlar mais
algumas partes do que outras.
O código pode ser obtido em https://github.com/hector-vido/flask-api e a documentação está no próprio README.md do
projeto, podendo ser visualizada o próprio GitHub.
Clone o projeto na raíz do seu usuário:

1 cd
2 git clone https :// github.com/hector -vido/flask -api.git

Precisaremos do Flask:

1 su -c 'apt -get update && apt -get install -y python3 -flask '
2 cd ~/flask -api
3 python3 app.py

Caso deseje utilizar o virtualenv faça o seguinte:

1 cd ~/flask -api
2 python3 -m venv .
3 source ~/flask -api/bin/activate
4 pip install flask
5 python3 app.py

Para ambos os casos é possível acessar a aplicação no endereço http://localhost:5000/.

18
Figura 4.8: Flask API 1

Esta é página inicial - e a única que recebe alterações. Podemos alterar os seguintes itens:
• O título que aparece na barra
• O grande título na frente do fundo roxo
• O texto abaixo deste grande título

Figura 4.9: Flask API 2

Esta parte está um pouco mais abaixo do início, aqui podemos alterar os seguintes itens:
• O título escuro centralizado
• O texto abaixo deste título centralizado
• O ícone, o título e o texto dos três blocos

19
Objetivo
O objetivo é tentar utilizar a api apenas consultando a documentação e verificando as mensagens e status de retorno - sem
a ajuda do professor, se é que você tem um!
Quando se sentir confortável crie um script em Python que utilize a API do site para completar os seguintes items:
1. Alterar o título da página inicial para “4Linux - SysAdmin & APIs”.
2. Alterar o parágrafo do fundo roxo para “Django, Flask e Pyramid são alguns exemplos de frameworks para Python.
Neste caso estamos utilizando o Flask por ser o mais simples e prático!”.
3. Criar um bloco com o ícone “ti-game” de título “Gamefication” e conteúdo “Se eu consegui colocar este bloco no site,
significa que entendi como funciona uma API REST!”.
E valide se suas alterações funcionaram com o seguinte comando dentro do diretório clonado:

1 python3 validate.py

OBS: As verificações são case-insensitive mas os espaços são importantes. Mas isso não signiica que devemos
deixar o site feio.

20
Capítulo 5

Docker

Figura 5.1: Docker

Docker é uma ferramenta open source escrita em Go que automatiza a criação de contêineres dentro de ambientes Linux,
abstraindo a complexidade e adicionando uma camada que facilita o desenvolvimento de aplicações, empacotando-as
juntamente com suas dependências em formatos de simples manutenção.
Sua primeira versão apareceu em março de 2013 durante a conferência de Python chamada PyCon através de uma empresa
chamada dotCloud. Seu sucesso meteórico fez com que a empresa trocasse de nome para Docker, Inc.

Contêineres
Em Linux, contêineres são processos que funcionam isoladas do restante do sistema, diferentemente da virtualização de
máquinas em que o processo em sí é um outro sistema com dezenas de outros processos. O Docker não é a tecnologia
de contêiner, os contêineres são resultados de uma junção de recursos do kernel do Linux, como namespaces e cgroups.
Os contêineres compartilham o kernel e algumas outras bibliotecas essenciais da máquina hospedeira, porém dentro de sí
contêm todas as dependências necessárias para que a aplicação funcione, incluindo um pequeno sistema operacional, mas
bem diferente dos das máquinas virtuais. Contêineres são efêmeros, ou seja, temporários, seus dados normalmente são são
persistidos.
Assim como as máquinas virtuais, os contêineres dependem de uma imagem, essa imagem é quem contêm todas as
dependências bem como nossa aplicação.

21
Máquinas Virtuais
As máquinas virtuais eram até então uma das únicas formas de melhor alocar o hardware para determinadas aplicações e
ainda manter um elevado nível de isolamento. São dispostas em uma infraestrutura que possuí um Hypervisor responsável
pela virtualização - as vezes é necessário que o próprio Hypervisor faça o papel de sistema operacional, porém em outras
vezes, como o é o caso com o VirtualBox ou mesmo o QEMU, existe um sistema operacional comum fornecendo o suporte
necessário. Depois do Hypervisor, cada VM é composta por um Sistema Operacional, cada qual com seus devidos processos
repetindo-se todas as vezes que uma nova máquina virtual é criada. Acima dos sistemas operacionais de cada VM existe a
aplicação, quase sempre compartilhando bibliotecas com o resto do sistema.

VM vs Contêiner

Figura 5.2: Container x VM

Para cada máquina virtual, assim como em sua máquina física, existirá um kernel, bibliotecas, binários e outros processos
que estarão carregados apenas para manter o sistema operacional em funcionamento. Essa necessidade não existe nos
contêineres, já que se utilizam do sistema operacional hospedeiro para os processos básicos. As inicializações também
são um ponto chave, as máquinas virtuais levam quase o mesmo tempo que máquinas físicas para se inicializarem, já os
contêineres podemos dizer que são quase instantâneos.
Por mais detalhes que possamos abordar, essa simples comparação já demonstra que contêineres utilizam menos recursos,
e por tanto são mais leves.
OBS: Em virtualização, chamamos a máquina física de host e as virtuais de guests.

Instalação
A instalação do Docker é muito simples, por mais que seja um curso voltado à programação, por questões didáticas vamos
abordar a mais completa.

Debian
Como root executar os seguintes comandos:

1 apt -get update


2 apt -get install -y apt -transport -https ca -certificates curl gnupg2 software -properties -common
3 curl -fsSL https :// download.docker.com/linux/debian/gpg | sudo apt -key add -
4 add -apt -repository "deb [arch=amd64] https :// download.docker.com/linux/debian $(lsb_release -cs)
stable"
5 apt -get update
6 apt -get install -y docker -ce docker -ce -cli containerd.io
7 systemctl enable docker
8 systemctl start docker

22
CentOS
1 yum install -y yum -utils device -mapper -persistent -data lvm2
2 yum -config -manager --add -repo 'https :// download.docker.com/linux/centos/docker -ce.repo '
3 yum install -y docker -ce docker -ce -cli containerd.io
4 systemctl enable docker
5 systemctl start docker

Os comandos acima fizeram o seguinte:


• Instalaram os pacotes necessários para começar a instalação
• Adicionaram o repositório do Docker e a chave de verificação na máquina
• Baixaram e instalaram os pacotes do Docker
• Habilitaram o processo na inicialização e o iniciaram
Para executar os comandos do docker sem utilizar o usuário root basta adicionar o seu usuário ao grupo do docker e relogar-
se no sistema:

1 usermod -G docker -a developer

OBS: É possível ativar o grupo do docker para a sessão atual utilizando o comando: newgrp docker

Testando a instalação
Para testar se o docker está funcionando, execute o comando de teste:

1 docker run hello -world

Este comando testou tudo o que precisávamos a respeito do Docker:


1. Procurou a imagem localmente, não encontrou e fez o download do registry público em hub.docker.com
2. Iniciou um contêiner com base naquela imagem
3. Executou o comando de dentro do contêiner
4. Exibiu a mensagem na sua tela

Primeiros Passos
Abaixo, segue uma lista de comandos básicos para a utilização do Docker.

Listagem
Temos apenas um contêiner, mas é suficiente:

1 docker ps
2 docker container ls

Nada apareceu, certo? Apenas o cabeçalho. Isso é porque nosso contêiner terminou, não houve um processo persistente
capaz de deixar o contêiner funcionando. Neste caso, para listarmos todos os contêineres precisamos do parâmetro –all ou
-a.

1 docker ps -a # ou --all
2 docker container ls -a

23
A saída será a seguinte:

1 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES


2 c7dba0901d6d hello -world "/ hello" 3 seconds ago Exited (0) 2 seconds ago youthful_mclean

Todo contêiner possui um ID aleatório de 64 caracteres, mas aqui apenas os 12 primeiros aparecem, uma imagem base que
neste caso é hello-world, um comando - /hello - e um nome que caso não seja especificado é composto de um adjetivo e o
nome de um cientista famoso. Existem dezenas de outros parâmetros, mas citamos apenas os mais importantes.

Imagens
As imagens são a base de onde os contêineres usam suas bibliotecas e binários:

1 docker pull httpd


2 docker images
3 docker rmi httpd
4 docker image rm httpd

Iniciando
Para iniciar um contêiner, podemos utilizar o subcomando run:

1 docker run fedora

Porém, neste caso, a imagem do fedora tentará iniciar um shell bash, e para isso precisamos dos parâmetros -t para alocar
um terminal virtual, e -i para o modo interativo, ou seja, para podermos escrever coisas no shell que se abrirá dentro do
contêiner:

1 docker run -ti fedora


2 docker run --tty --interactive fedora

Obs: Para sair do contêiner sem finalizar o processo principal - o que pararia o contêiner - podemos utilizar a combinação
CTRL + P + Q.
Para não ficarmos com o terminal atachado no processo do contêiner e mandá-lo para background podemos utilizar a opção
-d:

1 docker run -dti --name c1 fedora


2 docker run --detach --tty --interactive --name c1 fedora
3 docker rm c1

Detalhes
É possível extrair mais detalhes das execuções dos contêineres com os seguintes comandos:

1 docker run --name fedorinha -dti fedora


2 docker inspect fedorinha

Listar os processos dentro do contêiner:

1 docker top fedorinha

24
E o consumo de recursos:

1 docker stats fedorinha

Limpeza
Para remover todos os contêineres, independente se estão rodando ou não:

1 docker rm -f $(docker ps -qa)

Imagens
Onde estão as imagens do Docker que estamos baixando? Estas imagens estão no hub.docker.com.

Figura 5.3: Docker Hub

Procure por outras imagens ou role a página para baixo, veja que nem todas as imagens são oficiais. Sempre que for utilizar
imagens dê preferência para as oficiais ou aquelas com mais estrelas.
Também podemos buscar imagens pelo próprio terminal, com uma interface um pouco mais limitada:

1 docker search python

A saída será semelhante a seguinte:

1 NAME DESCRIPTION STARS OFFICIAL AUTOMATED


2 python Python is an interpreted …, 5007 [OK]
3 django Django is a free web …appl 940 [OK]
4 pypy PyPy is a fast , …compliant 234 [OK]
5 arm32v7/python Python is an interpreted …, 46
6 joyzoursky/python -chromedriver Python with Chromedriver …, 42 [OK]
7 nikolaik/python -nodejs Python with Node.js 40 [OK]
8 centos/python -35- centos7 Platform for building …and 38
9 circleci/python Python is an interpreted …, 37
10 centos/python -36- centos7 Platform for building …and 28

25
Podemos listar as imagens dos contêineres com o seguinte comando:

1 docker images
2 docker image ls

Existem centenas de imagens do Docker, umas são menores do que as outras. Muitas vezes as imagens menores são
identificadas por uma tag. Todas as imagens possuem tags, e caso não especifiquemos, o docker utilizará a tag :latest.

1 docker pull httpd:latest


2 docker pull httpd
3 docker pull httpd:alpine
4 docker pull debian
5 docker pull debian:buster -slim

Verifique o tamanho das imagens:

1 docker images

Faça a mesma coisa com o nginx e veja a diferença.

Camadas
Façamos o seguinte teste, vamos baixar uma image do alpine - uma pequena distribuição, muito utilizada em contêineres,
e algumas outras imagens:

1 docker pull alpine


2 docker pull python:alpine
3 docker pull php:alpine

Veja uma das saídas:

1 alpine: Pulling from library/php


2 c9b1b535fdd9: Already exists
3 c1c0a1817bec: Pull complete
4 cdd5b3ea1fc3: Pull complete
5 db87396003bd: Pull complete
6 ff6d460d7b33: Pull complete
7 65 eb6d20c777: Pull complete
8 8c0455ae8de1: Pull complete
9 cb8921b52d2e: Pull complete
10 27936 edf200c: Pull complete
11 Digest: sha256 :1864 a86da5eaf37f69d193006eda5181a89d4186a2e91e914e87db57e6e51dd1
12 Status: Downloaded newer image for php:alpine
13 docker.io/library/php:alpine

O importante a notar nesta saída é a linha c9b1b535fdd9: Already exists.


As imagens do Docker são construídas através de camadas e as camadas podem - e devem - ser reutilizadas pelos contêineres
e outras imagens. Dessa forma o Docker sabe quais camadas já estão presentes tanto para baixar como para subir imagens,
economizando espaço na máquina e banda de rede.
Os contêineres são baseados em uma imagem somente leitura. Quando um contêiner é criado uma pequena camada que
permite escrita é criada em união com a camada somente leitura e isso constituí seu filesystem. Outros contêineres podem
ser criados e cada qual terá sua própria camada editável em união com a mesma camada estática da imagem. O nome disto é
union mount e um dos exemplos disponíveis no linux é o UnionFS.
Quando uma leitura é solicitada, o sistema primeiro verifica na camada superior se o arquivo a ser lido está presente nesta
camada, caso não encontre o sistema passa a percorrer as camadas inferiores até encontrar o arquivo desejado.

26
Figura 5.4: Layers

Porém quando um arquivo presente na imagem base é alterado - lembre-se que a imagem é somente leitura - este arquivo é
copiado para a camada de escrita e modificado durante a cópia através de um processo chamado COW.

Exercícios
1. Baixe a versão 3.8 da imagem do python baseada em alpine.

1. Criar um contêiner chamado redis com a imagem redis da versão 5 baseada em qualquer versão do alpine.
2. Descobrir o endereço IP do contêiner e a porta do Redis
3. Executar um netcat no contêiner para receber a mensagem de retorno - seguir instruções abaixo:

1 apt -get install -y netcat


2 echo "PING" | nc -n <endereco > <porta >

OBS: Aperte CTRL + C para sair

Criando Aplicações
Veremos agora alguns aspectos mais interessantes com contêineres um pouco mais complexos.

Portas
Para iniciar um contêiner e expor uma porta na máquina através de NAT, podemos utilizar o parâmetro -p:

1 docker run -dti -p 8080:80 httpd


2 docker run -dti --publish 8080:80 httpd

Variáveis de ambiente
Como as senhas, pontos de acesso e centenas de outras configurações mudam com o tempo, é interessante disponibilizar
esses valores através de variáveis de ambiente com o parâmetro -e:

1 docker run -dti --name alpine -e NOME=Python -e CATEGORIA=Sysadmin alpine # --env

27
Dentro do contêiner podemos verificar as variáveis da seguinte forma:

1 docker exec -ti alpine sh


2 echo $NOME
3 echo $CATEGORIA

Variáveis de ambiente são muito interessante para modificar o comportamento das imagens com base no ambiente em que
está executando, como por exemplo, produção ou desenvolvimento. Um bom guia para desenvolver aplicações “modernas”
pode ser encontrado em https://12factor.net/.

Volumes
Os contêineres quando removidos, levam consigo todos os dados que foram modificados durante seu ciclo de vida. Para
persistir estes dados, utilizamos volumes, que são locais fora do contêiner utilizados para guardar arquivos.
Existem alguns tipos de volumes que podemos utilizar no Docker, o primeiro deles e talvez um dos mais interessantes para
nossos testes é o ponto de montagem, em que um diretório na sua máquina é disponibilizado dentro do contêiner:

1 mkdir -p ~/ html
2 echo '<marquee ><h1 >Docker !</h1 ></marquee >' > ~/ html/index.html
3 docker run -dti -p 8080:80 -v /root/html :/usr/local/apache2/htdocs/ httpd

A segunda forma é especificar um nome para o volume, e o Docker cuidará da gerência:

1 docker run -dti --name mysql -e MYSQL_ROOT_PASSWORD =4 linux -v mysql_data :/var/lib/mysql mysql :5.7

Logs
Podemos visualizar os logs dos contêineres da seguinte forma:

1 docker logs apache # podemos passar -f para seguir a saída

A saída será os logs da aplicação dentro do contêiner:

1 AH00558: httpd: Could not reliably determine the server 's fully qualified domain name , using
172.17.0.2.
2 AH00558: httpd: Could not reliably determine the server 's fully qualified domain name , using
172.17.0.2.
3 [Sat Mar 14 02:15:40.230545 2020] [mpm_event:notice] [pid 1:tid 139629883792712] AH00489: Apache
/2.4.41
4 [Sat Mar 14 02:15:40.230587 2020] [pid 1:tid 139629883792712] AH00094: Command line: 'httpd -D
FOREGROUND '

Exercícios
1. Crie um contêiner baseado em nginx:alpine com o mesmo conteúdo adicionado ao contêiner do apache. Procurar na
documentação da imagem presente no Docker Hub para descobrir o diretório correto.

A maioria dos contêineres podem ter seus comandos alterados passando este comando ao final do docker run depois do
nome da imagem.
O python 3 possui um servidor HTTP embutido que pode ser executado através do seguinte comando:

28
1 # python3 -c 'help (" modules ") ' - para listar os módulos instalados
2 python3 -m http.server -d site # -d indica um diretório

Com esta informação:


1. Clone o repositório https://github.com/4linux/4542-site.git em sua home:

1 mkdir ~/ site
2 git clone https :// github.com/4 linux /4542 - site.git ~/ site

2. Inicie um contêiner chamado site baseado em qualquer versão acima do python 3.7 que consiga exibir no navegador
o conteúdo do diretório ~/site.
2.1. Utilize um volume para o diretório ~/site 2.2. Exponha a porta 8000
Se tudo der certo, o site aparecerá:

Figura 5.5: Site

OBS: Em versões anteriores do python não é possível especificar um diretório

Dockerfile
O Dockerfile é a forma de criarmos nossas imagens, através dele é possível fechar imagens com nossa aplicação e suas
dependências. Existe uma outra forma de criar uma imagem com o Docker, utilizando-se do comando docker commit,
porém é uma forma bastante rudimentar e não vamos abordá-la.
Com o Dockerfile é possível versionar a criação da sua imagem e seu formato de fácil leitura ajuda na compreensão do que
foi feito.
Por padrão, o nome do arquivo é Dockerfile, com a primeira letra em maíusculo:

1 mkdir ~/ minhaimagem
2 cd ~/ minhaimagem
3 vim Dockerfile

Dentro do arquivo, adicione os seguintes comandos:

29
1 FROM alpine
2 RUN apk add curl vim htop
3 CMD sh

Os comandos utilizados fazem o seguinte:


• FROM especifica a imagem base para a construção da atual
• RUN executa comandos no momento de criação da imagem
• CMD especifica o comando utilizado pelo contêiner
Uma vez que tenhamos escrito nosso Dockerfile, precisamos construir a imagem através do subcomando build:

1 docker build -t minha_imagem .


2 docker images
3 docker run -dti --name meu_conteiner minha_imagem

Imagine que na imagem anterior nossa intenção seja adicionar um arquivo de configuração qualquer dentro da imagem
durante a construção. Neste caso, utilizamos o comando COPY:

1 mkdir ~/ minhaimagem2
2 cd ~/ minhaimagem2
3 echo 'Python Sysadmin - Docker ' > arquivo.txt
4 vim Dockerfile

Dentro do novo arquivo, adicione os seguintes comandos:

1 FROM alpine
2 RUN apk add curl vim htop
3 # Os diretórios sempre são criados
4 COPY arquivo.txt /arquivos/arquivo.txt
5 CMD sh

OBS: Também é possível adicionar o diretório inteiro da seguinte forma: COPY . /dir e ignorar arquivos através
do .dockerignore assim como fazemos no git.
Ao construir esta imagem, o arquivo “arquivo.txt” sempre estará dentro dos contêineres gerados:

1 docker build -t minha_imagem2 .


2 docker run -ti --name meu_conteiner2 minha_imagem2 cat /arquivos/arquivo.txt

Exercício
Limpe o ambiente:

1 rm -rf ~/. site


2 mkdir -p ~/ site
3 git clone https :// github.com/4 linux /4542 - site.git ~/ site

Criar um Dockerfile baseado nas imagens apache ou nginx - sinta-se livre se quiser utilizar a tag alpine - com as seguintes
características:
1. Utilizar EXPOSE para informar que a porta 80 será utilizada
2. Utilizar COPY para copiar o conteúdo do diretório ~/site para o diretório correto
3. Ignorar - com o arquivo .dockerignore - a pasta .git
4. Chamar a imagem de “site:v1”
5. Iniciar um contêiner baseado nesta imagem escutando na porta 9090
6. Acessar o servidor web do contêiner pelo navegador

30
Registry
Um registry é um repositório de imagens, um local de onde podemos baixá-las e enviá-las. Neste caso, iremos utilizar o
Docker Hub.
Crie uma conta em https://hub.docker.com/ e então digite no terminal:

1 docker login -u seuusuario

E digite sua senha. Se o processo de cadastro funcionou, agora estamos autenticados para enviar nossas imagens para nossa
conta! Vamos enviar a imagem que acabamos de criar:

1 docker push site

O que será que aconteceu? O Docker Hub rejeitou nossa imagem! Isso é porque as imagens do Docker com nomes simples
pertencem a uma conta especial do próprio Docker Hub, a library.
Por exemplo, a imagem alpine do Docker Hub tem o seguinte nome:

1 docker.io/library/alpine:latest

Podemos interpretar da seguinte forma:

1 endereco/conta/imagem:tag

O binário do docker está preparado para adicionar todo o trecho docker.io/library quando não for especificado. Isso significa
que se possuir um registry privado na sua empresa, precisará referenciar o endereço completo, exemplo:

1 registry .4 linux.com.br/sysadmin/python:latest

No nosso caso precisaremos dizer que nossa imagem não pertence ao “library” e sim a nossa conta, e fazemos isso utilizando
o subcomando tag:

1 docker tag site seuusuario/site

OBS: O subcomando tag apenas cria um novo apelido para a imagem.


Agora podemos enviá-la:

1 docker push seuusuario/site

Vá até o Docker Hub e procure na sua conta pela sua imagem. Depois de alguns tempo ela aparecerá nas buscas. É possível
deixar uma imagem privada gratuitamente, acima disso é preciso pagar.
Qualquer um poderá baixar sua imagem, basta digitarem:

1 docker pull seuusuario/site

31
Exercício
Agora que já sabemos como criar contêineres um pouco mais atrativos e como enviar essa nossa imagem para um repositório
público vamos condensar as coisas um pouco.
Fomos contratados por uma empresa chamada ACME - A Company that Made Everything - para desenvolver um “script”
a fim de facilitar seu processo de ETL. As três principais exigências são que a aplicação seja escrita em Python, utilize
MongoDB e que rode em contêiner, já que futuramente utilizarão Kubernetes em sua infraestrutura.
O objetivo é puxar os dados presentes em https://raw.githubusercontent.com/hector-vido/flask-api/master/samples/
usuarios.csv e cadastrar dentro do MongoDB somente aqueles com salário maior ou igual a R$ 3000,00.
Nossos objetivos são:
1. Iniciar um contêiner do MongoDB versão 4 com as seguintes características:
1.1. - O nome do contêiner deverá ser “mongodb”
1.2. - O nome do usuário administrador deverá ser “acme”
1.3. - A senha do administrador deverá ser “!Abc123” 1.4. - O nome da base de dados deverá ser “acme”
1.5. - O nome da collection será usuarios - o python a criará durante o primeiro insert
1.6. - Utilizar um volume para persistir os dados do MongoDB
2. Criar uma imagem chamada “acme-etl” com as seguintes características:
2.1. - Basear a imagem em alpine puro
2.2. - Instalar o pacote py3-pip
2.3. - Instalar as dependências pelo pip3 e seu próprio “requirements.txt”
2.4. - Copiar os arquivos para /app
2.5. - Quando o contêiner for iniciado, ele executará o script e se encerrará
2.6. - Especificar o id como ”_id” para evitar que o MongoDB atribua seu próprio _id
2.7. - Opcional: O script não poderá inserir dados com “id” repetido - Procurar por upsert
3. Criar uma imagem chamada “acme-json” com as seguintes características:
3.1. - A imagem deverá contêr o código fornecido abaixo
3.2. - Basear a imagem em alpine puro
3.3. - Instalar o pacote py3-pip
3.4. - Instalar as dependências pelo pip3 e o “requirements.txt” fornecido abaixo
3.5. - Copiar os arquivos para /app
3.6. - Especificar que a porta exposta será a 5000
3.7. - O acesso ao MongoDB será feito através de variáveis de ambiente:
3.7.1. - MONGO_HOST - procurar pelo IP do MongoDB
3.7.2. - MONGO_USER
3.7.3. - MONGO_PASS
4. O contêiner baseado em “acme-etl” deverá se chamar “etl”
5. O contêiner baseado em “acme-json” deverá:
5.1 - Se chamar “flask”
5.2 - Escutar na porta 5000

acme-json
Veremos Flask mais adiante, por enquanto precisaremos apenas nos preocupar com o MongoDB.
O código abaixo pode ser encontrado em https://github.com/hector-vido/flask-api/tree/master/samples/acme-json:
app.py

1 #!/ usr/bin/env python3


2
3 from os import environ
4 from pymongo import MongoClient
5 from flask import Flask , jsonify
6
7 app = Flask(__name__)
8

32
9 client = MongoClient('mongodb ://{0}:{1}@{2}:27017/ admin '.format(environ['MONGO_USER '], environ['
MONGO_PASS '], environ['MONGO_HOST ']))
10 db = client.acme
11
12 @app.route('/')
13 def home ():
14 return jsonify ([u for u in db.usuarios.find ()])
15
16 app.run(host='0.0.0.0 ', port =5000 , debug=True)

requirements.txt

1 flask
2 pymongo

33
Capítulo 6

Docker API

Apesar de não parecer, o Docker funciona através de uma arquitetura cliente e servidor que interage através de uma API
REST. A documentação desta api pode ser encontrada em https://docs.docker.com/engine/api/latest/.
Inicialmente as chamadas a essa API estão disponíveis somente através de um unix socket, porém é possível fazer as
chamadas em portas TCP, desde que seja habilitado no serviço do Docker. Sempre que executamos o comando docker …
uma chamada a API é feita.

Figura 6.1: Docker API

Agora não utilizaremos o docker cli, acessaremos a API diretamente!

34
Unix Socket
Na máquina local podemos acessar a API sem muitas dificuldades, exatamente como o comando docker faz, porém com o
curl:

1 curl --unix -socket /var/run/docker.sock http :/v1 .40/ info


2 curl --unix -socket /var/run/docker.sock http :/v1 .40/ containers/json?all=true

TCP
Mas se nossa intenção é acessá-la externamente, existem algumas formas de liberar o acesso através de um endereçoe uma
porta. Anteriormente podíamos modificar apenas o service do Docker adicionando a opção -H tcp://, mas nas versões mais
recentes podemos alterar ou criar o daemon.json:

1 cat > /etc/docker/daemon.json <<EOF


2 {
3 "hosts": ["unix :/// var/run/docker.sock", "tcp ://0.0.0.0:2375"]
4 }
5 EOF

O endereço 0.0.0.0 indica que todas as interfaces de rede da máquina vão aceitar conexões, e as portas que podemos utilizar
por padrão são 2375 - para conexões em texto comum - e 2376 para conexões criptografadas.
Remova as opções conflitantes do service:

1 sed -Ei 's,-H fd:// ,,' /lib/systemd/system/docker.service


2 # Substituí a configuração do unix socket por nada
3 systemctl daemon -reload
4 # Recarrega os services
5 systemctl restart docker
6 # Reinicia o docker

Agora, além do curl, podemos acessar a API do Docker através do Postman ou de qualquer processo que enxergue a nossa
VM sem estar dentro dela.
Podemos pegar informações:

1 curl 'http ://127.0.0.1:2376/ v1 .40/ info '

Listar os contêineres:

1 curl 'http :// localhost :2376/ v1 .40/ containers/json?all=true '

Criar e iniciar um contêiner:

1 curl -X POST http ://127.0.0.1:2376/ v1 .40/ images/create?fromImage=httpd:alpine


2 curl -X POST -H 'Content -Type: application/json ' \
3 -d '{" Image" : "httpd:alpine "}' http ://127.0.0.1:2376/ v1 .40/ containers/create
4 curl -X POST 'http ://127.0.0.1:2376/ v1 .40/ containers /33923 d928d17/start '

35
Python
Provavelmente, agora é simples de imaginar que podemos utilizar a biblioteca requests do Python para fazer esse tipo de
chamada, certo? Sim! Mas para o docker já existe uma biblioteca que facilita essa comunicação Docker SDK for Python.
Vamos instalá-la!

1 apt -get install -y python3 -docker

Ou, se preferir utilizar o venv:

1 cd
2 source 521/ bin/activate
3 pip install docker

Listagem
Vamos testar a conexão do Python com Docker, crie um arquivo chamado container_list.py dentro do diretório 512:

1 cd ~/512
2 mkdir aula -02
3 cd aula -02
4 vim container_list.py

container_list.py

1 #!/ usr/bin/env python3


2
3 import docker
4
5 client = docker.DockerClient(base_url='unix :// var/run/docker.sock ')
6 for c in client.containers.list(all=True):
7 print(c)

A API é bastante sugestiva, neste caso instanciamos um DockerClient que estabelece uma conexão com o docker daemon
através do unix socket. Depois listamos todos os contêineres - equivalente a opção -a - e escrevemos na tela os contêineres
encontrados.
Os métodos da classe DockerClient estão diretamente ligados aos endpoints da API do Docker, isso significa que cada
método trata de uma parte da api, por exemplo:
• containers -> client.containers.list()
• images -> client.images.pull()
• volumes -> cliente.volumes.prune()
É possível notar que o retorno de client.containers.list é uma lista com objetos da classe container. Vamos expandir um pouco
mais o código:

1 #!/ usr/bin/env python3


2
3 import docker
4
5 client = docker.DockerClient(base_url='unix :// var/run/docker.sock ')
6 for c in client.containers.list(all=True):
7 print(c.short_id , c.name , c.image.tags [0])

É possível selecionar um contêiner específico com base em seu id ou nome:

36
1 print(client.containers.get('redis ').id)
2 print(client.containers.get('cd1c11b4b5 ').name)

Exercício
1. Limpe todos os contêineres da máquina, sem exceção, utilizando o python
1.1 - É possível remoter todos os contêineres parados de uma vez, mas não os que estão rodando 1.2 - Precisaremos
percorrer todos os contêineres e removê-los um a um neste caso 1.3 - O método utilizado para remover um contêiner
é remove faz parte da classe Container. 1.4 - É possível utilizar um parâmetro para forçar a remoção
2. Execute o seguinte comando, para carregar alguns contêineres na máquina:
2.1 - curl -sL https://raw.githubusercontent.com/hector-vido/flask-api/master/samples/docker/create-containers.sh |
bash 2.2 - Procure pelos métodos stop e start na documentação da API sobre a classe Container 2.3 - Escreva um script
em python que pare todos os contêineres menos os baseados em apache, nginx e supermario
OBS: Se terminou o exercício, descubra o endereço do “contêiner do mario” e acesse pelo navegador na porta
8080. Utilize as setas e as teclas A e S.

Criação
Não foi dito durante o curso, mas o comando docker run é a junção de outros dois comandos, o create e o start. Na maioria
dos casos não precisamos nos preocupar isso, e aqui não é uma exceção.
Para criar um contêiner utilizando a API para o Python é muito simples. Crie um arquivo chamado container-create.py:

1 cd ~/512
2 mkdir aula -02
3 cd aula -02
4 vim container_create.py

container_create.py

1 #!/ usr/bin/env python3


2
3 import docker
4
5 client = docker.DockerClient(base_url='unix :// var/run/docker.sock ')
6 client.containers.run('httpd:alpine ', name='pypache ', ports ={'80' : '8080 '}, detach=True)

O único detalhe diferente do que utilizamos na linha de comando é a ordem das portas, primeiro é a porta do contêiner
seguido pela porta da máqui.
OBS: Não se esqueça de utilizar o detach nos casos em que o contêiner ficará rodando.

Exercício
Criar um script em python que inicialize a infraestrutura do exercício da ACME.
1. O script deverá um parâmetro com o valor start ou stop
2. Utilizar sys.argv
3. Ao receber o parâmetro start os três contêineres deverão iniciar
4. Ao receber o parâmetro stop os três contêineres deverão parar

37
Capítulo 7

Git

Git é um sistema controlador de versão distribuído. Através de sua utilização é possível manter o histórico e a visualização
de todas as alterações em diversos tipos de documento de texto, incluindo, mas não limitando-se a códigos de linguagens
de programação. É possível obter detalhes de uma determinada alteração, como quem fez, quando fez o que o foi alterado
em relação a versão anterior. O git trabalha com branches, que são ramificações do repositório original. Essas ramificações
são modificadas pelas equipes e então geralmente são anexas a branch principal conhecida como master.

História
Foi criado em 2005 por Linus Torvalds, o criador do kernel do Linux, como substituto ao bitkeeper para versionar o código
do kernel. Na época, o bitkeeper passou a cobrar pela sua utilização e então a equipe resolveu criar seu próprio versionador.
Curiosamente, o resultado deste evento resultou na explosão da aceitação do git como ferramenta de versionamento e hoje
em dia, em empresas que trabalham com ferramentas opensource, git é praticamente sinônimo de versionamento de código.
Obs: Hoje em dia o bitkeeper passou a ser um projeto opensource como podemos ver em https://www.bitkeeper.org/.

Clientes
Devido a facilidade de utilização juntamente a um terminal - presente em praticamente qualquer editor hoje em dia - grande
parte dos desenvolvedores utilizam o próprio comando git para fazer todo o gerenciamento. Durante o curso utilizaremos
somente o comando git, mas para aqueles que preferem uma ferramenta visual de qualidade, open source e multiplataforma,
existe um projeto chamado Git Kraken.

Utilizando o Git
Vamos executar uma série de comandos para que possamos nos habituar com a manipulação do git e aproveitar para abordar
alguns casos mais comuns de uso.

Criando
Para criar um repositório do git, tudo o que precisamos fazer é entrar na pasta em que queremos versionar e executar o
seguinte comando:

1 git init

Você pode verificar que o repositório foi criado pela presença da pasta .git no diretório:

1 ls -la

38
A saída será semelhante a seguinte:

1 .
2 ..
3 .git
4 app.py

É muito comum começarmos criando um repositório em um dos serviços como GitHub, GitLab ou BitBucket, porém um
repositório é iniciado desta forma.

Clonando
Muitas vezes já temos um repositório de onde queremos obter o código fonte, ou criamos um repositório vazio em um dos
serviços online. Neste caso, não podemos iniciar um repositório, mas sim cloná-lo:

1 git clone 'https :// github.com/python/cpython.git'

O comando clone, caso seja executado sem um parâmetro adicional depois do endereço, clonará o repositório em um
diretório com o mesmo nome do arquivo .git, neste caso cpython.

Gravando Alterações
Uma vez que seu repositório esteja pronto, ou que você tenha adicionado os arquivos desejados, é preciso confirmar - commit
- estas alterações. Muitas vezes a palavra commit é utilizada no português como o verbo commitar. O processo consiste em: -
Adicionar o arquivo novo ou alterado - Criar uma confirmação com uma mensagem

1 git add app.py


2 git commit --message 'Minha primeira alteração!' # -m é a abreviação

Verificando Alterações
Antes de gravar as alterações, é possível ver o que foi adicionado, o que foi criado e o que foi modificado com o seguinte
comando:

1 git status

Também é possível ver quais alterações foram feitas em determinados arquivos, desde que o comando seja executado antes
do commit:

1 git diff app.py

É possível consultar o histórico de alterações de várias formas. A mais simples delas é obter uma lista com todos os commits
realizados até o momento, você pode sair do buffer com a tecla “q”:

1 git log

Uma forma mais detalhada é utilizar o comando blame - culpar - para analisar linha a linha o usuário que alterou e o código
do commit:

1 git blame app.py

39
Subir alterações
Uma vez que tenhamos gravado essas alterações me nosso repositório local, podemos enviá-las para um repositório remoto,
garantindo o acesso através de outros computadores e locais. As vezes o repositório remoto não foi criado e portanto não
foi adicionado, neste caso adicionamos o projeto remoto dando um apelido para o endereço que geralmente é origin:

1 git remote add origin 'https :// github.com/python/cpython.git'

Uma vez configurado, podemos sincronizar as alterações com o subcomando push, passando o apelido do endereço remoto
e a branch que queremos enviar:

1 git push origin master

Baixar alterações
Imagine que você tenha trabalhado todo o dia anterior e ao final do expediente tenha enviado suas alterações para o
repositório remoto. Durante a noite outras pessoas trabalharam no código e também enviaram suas alterações. No início
do outro dia, para evitar conflitos, antes de começar a alterar o código novamente, o ideal é baixar todas as alterações. O
subcomando para isso se chama pull e como parâmetro passamos o apelido do endereço remoto e a branch que queremos
atualizar:

1 git pull origin master

Conflitos
Quando estamos trabalhando sozinho em um repositório, raramente teremos problemas de conflito. Mas quando um
time inteiro está trabalhando, enviando e recebendo alterações constantemente, muitas vezes teremos que resolver alguns
conflitos que o git não conseguiu antes de podermos enviar nossas alterações.
No geral, o git tenta fazer o que chamamos de automatic merge e isso funciona na maioria das vezes.
Imagine dois usuários, João e Maria, que estão trabalhando no mesmo projeto, e fizeram o pull logo no início do dia.

Primeiro Cenário
Antes do almoço Maria alterou uma função qualquer e subiu suas alterações. Logo em seguida João alterou outra função e
tentou subir suas alterações:

1 git add .
2 git commit -m 'Função nuclear '
3 git push origin master

O retorno é semelhante ao seguinte:

1 ! [rejected] master -> master (fetch first)


2 error: failed to push some refs to 'git@github.com:seu -usuario/repositorio.git '
3 hint: Updates were rejected because the remote contains work that you do
4 hint: not have locally. This is usually caused by another repository pushing
5 hint: to the same ref. You may want to first integrate the remote changes
6 hint: (e.g., 'git pull ...') before pushing again.
7 hint: See the 'Note about fast -forwards ' in 'git push --help ' for details.

O git no entanto avisa ao João que antes de subir qualquer nova alteração, ele precisará baixar o que já foi feito.

40
Segundo Cenário
A tarde, ambos fizeram alterações na mesma seção do arquivo app.py e subiram suas alterações pelo git. O último a subir
suas alterações deverá baixar e corrigir o que já foi feito:

1 git add .
2 git commit -m 'Alterações na recursividade do arquétipo gerador '
3 git push origin master

Mas desta vez, o git não conseguirá resolver os conflitos e deixará a tarefa para o usuário:

1 From github.com:seu -usuario/repositorio


2 * branch master -> FETCH_HEAD
3 Auto -merging app.py
4 CONFLICT (content): Merge conflict in app.py
5 Automatic merge failed; fix conflicts and then commit the result.

Trocando de branch
As vezes, para não bagunçar a branch principal, criamos uma branch de desenvolvimento e subimos nossa alteração para
esta branch até que estejam suficientemente estáveis para serem mescladas a branch master.

1 git branch dev


2 git checkout dev
3 echo '# Documentação ' > README.md
4 git add .
5 git commit -m 'Adição da documentação '
6 git push origin dev

O comando checkout é responsável pela troca de branches. Podemos verificar em qual branch estamos, através do comando:

1 git branch

E verificar todas as branches existentes da seguinte forma:

1 git branch -a

Merge
Uma vez que tenhamos alcançado o estado aceitável em nossa branch, podemos mesclá-la com a master:

1 git checkout master


2 git merge dev
3 git push origin master

Pull
Uma outra forma de atualizar uma branch é utilizar-se de outra branch remota:

1 git checkout master


2 git pull origin dev
3 git push origin master

41
Capítulo 8

Gerenciadores do Git

O git é o software responsável por versionar nosso código localmente. Porém quando criamos repositórios remotos é
interessante facilitar a gerência de todos os aspectos. Não somente o versionamento, mas controlar usuários, autenticações
e permissões, e para isso utilizamos algum serviço ou instalamos um em nossa infraestrutura local.
Os gerenciadores como serviço mais conhecidos são:
• GitHub
• Bitbucket
• GitLab
Para o nosso curso precisaremos de um gerenciador instalado em nossa infraestrutura local, para isso podemos utilizar o
Gitlab ou o Gogs.

GitLab
Gitlab é um gerenciador de repositórios baseados em git. Surgiu em setembro de 2011 e hoje pode ser considerado como
o gerenciador mais conhecido dentro da comunidade de software livre. Além do gerenciamento de repositórios, o GitLab
possuí todas as ferramentas necessárias para um time que utilize metodologias ágeis, como KanBan, esteira de produção,
repositório de artefatos e deploy. A desvantagem da abrangência de serviços está no fato de precisarmos de no mínimo
4GB de memória RAM para operá-lo sem problemas inesperados. Possuí o serviço online https://gitlab.com/ semelhante ao
GitHub e uma versão que pode ser instalada em sua infraestrutura interna de forma privada. Possuí uma versão community
e uma versão enterprise.

Instalação
A forma de instalação do GitLab será através de um contêiner do Docker utilizando um compose-file. A documentação oficial
pode ser encontrada em https://docs.gitlab.com/omnibus/docker/.

Gogs
Gogs é um outro gerenciador de repositórios baseados em git. Surgiu em 2015 e limita-se somente a fornecer gerenciamento
dos repositórios. Seu foco é a simplicidade, baixa dependência e pouca utilização de recursos, podendo funcionar em uma
máquina baseada em Raspberry Pi.

42
Capítulo 9

Jenkins

Jenkins é uma ferramenta utilizada para integração contínua e entrega contínua, popularmente conhecido como CI/CD -
Continuous Integration e Continuous Delivery. Seu papel é integrar à esteira de produção do software outros programas
responsáveis por testes unitários, análise de código, testes de stress e qualidade para que então uma nova versão possa ser
disponibilizada em produção, ou em algum repositório de artefatos, com uma alta taxa de confiabilidade. O Jenkins é um
dos grandes nomes ao lado do Docker dentro do universo DevOps.

História
O Jenkins surgiu de um fork de seu antecessor conhecido como Hudson, desenvolvido dentro da Sun Microsystems.
A primeira versão do Hudson apareceu em meados de 2005, porém em 2009 a Oracle Corporation comprou a Sun e
consequentemente adquiriu direito sobre todos seus produtos. Após uma disputa pelo nome Hudson - por parte da
comunidade mantenedora e que a Oracle alegava ser proprietária - em 2011 uma votação dos contribuintes culminou no
fork do projeto para outro de nome Jenkins. O projeto do Hudson foi doado para a Apache Foundation em 2012, e em 2017
foi descontinuado e considerado obsoleto, concentrando todos os esforços no já consagrado Jenkins.

Instalação
A instalação do Jenkins será feita através de um contêiner do Docker utilizando um compose-file. A documentação pode ser
encontrada em https://jenkins.io/doc/book/installing/.

43
Capítulo 10

Preparação

Crie os três diretórios a seguir:


• mkdir -p /srv/gitlab/config
• mkdir -p /srv/gitlab/data
• mkdir -p /srv/jenkins/
O contêiner do Jenkins funciona com menos permissões do que estamos acostumados, então altere as permissões do
diretório para refletir o usuário que existirá dentro do contêiner:

1 sudo su
2 chown 1000:1000 /srv/jenkins

Adicione as entradas para cada contêiner dentro do arquivo /etc/hosts. Dessa forma a comunicação entre os contêineres e
com os contêineres ficará facilitada:

1 sudo su
2 echo '172.27.11.10 gitlab.example.com' >> /etc/hosts
3 echo '172.27.11.20 jenkins.example.com' >> /etc/hosts

Dentro do diretório app crie o arquivo docker-compose.yml com o seguinte conteúdo:

1 version: '3.0'
2
3 services:
4 gitlab:
5 image: 'gitlab/gitlab -ce:latest '
6 hostname: 'gitlab.example.com '
7 restart: always
8 ports:
9 - '22:22'
10 - '80:80'
11 - '443:443 '
12 volumes:
13 - '/srv/gitlab/config :/etc/gitlab '
14 - '/srv/gitlab/data :/var/opt/gitlab '
15 extra_hosts:
16 - 'jenkins.example.com :172.27.11.20 '
17 networks:
18 interna:
19 ipv4_address: '172.27.11.10 '
20 jenkins:
21 image: 'jenkins/jenkins:lts '
22 hostname: 'jenkins.example.com '
23 restart: always

44
24 ports:
25 - '8080:8080 '
26 volumes:
27 - '/srv/jenkins /:/ var/jenkins_home '
28 extra_hosts:
29 - 'gitlab.example.com :172.27.11.10 '
30 networks:
31 interna:
32 ipv4_address: '172.27.11.20 '
33
34 networks:
35 interna:
36 driver: bridge
37 ipam:
38 config:
39 - subnet: '172.27.11.0/24 '

Inicie os contêineres com o seguinte comando:

1 docker -compose up # adicione -d para não perder o terminal

Após a inicialização, existem duas configurações a serem feitas.

Gitlab
O Gitlab estará acessível através do endereço gitlab.example.com. Defina a nova senha para Gitlab como 4linux123. O usuário
padrão para fazer o login é o root.
A documentação para a API do Gitlab é muito bem detalhada e pode ser encontrada em https://docs.gitlab.com/ee/api/.
Existe um módulo do Python responsável para comunicação com o GitLab, mas neste caso utilizaremos o próprio requests.

Jenkins
O Jenkins estará acessível através do endereço jenkins.example.com:8080. Para o Jenkins você precisará utilizar o docker logs
ou acessar o conteúdo do arquivo indicado na página de instalação para desbloqueá-la. Instale os plugins recomendados.
Defina o usuário como admin e a senha como 4linux123.
A documentação do Jenkins pode ser visualizada no canto inferior de suas páginas ou em https://wiki.jenkins.io/display/
JENKINS/Remote+access+API.
Também utilizaremos requests para nos comunicar com o Jenkins.

45
Capítulo 11

Flask

Flask é um micro-framework para aplicações web desenvolvido em Python. O “micro” é devido a sua leveza, pois é um
framework bem enxuto, onde o desenvolvedor escolhe quais bibliotecas irá incorporar ao projeto de acordo com as
necessidades da sua aplicação. O Flask foi desenhado justamente para tornar fácil e rápido o início do desenvolvimento,
mas com capacidade de expandir o nível de complexidade da aplicação.
Flask é um dos frameworks que rodam em WSGI (acrônimo de Web Server Gateway Interface), que é uma convenção/especificação
de comunicação entre servidores web e aplicações python mediante chamadas simples.

Aplicação Simples
Uma exemplo de uma aplicação bem simples - retornando apenas um json ao acessar o endereço raíz - poderia ser feito da
seguinte maneira:
flask-01.py

1 #!/ usr/bin/ python3


2 from flask import Flask , jsonify
3
4 app = Flask(__name__)
5
6 @app.route('/')
7 def home ():
8 return jsonify ({"status" : "Rodando ..."})

Poderíamos executar esta aplicação da seguinte forma:

1 FLASK_APP='flask -01. py' flask run

Blueprints
Blueprints são pequenos módulos do Flask em que separamos nossas aplicações para manter a estrutura do projeto mais
organizada. Caso nossa aplicação cresça, o arquivo principal ficará muito grande. Neste caso, separamos a aplicação em
pedaços menores colocando, por exemplo, as rotas referentes a usuários em um arquivo blueprints/usuarios.py
blueprints/usuarios.py

1 from flask import Blueprint , jsonify


2
3 usuarios = Blueprint('usuarios ', __name__)
4 @usuarios.route('/')

46
5 def home ():
6 return jsonify ([{"id" : 1, "nome" : "Dennis Ritchie"}, {"id" : 2, "nome" : "Guido van Rossum"}])

O arquivo principal, responsável por adicionar o blueprint poderia ser da seguinte forma - vamos aproveitar tornar a “/” final
opcional desavitando strict_slashes:

1 #!/ usr/bin/ python3


2 from flask import Flask , jsonify
3 from blueprints.usuarios import usuarios
4
5 app = Flask(__name__)
6 app.url_map.strict_slashes = False
7 app. register_blueprint(usuarios , url_prefix='/usuarios ')
8
9 @app.route('/')
10 def home ():
11 return jsonify ({"status" : "Rodando ..."})

47
Capítulo 12

Jinja

Jinja é uma linguagem de template para Python, baseada nos templates do Django. É bastante intuitiva e sua sintaxe é
utilizada em vários outros lugares. Talvez o melhor exemplo seja os arquivos de template do Ansible. Com o Jinja é possível
escrever condições, repetições e váriavés dentro do HTML que serão mais tardes substituídas pelo Python.

Básico
Um template simples de Jinja pode ser definido da seguinte forma:

1 <! DOCTYPE html >


2 <html lang="en">
3 <head >
4 <title >{{ title }}</title >
5 </head >
6 <body >
7 <ul id="navigation">
8 {% for item in navigation %}
9 <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
10 {% endfor %}
11 </ul>
12
13 <h1>My Webpage </h1>
14 {{ a_variable }}
15
16 {# a comment #}
17 </body >
18 </html >

Os valores entre duas chaves - {{ nome }} - são considerados variáveis e substituídas pelo Python caso uma variável tenha sido
enviada pelo Flask, do contrário, receberá um valor vazio.
Já os trechos entre {% %} são considerados blocos e podem controlar a lógica de exibição ou mesmo uma estrutura de
controle/repetição.

Variáveis
1 @app.route('/')
2 def home ():
3 return render_template('flask -01. html ', titulo='Flask site!')

Filtros
O Jinja suporta alguns filtros - ou modificadores - do lado do template, simplicando o código do lado do backend e separando
melhor a lógica de exibição:

48
1 <! DOCTYPE html >
2 <html lang="en">
3 <head >
4 <title >Flask </title >
5 </head >
6 <body >
7 <h1>{{ conteudo|upper }}</h1>
8 <body >
9 </html >

Neste caso a variável conteudo será transformada em caixa alta. Existem outros modificadores interessantes:
• safe - Não remove caracteres especiais como HTML, JavaScript e etc.
• lower - Transforma todas as letras em caixa baixa.
• join - Unifica uma lista em uma string.
A lista completa de filtros padrão pode ser vista em https://jinja.palletsprojects.com/en/2.11.x/templates/#builtin-filters.

Blocos
Blocos no Flask podem ser definidos de várias formas. Podem exitir blocos que herdam conteúdo de outros templates, blocos
exibidos dentro de condições e blocos dentro de loops de repetição.

Condições
Uma aplicação que envie para o método render_template uma variável indicando true ou false pode exibir ou esconder um
determinado bloco HTML:
flask-03.py

1 #!/ usr/bin/ python3


2 from flask import Flask , render_template
3
4 app = Flask(__name__)
5
6 @app.route('/')
7 def home ():
8 return render_template('flask -03. html ', exibir=False)

E o template utilizado para definir o bloco:


templates/flask-03.html

1 <h1>Flask!</h1>
2 <p>Aqui um pequeno trecho de texto que sempre deve aparecer ...</p>
3 {% if exibir %}
4 <h2>Segredo </h2>
5 <p>E aqui um trecho secreto que só aparecerá ao mudar a varíavel <b>exibir </b>.</p>
6 {% endif %}

Repetições
Uma pequena rota no Flask que envia uma variavel chamada usuarios com uma lista de usuários do banco para o método
render_template:

1 @app.route('/usuarios ')
2 def usuarios ():
3 usuarios = db.usuarios.find ()
4 return render_template('usuarios.html ', usuarios=usuarios)

49
O método poderia receber os usuários da seguinte forma, repetindo na página resultante um bloco para cada usuário:

1 <ul>
2 {% for u in usuarios %}
3 <li><a href="#{{ u.id }}">{{ u.nome }}</a></li>
4 {% endfor %}
5 </ul>

O resultado final seria semelhante ao seguinte:


• Dennies Ritchie
• Guido van Rossum
• Larry Wall
A grande vantagem é que não importa o número de usuários retornados, o template se repetiria quantas vezes fossem
necessárias.

50
Capítulo 13

LDAP

Para simplificar ao máximo, podemos dizer que o LDAP é um protocolo utilizado para autenticação de usuários e consulta
de seus registros. Existem muitos softwares que implementam LDAP, dentro eles o OpenLDAP, o 389 dentro do FreeIPA ou
mesmo o Active Directory da Microsoft.

Mas e o banco?
É muito comum existirem dúvidas sobre a utilidade do LDAP, principalmente em relação a autenticações controladas por
bancos de dados convencionais como MySQL, PostgreSQL, Firebird ou MongoDB.
Mas é importante lembrar que existem integrações com o LDAP na maioria dos softwares desenvolvidos, o que facilita muito
sua adoção. Ainda por um lado mais técnico, as autenticações no LDAP podem acontecer sem um usuário intermediário - o
usuário do banco - eliminando uma etapa e facilitando o arquivamento desses acessos:

Figura 13.1: LDAP vs DMBS

OpenLDAP
O OpenLDAP é uma das implementações do LDAP mais conhecidas dentro do Linux, existem várias formas de instalá-lo e
a instalação difere entre as distribuições. Para este curso utilizaremos um contâiner com uma instalação configurada do
OpenLDAP. Modifique o compose file adicionando o contâiner do OpenLDAP:

1 version: '3.0'
2
3 services:

51
4 gitlab:
5 image: 'gitlab/gitlab -ce:latest '
6 hostname: 'gitlab.example.com '
7 restart: always
8 ports:
9 - '22:22'
10 - '80:80'
11 - '443:443 '
12 volumes:
13 - '/srv/gitlab/config :/etc/gitlab '
14 - '/srv/gitlab/data :/var/opt/gitlab '
15 extra_hosts:
16 - 'jenkins.example.com :172.27.11.20 '
17 networks:
18 interna:
19 ipv4_address: '172.27.11.10 '
20 jenkins:
21 image: 'jenkins/jenkins:lts '
22 hostname: 'jenkins.example.com '
23 restart: always
24 ports:
25 - '8080:8080 '
26 volumes:
27 - '/srv/jenkins /:/ var/jenkins_home '
28 extra_hosts:
29 - 'gitlab.example.com :172.27.11.10 '
30 networks:
31 interna:
32 ipv4_address: '172.27.11.20 '
33 ldap:
34 image: osixia/openldap
35 hostname: 'ldap.example.com '
36 restart: always
37 environment:
38 LDAP_DOMAIN: 'ldap.example.com '
39 LDAP_ADMIN_PASSWORD : 4linux
40 ports:
41 - '3386:386 '
42 - '6636:636 '
43 volumes:
44 - '/srv/ldap/data :/var/lib/ldap '
45 - '/srv/ldap/conf :/etc/ldap/slapd.d'
46 networks:
47 interna:
48 ipv4_address: '172.27.11.30 '
49
50 networks:
51 interna:
52 driver: bridge
53 ipam:
54 config:
55 - subnet: '172.27.11.0/24 '

Verifque se a conexão com o servidor LDAP está funcionando. Você pode entrar no contêiner ou executar o comando por
fora diretamente. Demonstrarei acessando o contêiner:

1 docker exec -ti <container_ldap > bash

Buscar Entradas
E então, dentro do contêiner execute:

1 ldapsearch -D 'cn=admin ,dc=ldap ,dc=example ,dc=com' -w '4linux ' -b 'dc=ldap ,dc=example ,dc=com'

52
Adicionar Usuário
A adição de usuários no LDAP, ou de qualquer tipo de registro é feita através de arquivos LDIF, por exemplo:
user.ldif

1 dn: uid=dennis.ritchie ,dc=ldap ,dc=example ,dc=com


2 objectClass: top
3 objectClass: account
4 objectClass: posixAccount
5 cn: Dennis Ritchie
6 uid: dennis.ritchie
7 uidNumber: 10000
8 gidNumber: 10000
9 homeDirectory: /srv/home/dennis.ritchie
10 loginShell: /bin/bash
11 userPassword: {SSHA} MhndfhVccrnp3Ynam7WhQOp3Eoy /f1YT

Poderíamos adicionar este arquivo a nossa base da seguinte forma:

1 ldapadd -D 'cn=admin ,dc=ldap ,dc=example ,dc=com' -w '4linux ' -f 'user.ldif '

Felizmente existem outras formas de interagir com o LDAP…

Apache Directory Studio


O Apache Directory Studio é um software capaz de facilitar o gerenciamento de bases LDAP fornecendo uma interface
gráfica bastante útil. Baixe a versão para o seu sistema operacional em https://directory.apache.org/studio/downloads.html.

1 wget 'http :// ftp.unicamp.br/pub/apache/directory/studio /2.0.0. v20180908 -M14/ApacheDirectoryStudio


-2.0.0. v20180908 -M14 -linux.gtk.x86_64.tar.gz'
2 tar -xzf 'ApacheDirectoryStudio -2.0.0. v20180908 -M14 -linux.gtk.x86_64.tar.gz'
3 cd ApacheDirectoryStudio /
4 ./ ApacheDirectoryStudio

ldap3
O ldap3 é um módulo escrito puramente em Python para a versão 3 do protocolo LDAP. Pode ser utilizado a partir da versão
2.6 ou qualquer versão 3 do Python.
Instale o módulo em sua máquina:

1 apt -get install -y python3 -ldap3


2 #pip3 install ldap3

1 from ldap3.utils.hashed import hashed


2 from ldap3 import Server , Connection , MODIFY_REPLACE , HASHED_SHA
3
4 ldif = {
5 'cn': 'Larry ',
6 'sn': 'Wall ',
7 'mail ': 'larry.wall@perl.com',
8 'uidNumber ': 10001 ,
9 'gidNumber ': 10001 ,
10 'homeDirectory ': '/srv/home/larry.wall ',
11 'uid': 'larry.wall ',
12 'userPassword ' : hashed(HASHED_SHA , '123')

53
13 }
14
15 server = Server('127.0.0.1 ', use_ssl=False)
16 ldap = Connection(server , user='cn=admin ,dc=ldap ,dc=example ,dc=com', password='4linux ')
17
18 if not ldap.bind ():
19 print('Problemas ao se autenticar ')
20 exit (1)
21
22 object_classes = ['top', 'posixAccount ', 'person ', 'inetOrgPerson ']
23 ldap.add('uid=larry.wall ,dc=ldap ,dc=example ,dc=com', object_classes , ldif)

54
Capítulo 14

Apache

1 apt -get install -y apache2 libapache2 -mod -wsgi -py3

Copiar aplicação
1 sudo cp -r dashboard /var/www

Criar WSGI
Criar o arquivo dashboard.wsgi em /var/www/dashboard:

1 import sys
2 sys.path.insert(0, '/var/www/dashboard/')
3
4 from app import app as application

Configuração do Site
Editar o arquivo /etc/apache2/sites-enabled/000-default.conf :

1 <VirtualHost *:80>
2
3 WSGIDaemonProcess dashboard user=www -data group=www -data threads =5
4 WSGIScriptAlias / /var/www/dashboard/dashboard.wsgi
5
6 <Directory /var/www/dashboard >
7 WSGIProcessGroup dashboard
8 WSGIApplicationGroup %{ GLOBAL}
9 Order deny ,allow
10 Allow from all
11 </Directory >
12 </VirtualHost >

55
Capítulo 15

Logs

Log é o registro de uma determinada ação em um determinado tempo. Praticamente qualquer aplicação dentro de uma
sistema operacional grava log. É através dos logs que podemos verificar o porque alguma aplicação apresentou problema,
se estamos sofrendo sobrecarga em algum serviço ou até mesmo algum tipo de invasão.

logging
O Python na versão 3 já possuí embutido em sí uma classe e um objeto para capturar e gerenciar esses logs. O Flask, por sua
vez, utiliza este objeto e o adiciona dentro da variável app para fazer suas próprias modificações. Ao invés de destruir o que
o Flask já fez, vamos utilizar o logging da aplicação, ao invés de escrever na tela, vamos criar arquivos que rotacionam a cada
1MB:

1 from logging.config import dictConfig


2
3 dictConfig ({
4 'version ': 1,
5 'formatters ': {'default ': {
6 'format ': '[%( asctime)s] %( levelname)s in %( module)s: %( message)s',
7 }},
8 'handlers ': {'wsgi ': {
9 'class ': 'logging.handlers. RotatingFileHandler ',
10 'filename ': 'logs/app.log',
11 'formatter ': 'default ',
12 'maxBytes ': 10485760 ,
13 'backupCount ': 3
14 }},
15 'root ': {
16 'level ': 'INFO ',
17 'handlers ': ['wsgi ']
18 }
19 })

Este trecho de código deve ser definido antes da instanciação da variável app, do contrário teremos que remover o logging
definido e então aplicar nossas configurações:

1 from flask.logging import default_handler


2
3 app.logger.removeHandler(default_handler )

Uma vez que tenhamos configurado o log ao nosso modo, podemos escrever mensagens através dos métodos info, error,
warning e etc:

56
1 app.logger.info('Log de info ')
2 app.logger.error('Log de erro ')
3 app.logger.debug('Debugando ...')

57
Capítulo 16

Paramiko

Paramiko é um módulo que implementa conexões SSH escrito puramente em Python. Isso significa que não há utilização
de bibliotecas externas.

Instalação
1 apt -get install -y python3 -paramiko
2 #pip3 install paramiko

A utilização do Paramiko é muito simples. Importamos o módulo, e então instanciamos um objeto do SSHClient.

1 import paramiko
2
3 client = paramiko.client.SSHClient ()

Depois carregamos as chaves conhecidas pelo computador, sem nenhum parâmetro o arquivo know-hosts é carregado por
padrão.

1 client. load_system_host_keys ()

E então, nos conectamos a um computador, podemos utilizar um usuário com senha ou chave privada, vamos utilizar a
chave:

1 client.connect('172.17.0.2 ', username='root ', key_filename='/home/hector /.ssh/id_rsa ')

Uma vez conectado, podemos executar comandos na máquina remota. Ao definir as três variáves em forma de tupla
podemos trabalhar com input de dados - mas não é tão simples como o restante, capturar os dados da saída comum e de
erros. O retorno é como um texto de um arquivo qualquer:

1 stdin , stdout , stderr = client.exec_command('apt -get install -y htop sl')


2 for l in stdout:
3 print(l.strip ())

Com a conexão feita, enviar e puxar arquivos torna-se uma tarefa trivial. Os métodos get e put fazem todo o trabalho. É
preciso instanciar um objeto sftp, e fazê-lo sem ajuda da conexão já estabelecida é um trabalho detalhista, ainda bem que
o método open_sftp() do client facilita essa inicialização. O método put especifica primeiro um arquivo local e então seu
destino remoto, ambos incluindo o nome. Já o método get se comporta de forma oposta, especificando primeiro um arquivo
remoto e então seu destino local.

58
1 sftp = client.open_sftp ()
2 sftp.put('ssh.py', 'ssh.py')
3 sftp.get('/etc/os -release ', 'os -release ')

59
Capítulo 17

AWS

AWS ou Amazon Web Services é um serviço de cloud disponibilizado pela Amazon. Neste curso, veremos apenas uma
pequena fração específica do universo que é a AWS, o EC2.

EC2
EC2 significa Elastic Cloud Compute - todos os serviços da AWS com letras repetidas consecutivamente recebem um número
depois da primeira letra. Representam as máquinas virtuais que podemos criar dentro da cloud.
Para facilitar a criação de instâncias é mais fácil criar um Security Group para utilizarmos nos códigos em python que
escrevermos. Mas o mais importante é permitir que nosso script se conecte na AWS, criando uma permissão através do
IAM.

awscli
O awscli - command line interface - nos permite instanciar e gerenciar serviços na AWS. Mas também serve para configurar
os arquivos necessários para a conexão, seja através do cli ou do python. Estes arquivos estão localizados na home do usuário
atual e podem ser criados manualmente:

1 .aws/���
2 config���
3 credentials

config

1 [default]
2 region = us -east

credentials

1 [default]
2 aws_access_key_id = 123
3 aws_secret_access_key = abc

No nosso caso, vamos utilizar o comando aws para facilitar a criação e evitar problemas:

1 apt -get install -y awscli

60
Uma vez instalado, execute o subcomando configure para criar os arquivos mencionados com os valores extraídos da sua
conta:

1 aws configure

boto3
Para nos conectar a AWS utilizaremos um módulo do python chamado boto3. O nome boto foi inspirado no boto da
Amazônia que consegue se mover rapidamente pelo rio Amazonas, fazendo referência ao nome Amazon. Para instalar o
boto3

1 apt -get install -y python3 -boto3


2 #pip3 install boto3

Vamos começar importando o boto3:

1 #!/ bin/ python


2 import boto3

O boto3 trabalha com praticamente todos os serviços da AWS, então a inicialização da instância trabalha em um design
pattern parecido com o Fabric, ou seja, eu digo o recurso que quero, e o método me retorna - fabrica - um recurso daquele
tipo.

1 resource = boto3.resource('ec2')

Dessa forma trabalharemos somente com o que é pertinente ao ec2. Caso precisássemos trabalhar com o s3 bastaria
especificar este nome dentro do parênteses.
Feito isso, podemos criar nossa instância. Para criar uma instância precisamos obrigatóriamente de um ami - amazon
images - que é a imagem do sistema operacional que queremos instalar na máquina. Existem milhares deles disponíveis.
Também precisamos especificar o tipo da máquina com InstanceType e a quantidade mínima e máxima de instâncias - para
criar um pool. Outra configuração obrigatória é a chave que vamos utilizar para acessar a máquina, neste caso python, uma
chave que já criamos anteriormente dentro da AWS.

1 resource.create_instances (ImageId='ami -07 d0cf3af28718ef8 ', InstanceType='t2.micro ', KeyName='python '


, MaxCount =1, MinCount =1)

O boto.resource faz todo o trabalho de gerenciar os recursos, mas para procurar e listar nosso serviços o client é muito mais
cômodo:

1 client = boto3.client('ec2')
2 response = client.describe_instances ()

Podemos então unir os dois e terminar - remover - todas instâncias EC2 da nossa conta:

1 for r in response['Reservations ']:


2 for i in r['Instances ']:
3 resource.instances.filter(InstanceIds =[i['InstanceId ']]).terminate ()

61

Você também pode gostar