Você está na página 1de 19

Introdução

O case proposto tem como premissa a utilização de containers e escolhemos utilizar um orchestrador
Kubernetes (padrão de mercado) para que faça a função de orchestração do containers e realizar o deploy
dos componentes server-api, kong(ingress), fluent-bit e prometheus.
Devido a limitação de meu notebook (possui 4gb de ram) e para seguir boas práticas, alguns itens como
grafana/mysql foi executado na mesma máquina do kube-master com a premissa de estar isolado do cluster
kubernetes e precisar persistência de dados no disco.Apesar do Prometheus utilizar um TSDB o mesmo
precisa de integração ao kubernetes para realizar o scrape via service discovery.
Utilizamos 3 endpoint de restapi que são: top5, hour, location para que os clientes possam requisitar as
informações em formato json.

Informações da topologia

Servidores:

kube-master - 192.168.137.5
kube-worker - 192.168.1376
Mysql - 192.168.137.5
Grafana - 192.168.137.5
Namespaces

Prometheus Namespace

Namespace responsável por monitoramento do pods e do ingress, ou seja, namespace responsável


por scrape pods e kubernetes pelo prometheus.

Prometheus endpoint : http://192.168.137.6:30090/

NAME READY STATUS RESTARTS AGE


pod/prometheus-deployment-75cf78d9d6-8c5hq 1/1 Running 4 3h19m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/prometheus NodePort 10.97.9.204 <none> 9090:30090/TCP 176m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/prometheus-deployment 1/1 1 1 3h21m
NAME DESIRED CURRENT READY AGE
replicaset.apps/prometheus-deployment-75cf78d9d6 1 1 1 3h19m

Namespace Logging

Namespace responsável por enviar logs dos pods/aplicação para o elasticsearch pelo pod fluent-bit.
Este deployment foi efetuado através do Tipo DaemonSet, ou seja, todo node terá um pod de fluent-
bit

OBS: fluent-bit foi configurado para enviar para um elasticsearch no servidor 192.168.137.5 porém o
meu notebook contém apenas 4gb de memória e o elasticsearch depende de 1GB dedicado só pra
ele portanto não tem como usar o elasticsearch neste teste ou outra datastore pra logs.

NAME READY STATUS RESTARTS AGE


pod/fluent-bit-nh8d7 1/1 Running 8 2d2h
pod/fluent-bit-vjzcv 1/1 Running 9 2d2h
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR
AGE
daemonset.apps/fluent-bit 2 2 2 2 2 <none> 2d2h

Configuração do configmap:

@INCLUDE input-kubernetes.conf
@INCLUDE filter-kubernetes.conf
@INCLUDE output-elasticsearch.conf
input-kubernetes.conf: |
[INPUT]
Name tail
Tag kube.*
Path /var/log/containers/*.log
Parser docker
DB /var/log/flb_kube.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
output-elasticsearch.conf: |
[OUTPUT]
Name es
Match *
Host ${FLUENT_ELASTICSEARCH_HOST}
Port ${FLUENT_ELASTICSEARCH_PORT}
Logstash_Format On
Replace_Dots On
Retry_Limit False

Logs demonstrando a tentativa da conexão:

kubectl logs fluent-bit-nh8d7 -n logging | tail -10


[2019/11/11 04:18:06] [error] [io] TCP connection failed: 192.168.137.5:9200 (Connection refused)
[2019/11/11 04:18:06] [error] [io] TCP connection failed: 192.168.137.5:9200 (Connection refused)
[2019/11/11 04:18:06] [error] [io] TCP connection failed: 192.168.137.5:9200 (Connection refused)
[2019/11/11 04:18:07] [error] [io] TCP connection failed: 192.168.137.5:9200 (Connection refused)
[2019/11/11 04:18:08] [error] [io] TCP connection failed: 192.168.137.5:9200 (Connection refused)
[2019/11/11 04:18:09] [error] [io] TCP connection failed: 192.168.137.5:9200 (Connection refused)

Namespace kong

Esse namespace é utilizado para todo tráfego de entrada (ingress) e também para o kong ingress
controller para aplicar configurações diretamente pelo kubernetes crds.

NAME READY STATUS RESTARTS AGE


pod/ingress-kong-858455bbc4-qtsnr 2/2 Running 51 2d19h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
AGE
service/kong-proxy LoadBalancer 10.109.73.199 <pending> 80:31188/TCP,443:30545/TCP
2d19h
service/kong-validation-webhook ClusterIP 10.99.103.31 <none> 443/TCP
2d19h
service/prometheus-metrics NodePort 10.109.37.157 <none> 9542:30079/TCP
3h55m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-kong 1/1 1 1 2d19h
NAME DESIRED CURRENT READY AGE
replicaset.apps/ingress-kong-858455bbc4 1 1 1 2d19h

Código python do script para coletar as tags do twitter e guardar no servidor


mysql

import pymysql
import tweepy
import sys
reload(sys)
sys.setdefaultencoding("utf-8")

arguments = len(sys.argv) - 1

# output argument-wise
position = 1

class conexao_twitter:

def connectar_twitter(self):
try:
chave_consumidor = 'nzVZxlvnDhF9N6fAK8cpzUfZ2'
segredo_consumidor = 'YCDkKdre5szYwU8HnHBsDqXpnHAM9RTGCERXyUz0ssTWVL7lGt'
token_acesso = '600587786-9ZmBhKZnUvAQ1RPtcB5UjxaVlnokHiotDV50ZFCE'
token_acesso_segredo = 'kCkGJah5XbD1yqSK5fqLKARIsDndufZldRpNP1sagMcKk'
autenticacao = tweepy.OAuthHandler(chave_consumidor, segredo_consumidor)
autenticacao.set_access_token(token_acesso, token_acesso_segredo)
return tweepy.API(autenticacao)
except:
return "Error conectar ao twitter"

class conecta_db:
def conectar_db(self):
try:
conexao = pymysql.connect(db='twitter', user='root', passwd='',charset='utf8mb4',use_unicode=False)
return conexao
except:
return "Error conectar ao twitter"

class coleta_tag:
def coletar_tag(self,hashtag):
hashtag="#" + hashtag
try:
resultados = conectado_twitter.search(q='{}'.format(hashtag),count=100)
for tweet in resultados:
frase = tweet.text
date = tweet.created_at
user = tweet.user.screen_name
location = tweet.user.location
followers = tweet.user.followers_count
language = tweet.user.lang
conecta_db_cons = conecta_db()
conectado_db = conecta_db_cons.conectar_db()
db_cursor = conectado_db.cursor()
frase=frase.replace("\"", "'")
frase=frase.replace("\n", " ")
db_cursor.execute('INSERT INTO tweets(user,location,tweet,followers,hashtag ) VALUES
("{}","{}","{}","{}","{}")'.format(user,location,frase,followers,hashtag))
conectado_db.commit()
conectado_db.close()
return "Sucesso ao coletar tag"
except:
return "Error ao coletar tag"

conexao_twitter = conexao_twitter()
conectado_twitter = conexao_twitter.connectar_twitter()

while (arguments >= position):


#print ("parameter %i: %s" % (position, sys.argv[position]))
hashtag="#" + sys.argv[position]
print "coletando tweets com a hashtag: " + sys.argv[position]
#position = position + 1
coleta_tag_cons = coleta_tag()
coleta_tag_cons.coletar_tag(sys.argv[position])
position = position + 1

Print execução:

Schema Mysql do banco de dados

CREATE TABLE `tweets` (


`id` int(11) NOT NULL AUTO_INCREMENT,
`user` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL,
`location` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`data` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`tweet` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`followers` int(11) DEFAULT NULL,
`hashtag` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=146 DEFAULT CHARSET=utf8mb4 CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci

Metricas

Utilizamos o template do kong padrão para realizar queries com datasource do prometheus para plotar
gráficos de performance do nosso serviços como RPS, Latencia, bandwith e Caching.

Podemos visualizar os gráficos do Grafana conforme imagens abaixo:


Request Rate
Latency
Bandwidth
Prometheus:
Código server-api

Código abaixo por responder API através de routes utilizando o framework API Bottle e a lib mysql
pymysql:

import string
import json
import uuid
import pymysql
from bottle import route, run, request, abort, post

from time import time


if __name__ == "__main__":

class conexao_db:
def connect_db(self):
try:
conexao = pymysql.connect(host='192.168.137.5',db='twitter', user='twitter', passwd='twitter')
#cursor = conexao.cursor()
return conexao
except:
print("Error Connecting database")

@route('/top5',method='GET')
def index():
conexaodb = conexao_db()
conectado_db = conexaodb.connect_db()
cursor = conectado_db.cursor()
cursor.execute('select id,user,followers from tweets order by followers desc limit 5;')
rows = cursor.fetchall()
conectado_db.commit()
conectado_db.close()
payload = []
content = {}
for result in rows:
content = {'id': result[0], 'username': result[1], 'followers': result[2]}
payload.append(content)
content = {}
return json.dumps(payload)

@route('/location',method='GET')
def index():
conexaodb = conexao_db()
conectado_db = conexaodb.connect_db()
cursor = conectado_db.cursor()
cursor.execute('select location,count(id),hashtag from tweets group by location,hashtag;;')
rows = cursor.fetchall()
conectado_db.commit()
conectado_db.close()
payload = []
content = {}
for result in rows:
content = {'location': result[0], 'tag': result[1], 'count': result[2]}
payload.append(content)
content = {}
return json.dumps(payload)

@route('/hour',method='GET')
def index():
conexaodb = conexao_db()
conectado_db = conexaodb.connect_db()
cursor = conectado_db.cursor()
cursor.execute("SELECT date_format(data,'%Y-%m-%d %H'), count(1) as tweets FROM tweets
GROUP BY 1;")
rows = cursor.fetchall()
conectado_db.commit()
conectado_db.close()
payload = []
content = {}
for result in rows:
content = {'data-hora': result[0], 'count': result[1]}
payload.append(content)
content = {}
return json.dumps(payload)

run(host='0.0.0.0', port=8080)

Queries executados pelo server api

select user,followers from tweets order by followers desc limit 5;


select location,count(id),hashtag from tweets group by location,hashtag;
select date_format(data,'%Y-%m-%d %H'), count(1) as tweets FROM tweets GROUP BY 1;

Criando Image

DockerFIle:

FROM python
RUN pip install pymysql bottle
RUN mkdir /server
COPY server.py /server/
CMD ["python","/server/server.py"]
expose 8080
Acessando a aplicação através do ingress:
Testing

Problemas encontrados e não resolvidos:


● Devido a falta do servidor elasticsearch por falta de recurso do notebook, não iremos realizar
a parte do logging , porém o fluent-bit está configurado corretamente no cluster como
Daemonset para envio dos logs

● O frontend para api rest tentei utilizar angular porém enfrentei um erro com o cors e não
conseguir finalizá-lo.

Você também pode gostar