Objetivo: Demonstrar como orquestrar containers usando Kubernetes para implantar uma aplicação web simples, garantindo alta disponibilidade e escalabilidade.
Pré-requisitos:
- Docker instalado.
- Kubernetes instalado (Minikube).
- kubectl instalado (Minikube).
Descrição da Aplicação: Vamos usar uma aplicação web simples em Python com Flask, que exibe uma mensagem "Hello, World!".
-
Crie o diretório do projeto:
mkdir flask-app cd flask-app
-
Crie o arquivo
app.py
:from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return "Hello, World!" if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)
-
Crie o arquivo
requirements.txt
:Flask==2.0.1 Werkzeug==2.0.1
-
Crie o arquivo
Dockerfile
:# Use uma imagem base do Python FROM python:3.9-slim # Defina o diretório de trabalho WORKDIR /app # Copie os arquivos de requisitos e instale as dependências COPY requirements.txt requirements.txt RUN pip install -r requirements.txt # Copie o código da aplicação COPY app.py app.py # Defina o comando padrão a ser executado quando o container iniciar CMD ["python", "app.py"]
-
Autentique com o Docker:
docker login
-
Construa a imagem Docker:
docker build -t flask-app:1.0 .
-
Teste a aplicação localmente:
docker run -p 5001:5000 flask-app:1.0
Acesse
http://localhost:5001
para verificar se a aplicação está funcionando.
-
Crie o arquivo
deployment.yaml
:apiVersion: apps/v1 kind: Deployment metadata: name: flask-app-deployment spec: selector: matchLabels: app: flask-app replicas: 3 template: metadata: labels: app: flask-app spec: containers: - name: flask-app image: flask-app:1.0 ports: - containerPort: 5000
-
Crie o arquivo
service.yaml
:apiVersion: v1 kind: Service metadata: name: flask-app-service labels: name: flask-app-service spec: ports: - protocol: TCP port: 5051 targetPort: 5000 selector: app: flask-app type: NodePort
-
Inicie o Minikube:
minikube start
-
Carregue a imagem Docker para o Minikube:
eval $(minikube docker-env) docker build -t flask-app:1.0 .
-
Aplique as configurações do Kubernetes:
kubectl apply -f deployment.yaml kubectl apply -f service.yaml
-
Verifique o status do deployment:
kubectl get deployments kubectl get pods
-
Acesse a aplicação:
- Obtenha a URL:
minikube service flask-app-service --url
- Acesse a URL fornecido.
- Obtenha a URL:
Nesta aula prática, você aprendeu a:
- Criar uma aplicação web simples com Flask.
- Dockerizar a aplicação.
- Criar arquivos de configuração do Kubernetes para deployment e serviço.
- Implantar a aplicação no Kubernetes usando Minikube.
Desafios adicionais:
- Configure um Ingress para gerenciar o tráfego.
- Adicione uma configuração de volume persistente para armazenar dados.
- Explore a escalabilidade alterando o número de réplicas.
No contexto do Kubernetes, o Docker desempenha um papel crucial na criação e na execução dos containers que compõem os Pods, que são a menor unidade de implantação no Kubernetes.
-
Empacotamento de Aplicativos em Containers: O Docker é uma tecnologia de contêinerização que permite empacotar aplicativos e suas dependências em contêineres, garantindo que eles sejam executados de maneira consistente em qualquer ambiente. No contexto do Kubernetes, os aplicativos são empacotados em imagens Docker, que são usadas para criar e executar containers.
-
Implantação de Contêineres no Kubernetes: O Kubernetes utiliza containers Docker como a unidade básica de implantação. O Kubernetes é capaz de implantar, gerenciar e escalar esses containers de forma eficiente e automatizada.
-
Criação de Pods: No Kubernetes, um Pod é um grupo de um ou mais containers, com armazenamento e configurações de rede compartilhados. Os containers dentro de um Pod compartilham o mesmo ambiente de rede e podem se comunicar entre si usando o loopback do Pod.
-
Kubelet e Docker: O Kubelet é um componente do Kubernetes que executa em cada nó do cluster e é responsável por iniciar e monitorar os containers dentro dos Pods. O Kubelet interage diretamente com o Docker (ou outro runtime de contêiner, como containerd) para criar e gerenciar os containers conforme necessário.
-
Portabilidade e Consistência: O Docker fornece uma camada de abstração que torna os aplicativos portáteis e independentes do ambiente de execução. Isso permite que os aplicativos sejam desenvolvidos e testados localmente usando Docker e, em seguida, implantados em qualquer ambiente Kubernetes sem modificações.
O Docker desempenha um papel fundamental no processo de implantação de aplicativos no Kubernetes, fornecendo uma maneira padronizada e eficiente de empacotar, distribuir e executar aplicativos em contêineres.
O Deployment e o Service são dois recursos fundamentais no Kubernetes que desempenham papéis diferentes, mas complementares, na implantação e na exposição de aplicativos.
Um Deployment no Kubernetes é uma descrição declarativa de um conjunto de Pods, juntamente com a configuração de como eles devem ser implantados e atualizados. Ele fornece uma maneira de declarar, gerenciar e atualizar o estado desejado de uma aplicação distribuída. Aqui estão alguns pontos-chave sobre Deployments:
-
Gestão de Replicas: O Deployment gerencia automaticamente um conjunto de réplicas de Pods, garantindo que o número especificado de Pods esteja sempre em execução.
-
Atualizações e Rollbacks: Os Deployments permitem atualizações de forma controlada e rollback para versões anteriores, garantindo que a aplicação permaneça disponível durante o processo de atualização.
-
Autoreparo: Se um Pod falhar, o Deployment irá automaticamente substituí-lo por um novo Pod saudável.
-
Rótulos e Seletores: Os Deployments usam rótulos e seletores para definir conjuntos de Pods que compõem a aplicação. Isso permite a fácil gestão de conjuntos de Pods relacionados.
Um Service no Kubernetes é um recurso que define uma política de acesso de rede para um conjunto de Pods. Ele fornece uma maneira estável e uniforme de acessar os Pods, independentemente de onde eles estejam implantados na infraestrutura subjacente. Aqui estão alguns pontos-chave sobre Services:
-
Descoberta de Serviços: Um Service fornece um ponto de acesso único para um conjunto de Pods, permitindo que outros componentes do sistema os encontrem dinamicamente sem precisar conhecer os detalhes de implementação.
-
Balanceamento de Carga: Os Services podem fornecer balanceamento de carga entre vários Pods, distribuindo o tráfego de entrada de forma equilibrada entre eles.
-
Exposição de Aplicativos: Os Services permitem que os Pods sejam expostos para dentro ou fora do cluster Kubernetes, facilitando a exposição de aplicativos para o tráfego externo.
-
Integração de DNS: Cada Service obtém um registro DNS automático no cluster Kubernetes, permitindo que outros serviços usem seu nome DNS para acessá-lo.
Em resumo, enquanto o Deployment gerencia a implantação e a escalabilidade dos Pods que compõem a aplicação, o Service gerencia a exposição de rede dos Pods, garantindo que eles possam ser acessados de forma confiável por outros componentes do sistema. Juntos, esses recursos fornecem uma base sólida para implantar e executar aplicativos distribuídos no Kubernetes.
minikube start
minikube addons enable metrics-server
minikube addons enable metrics-server
minikube dashboard
Confira como ficou o dashboard do K8s
Para adicionar um serviço de APM de código aberto ano projeto Flask sem usar Docker Compose, uma boa opção é usar o Prometheus para monitoramento e o Grafana para visualização. Vamos configurar essas ferramentas manualmente.
-
Instalar o Prometheus:
Baixe e instale o Prometheus seguindo as instruções no site oficial.
-
Configurar o Prometheus:
Crie um arquivo de configuração para o Prometheus (
prometheus.yml
):global: scrape_interval: 15s scrape_configs: - job_name: 'flask-app' static_configs: - targets: ['localhost:8000']
-
Iniciar o Prometheus:
Execute o Prometheus com a configuração criada:
prometheus --config.file=prometheus.yml
-
Atualizar o Código do Flask:
Modifique o
app.py
para expor as métricas do Prometheus:from flask import Flask, jsonify, request, render_template from prometheus_flask_exporter import PrometheusMetrics from prometheus_client import start_http_server, Summary, Counter, Gauge, generate_latest app = Flask(__name__, template_folder="templates") metrics = PrometheusMetrics(app) metrics.info('app_info', 'Application info', version='1.0.0') # Create metrics REQUEST_TIME = Summary('request_processing_seconds', 'Time spent processing request') REQUEST_COUNT = Counter('request_count', 'Number of requests processed', ['endpoint']) IN_PROGRESS = Gauge('inprogress_requests', 'Number of requests in progress', ['endpoint']) # Exemplo de dados simulados em um banco de dados db = [ {"id": 1, "nome": "Chaves", "idade": 40}, {"id": 2, "nome": "Seu Madruga", "idade": 50}, {"id": 3, "nome": "Chiquinha", "idade": 30}, {"id": 4, "nome": "Quico", "idade": 10}, {"id": 5, "nome": "Dona Florinda", "idade": 45}, {"id": 6, "nome": "Professor Girafales", "idade": 35}, {"id": 7, "nome": "Seu Barriga", "idade": 60}, {"id": 8, "nome": "Don Ramón", "idade": 40} ] @app.route('/') @REQUEST_TIME.time() def home(): endpoint = '/' IN_PROGRESS.labels(endpoint=endpoint).inc() REQUEST_COUNT.labels(endpoint=endpoint).inc() IN_PROGRESS.labels(endpoint=endpoint).dec() return render_template('index.html') # Endpoint para retornar todos os registros @app.route('/api/data', methods=['GET']) @REQUEST_TIME.time() def get_all_data(): endpoint = '/api/data' IN_PROGRESS.labels(endpoint=endpoint).inc() REQUEST_COUNT.labels(endpoint=endpoint).inc() IN_PROGRESS.labels(endpoint=endpoint).dec() return jsonify(db) # Endpoint para retornar um registro específico @app.route('/api/data/<int:data_id>', methods=['GET']) @REQUEST_TIME.time() def get_data(data_id): endpoint = f'/api/data/{data_id}' IN_PROGRESS.labels(endpoint=endpoint).inc() REQUEST_COUNT.labels(endpoint=endpoint).inc() IN_PROGRESS.labels(endpoint=endpoint).dec() data = next((item for item in db if item["id"] == data_id), None) if data: return jsonify(data) else: return jsonify({"message": "Data not found"}), 404 # Endpoint para adicionar um novo registro @app.route('/api/data', methods=['POST']) @REQUEST_TIME.time() def add_data(): endpoint = '/api/data' IN_PROGRESS.labels(endpoint=endpoint).inc() REQUEST_COUNT.labels(endpoint=endpoint).inc() IN_PROGRESS.labels(endpoint=endpoint).dec() new_data = request.json if "id" in new_data and "nome" in new_data and "idade" in new_data: db.append(new_data) return jsonify({"message": "Data added successfully"}), 201 else: return jsonify({"message": "Incomplete data"}), 400 # Endpoint para atualizar um registro existente @app.route('/api/data/<int:data_id>', methods=['PUT']) @REQUEST_TIME.time() def update_data(data_id): endpoint = f'/api/data/{data_id}' IN_PROGRESS.labels(endpoint=endpoint).inc() REQUEST_COUNT.labels(endpoint=endpoint).inc() IN_PROGRESS.labels(endpoint=endpoint).dec() data = next((item for item in db if item["id"] == data_id), None) if data: data.update(request.json) return jsonify({"message": "Data updated successfully"}) else: return jsonify({"message": "Data not found"}), 404 # Endpoint para deletar um registro @app.route('/api/data/<int:data_id>', methods=['DELETE']) @REQUEST_TIME.time() def delete_data(data_id): endpoint = f'/api/data/{data_id}' IN_PROGRESS.labels(endpoint=endpoint).inc() REQUEST_COUNT.labels(endpoint=endpoint).inc() IN_PROGRESS.labels(endpoint=endpoint).dec() global db db = [item for item in db if item["id"] != data_id] return jsonify({"message": "Data deleted successfully"}) @app.route('/metrics') def metrics(): return generate_latest(), 200, {'Content-Type': 'text/plain; charset=utf-8'} if __name__ == '__main__': start_http_server(8000) app.run(host='0.0.0.0', port=5000)
-
Reconstruir e Executar o Container:
Reconstrua e execute a imagem Docker:
docker build -t flask-app:1.0 . docker run -p 5001:5000 -p 8000:8000 flask-app:1.0
-
Instalar o Grafana:
Baixe e instale o Grafana seguindo as instruções no site oficial.
-
Iniciar o Grafana:
Execute o Grafana:
services start grafana
-
Configurar o Grafana:
- Acesse o Grafana no navegador em
http://localhost:3000
. - Faça login com as credenciais padrão (usuário:
admin
, senha:admin
). - Adicione um novo Data Source:
- Vá para
Configuration > Data Sources > Add data source
. - Selecione
Prometheus
. - Configure a URL do Prometheus como
http://localhost:9090
. - Clique em
Save & Test
.
- Vá para
- Acesse o Grafana no navegador em
-
Criar um Dashboard:
- Vá para
Create > Dashboard
. - Adicione um novo painel (Panel):
- Selecione a métrica que deseja visualizar (
request_count
,request_processing_seconds
, etc.). - Configure o gráfico de acordo com suas necessidades.
- Selecione a métrica que deseja visualizar (
- Salve o dashboard.
- Vá para
-
Verificar se o Prometheus está Coletando Métricas:
Acesse o Prometheus em
http://localhost:9090
e verifique se as métricas estão sendo coletadas. -
Visualizar Métricas no Grafana:
Acesse o Grafana em
http://localhost:3000
e veja se o dashboard está exibindo as métricas corretamente.
Aqui estão alguns exemplos de consultas Prometheus (PromQL) que você pode usar para monitorar o seu aplicativo Flask:
Para obter o número total de requisições processadas pela aplicação:
request_count_total
Para calcular o tempo médio de processamento das requisições:
rate(request_processing_seconds_sum[1m]) / rate(request_processing_seconds_count[1m])
Para obter o número atual de requisições em progresso:
inprogress_requests
Para obter a taxa de requisições processadas por segundo nos últimos 5 minutos:
rate(request_count_total[5m])
Para obter o uso de CPU por container (se você estiver coletando métricas de CPU do seu ambiente):
sum(rate(container_cpu_usage_seconds_total[1m])) by (container_label_io_kubernetes_pod_name)
Para obter o uso de memória por container (se você estiver coletando métricas de memória do seu ambiente):
sum(container_memory_usage_bytes) by (container_label_io_kubernetes_pod_name)
Para obter a contagem de requisições que resultaram em erro (supondo que você esteja registrando erros em uma métrica separada):
rate(request_errors_total[5m])
Para usar essas consultas no Grafana, você pode criar painéis (dashboards) e adicionar gráficos (panels) com essas consultas. Aqui estão os passos básicos:
-
Adicionar um Painel no Grafana:
- No Grafana, vá para
Create > Dashboard
. - Clique em
Add new panel
.
- No Grafana, vá para
-
Configurar a Consulta:
- Na seção
Query
, selecione seu data source Prometheus. - Insira uma das consultas Prometheus mencionadas acima na caixa de consulta.
- Na seção
-
Configurar o Gráfico:
- Personalize o gráfico conforme necessário (e.g., título, eixos, intervalos de tempo).
- Clique em
Apply
para salvar o painel.
Aqui está um exemplo de consulta no Grafana usando a métrica request_count_total
:
- Consulta:
rate(request_count_total[5m])
O Grafana é uma ferramenta poderosa para visualização de dados e pode se conectar a vários backends de dados, incluindo Prometheus. Abaixo estão alguns exemplos de consultas Grafana (escritas em PromQL) que você pode usar para monitorar métricas comuns em um ambiente de produção.
Para obter o número total de requisições processadas pela aplicação:
sum(request_count_total)
Para obter a taxa de requisições processadas por segundo nos últimos 5 minutos:
rate(request_count_total[5m])
Para calcular o tempo médio de processamento das requisições:
rate(request_processing_seconds_sum[5m]) / rate(request_processing_seconds_count[5m])
Para obter o uso de CPU por container:
sum(rate(container_cpu_usage_seconds_total[5m])) by (container_label_io_kubernetes_pod_name)
Para obter o uso de memória por container:
sum(container_memory_usage_bytes) by (container_label_io_kubernetes_pod_name)
Para obter a contagem de requisições que resultaram em erro:
rate(request_errors_total[5m])
Para calcular o tempo de processamento no percentil 95 das requisições:
histogram_quantile(0.95, sum(rate(request_processing_seconds_bucket[5m])) by (le))
Para visualizar quantos pods estão em diferentes estados (Running, Pending, etc.):
count(kube_pod_status_phase{phase="Running"})
Para obter a latência média das requisições HTTP:
histogram_quantile(0.5, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
Para visualizar o número de pods que foram reiniciados:
sum(kube_pod_container_status_restarts_total)
-
Adicionar um Painel no Grafana:
- No Grafana, vá para
Create > Dashboard
. - Clique em
Add new panel
.
- No Grafana, vá para
-
Configurar a Consulta:
- Na seção
Query
, selecione seu data source Prometheus. - Insira uma das consultas Prometheus mencionadas acima na caixa de consulta.
- Na seção
-
Configurar o Gráfico:
- Personalize o gráfico conforme necessário (e.g., título, eixos, intervalos de tempo).
- Clique em
Apply
para salvar o painel.
-
Configurar o Data Source:
- Vá para
Configuration > Data Sources > Add data source
. - Selecione
Prometheus
. - Configure a URL como
http://localhost:9090
(ou onde o Prometheus está rodando).
- Vá para
-
Adicionar o Painel:
- Vá para
Create > Dashboard
. - Clique em
Add new panel
. - Insira a consulta
rate(request_count_total[5m])
. - Personalize o gráfico e clique em
Apply
.
- Vá para
-
Visualizar o Dashboard:
- No dashboard, você verá a taxa de requisições por segundo visualizada como um gráfico de séries temporais.
- Título:
Requisições por Segundo
- Consulta:
rate(request_count_total[5m])
- Título:
Uso de CPU por Pod
- Consulta:
sum(rate(container_cpu_usage_seconds_total[5m])) by (container_label_io_kubernetes_pod_name)
- Título:
Latência Média das Requisições HTTP
- Consulta:
histogram_quantile(0.5, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
Confira como ficou o dashboard no Grafana