ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Docker容器化部署爬虫全流程:从Dockerfile到docker-compose实践

Docker容器化部署爬虫全流程:从Dockerfile到docker-compose实践 做爬虫的朋友应该都经历过这个阶段本地脚本跑得好好的requests一梭子下去数据哗哗往库里写。结果把代码扔到服务器上要么提示ModuleNotFoundError要么MySQL连不上要么爬着爬着进程没了日志里啥也没留下。我第一次做爬虫部署的时候就被这种“环境的诅咒”折磨得够呛后来彻底切到Docker容器化部署才算是把这一整套流程捋顺了。这篇文章我想把Docker容器化部署爬虫项目的完整流程从头到尾写一遍——从为什么要容器化、怎么写Dockerfile到docker-compose编排、定时调度、反爬应对、上线之后的运维全部串起来讲。适合那些已经会写爬虫但部署经验还停留在“scp加python xxx.py”阶段的朋友参考。我能保证的是里面每个步骤都是我实际跑过、踩过坑之后沉淀下来的东西不是网上那种贴个Dockerfile就完事的教程。1. 先聊聊爬虫部署里那些“换台机器就翻车”的事1.1 环境不一致是最大的坑我最早部署爬虫流程很简单本地把脚本写完scp传到服务器装个Pythonpip install一堆依赖然后nohup python main.py 。第一台服务器这么搞勉强能用第二台就开始出幺蛾子。最常见的翻车现场有这么几类Python版本不一致。本地是3.11服务器上是系统自带的3.6某些语法直接不兼容比如dict的合并运算、dataclass、f-string的一些新特性。报错不会指出版本问题只会莫名其妙地SyntaxError。系统级依赖缺失。爬虫项目最怕的不是pure-python的requests而是lxml、mysqlclient、cryptography这些带C扩展的库。lxml需要libxml2和libxsltmysqlclient需要libmysqlclient-dev和gcc装不装得上看运气。Windows和Linux的差异。本地开发用Windows代码里的路径是C:\\data\\output.csv放到Linux上直接崩。还有文件编码本地默认gbk写的CSV到了服务器上中文乱码。Selenium方案更惨。本地装了个Chrome浏览器驱动版本匹配得好好的服务器上一看连浏览器都没有。就算装了Chrome还得操心chromedriver版本和Chrome版本对不对得上。这些问题单看都不难解决但每次换环境、换机器、给同事交接都要重新踩一遍。而且踩完还不一定记得住所有坑因为当时是怎么解决的往往只存在于聊天记录里。1.2 容器化解决的核心问题Docker容器化把这套问题整个打包封死了。核心逻辑很朴素把运行环境连同代码一起做成镜像到哪台机器上都是同一个运行环境。镜像一旦构建成功里面就固定了Python版本、系统依赖、pip依赖、时区设置、工作目录。服务器上只需要装一个Docker引擎什么Python、什么lxml编译环境统统不用管。这带来的实际好处是一次构建到处运行。本地构建好的镜像推到镜像仓库服务器上pull下来直接跑。换服务器、扩容、迁移成本瞬间降到原来的十分之一。版本完全锁定。requirements.txt锁了依赖版本镜像本身就锁了一个不可变的依赖组合再也不存在“那台老服务器上装的还是旧版requests”这种诡异问题。依赖关系可视化。爬虫项目通常要配MySQL、Redis用Docker Compose可以把这些中间件和爬虫一次性拉起来谁依赖谁、怎么通信全部声明在文件里新人接手也能一目了然。故障恢复更省心。容器挂了自动重启日志统一走docker logs内存爆了有资源限制不会把整台服务器拖垮。这里要说一句安装的事情。Windows和macOS上直接用Docker Desktop装完基本开箱即用Linux服务器一般装docker-ceapt install docker.io也行两三个命令的事。网上教程很多我就不展开写了但有一个提醒装完Docker Desktop如果报virtualization support not detected大概率是BIOS里虚拟化没开去主板设置里把VT-x/AMD-V打开就好别急着重装系统。2. 把爬虫装进镜像Dockerfile从基础到能用2.1 基础镜像选择slim与alpine的取舍写Dockerfile的第一步是选基础镜像。爬虫项目最常见的选择是python:3.11-slim和python:3.11-alpine。很多人一上来就选alpine因为它小——python:3.11-alpine大概50MB而slim版本有120MB左右。但我要泼一盆冷水爬虫项目真的不建议用alpine。alpine用的是musl libc和大多数Linux发行版的glibc不一样。lxml、cryptography、pandas、mysqlclient这些带C扩展的库在musl环境下经常要现场编译。现场编译就要装gcc、musl-dev、python3-dev装完镜像体积反而比slim还大而且编译时间长得让人怀疑人生。我踩过一次alpine装pandas整整编译了二十多分钟最后还因为某个头文件路径不对直接失败。python:3.11-slim基于Debianglibc体系绝大多数pip包都有现成的wheel装起来是下载即用。体积多个几十MB换来的是一路顺畅的安装体验这个性价比非常高。如果项目还在用Python 3.9或更老也建议选对应版本的slim镜像。爬虫这种脚本型项目老版本镜像完全够用不需要追着3.13这种最新版跑——等第三方库兼容性跟上你早就升级了。2.2 依赖安装与构建缓存的讲究写Dockerfile有一条重要的经验把不常变的操作放在前面把经常改的代码放在最后。Docker构建是有层缓存的每一行指令会生成一层只要这层之前的层没变构建时就直接复用缓存不会重新执行。明白了这个原理Dockerfile的结构就很清晰了。我实际项目里用的Dockerfile长这样FROM python:3.11-slim ENV TZAsia/Shanghai \ PYTHONUNBUFFERED1 \ PYTHONIOENCODINGutf-8 \ PIP_INDEX_URLhttps://pypi.tuna.tsinghua.edu.cn/simple \ PIP_DISABLE_PIP_VERSION_CHECK1 WORKDIR /app RUN apt-get update \ apt-get install -y --no-install-recommends \ tzdata curl iputils-ping \ ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ echo Asia/Shanghai /etc/timezone \ rm -rf /var/lib/apt/lists/* COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [python, main.py]几个关键点说明一下第一PIP_INDEX_URL指向国内pip镜像。Docker构建时是在容器环境里执行的默认访问官方PyPI速度时快时慢有时候干脆超时。设成清华镜像后构建速度能快好几倍。这里提一句构建服务器在国外的话就不需要设这个但在国内部署项目时我建议无条件加上。第二先COPY requirements.txt再COPY整个项目。这样只要requirements.txt没变pip install这层就会命中缓存。如果先把整个项目COPY进去哪怕你只改了一个爬虫脚本构建时都要把全部依赖重新装一遍那酸爽谁试谁知道。第三PYTHONUNBUFFERED1必须加。Python的输出默认是行缓冲的print的内容在日志文件里可能滞后很久容器一旦崩溃最后一屏日志就丢了。设了这个变量之后print和logging会立即刷新出去排查问题的时候能看到最实时的状态。第四装上curl和iputils-ping。这俩不是为了跑爬虫是为了排查问题。容器之间网络不通的时候你得能进容器里curl一下、ping一下。没有这两个工具排查网络问题全靠猜非常痛苦。2.3 时区、编码、系统依赖一个都不能少新手最容易忽略的两个坑就是时区和编码。时区问题表现为容器默认是UTC时间你的爬虫代码里用了datetime.datetime.now()存到数据库的时间比北京时间慢8个小时。很多人在线上跑了一两个月直到发现报表数据不对才注意到。解决办法就是Dockerfile里那两行ln -snf和echo把时区软链指到Asia/Shanghai。Debian系的slim镜像还要先装tzdata不然没有时区数据文件。编码问题表现为爬取回来的数据里有中文print到日志是好的但写入文件就报UnicodeEncodeError或者写成UTF-8文件后Windows上打开是乱码。我的建议是在Python脚本里做两层保障import sys import io sys.stdout io.TextIOWrapper(sys.stdout.buffer, encodingutf-8)不过更稳妥的是用环境变量把整个运行环境的编码钉死——Dockerfile里已经写了PYTHONIOENCODINGutf-8这会让标准输出、标准错误都用UTF-8处理绝大多数编码问题都能一次性消掉。系统依赖方面爬虫项目经常需要额外的库要用lxml解析HTML就需要libxml2和libxslt要操作图片需要libjpeg、zlib要跑Selenium需要Chrome和一堆浏览器运行时依赖。这些依赖写在Dockerfile的apt-get install里但要记得最后加一句rm -rf /var/lib/apt/lists/*清理apt缓存不然镜像体积会膨胀不少。3. 用docker-compose一次性拉起爬虫全家桶3.1 compose文件怎么设计才不踩雷爬虫项目很少是单打独斗的——数据要存MySQL请求排队要用Redis有的还要接调度系统。如果一个个docker run光是记参数就够头疼。docker-compose把多个容器编排成一个整体一起构建、一起启动、一起停止。我常用的compose文件结构是这样services: mysql: image: mysql:8.0 container_name: crawler-mysql environment: MYSQL_ROOT_PASSWORD: ChangeMe123 MYSQL_DATABASE: spider_data command: --character-set-serverutf8mb4 --collation-serverutf8mb4_unicode_ci volumes: - mysql-data:/var/lib/mysql healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -pChangeMe123] interval: 10s timeout: 5s retries: 5 redis: image: redis:7-alpine container_name: crawler-redis command: [redis-server, --appendonly, yes] volumes: - redis-data:/data crawler: build: . container_name: crawler-app depends_on: mysql: condition: service_healthy redis: condition: service_started env_file: .env restart: unless-stopped volumes: - ./logs:/app/logs networks: - crawler-net volumes: mysql-data: redis-data: networks: crawler-net: driver: bridge这里有几个细节非常关键。MySQL容器我显式指定了utf8mb4字符集。爬虫存的数据里什么都有表情符号、生僻字、各种全角符号默认的latin1或者utf8都会出问题。--character-set-serverutf8mb4是必加的不然存emoji的时候直接报错存中文也可能出现问号。depends_on这个参数我强烈建议配合healthcheck用。很多人写depends_on: [mysql, redis]就觉得搞定了但这个参数只保证容器启动了不保证MySQL初始化完成。MySQL第一次启动要初始化数据目录可能要十几秒这期间爬虫进程连数据库必然失败。加了healthcheck和condition: service_healthycompose会等MySQL真正可用了再启动爬虫这个坑基本就补上了。Redis那个容器我用--appendonly yes开启了AOF持久化。爬虫项目里的Redis常常用来存待抓取的URL队列、去重集合丢了可就全白干了。还有一个多年的老梗要提compose文件里写不写version字段。现在用Docker Compose V2的话这个字段是废弃的写了反而可能触发警告。我上面那份文件就没有version直接用最新语法干净利落。3.2 docker网络不通完整的排查链路在这里容器编排起来之后最常遇到的问题就是网络不通。爬虫容器连不上MySQL报错往往是这样的pymysql.err.OperationalError: (2003, Cant connect to MySQL server on localhost (connect refused))凡是遇到这个我第一个检查的永远是连接地址。容器内部的localhost指的是容器自己不是你宿主机上的MySQL。代码里写死localhost的在本地跑没问题一进容器必死无疑。正确的做法是改用compose里的服务名也就是mysql——Docker内置DNS会把服务名解析成对应容器的IP。排网络问题我有一套固定的排查链路分享给你确认容器在同一个网络里。执行docker network ls看有没有自定义网络再看docker inspect crawler-app的Networks字段里有没有包含crawler-net。两个容器不在同一网络是无法用服务名互通的。进容器里实测连通性。docker exec -it crawler-app bash然后ping mysql。如果ping不通检查是不是网络配错了如果ping得通但业务代码连不上那就是端口或者认证的问题。检查端口映射和服务监听。MySQL容器如果只想被其他容器访问可以不映射端口但宿主机上的客户端要连就得有ports: 3306:3306。另外注意云服务器上的安全组很多情况下容器端口开了、映射也写了但云平台安全组没放行宿主机外面依然连不上。看DNS配置。容器里cat /etc/resolv.conf如果DNS不对外网请求会解析失败。可以在daemon.json里自定义dns或者用docker run --dns 8.8.8.8compose里配dns:字段也行。我把常见现象列了个表方便你对照定位现象最可能原因解决办法爬虫容器连不上MySQL代码里用了127.0.0.1/localhost改成服务名mysql容器间ping不通没加入同一自定义网络在compose里定义共享网络宿主机访问不了MySQL端口未映射或云安全组未放行写ports映射再检查安全组容器内外网DNS解析失败宿主DNS异常或容器dns配置不对修改daemon.json自定义DNS3.3 MySQL与Redis的数据持久化方案容器是极其无情的——删掉就没了容器里的数据也跟着消失。爬虫攒的数据是核心资产绝对不能放在容器文件系统里。解决方式是宿主机挂载卷。上面compose文件里的mysql-data:/var/lib/mysql就是named volume由Docker管理存在宿主机的/var/lib/docker/volumes/下。这种方案的好处是数据文件不受容器生命周期影响docker-compose down都不会丢重建容器后数据原样回来。除了数据库爬虫还会产生很多文件类产物CSV、JSON、图片、日志。这些我推荐用bind mount把宿主机目录直接挂进容器volumes: - ./logs:/app/logs - ./data:/app/data这里有个权限的坑。bind mount的目录所有权取决于宿主机上目录的UID/GID容器内进程可能没有写入权限。我遇到过多次爬虫在容器里跑写文件报Permission denied一看是宿主机目录属于root容器内是非root用户。解决办法很简单宿主机上先chmod -R 777 logs data或者chown -R 1000:1000大部分Python镜像默认用户是root但如果你在Dockerfile里切换了非root用户就得改对UID。4. 定时任务、代理与反爬策略在容器里的落地4.1 定时调度宿主cron还是容器内cron爬虫项目几乎都离不开定时调度。每天凌晨爬一次、每小时抓一轮、周末跑全量。在本地可以用系统的计划任务容器里怎么搞方案一宿主机的cron docker exec。这是我最推荐的做法简单、可靠、好排查。宿主机上crontab -e写一行0 2 * * * docker exec crawler-app python /app/scripts/daily_task.py /var/log/crawler_cron.log 21优点是调度逻辑和容器完全解耦。宿主机cron不会跟容器一起挂掉日志也能直接写到宿主机的文件里。要注意cron默认的环境变量很少PATH里往往没有docker——建议写成/usr/bin/docker的全路径或者在cron脚本开头export PATH/usr/bin:/bin:$PATH。方案二容器内装cron crond运行。这种方式把调度逻辑也封装进镜像里迁移机器不用再配宿主机cron。Dockerfile里装cron把定时任务文件放进容器最后用supervisord同时拉起cron和爬虫进程RUN apt-get install -y cron COPY cron.d/spider-cron /etc/cron.d/spider-cron RUN crontab /etc/cron.d/spider-cron0 2 * * * root python /app/scripts/daily_task.py /app/logs/cron.log 21cron在容器里跑有两个坑。一是时区前面Dockerfile里已经设了Asia/Shanghaicron会继承这个时区但如果没设定时任务会比预期晚8小时。二是环境变量cron执行时不会自动加载/etc/profilePython解释器路径、PYTHONIOENCODING这些都要在脚本或cron行里显式指定否则可能跑一个残缺的环境。我现在的项目是两个方案混用的简单的定时任务用宿主cron因为改crontab不用重新构建镜像涉及多进程编排的用supervisord方案让容器内自己管理。4.2 代理池接入容器的正确姿势很多爬虫场景需要代理。要么是目标接口对单一IP有频率限制要么是需要访问第三方API服务要么是想让请求从多个出口IP轮询发出。不管哪种情况容器里接代理都有讲究。如果你的代理服务是O口一个固定地址的直接在容器里设环境变量就行environment: - HTTP_PROXYhttp://proxy-host:8080 - HTTPS_PROXYhttp://proxy-host:8080注意这里有一个很容易踩的坑HTTP_PROXY只对代码里用requests这类库发请求时生效吗不是的。requests默认不会自动读取HTTP_PROXY环境变量——它有自己的proxies参数但如果你不显式传它也会检查环境变量。所以要稳最好在代码里显式写import os proxies { http: os.getenv(HTTP_PROXY, ), https: os.getenv(HTTPS_PROXY, ), } session requests.Session() session.proxies.update(proxies)如果项目维护了一个代理池IP列表存在Redis里爬虫每次请求前随机取一个代理这个逻辑在容器里跑完全没问题只要Redis连接地址用服务名redis就行。还要提醒一句代理质量直接决定爬虫的稳定性。我遇到过代理返回200但内容是乱七八糟的跳转页、代理超时却消耗了大量请求时间、代理IP源乱返回跨域数据等。建议在代码里加个代理校验逻辑——取到代理先发个轻量的测试请求不可用就换下一个别把坏代理送到正式抓取流程里。另外任何时候都要守住合规底线爬目标站之前先看对方的robots.txt和服务条款控制请求频率别把站点打挂。技术上能做的不代表应该做这是我在这个领域做了很多年项目后的最核心心得。4.3 降低被封概率的容器内实践反爬是爬虫绕不开的话题。最基本的反爬三道锁User-Agent检测、请求频率限制、IP封禁。容器化部署后这些策略的实现方式稍微调整一下。UA池可以换成文件放在镜像里代码每次随机选一个USER_AGENTS [ Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36, ]随机延迟放Redis里做全局频控更优雅。多个爬虫实例同时跑的时候光靠每个实例内部随机延迟是不够的总的请求量可能还是很高。用Redis的INCR加过期时间在代码里做一个简单的令牌桶或滑动窗口import redis import time r redis.Redis(hostredis, port6379, db0) def check_rate_limit(key, max_requests10, window60): count r.incr(key) if count 1: r.expire(key, window) return count max_requests对于依赖JavaScript渲染的站点容器里跑Selenium是家常便饭。这里我建议要么在爬虫容器里装Chromium浏览器加chromedriver要么直接用selenium/standalone-chrome镜像单独起一个浏览器服务爬虫容器通过Remote WebDriver连接它。后者的好处是浏览器崩溃了重启浏览器容器就行不影响爬虫逻辑。但要注意这个镜像体积很大内存占用也不小部署的服务器最好是2G内存起步。前端加密的站点比如要逆向JS逻辑算签名的容器化之后倒没有额外难度——加密逻辑是代码的一部分Python和Node在容器里都能跑只是要把Node环境预先装进Dockerfile里。这部分我不展开但有一个原则先确认目标站点的服务条款和采集边界再做技术选型。5. 上线只是开始日志、资源与故障自愈5.1 日志别只print要能被docker logs捞出来爬虫挂着没数据、凌晨任务失败没人发现这些问题的根子往往是日志管理太随意。本地开发的时候随手print没关系容器环境里要让日志流动起来。我要求项目的日志输出到标准输出而不是文件这样docker logs crawler-app --since 10m就能直接看最近的运行日志。Python的logging配置非常简单import logging logging.basicConfig( levellogging.INFO, format%(asctime)s [%(levelname)s] %(name)s: %(message)s, datefmt%Y-%m-%d %H:%M:%S, handlers[logging.StreamHandler()], ) logger logging.getLogger(spider)日志格式里一定要带时间和级别。容器的docker logs本来就会打时间戳但你自己脚本里的日志最好也带时间——docker logs的时间戳是Docker加的日志内容里的时间是业务层面的两者对得上排查问题才能定位到具体哪次请求、哪个环节。如果确实需要把日志落到文件别忘了在compose里挂载volume并且给logging加上RotatingFileHandler不然日志文件会无限膨胀把磁盘塞爆。还有一个实用技巧定时任务的执行结果最好在代码结束时打印一个明确的状态。爬虫跑完打印spider job finished, total: 123, failed: 0cron重定向到宿主日志每天早上扫一眼就知道昨晚跑没跑完。这个习惯帮我在线上避免了无数次半夜爬起来看日志。5.2 给容器装上内存保险丝Python爬虫的内存泄漏是常见病。requests的session没释放、循环里不断累积列表、Selenium打开了一个个标签页不关闭时间一长内存占用就蹭蹭往上涨。容器的好处是Docker可以给你限制资源不让它把宿主机拖死。compose里限制资源有两种写法。如果是Docker Compose直接跑用mem_limitmem_limit: 512m cpus: 1.0如果是Swarm模式用deploy段deploy: resources: limits: cpus: 1.0 memory: 512M设了内存限制之后容器一旦超限会被内核OOM杀掉然后restart: unless-stopped会把它拉起来。这看起来粗暴但比让进程在宿主机上无限吃内存、最后把整台机器拖到无响应要好得多。实际操作中我会配合docker stats来观察docker stats crawler-app如果发现内存持续增长、稳定上升比如从200M慢慢涨到400M再到接近上限被重启那就基本可以断定代码里有内存泄漏点。常见的处理是在爬虫主循环里定期gc.collect()、限制列表长度、用requests.Session().close()或者上下文管理器。实在查不出来就写一个守护逻辑每N轮抓取完成后主动os._exit(0)让容器重启虽然土但确实管用。5.3 崩溃自愈与告警通知容器是自己重启了但重启之后任务没跑完怎么办比如凌晨两点爬到一半容器OOM重启后爬虫继续跑但任务状态已经乱了。这时候需要两个层面的保障。第一层是代码层面的任务断点续跑。把已抓取的URL集合存在Redis里启动时加载每次抓取前先检查是否已抓过。这样即使容器中途重启重新跑起来也不会重复抓取大量数据。第二层是告警。爬虫挂了没人知道等于白挂。我现在的做法是在爬虫容器的守护进程里加健康检测import requests def send_alert(message): requests.post(https://your-alert-webhook, json{text: message}) def main(): while True: try: run_spider() logger.info(spider completed) except Exception as exc: logger.exception(spider crashed) send_alert(f爬虫异常: {exc}) time.sleep(60) # 继续下一轮而不是退出进程这样即使某个任务抛异常主进程也不会退出而是记录日志、发出告警、等待下一轮继续。配合restart: unless-stopped容器层面和代码层面双保险。告警渠道可以用企业微信机器人、钉钉机器人或者邮件随便选一个。核心诉求是异常发生时有一个能主动找到你的通道而不是你等日志从哪冒出来。6. 我沉淀下来的一套爬虫容器化模板与避坑清单6.1 一份可直接抄的目录结构写了这么多最后分享一套我实际项目中沉淀下来的模板。结构清晰扩展方便新项目直接复制改改就能上。spider-project/ ├── Dockerfile ├── docker-compose.yml ├── .dockerignore ├── .env ├── requirements.txt ├── config.py # 全局配置读取环境变量 ├── main.py # 手动跑一次入口 ├── crawlers/ │ ├── __init__.py │ ├── base.py # 通用抓取逻辑、UA池、延迟、代理 │ └── job_a.py # 具体站点的爬虫 ├── storage/ │ ├── __init__.py │ ├── db.py # SQLAlchemy连接与session │ └── redis_client.py # Redis连接封装 ├── scripts/ │ └── daily_task.py # 定时任务脚本 └── logs/ # 宿主机挂载的日志目录.dockerignore别偷懒不写把logs/、data/、.git/、__pycache__/、.venv/都忽略掉不然构建上下文会把你本地几G的缓存文件全打进镜像构建慢到怀疑人生。数据库访问我建议直接用SQLAlchemy别手写一堆裸SQL。storage/db.py里放一个engine和session_factory爬虫代码里统一从这里拿session数据入库的字段校验、批量插入、去重逻辑都集中在这一层后面维护会很顺手。6.2 高频踩坑记录表最后把我这些年容器化部署爬虫项目高频翻车的点整理成一张表算是压箱底的避坑清单坑现象规避方案MySQL 8认证插件不兼容老客户端报caching_sha2_password错误pip安装cryptography或建用户时指定IDENTIFIED WITH mysql_native_password容器时区不对入库时间比北京时间慢8小时Dockerfile装tzdata并设置TZAsia/Shanghaicron环境变量缺失定时任务报python: command not foundcrontab里写全路径或用bash -lc包一层bind mount权限不够容器内写文件报Permission denied宿主机目录chmod/chown或Dockerfile里切换用户前先建好目录权限pip install慢或超时构建卡在安装依赖阶段设置PIP_INDEX_URL指向国内镜像代码改了但镜像没更新跑的还是旧逻辑确保.dockerignore没忽略源码目录重新docker compose build --no-cache crawler容器端口映射冲突宿主机端口被占用compose起不来换宿主端口或停掉占用进程用docker ps先查Selenium容器内存飙升标签页不关闭内存持续增长每轮任务后driver.quit()限制容器内存并允许自动重启这张表里的每一条我都真金白银地在线上踩过。很多坑看起来不起眼但它们共同的特点是报错信息不直接都藏在日志深处一不小心就是通宵排障。套用这套模板正常一个爬虫项目从零到上线快的话半天就能完成。本地写完爬虫配上Dockerfile和compose构建镜像推到服务器docker compose up -d跑起来数据就能稳定地往库里流了。如果你现在还在用scp加nohup的方式部署爬虫我建议你花一个下午把项目迁到Docker里。迁完你会在下一次换服务器、下一次任务凌晨崩溃自动恢复、下一次给同事交接环境时感谢自己做了这个决定。别问我为什么说得这么笃定——我就是那个被环境问题折磨了几十次之后才彻底投奔容器化的人。
返回列表