Skip to content

Repository files navigation

Node.js Hello World API

Esse repositório visa conter os arquivos necessários para deployar uma aplicação utilizando GitHub Actions em um cluster local (Minikube), assim como monitorá-la através do Prometheus, Loki e Grafana.

Requisitos

Preparando o ambiente

  1. Inicie o minikube:
minikube start
  1. Habilite o addon de métricas do Kubernetes:
minikube addons enable metrics-server
  1. Crie uma ServiceAccount no Kubernetes que será usada para autenticar no cluster e realizar o deploy:
kubectl create serviceaccount ci-deployer
  1. Crie uma role para a ServiceAccount com as permissões necessárias:

    • Aplique o arquivo disponível em ./k8s_config/serviceaccount_role.yml no Kubernetes:
    kubectl apply -f ./k8s_config/serviceaccount_role.yml
  2. Atrele a role a ServiceAccount criada:

kubectl create rolebinding deployer-binding --role=deployment-manager --serviceaccount=default:ci-deployer
  1. Crie um token para a ServiceAccount:
kubectl create token ci-deployer
  1. Salve o token na variável KUBERNETES_TOKEN do environment Production deste repositório (settings -> Environments -> Production). Isso é necessário para que o workflow do GitHub Actions consiga recuperar o valor do token e realizar o deploy da aplicação. As demais variáveis não precisam ser alteradas por hora.

  2. Crie um túnel de conexão entre sua máquina local e o cluster Kubernetes (isso é necessário para poder expor os serviços e acessá-los pelo IP de sua máquina local):

sudo minikube tunnel
  1. Habilite um proxy que exponha o cluster na porta 8080 e aceite requisições da internet (necessário para que o workflow do GitHut Actions consiga fazer requisições de deploy para o cluster):
kubectl proxy --address='0.0.0.0' --accept-hosts='^*$' --port=8080
  1. Configure o ngrok da seguinte forma (o token a seguir foi criado especificamente para esse teste):
ngrok authtoken 2sQD8N7D1tOyRL4D24tBonlhy8T_5URgkABUrGxuHhMZLJFPQ
  1. Exponha a porta 8080 (a qual se refere a porta que o cluster Kubernetes foi exposto localmente anteriormente) para a internet usando o ngrok:
ngrok http 8080
  1. Copie a URL pública exibida no campo Forwarding e salve na variável KUBERNETES_URL do environment Production deste repositório (settings -> Environments -> Production). Essa URL será usada para disparar o deploy através do GitHub Actions.

  2. Instale o Prometheus e o Grafana com o helm:

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install monitoring prometheus-community/kube-prometheus-stack
  1. Instale o Loki com o helm:
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
helm upgrade --install loki grafana/loki-stack
  1. Faça um redirecionamento de portas para poder acessar os serviços localmente:
kubectl port-forward svc/monitoring-kube-prometheus-prometheus 9090:9090
kubectl port-forward svc/monitoring-grafana 3001:80
kubectl port-forward svc/loki 3100:3100
  1. Adicione o Loki como uma fonte de dados no Grafana:
  • Para acessar o Grafana, acesse a url http://localhost:3001 e logue com as credenciais admin (usuário) e prom-operator (senha).
  • Vá em Connections -> Data sources -> Add new data source -> Loki.
  • Insira no campo URL o valor http://loki:3100 e clique em Save & test (é possível que apresente um erro no teste, mas a fonte de dados já foi adicionada e pode ser consumida sem problemas).
  1. Crie o dashboard de monitoramento no Grafana:
  • Acesse Dashboards -> New -> Import -> Upload dashboard JSON file e selecione o arquivo presente em ./grafana_config/dashboard.json.
  • Selecione o Data Source do Prometheus e do Loki.
  • Clique em Import (Por hora não haverá dados pois não fizemos o deploy da aplicação).
  1. Crie os alertas no Grafana:
  • Acesse Alerting -> Alert rules -> New alert rule.
  • Uso de memória
    • Passo 1
      • Defina um nome (Ex: Memory usage > 80%)
    • Passo 2
      • No campo de query, selecione a query do tipo code e insira: (sum by(pod) (container_memory_usage_bytes{pod=~"api-node.*"}) * 100) / sum by(pod) (kube_pod_container_resource_limits{resource="memory"})
      • Modifique na Expression C o valor de 0 para 80 no campo IS ABOVE
    • Passo 3
      • Clique em New folder e crie uma pasta chamada api-node-alerts
      • Clique em **New evaluation group, nomeie api-node-interval e mantenha o intervalo padrão
    • Passo 4
      • Selecione o Contact point padrão criado pelo Grafana
    • Salve
  • Uso de CPU
    • Passo 1
      • Defina um nome (Ex: CPU usage > 80%)
    • Passo 2
      • No campo de query, selecione a query do tipo code e insira: (sum by(pod) (rate(container_cpu_usage_seconds_total{pod=~"api-node.*"}[5m])) * 100) / sum by(pod) (kube_pod_container_resource_limits{resource="cpu"})
      • Modifique na Expression C o valor de 0 para 80 no campo IS ABOVE
    • Passo 3
      • Selecione a pasta criada
      • Selecione o evalutation group criado
    • Passo 4
      • Selecione o Contact point padrão criado pelo Grafana
    • Salve
  • Quantidade de répicas
    • Passo 1
      • Defina um nome (Ex: Number of replicas < 2)
    • Passo 2
      • No campo de query, selecione a query do tipo code e insira: count(kube_pod_container_info{pod=~"api-node.*"})
      • Modifique na Expression C o valor de 0 para 2 e troque de IS ABOVE por IS BELOW
    • Passo 3
      • Selecione a pasta criada
      • Selecione o evalutation group criado
    • Passo 4
      • Selecione o Contact point padrão criado pelo Grafana
    • Salve
  1. Faça o Deploy da aplicação fazendo um push para a branch main.

CI/CD

A esteira de CI/CD foi construída em cima da ferramenta de Workflow do GitHub Actions. As configurações do Workflow estão armazenadas no arquivo ./.github/workflows/deploy.yml.

Steps

  1. Configuring Git

    • Definição das configurações user.name e user.email do Git. Isso é necessário para que o npm consiga commitar e fazer push para o GitHub dos arquivos JSON contendo as mudanças de versão.
  2. Get the source branch name

    • Recupera o nome da branch a qual foi mergeada para a main e ocasionou o deploy. Isso é feito fazendo uma requisição para API do GitHub buscando a branch de origem dentre os Pull Requests fechados que contenham o hash de commit que disparou o workflow.
  3. Get release type

    • Baseado no nome da source branch recuperado anteriormente, defini o tipo de release para poder realizar o bump da versão posteriormente.
      • Se o nome da branch iniciar com release/, o tipo de release será major.
      • Se o nome da branch iniciar com feature/, o tipo de release será minor.
      • Se o nome da branch iniciar com hotfix/, o tipo de release será patch.
      • Caso não se enquadre em nenhuma das regras acima, o tipo de release também será patch.
  4. Bump version

    • Realiza e commita no GitHub o bump da versão utilizando o comando npm version. Foi criado um token no GitHub para poder realizar esta ação.
  5. Docker Build

    • É feito o build da imagem Docker da aplicação.
  6. Docker Push

    • O push é feito para um repositório no Docker Hub. Foram criados Environment Secrets no repositório para armazenar os dados de autenticação no Docker Hub. A imagem é taggeada e enviada para o Docker Hub de acordo com a versão retornada pelo bump feito anteriormente.
  7. Release

    • Uma action externa é usada para criar uma Tag e Release no Github da versão que está sendo deployada.
  8. Set up Kubectl

    • Uma action externa é usada para instalar e configurar o kubectl na runner que está executando o Workflow.
  9. Set up deployment file

    • O arquivo ./deploy/deployment.yml que contém o que será deployado no Kubernetes é modificado para que seja preenchido dinâmicamente o valor da propriedade image com a imagem e tag correta que foi enviada para o Docker Hub.
  10. Deploy

  • É feito o deploy utilizando o kubectl e referenciando o servidor Kubernetes que está rodando localmente.

Todos os tokens, secrets, e demais variáveis relevantes que não devem ficar expostas no código foram armazenadas em um Environment secrets chamado Production. Assim o arquivo do workflow pode usá-las durante o deploy sem expor informações sensíveis.

Kubernetes

Ambiente

O Minikube foi a solução escolhida para executar um cluster Kubernetes localmente. Sua configuração padrão foi mantida, exceto pela habilitação do addon metrics-server e habilitação de um tunnel para permitir a comunicação entre os serviços das aplicações e a máquina local.

Para possibilitar que a comunicação entre o GitHub Actions e o cluster Kubernetes existisse, foi criado uma ServiceAccount e uma role com permissões específicas para o deploy, assim como um token para tal. O arquivo de configuração da role está disponível em ./k8s_config/serviceaccount_role.yml.

Além disso, o comando kubectl proxy foi usado para expor e aceitar requisições vindas da internet para o cluster para que o GitHub Actions conseguisse disparar o deploy. Também foi usado o comando kubectl port-forward para redirecionar as portas dos serviços do Prometheus, Grafana e Loki para portas locais da máquina que está estiver o cluster.

Aplicação

O arquivo da aplicação deployada contendo os manifestos do Kubernetes contém quatro tipos de componentes: Deployment, Service, ConfigMap e HorizontalPodAutoscaler.

  • Deployment
    • Componente principal da aplicação contendo especificações de 2 réplicas iniciais. Cada réplica contém os seguintes recursos:
      • 20m e 35m de requests e limits de CPU respectivamente.
      • 256Mi e 1024Mi de requests e limits de memória respectivamente
    • Além disso, há a configuração de uma variável de ambiente que referencia um valor configurado no ConfigMap da aplicação.
  • Service
    • Service do tipo LoadBalancer usado para fazer as requisições para a aplicação.
  • ConfigMap
    • Usado para armazenar o valor da porta de execução de API internamente no pod apenas para exemplificar um caso de uso do ConfigMap.
  • HorizontalPodAutoscaler
    • Configuração do HPA para escalar a aplicação até 10 réplicas, mantendo o mínimo de 2, caso o uso de CPU no pod ultrapasse 70%.

Para fins de exemplificação de logs, foram adicionados logs para cada requisição feita para as duas rotas já existentes da API

Observabilidade

Para a implementação da observabilidade, foi optado pelo uso do Grafana (criação dos dashboards e alertas), Prometheus (envio de métricas de infraestrutura) e Loki (envio de métricas de logs).

Foi utilizado as stacks do Prometheus, Grafana e Loki já disponíveis para o helm.

  • Dashboards
    • Uso de memória
      • Métrica histórica di uso de memória
      • Métrica atual do uso de memória
    • Uso de CPU
      • Métrica histórica do uso de CPU
      • Métrica atual do uso de CPU
    • Número de réplicas
      • Métrica histórica do número de réplicas
      • Métrica atual do número de réplicas
    • Logs
      • Logs de inicialização da aplicação
      • Logs de requisição para as rotas existentes

O Dashboard possue filtros por Pod, podendo exibir métricas de pods específicos, ou de todos eles.

  • Alertas
    • Uso de memória > 80% (baseado nos limits)
    • Uso de CPU > 80% (baseado nos limits)
    • Quantidade de réplicas < 2

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages