Docker内Python子进程无网络访问的问题解决求助
问题:Docker环境下Python子进程无法访问网络导致AsyncTelebot请求超时
问题背景
本地运行基于AsyncTelebot的Python应用一切正常,但部署到Docker容器后,主进程之外的Python子进程无法访问网络,导致AsyncTelebot调用getMe接口时出现RequestTimeout错误。
相关代码
async def add_bot(self, api_key: str): if api_key not in self.bots: if await self._check_bot(api_key): bot = AsyncTeleBot(api_key) self.bots[api_key] = bot terminate_flag = multiprocessing.Event() self.terminate_flags[api_key] = terminate_flag process = multiprocessing.Process(target=self.bot_polling_process, args=(api_key, terminate_flag), daemon=True) self.processes[api_key] = process process.start() else: raise InvalidBotKeyException('Неверный API ключ для бота') else: raise BotAlreadyWorksException('Такой бот уже работает') @staticmethod def bot_polling_process(api_key, terminate_flag): bot = AsyncTeleBot(api_key) setup_handlers(bot) async def polling(): while not terminate_flag.is_set(): try: await bot.infinity_polling(timeout=10, request_timeout=90, interval=0) except Exception as e: print(f"Exception occurred: {e}") continue asyncio.run(polling())
错误日志
2024-07-29 17:17:32,856 (asyncio_helper.py:103 MainThread) ERROR - TeleBot: "Unknown error: RuntimeError" backend | 2024-07-29 17:17:32,857 (async_telebot.py:370 MainThread) ERROR - TeleBot: "Infinity polling exception: Request timeout. Request: method=get url=getMe params=<aiohttp.formdata.FormData object at 0x7fa295e432b0> files=None request_timeout=300" backend | 2024-07-29 17:17:32,859 (async_telebot.py:372 MainThread) ERROR - TeleBot: "Exception traceback: backend | Traceback (most recent call last): backend | File "/usr/local/lib/python3.10/site-packages/telebot/async_telebot.py", line 366, in infinity_polling backend | await self._process_polling(non_stop=True, timeout=timeout, request_timeout=request_timeout, backend | File "/usr/local/lib/python3.10/site-packages/telebot/async_telebot.py", line 423, in _process_polling backend | self._user = await self.get_me() backend | File "/usr/local/lib/python3.10/site-packages/telebot/async_telebot.py", line 2579, in get_me backend | result = await asyncio_helper.get_me(self.token) backend | File "/usr/local/lib/python3.10/site-packages/telebot/asyncio_helper.py", line 154, in get_me backend | return await _process_request(token, method_url) backend | File "/usr/local/lib/python3.10/site-packages/telebot/asyncio_helper.py", line 105, in _process_request backend | raise RequestTimeout("Request timeout. Request: method={0} url={1} params={2} files={3} request_timeout={4}".format(method, url, params, files, request_timeout, current_try)) backend | telebot.asyncio_helper.RequestTimeout: Request timeout. Request: method=get url=getMe params=<aiohttp.formdata.FormData object at 0x7fa295e432b0> files=None request_timeout=300 backend | "
当前Docker配置
Dockerfile
FROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
docker-compose.yml
version: '3.8' networks: dev: services: mongo: image: mongo:latest container_name: mongo environment: - MONGO_INITDB_DATABASE=telegram_posts ports: - "27017:27017" volumes: - mongo-data:/data/db networks: - dev redis: image: redis:latest container_name: redis ports: - "6379:6379" networks: - dev backend: build: context: ./telePosting dockerfile: Dockerfile container_name: backend environment: - MONGO_URL=mongodb://mongo:27017 - MONGO_DB_NAME=telegram_posts - REDIS_URL=redis - REDIS_PORT=6379 - PYTHONUNBUFFERED=1 ports: - "8000:8000" depends_on: - mongo - redis networks: - dev frontend: build: context: ./telePostFrontend/vite-project dockerfile: Dockerfile container_name: frontend ports: - "3000:80" depends_on: - backend networks: - dev volumes: mongo-data:
解决方案
1. 改用多线程替代多进程
AsyncTelebot本身基于异步IO模型,多线程比多进程更适配,且不会触发Docker下的网络隔离问题。修改代码替换multiprocessing.Process为threading.Thread:
import threading async def add_bot(self, api_key: str): if api_key not in self.bots: if await self._check_bot(api_key): bot = AsyncTeleBot(api_key) self.bots[api_key] = bot terminate_flag = threading.Event() self.terminate_flags[api_key] = terminate_flag thread = threading.Thread(target=self.bot_polling_thread, args=(api_key, terminate_flag), daemon=True) self.processes[api_key] = thread thread.start() else: raise InvalidBotKeyException('Неверный API ключ для бота') else: raise BotAlreadyWorksException('Такой бот уже работает') @staticmethod def bot_polling_thread(api_key, terminate_flag): bot = AsyncTeleBot(api_key) setup_handlers(bot) async def polling(): while not terminate_flag.is_set(): try: await bot.infinity_polling(timeout=10, request_timeout=90, interval=0) except Exception as e: print(f"Exception occurred: {e}") continue asyncio.run(polling())
2. 确保子进程独立初始化网络资源
主进程的aiohttp会话在子进程中可能无法正常复用,修改子进程代码,显式创建独立的客户端会话:
@staticmethod def bot_polling_process(api_key, terminate_flag): import aiohttp # 为子进程创建独立的aiohttp会话 session = aiohttp.ClientSession() bot = AsyncTeleBot(api_key, session=session) setup_handlers(bot) async def polling(): while not terminate_flag.is_set(): try: await bot.infinity_polling(timeout=10, request_timeout=90, interval=0) except Exception as e: print(f"Exception occurred: {e}") continue # 退出前关闭会话 await session.close() asyncio.run(polling())
3. 配置Docker容器DNS
子进程可能存在域名解析失败的问题,在docker-compose.yml的backend服务中添加公共DNS服务器:
services: backend: ... dns: - 8.8.8.8 - 1.1.1.1 ...
4. 验证容器网络连通性
进入容器测试外部网络是否可达,排查基础网络问题:
docker exec -it backend ping api.telegram.org
如果ping不通,需要检查宿主机防火墙规则、网络代理配置,或确认Docker网络模式是否正常。
5. 显式指定Docker网络模式
在docker-compose.yml的backend服务中显式设置网络模式为bridge(默认模式,但显式设置可排除配置异常):
services: backend: ... network_mode: bridge ...
内容的提问来源于stack exchange,提问作者Faddey Valuev
相关产品推荐
相关产品推荐

