
在本文中,我想谈谈我的爱好项目,即搜索和分类从社交网络VKontakte租用公寓的广告的广告,以及将其移至k8s的经验。
目录
- 关于项目的一点
- 介绍k8s
- 为搬家做准备
- K8s配置开发
- K8s集群部署
关于项目的一点

2017年3月,我推出了一项服务,用于解析和分类来自社交网络VKontakte的出租公寓广告的广告。
在这里,您可以了解更多有关我如何尝试以不同方式对广告进行分类并最终决定使用Yandex Tomita Parser词法分析器的信息。
在这里,您可以了解项目开始时的架构,使用了哪些技术以及原因。
该服务的第一个版本的开发花费了大约一年的时间。 为了部署服务的每个组件,我在Ansible中编写了脚本。 由于重新设计的代码错误或组件配置不正确,该服务有时不起作用。
在2019年6月前后,解析器代码中检测到错误,由于未收集新公告。 决定暂时禁用它,而不是进行其他更正。
恢复服务的原因是对k8s的研究。
介绍k8s
k8s是开源软件,用于自动化容器化应用程序的部署,扩展和管理。
整个服务基础结构由yaml格式的配置文件描述(最常见)。
我不会谈论k8的内部结构,而只提供有关其某些组件的一些信息。
K8s组件
- 吊舱是最小的单位。 它可能包含将在同一节点上启动的多个容器。
容器内的容器:
- 具有公共网络,并且可以通过127.0.0.1:$containerPort相互访问;
- 没有通用的文件系统,因此您不能直接将文件从一个容器写入另一个容器。
- 部署-监视Pod的工作。 它可以增加所需数量的Pod实例,如果它们掉落则重新启动它们,并执行新Pod的部署。
- PersistentVolumeClaim-数据仓库。 默认情况下,它与本地节点文件系统一起使用。 因此,如果您希望不同节点上的两个不同Pod具有相同的文件系统,则必须使用Ceph之类的网络文件系统。
- 服务-代理来往Pod的请求。
服务类型:
- LoadBalancer-用于与外部网络进行交互,并在多个Pod之间进行负载平衡;
- NodePort(仅30000-32767端口)-用于与外部网络进行交互而无需负载平衡;
- ClusterIp-用于在群集的本地网络中进行交互;
- ExternalName-用于Pod与外部服务之间的交互。
- ConfigMap-配置的存储。
为了使k8s在ConfigMap更改时以新的配置重新启动Pod,您应该在ConfigMap的名称中指明版本,并在每次ConfigMap更改时对其进行更改。
秘密也是如此。
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
- 秘密-秘密配置的存储(密码,密钥,令牌)。
- 标签-分配给k8s组件的键/值对,例如Pod。
刚接触k8s时,可能还不清楚如何使用标签。 以下是配置,解释了使用标签的基本原理:
带标签的示例配置 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
为搬家做准备
功能修整
为了使服务更加稳定和可预测,我不得不删除所有其他效果不佳的组件,并对主要组件进行一些重写。
因此,我决定拒绝:
- 为VK以外的网站解析代码;
- 请求代理组件;
- VKontakte和Telegram中的新公告通知的组成部分。
服务组成
完成所有更改后,内部服务开始如下所示:

- 查看-在网站上搜索和显示广告(NodeJS);
- 解析器-广告分类器(Go);
- 收集器-收集,处理和删除广告(PHP):
- cron-explore-一个控制台团队,正在VKontakte上寻找团体来出租房屋;
- cron-collect-控制台命令,用于转到cron-explore编译的组并自行收集广告;
- cron-delete-删除过期公告的控制台命令;
- Consumer-parse-队列处理程序,从cron-collect接收作业。 它使用解析器组件对广告进行分类;
- 消费者收集-从消费者分析获取作业的队列处理程序。 它过滤掉不良和重复的广告。
构建Docker映像
为了管理组件并以一种样式对其进行监视,我决定:
- 将组件配置放入环境变量中,
- 在stdout中写入日志。
图像本身没有特定内容。
K8s配置开发
因此,我将组件包含在Docker映像中,然后开始开发k8s配置。
部署中将突出显示所有充当守护程序的组件。 每个守护程序必须在集群中可访问,因此每个人都有一个服务。 必须定期执行的所有任务都在CronJob中工作。
所有静态变量(图片,js,css)都存储在视图容器中,并且Nginx容器应该分发它。 两个容器都放在一个Pod中。 Pod中的文件系统没有混乱,但是在Pod的开始处,您可以将所有静态信息复制到两个容器共有的emptyDir文件夹中。 此文件夹将为不同的容器共享,但只能在一个Pod中共享。
使用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"]
收集器组件在Deployment和CronJob中使用。
所有这些组件都访问VKontakte API,并且必须将共享访问令牌存储在某处。
为此,我使用了PersistentVolumeClaim,并将其连接到每个Pod。 这样的文件夹将为不同的Pod共享,但只能在一个节点内共享。
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也用于存储数据库数据。 结果,我们得到了这样一个方案(一个组件的荚被成块地收集):

K8s集群部署
首先,我使用Minikube在本地部署了集群。
当然,有一些错误,所以团队为我提供了很多帮助。
kubectl logs -f pod-name kubectl describe pod pod-name
在我学会了如何在Minikube中部署集群之后,在DigitalOcean中部署它并不困难。
总之,我可以说该服务已经稳定了2个月。 完整的配置可以在这里找到。