用 Kubernetes 部署和管理 Scrapy 爬虫
在上节中,我们已经学习了 Kubernetes 的基本操作,本节中我们进行一个实战练习,将代理池、账号池、Scrapy 爬虫项目部署到 Kubernetes 集群上来运行。
Namespace
在开始之前,我们首先新建一个 kubernetes 文件夹并将命令行切换到该文件夹。在该文件夹中,我们会创建多个 Kubernetes 的 YAML 部署文件用于资源部署。
另外,我们需要新建一个 Namespace,叫作 crawler,我们将所有的资源都部署到这个 Namespace 下。创建 Namespace 的命令如下:
kubectl create namespace crawler
创建成功后,我们就开始部署吧。
Redis
首先,我们可以先进行 Redis 数据库的部署,因为代理池、账号池、Scrapy 爬虫项目都是以 Redis 为基础的。
此处我们部署一个最基础的单实例 Redis 数据库。首先在 kubernetes 文件夹下创建一个 redis 文件夹,再在 redis 文件夹中创建一个 deployment.yaml 文件,其内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: redis
name: redis
namespace: crawler
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- image: redis:alpine
name: redis
resources:
limits:
memory: "2Gi"
cpu: "200m"
requests:
memory: "500Mi"
cpu: "200m"
ports:
- containerPort: 6379
这里我们声明了一个 Deployment,并在 containers 字段里面使用 redis:alpine 这个镜像进行部署,replicas 实例个数设置为 1,端口设置为 6379。
接下来,在 redis 文件夹下创建一个 service.yaml 文件,其内容如下:
apiVersion: v1
kind: Service
metadata:
labels:
app: redis
name: redis
namespace: crawler
spec:
ports:
- name: "6379"
port: 6379
targetPort: 6379
selector:
app: redis
这里的 Service 同样声明使用 6379 端口,targetPort 需要和 Deployment 的 containerPort 对应起来,也是 6379。
接下来,我们切换到 redis 文件夹的上级文件夹,即 kubernetes 文件夹,执行如下命令进行 Redis 的部署:
kubectl apply -f redis
注意这里 -f 后面跟的是一个文件夹名称,这样会应用该文件夹下所有的 YAML 文件,相当于执行了如下两条命令:
kubectl apply -f redis/deployment.yaml
kubectl apply -f redis/service.yaml
可以看到如下的运行结果:
deployment.apps/redis created
service/redis created
稍微等待片刻,Redis 数据库就部署成功了。
我们可以使用如下命令查看 Redis 的部署状态:
kubectl get deployment/redis -n crawler
运行结果类似如下:
NAME READY UP-TO-DATE AVAILABLE AGE
redis 1/1 1 1 15s
这个结果表明刚才声明的 Deployment 在 Kubernetes 的部署情况,我们可以看到 READY 这一列的结果是 1/1,这说明期望部署 1 个 Redis 实例。现在已经部署了 1 个实例,所以已经部署成功了。
如果你看到的结果不是这样的,可以耐心等待一会,可能现在 Kubernetes 还在下载 Redis 相关镜像。你可以使用kubectl命令查看相关Pod运行状态。但如果长时间都无法部署成功,请检查Kubernetes 日志。
另外,为了方便学习和操作,部署的仅仅是最基础的Redis数据库实例,并没有配置Redis集群,也没有配置持久化存储。在实际生产环境中推荐使用Helm部署Redis集群,具体的操作可以参考 https://github.com/bitnami/charts/tree/master/bitnami/redis。
代理池
Redis 数据库部署好了,接下来就开始部署代理池了。
在 Kubernetes 文件夹下新建 proxypool 文件夹,再在 proxypool 文件夹下创建 deployment.yaml 文件,其内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: proxypool
name: proxypool
namespace: crawler
spec:
replicas: 1
selector:
matchLabels:
app: proxypool
template:
metadata:
labels:
app: proxypool
spec:
containers:
- env:
- name: REDIS_HOST
value: 'redis.crawler.svc.cluster.local'
- name: REDIS_PORT
value: '6379'
image: germey/proxypool
name: proxypool
resources:
limits:
memory: "500Mi"
cpu: "300m"
requests:
memory: "500Mi"
cpu: "300m"
ports:
- containerPort: 5555
和Redis的Deployment声明类似,这里我们按照同样的格式声明了代理池的Deployment,这里镜像使用的是germey/proxypool,即我的DockerHub上的代理池镜像,当然这里你也可以自行替换成你的镜像。另外值得注意的是,这里通过env声明了两个环境变量,指定了Redis的链接地址,其中 REDIsHosT的值是redis.crawler.svc.cluster.local,这个值是有一定规律的,是Kubernetes根据我们部署的Service和Namespace名称自动生成的,其格式是<service-name>.<namespace-name>.svc.cluster-domain>。一般情况下cluster-domain的值为cluster.local,而此时Namespace的名称为 crawler,Redis的Service的名称是redis,所以最后的结果就是redis.crawler.svc.cluster.local。在Kubernetes其他容器里,可以通过这样的Host访问其他容器。REDISPORT这里就是RedisService的运行端口,即6379。另外,这里还指明了容器运行端口containerPort为5555。
接下来,再创建一个对应的Service,在proxypool文件夹下新建service.yaml,其内容如下:
apiVersion: v1
kind: Service
metadata:
labels:
app: proxypool
name: proxypool
namespace: crawler
spec:
ports:
- name: "5555"
port: 5555
targetPort: 5555
selector:
app: proxypool
还是同样的格式,这里声明了 Service 的运行端口还是 5555,targetPort 和 containerPort 对应起来,也是 5555。
接下来,执行如下命令进行部署:
kubectl apply -f proxypool
运行成功的结构类似如下:
deployment.apps/proxypool created
service/proxypool created
和 Redis 类似,这里使用如下命令即可查看代理池的部署状态:
kubectl get deployment/proxypool -n crawler
如果出现类似如下结果,就说明部署成功了:
NAME READY UP-TO-DATE AVAILABLE AGE
proxypool 1/1 1 1 63s
此时我们可以通过 kubectl 的 port-forward 命令将 Kubernetes 里面的服务转发到本地测试,执行如下命令:
kubectl port-forward svc/proxypool 8888:5555 -n crawler
port-forward 命令可以创建本地端口和 Kubernetes 服务的端口映射,这里指定了转发的服务为 svc/proxypool,svc 就是 Service 的意思,端口映射配置为 8888:5555。因为我们部署的代理池 Service 运行端口是 5555,这里我们将其转发到本机的 8888 端口上。
运行之后,会有类似如下的输出结果:
Forwarding from 127.0.0.1:8888 -> 5555
Forwarding from [::1]:8888 -> 5555
...
此时我们在本机浏览器上打开 http://localhost:8888/random,就可以直接访问到代理池的 API 服务了,如图 17-15 所示。
图17-15 运行结果
这就表明代理池正在运行并能正常提供 API 服务。验证完毕之后,停止如上命令即可,这不会对 Kubernetes 里面的代理池服务产生任何影响。
账号池
账号池和代理池的部署非常相似。在 kubernetes 文件夹下创建 accountpool 文件夹,在 accountpool 文件夹下创建 deployment.yaml 文件,其内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: accountpool
name: accountpool
namespace: crawler
spec:
replicas: 1
selector:
matchLabels:
app: accountpool
template:
metadata:
labels:
app: accountpool
spec:
containers:
- env:
- name: REDIS_HOST
value: 'redis.crawler.svc.cluster.local'
- name: REDIS_PORT
value: '6379'
- name: API_PORT
value: '6777'
- name: WEBSITE
value: antispider7
image: germey/accountpool
name: accountpool
resources:
limits:
memory: "500Mi"
cpu: "300m"
requests:
memory: "500Mi"
cpu: "300m"
ports:
- containerPort: 6777
这里的原理和代理池一样,它指定了REDISHOST和REDISPORT,同时额外指定了APIPORT和 WEBSITE这两个环境变量,并设置了containerPort为6777。
再创建 service.yaml 文件,其内容如下:
apiVersion: v1
kind: Service
metadata:
labels:
app: accountpool
name: accountpool
namespace: crawler
spec:
ports:
- name: "6777"
port: 6777
targetPort: 6777
selector:
app: accountpool
这里指定 Service 的运行端口为 6777。
执行如下命令进行部署:
kubectl apply -f accountpool
运行结果类似如下:
deployment.apps/accountpool created
service/accountpool created
这就说明部署命令执行成功了,稍等片刻,账号池也会部署成功。
同样,我们也可以使用同样的命令来将账号池服务转发到本地验证:
kubectl port-forward svc/accountpool 7777:6777 -n crawler
运行结果类似如下:
Forwarding from 127.0.0.1:7777 -> 6777
Forwarding from [::1]:7777 -> 6777
...
此时在本机浏览器打开 http://localhost:7777/antispider7/random,如果能正常获取到结果,就说明账号池也正常运行了。
爬虫项目
因为爬虫项目依赖账号池和代理池,所以这里我们最后才进行部署。在 kubernetes 文件夹下创建 scrapycompositedemo 文件夹,并在此文件夹下创建 deployment.yaml 文件,其内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: scrapycompositedemo
name: scrapycompositedemo
namespace: crawler
spec:
replicas: 1
selector:
matchLabels:
app: scrapycompositedemo
template:
metadata:
labels:
app: scrapycompositedemo
spec:
containers:
- env:
- name: ACCOUNTPOOL_URL
value: 'http://accountpool.crawler.svc.cluster.local:6777/antispider7/random'
- name: PROXYPOOL_URL
value: 'http://proxypool.crawler.svc.cluster.local:5555/random'
- name: REDIS_URL
value: 'redis://redis.crawler.svc.cluster.local:6379'
image: germey/scrapycompositedemo
name: scrapycompositedemo
resources:
limits:
memory: "500Mi"
cpu: "300m"
requests:
memory: "500Mi"
cpu: "300m"
这里我们配置了 germey/scrapycompositedemo 作为镜像,同时配置了 ACCOUNTPOOL_URL、PROXYPOOL_URL 以及 REDIS_URL,这里的 Host 都设定为了 redis.crawler.svc.cluster.local,端口都是各个服务的运行端口。
因为 Scrapy 爬虫项目并不提供 HTTP 服务,所以我们只需要部署 Deployment 即可。运行如下命令执行部署:
kubectl apply -f scrapycompositedemo
我们可以执行如下命令查看部署状态:
kubectl get deployment/scrapycompositedemo -n crawler
如果出现类似如下结果,就说明部署成功,并正常运行了:
NAME READY UP-TO-DATE AVAILABLE AGE
scrapycompositedemo 1/1 1 1 2m46s
另外,我们还可以查看 Pod 的状态:
kubectl get pod -n crawler
运行结果类似如下:
NAME READY STATUS RESTARTS AGE
accountpool-57d498655f-6wv74 1/1 Running 0 17m
proxypool-8646f8cbc7-64z98 1/1 Running 0 22m
redis-5689c9b5cb-zdftz 1/1 Running 0 44m
scrapycompositedemo-cbffd87dd-x8pj8 1/1 Running 0 3m54s
可以看到,前面部署的所有实例都正常运行了。
怎么知道爬虫有没有爬取到数据呢?我们可以运行命令查看日志:
kubectl logs scrapycompositedemo-cbffd87dd-x8pj8 --tail=20 -n crawler
这里我们通过 logs 命令输出一个 Pod 的日志,这里 Pod 的名称需要根据上面命令的输出结果得到。另外,这里指定了 --tail=20 代表输出最后 20 条日志,运行结果类似如下:
{'content': '我当时的感觉??和电影一模一样。电影不知不觉的。', 'id': '229701906'},
{'content': '突然想起这本书,是初中时候的。看电影之前的(当时比起电影,还是读书更吸引我),读的时候觉得故事还可以,好像建立了一个更庞大的世界观(虚构的世界秩序)来看。如果作者自己来拍,可能又是另一部《再造》',
'id': '2281425740'},
{'content': '真的不知道在说什么。电影也不知道在表达啥????', 'id': '2277859717'}],
'cover': 'https://img3.doubanio.com/view/subject/l/public/s1463073.jpg',
'id': '1449981',
'introduction': '',
'isbn': '9787020054398',
'name': '无极',
'page_number': 153,
'price': '24.00元',
'published_at': '2006-01-20T16:00:00Z',
'publisher': '人民文学出版社',
'score': '6.2',
'tags': ['郭敬明', '无极', '小说', '奇幻', '电影小说', '小四', '青春文学', '青春'],
'translators': []}
2023-03-13 16:36:23 [middlewares.proxy] DEBUG: set proxy 34.90.54.218:80
可以看到,Scrapy 爬虫项目正常运行并爬取到数据了。
Dashboard
我们可能发现,每次都要通过命令查看日志和运行状态非常麻烦,有没有更直观的查看 Kubernetes 资源的工具?当然有,我们可以直接使用 Kubernetes 推荐的 Dashboard。
官方文档链接为: https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/ 。我们可以试着部署一下:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.2.0/aio/deploy/recommended.yaml
|
随着 Dashboard 版本的更新,此命令可能并不是最新的,请参考官方文档的说明进行部署。 |
执行完上述命令之后,可能会看到如下结果:
然后执行如下命令:
kubectl proxy
通过本地访问 http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ ,即可看到一个 Dashboard 登录界面,如图 17-16 所示。
图 17-16 Dashboard 登录界面
此时可以参考官方文档的说明获取登录的 token,参考命令如下:
kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get sa/kubernetes-dashboard -o jsonpath="{.secrets[0].name}") -o go-template="{{.data.token | base64decode}}"
可以看到得到一个 token,类似的内容如下:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9Q3RFNkFHMzhMUWV6VnpPWmhNZDZOVMZhZmVlYTNZYS1xMSdxeHciFQ.eyjpc3MiOiJrdWJlcm5ldGVzLmlvL2NlcnZpY2VhY2NvdW50Iiwic3ViOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZWNlcm5ldGVzLWRhc2hib2FyZDpxa3ViZXJuZXRlZXMtZGFzaGhvYXJkIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2U6a3ViZXJuZXRlcy1kYXNoYm9hcmQsc3lzdGVtOmxpZHNlcm5ldGVzLWRhc2hib2FyZGIyZGlmcmVzc2JpbmcwMyAxZHVsaW5jYW1paWlYSm9lVmJtVDIwMiIsInZhZmY2Y291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQsa3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLm5hbWU6a3ViZXJuZXRlcy1kYXNoYm9hcmQsa3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC91aWQ6MVNvbW5pVmZpZC1MZjM2MZMwNDkNWlZLTlOMIwNkdK4NiOsMmVklTgzNzhNNzEyZDZLMjIyNIsiYiIYiI6InN5c3Rlbi9zZXIvaWNJYWNjb3VudDpydWxlcy1kYXNoYm9hcmQsa3ViZXJuZXRlcy1kYXNoYm9hcmQsa3ViZXJuZXRlcyJ9.BpsdW5gATDXdVZGbnRoZ_CD0ri1kYZ7RkAxu4eMNc2CTK88CbmennI_lSVLoe2u7ghjCLU24MZFb7DgJ2fasvCJhkQvoJybRVJA-n09dDLlpufflm02sGRX-MrbEozl4QlKE_puwr8psmRORDxVs24ytFCIS2rKgt6MDB086mAMjexSCUZScPnFwf1ttkZBBNVHdUigJly_Dhhqu3pLzwVra9SJld5hg7W58-v3Y3g3i1rt7VC8OlmR3LcseKav4gvpQw1SD_6Hdxq212euLE8yb8y7QtT8bLoaJMa5OO_GKfDRBdbY72chf1bc1P6EMJd3EP0sO9rU-gSK8YiToqG
|
由于 Secret 的名称可能不同,所以以上命令可能会不同,请自行根据实际情况修改,以官方文档为准。 |
接下来,将输出的 token 粘贴到 Dashboard 的登录页面中,即可登录成功,如图 17-17 所示。
图 17-17 登录成功后的页面
将命名空间切换为 crawler,即可看到各个服务的运行状态,如图17-18所示。
图17-18 crawler命名空间下各个服务的运行状态
比如,这里可以看到Deployments、ReplicaSets和Pods的运行状态都是正常的,颜色为绿色。我们还可以进-一步查看Scrapy爬虫项目对应的Pod,如图17-19所示。
图17-19 Scrapy爬虫项目对应的Pod
这里展示了 Pod 的详细信息,比如运行状态、时间、元数据等。另外,点击右上角(右起第四个)按钮,即可查看该Pod对应的运行日志,如图17-20所示。
图17-20 Pod对应的运行日志
可以看到,它在正常运行,并将爬取到的数据输出到了控制台。
另外,我们还可以增删爬虫的实例数。可以回到Deployments的管理页面,选择“规模”选项卡,如图17-21所示。
图17-21 “规模”选项卡
比如将目标副本数量调整为5,如图17-22所示。
图 17-22 调整目标副本数量
当然,我们也可以使用命令行实现资源放缩,具体如下:
kubectl scale -n crawler deployment scrapycompositedemo --replicas=5
执行之后,我们可以看到 Kubernetes 又新创建了 4 个 Pod。我们可以在 Dashboard 中直观地看到新创建的 Pod,如图 17-23 所示。
这样就相当于创建了 5 个爬虫实例,因为它们共享了一个 Redis 队列,所以它们就是 5 个分布式爬虫实例,可以协同运行。
如果我们想增加或减少爬虫数量,只需要更改目标副本数量即可,比如将其修改为 100,就相当于我们部署了 100 个爬虫实例,而且这 100 个爬虫是协同爬取的。是不是非常方便?
图17-23 新创建了4个Pod
总结
以上我们就通过一个实战练习实现了分布式爬虫在Kubernetes中的部署,并且还可以非常方便地使用Kubernetes对Scrapy爬虫项目进行管理。相比Scrapyd来说,Kubernetes的功能更加强大,是一个绝佳的管理Scrapy爬虫项目的利器。
本节涉及的知识点比较多,需要好好消化和练习。
本节代码详见 https://github.com/Python3WebSpider/ScrapyCompositeDemo/tree/docker ,注意是 docker 分支。