如何排查GitLab CI中pytest单TLS端口测试超时问题?
问题描述
我用pytest测试一个监听多端口的应用,在GitLab CI流水线运行时,除了一个TLS端口的测试外,其余测试都通过,但这个测试会超时且没有任何pytest输出。我已经加了-vvv参数提升日志级别,但还是看不到测试执行的具体情况。
当前测试运行命令
python3 -m pytest -vvv /tests
测试代码
def send_tcp(hostname, port, message, protocol=socket.SOCK_STREAM): s = socket.socket(socket.AF_INET, protocol) s.connect((hostname, int(port))) s.sendall(bytes(message, "utf-8")) s.shutdown(socket.SHUT_WR) s.close() def send_tcp_TLS(hostname, port, message): context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) context.verify_mode = ssl.CERT_OPTIONAL context.check_hostname = False context.load_verify_locations(cafile="rootCA.crt") s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssl_sock = context.wrap_socket(s, server_hostname=hostname) ssl_sock.connect((hostname, port)) ssl_sock.sendall(bytes(message, "utf-8")) ssl_sock.shutdown(socket.SHUT_WR) ssl_sock.close() def receive(message): r = redis.Redis(host="redis-input", port=6378) found = False timeout = time.time() + 20 while not found and time.time() < timeout: for value in r.lrange("inq", 0, -1): decoded = value.decode("utf-8") parsed = json.loads(decoded) if message in parsed["raw"]: found = True if not found: time.sleep(0.1) assert found def send_and_receive(hostname, port, message, protocol=socket.SOCK_STREAM, tls=False): if tls: send_tcp_TLS(hostname, port, message) else: send_tcp(hostname, port, message, protocol) receive(message.rstrip()) def test_z_tls(): send_and_receive("test-z-tls", 20516, "Test TLS z message", tls=True)
由于其他端口的TLS/非TLS测试都正常,我觉得问题不在测试代码本身。有没有办法获取pytest运行时的排查数据,定位超时原因?
排查方案
以下是几种能获取更多排查数据的实用方法:
1. 调整pytest参数,捕获实时输出
- 使用
-s关闭pytest的输出捕获,让测试过程中的print/log直接输出到控制台:python3 -m pytest -vvv -s /tests - 搭配
-x让测试在第一个失败时立即停止,避免等待其他测试执行,同时用--tb=short简化错误栈输出:python3 -m pytest -vvv -s -x --tb=short /tests - 启用日志实时输出,直接打印测试过程中的DEBUG级日志:
python3 -m pytest -vvv -s --log-cli-level=DEBUG /tests
2. 在测试代码中添加关键步骤日志
在send_tcp_TLS和receive函数中插入调试日志,记录每个阶段的执行状态,精准定位卡住的环节:
import logging logging.basicConfig(level=logging.DEBUG) def send_tcp_TLS(hostname, port, message): logging.debug(f"启动TLS连接: {hostname}:{port}") context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) context.verify_mode = ssl.CERT_OPTIONAL context.check_hostname = False logging.debug("加载根证书文件") context.load_verify_locations(cafile="rootCA.crt") s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) logging.debug("创建基础Socket完成") ssl_sock = context.wrap_socket(s, server_hostname=hostname) logging.debug("开始TLS握手") ssl_sock.connect((hostname, port)) logging.debug("TLS连接建立成功,发送测试消息") ssl_sock.sendall(bytes(message, "utf-8")) ssl_sock.shutdown(socket.SHUT_WR) ssl_sock.close() logging.debug("TLS连接关闭") def receive(message): logging.debug(f"开始从Redis查询目标消息: {message}") r = redis.Redis(host="redis-input", port=6378) found = False timeout = time.time() + 20 while not found and time.time() < timeout: elapsed = time.time() - (timeout - 20) logging.debug(f"第 {elapsed:.1f} 秒,查询Redis队列") values = r.lrange("inq", 0, -1) logging.debug(f"Redis返回 {len(values)} 条消息") for value in values: decoded = value.decode("utf-8") parsed = json.loads(decoded) if message in parsed["raw"]: logging.debug("找到匹配消息") found = True break if not found: time.sleep(0.1) assert found, f"超时未找到目标消息: {message}"
3. 在GitLab CI中添加前置环境检查
在测试步骤前,先验证目标端口的连通性和TLS握手是否正常,排除环境层面的问题:
test: script: # 检查端口是否开放 - nc -zv test-z-tls 20516 # 手动发起TLS连接,查看握手详情 - openssl s_client -connect test-z-tls:20516 -CAfile rootCA.crt # 运行测试 - python3 -m pytest -vvv -s /tests
4. 给测试设置超时并捕获状态
使用pytest-timeout插件给单个测试设置更短的超时,同时强制输出超时前的日志:
- 先安装插件:
pip install pytest-timeout - 运行测试时指定全局超时:
python3 -m pytest -vvv -s --timeout=10 /tests - 或者在测试代码中单独标记:
import pytest @pytest.mark.timeout(10) def test_z_tls(): send_and_receive("test-z-tls", 20516, "Test TLS z message", tls=True)
5. 查看GitLab CI完整作业日志
在GitLab作业页面中,切换到"完整日志"视图,检查标准错误输出(stderr)——有时候pytest的错误信息会输出到这里,而不是标准输出(stdout)。
内容的提问来源于stack exchange,提问作者elmerjfudd
相关产品推荐
相关产品推荐

