Como transferi meu projeto de hobby para o k8s

imagem

Neste artigo, gostaria de falar sobre meu projeto de hobby e pesquisa e classificação de anúncios para aluguel de apartamentos da rede social VKontakte e a experiência de migrá-lo para os k8s.


Sumário


  • Um pouco sobre o projeto
  • Apresentando o k8s
  • Preparando-se para a mudança
  • Desenvolvimento da configuração do K8s
  • Implantação de cluster K8s

Um pouco sobre o projeto


img



Em março de 2017, lancei um serviço de análise e classificação de anúncios para aluguel de apartamentos da rede social VKontakte.


Aqui você pode ler mais detalhadamente sobre como eu tentei classificar os anúncios de maneiras diferentes e, eventualmente, resolvi o analisador lexical Yandex Tomita Parser .


Aqui você pode ler sobre a arquitetura do projeto no início de sua existência e quais tecnologias foram usadas e por quê.


O desenvolvimento da primeira versão do serviço levou cerca de um ano. Para implantar cada componente do serviço, escrevi scripts no Ansible . Ocasionalmente, o serviço não funcionava devido a erros no código reprojetado ou na configuração incorreta dos componentes.


Por volta de junho de 2019, foi detectado um erro no código do analisador, devido ao qual novos anúncios não foram coletados. Em vez de outra correção, foi decidido desativá-lo temporariamente.


O motivo da restauração do serviço foi o estudo dos k8s.


Apresentando o k8s


O k8s é um software de código aberto para automatizar a implantação, dimensionamento e gerenciamento de aplicativos em contêiner.


Toda a infraestrutura de serviço é descrita por arquivos de configuração no formato yaml (na maioria das vezes).


Não falarei sobre a estrutura interna do k8s, mas apenas darei algumas informações sobre alguns de seus componentes.


Componentes do K8s


  • Pod é a menor unidade. Pode conter vários contêineres que serão lançados no mesmo nó.
    Recipientes dentro do vagem:
    • ter uma rede comum e podem acessar um ao outro através de 127.0.0.1:$containerPort;
    • não possui um sistema de arquivos comum, portanto, não é possível gravar arquivos diretamente de um contêiner para outro.
  • Implantação - monitora o trabalho do Pod. Ele pode aumentar o número necessário de instâncias do Pod, reiniciá-las se elas caírem e executar a implantação de novos Pods.
  • PersistentVolumeClaim - armazém de dados. Por padrão, ele funciona com o sistema de arquivos do nó local. Portanto, se você deseja que dois Pods diferentes em nós diferentes tenham um sistema de arquivos comum, use um sistema de arquivos de rede como o Ceph .
  • Serviço - solicitações de proxies de e para o Pod.
    Tipos de serviço:
    • LoadBalancer - para interação com uma rede externa com balanceamento de carga entre vários Pods;
    • NodePort (apenas 30000-32767 portas) - para interação com uma rede externa sem balanceamento de carga;
    • ClusterIp - para interação na rede local do cluster;
    • ExternalName - para interação entre o Pod e os serviços externos.
  • ConfigMap - armazenamento de configurações.
    Para que o k8s reinicie o Pod com novas configurações quando o ConfigMap for alterado, você deve indicar a versão no nome do ConfigMap e alterá-la sempre que o ConfigMap for alterado.
    O mesmo vale para o segredo.

Exemplo de configuração com o ConfigMap
containers: - name: collect-consumer image: mrsuh/rent-collector:1.3.1 envFrom: - configMapRef: name: collector-configmap-1.1.0 - secretRef: name: collector-secrets-1.0.0 

  • Segredo - armazenamento de configurações secretas (senhas, chaves, tokens).
  • Pares de etiqueta - chave / valor que são atribuídos aos componentes do k8s, por exemplo, Pod.
    No início do conhecimento dos k8s, pode não estar completamente claro como usar etiquetas. Aqui está a configuração que explica os princípios básicos do trabalho com etiquetas:

Exemplo de configuração com etiquetas
 apiVersion: apps/v1 kind: Deployment #  Deployment metadata: name: deployment-name #  Deployment labels: app: deployment-label-app # Label Deployment spec: selector: matchLabels: app: pod-label-app # Label,   Deployment    Pods   template: metadata: name: pod-name labels: app: pod-label-app # Label Pod spec: containers: - name: container-name image: mrsuh/rent-parser:1.0.0 ports: - containerPort: 9080 --- apiVersion: v1 kind: Service #  Service metadata: name: service-name #  Service labels: app: service-label-app # Label Service spec: selector: #  Service   matchLabels,  Deployment,      Labels app: pod-label-app # Label,   Service ,   Pod    ports: - protocol: TCP port: 9080 type: NodePort 

Preparando-se para a mudança


Corte de funcionalidade


Para tornar o serviço mais estável e previsível, tive que remover todos os componentes adicionais que funcionavam mal e reescrever os principais um pouco.
Então, eu decidi recusar:


  • código de análise para sites que não sejam VK;
  • solicitar componente proxy;
  • componente de notificações de novos anúncios no VKontakte e no Telegram.

Componentes de serviço


Depois de todas as alterações, o serviço interno começou a ficar assim:
Esquema


  • view - pesquisa e exibição de anúncios no site (NodeJS);
  • analisador - classificador de anúncios (Go);
  • coletor - coletando, processando e excluindo anúncios (PHP):
    • cron-explore - uma equipe de console que procura grupos no VKontakte para alugar casas;
    • cron-collect - um comando do console que vai para grupos compilados pelo cron-explore e coleta os próprios anúncios;
    • cron-delete - um comando do console que exclui anúncios expirados;
    • consumidor-análise - o manipulador de filas, que recebe tarefas do cron-collect. Classifica anúncios usando o componente analisador;
    • consumer-collect - o manipulador de filas que obtém trabalhos da análise do consumidor. Ele filtra anúncios ruins e duplicados.

Construir imagens do Docker


Para gerenciar os componentes e monitorá-los em um único estilo, decidi:


  • colocar a configuração do componente em variáveis ​​env,
  • escreva logs no stdout.

Não há nada específico nas próprias imagens.


Desenvolvimento da configuração do K8s


Então, peguei os componentes nas imagens do Docker e comecei a desenvolver a configuração do k8s.


Todos os componentes que funcionam como daemons estão destacados em Implantação. Cada daemon deve estar acessível dentro do cluster, para que todos tenham um Serviço. Todas as tarefas que devem ser executadas periodicamente funcionam no CronJob.


Todas as estáticas (figuras, js, css) são armazenadas no contêiner de exibição, e o contêiner Nginx deve distribuí-lo. Ambos os contêineres estão em um Pod. O sistema de arquivos no Pod não é atrapalhado, mas você pode copiar todas as estatísticas para a pasta emptyDir comum aos dois contêineres ao iniciar o Pod. Essa pasta será compartilhada para diferentes contêineres, mas apenas dentro de um Pod.


Exemplo de configuração com emptyDir
 apiVersion: apps/v1 kind: Deployment metadata: name: view spec: selector: matchLabels: app: view replicas: 1 template: metadata: labels: app: view spec: volumes: - name: view-static emptyDir: {} containers: - name: nginx image: mrsuh/rent-nginx:1.0.0 - name: view image: mrsuh/rent-view:1.1.0 volumeMounts: - name: view-static mountPath: /var/www/html lifecycle: postStart: exec: command: ["/bin/sh", "-c", "cp -r /app/web/. /var/www/html"] 

O componente coletor é usado no Deployment e no CronJob.


Todos esses componentes acessam a API do VKontakte e devem armazenar o token de acesso compartilhado em algum lugar.
Para isso, usei o PersistentVolumeClaim, conectado a cada Pod. Essa pasta será compartilhada para diferentes pods, mas apenas dentro de um nó.


Exemplo de configuração com PersistentVolumeClaim
 apiVersion: apps/v1 kind: Deployment metadata: name: collector spec: selector: matchLabels: app: collector replicas: 1 template: metadata: labels: app: collector spec: volumes: - name: collector-persistent-storage persistentVolumeClaim: claimName: collector-pv-claim containers: - name: collect-consumer image: mrsuh/rent-collector:1.3.1 volumeMounts: - name: collector-persistent-storage mountPath: /tokenStorage command: ["php"] args: ["bin/console", "app:consume", "--channel=collect"] - name: parse-consumer image: mrsuh/rent-collector:1.3.1 volumeMounts: - name: collector-persistent-storage mountPath: /tokenStorage command: ["php"] args: ["bin/console", "app:consume", "--channel=parse"] 

PersistentVolumeClaim também é usado para armazenar dados do banco de dados. Como resultado, obtivemos esse esquema (os pods de um componente são coletados em blocos):


Esquema


Implantação de cluster K8s


Para começar, implantei o cluster localmente usando o Minikube .
Claro, houve alguns erros, então as equipes me ajudaram muito.


 kubectl logs -f pod-name kubectl describe pod pod-name 

Depois que aprendi como implantar um cluster no Minikube, não foi difícil implantá-lo no DigitalOcean.


Em conclusão, posso dizer que o serviço está estável há 2 meses. A configuração completa pode ser encontrada aqui .

Source: https://habr.com/ru/post/pt484528/


All Articles