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

GitLab Runner中Docker化Flask应用数据库连接失败问题

问题:GitLab Runner中Docker化Flask应用Pytest数据库连接失败

问题背景

我有一个Flask Web应用,采用PyTest与MySQL数据库开展API自动化测试。本地运行时,无论是否Docker化,测试均能成功完成。但在基于Alpine Linux的GitLab Runner中执行Docker化流程时出现异常:Docker启动的服务能连接数据库,但通过docker exec执行pytest时,Flask测试客户端的登录POST请求触发报错,提示数据库连接为NoneType,同时出现Access denied for user的权限错误。曾尝试添加--network host参数,但未解决问题。

GitLab流水线配置

before_script:
  - apk update
  - apk add openssh
  - apk add --no-cache git

stages:
  - tests
 
tests:
  stage: tests
  script:
    - docker rm --force /myapp 2>/dev/null || true
    - docker image build --no-cache -t myapp .
    - docker run --name myapp -d -e GOOGLE_APPLICATION_CREDENTIALS="service_account.json" -p 5050:5050 myapp
    - docker ps
    - docker exec -e GOOGLE_APPLICATION_CREDENTIALS="service_account.json" myapp pytest

错误输出

Docker运行输出

$ docker run --name myapp -d -e GOOGLE_APPLICATION_CREDENTIALS="service_account.json" -p 5050:5050 myapp
096864c06de5fb2e8195cc1b87ab7635626759cb690dfc2d76eb640b3679c400
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                  PORTS                                       NAMES
096864c06de5        myapp           "gunicorn -b 0.0.0.0…"   1 second ago        Up Less than a second   0.0.0.0:5050->5050/tcp, :::5050->5050/tcp   myapp
790892bfc88e        b0757c55a1fd        "docker-entrypoint.s…"   8 minutes ago       Up 8 minutes                                                        runner-uz9hfgtk-project-3669-concurrent-0-1be2fbd9916f7e7c-build
df641fdce08f        f8517f1ae7c9        "/usr/bin/dumb-init …"   6 days ago          Up 6 days                                                           runner-uz9hfgtk-project-3740-concurrent-0-b0186612731c8779-docker-0-wait-for-service

Pytest错误输出

$ docker exec -e GOOGLE_APPLICATION_CREDENTIALS="service_account.json" myapp pytest
============================= test session starts ==============================
platform linux -- Python 3.8.18, pytest-7.4.4, pluggy-1.4.0
rootdir: /docker-myapp
plugins: anyio-4.1.0, pylama-7.7.1
collected 50 items
Running tests...
==================================== ERRORS ====================================
_ ERROR 
client = <FlaskClient <Flask 'app_factory'>>
    @pytest.fixture()
    def auth_client(client):
        basic_auth = get_basic_auth().popitem()
>       response = client.post(
            "/login",
            json={
                "email": "test@test.com",
                "password": "password",
            },
            headers={"Authorization": _basic_auth_str(basic_auth[0], basic_auth[1])},
        )
tests/conftest.py:33: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/local/lib/python3.8/site-packages/werkzeug/test.py:1039: in post
    return self.open(*args, **kw)
/usr/local/lib/python3.8/site-packages/flask/testing.py:222: in open
    return Client.open(
/usr/local/lib/python3.8/site-packages/werkzeug/test.py:993: in open
    response = self.run_wsgi_app(environ.copy(), buffered=buffered)
/usr/local/lib/python3.8/site-packages/werkzeug/test.py:884: in run_wsgi_app
    rv = run_wsgi_app(self.application, environ, buffered=buffered)
/usr/local/lib/python3.8/site-packages/werkzeug/test.py:1119: in run_wsgi_app
    app_rv = app(environ, start_response)
/usr/local/lib/python3.8/site-packages/flask/app.py:2464: in __call__
    return self.wsgi_app(environ, start_response)
/usr/local/lib/python3.8/site-packages/flask/app.py:2450: in wsgi_app
    response = self.handle_exception(e)
/usr/local/lib/python3.8/site-packages/flask/app.py:1867: in handle_exception
    reraise(exc_type, exc_value, tb)
/usr/local/lib/python3.8/site-packages/flask/_compat.py:39: in reraise
    raise value
/usr/local/lib/python3.8/site-packages/flask/app.py:2447: in wsgi_app
    response = self.full_dispatch_request()
/usr/local/lib/python3.8/site-packages/flask/app.py:1952: in full_dispatch_request
    rv = self.handle_user_exception(e)
/usr/local/lib/python3.8/site-packages/flask/app.py:1821: in handle_user_exception
    reraise(exc_type, exc_value, tb)
/usr/local/lib/python3.8/site-packages/flask/_compat.py:39: in reraise
    raise value
/usr/local/lib/python3.8/site-packages/flask/app.py:1950: in full_dispatch_request
    rv = self.dispatch_request()
/usr/local/lib/python3.8/site-packages/flask/app.py:1936: in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
/usr/local/lib/python3.8/site-packages/flask_httpauth.py:172: in decorated
    return self.ensure_sync(f)(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    @app.route("/login", methods=["POST"])
    @auth.login_required
    def _login():
        connection = config_parcer()
        response, status = get_login.login(
            request, connection, app.config["SECRET_KEY"], bcrypt
        )
>       connection.close()
E       AttributeError: 'NoneType' object has no attribute 'close'
main.py:818: AttributeError
---------------------------- Captured stdout setup -----------------------------
(1045, "Access denied for user 'app_user'@'XX.XXX.XX.XXX' (using password: YES)")
Connection refused. Attempts count: 1
(1045, "Access denied for user 'app_user'@'XX.XXX.XX.XXX' (using password: YES)")
Connection refused. Attempts count: 2
(1045, "Access denied for user 'app_user'@'XX.XXX.XX.XXX' (using password: YES)")
Connection refused. Attempts count: 3
'NoneType' object has no attribute 'cursor'

排查方向与解决方案

  1. 补充缺失的环境变量:
    docker exec仅传递了GOOGLE_APPLICATION_CREDENTIALS,但可能缺少数据库连接所需的其他变量(如DB_HOST、DB_USER、DB_PASSWORD)。本地运行时这些变量可能默认存在,但GitLab Runner中未传递。

    • 解决:在docker exec命令中补充所有数据库相关环境变量,或确保容器启动时已通过Dockerfile ENV、docker run -e加载这些变量,让exec进程继承容器环境。
  2. 检查测试环境配置:
    pytest可能加载了生产环境的数据库配置,而非测试环境配置。本地测试时默认使用本地测试库,但GitLab Runner中测试进程尝试访问生产库却无权限。

    • 解决:检查config_parcer()函数,确认测试环境下是否正确加载测试数据库配置;确保GitLab Runner所在IP段被数据库允许访问,测试账号权限配置正确。
  3. 添加服务就绪等待逻辑:
    docker run后立刻执行pytest,此时gunicorn服务可能未完全启动,数据库连接池未初始化。本地机器性能较好,服务启动快,未触发该问题。

    • 解决:在docker run和docker exec之间添加等待逻辑,比如用curl轮询应用健康检查接口(如http://localhost:5050/health),直到返回成功状态再执行测试。
  4. 排查网络权限问题:
    GitLab Runner的Docker网络可能存在特殊配置,导致容器内测试进程无法访问数据库。

    • 解决:检查数据库防火墙规则,确认Runner所在IP段是否被允许;或使用Docker Compose将应用与测试数据库放在同一自定义网络中,避免网络隔离问题。

总结

核心问题是测试进程无法获取有效数据库连接,大概率由环境变量缺失、测试配置错误、服务未就绪或网络权限限制导致。逐一排查上述方向,重点确保测试进程的环境与本地一致,且数据库访问权限配置正确。

内容的提问来源于stack exchange,提问作者George Rouvalis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 14:48:10