You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 06:54:57