系统定义:
2012年macbook pro运行Sierra,Docker版本: 17.12.0-ce-mac49 (21995)
问题是:
我在运行我的python应用程序时遇到了一个问题,在我的容器中运行"coverage run“。我将尝试在下面解释我正在做的事情..希望这是有意义的。试着把我正在尝试做的事情和我已经在这里做的事情放进尽可能多的信息中。
基本上我的情况是这样的。我正在启动一个基于python (金字塔)的web服务。它只是一个基于HTTP的API。没什么特别花哨的。当我对它运行邮递员测试时,我想要得到它的覆盖率数字,所以我通过执行“覆盖率run /path/app.py”开始,这很好用。我创建了一个.coveragerc,将焦点放在我想要分析的源代码上,在A容器之外,一切都很好。试图在我的容器化版本的这个应用程序中进行覆盖是另一回事。
作为参考..这是我的masked文件,里面有用#########标记遮盖起来的秘密内容
FROM ################:latest
# Add files
ADD . / /path/
# Set up Env
ENV GRAPHITE_SERVER=localhost
ENV APP_USER=root
ENV APP_HOME=/path
ENV APP_LOGS=$APP_HOME/logs
ENV PYTHONPATH=#################:$PYTHONPATH
ARG requested_environment=stage1
ARG coverage
ARG log_monitor
ENV COVERAGE=$coverage
ENV LOG_MONITOR=$log_monitor
ENV ENVIRONMENT=$requested_environment
ENV PATH=$APP_HOME/######:$PATH
RUN mkdir -p $APP_LOGS
RUN pip install -r $APP_HOME/requirements.txt
RUN cd $APP_HOME/external-libs/mysql-connector/mysql-connector-python-2.1.2/; python setup.py install
RUN ln -fs /usr/bin/python /usr/local/bin/python
EXPOSE 1234
# start app
ENTRYPOINT $COVERAGE $APP_HOME/path/path/app.py $LOG_MONITOR我的构建命令是
docker build . --build-arg requested_environment=stage1 --build-arg log_monitor=noMonitor --build-arg coverage="coverage run" -t stage1-latest
我想在容器内做同样的事情,就像在容器外做一样。意思是..。我想在容器内运行应用程序,并打开覆盖范围。因此,您可能认为构建容器与正常构建容器一样简单,入口点通常是
ENTRYPOINT /path/path/path/app.py
changed to
ENTRYPOINT coverage run /path/path/path/app.py但显然不是。
如果我使用入口点或启动应用程序的CMD构建容器..就像这些..。是的,我都试过了..
ENTRYPOINT coverage run /path/path/path/app.py
ENTRYPOINT bash -c "coverage run /path/path/path/app.py"
ENTRYPOINT ["bash' "-c","coverage run /path/path/path/app.py"]
ENTRYPOINT ["coverage","run","/path/path/path/app.py"]
CMD coverage run /path/path/path/app.py
CMD ["bash' "-c","coverage run /path/path/path/app.py"]
or
CMD ["coverage","run","/path/path/path/app.py"]然后像这样启动容器
docker run --name stage1-latest-container-instance -itp 1234:1234 stage1-latest
甚至在dockerfile中的CMD条目的情况下也是如此(因为它们是默认的,并且我用这个命令覆盖了它们)
docker run --name stage1-latest-container-instance -itp 1234:1234 stage1-latest coverage run /path/path/path/app.py
我的应用程序启动完全正常..工作没问题..但
没有创建.coverage文件!在容器的文件系统中的什么地方都看不到.coverage文件。
好的..。尝试不同的方法
将dockerfile更改为
ENTRYPOINT bash
or
CMD bash重建它。
然后使用docker run --name stage1-latest-container-instance -itp 1234:1234 stage1-latest运行容器
这会在docker容器中给我一个提示。
[root@ac25fb69bb2e /]#
然后从另一个窗口执行此操作
docker exec -it stage1-latest-container-instance coverage run /path/path/path/app.py
或其他方法来完成此docker exec,如
docker exec -it stage1-latest-container-instance bash -c "coverage run /path/path/path/app.py"
还是这样!不创建任何.coverage文件。
好的..。采用一种不同的方法,我甚至创建了一个名为runapp.sh的文件,并在其中放入以下行
#!/bin/bash
cd /path/path/path && coverage run app.py 然后用以下命令启动容器
docker run --name stage1-latest-container-instance -itp 1234:1234 stage1-latest /path/runapp.sh
没有.coverage文件,然后我尝试使用以下命令运行容器
docker run --name stage1-latest-container-instance -itp 1234:1234 stage1-latest bash
然后从另一个窗口执行
docker exec -it stage1-latest-container-instance /path/runapp.sh
仍然没有.container文件
好的..。最后的办法..
进入我在交互模式(-it)的dockerfile中运行"docker run“之后得到的提示符。并手动启动我的应用程序,其中包含覆盖范围。
[root@ac25fb69bb2e /]# coverage run /path/path/path/app.py
令人惊讶的是..创建.coverage文件没有问题...
这是什么鬼东西!我搞不懂这件事..我不知道发生了什么.但是,从容器外部对容器执行的命令与在容器内的单个shell内手动执行的命令之间似乎完全脱节,因为它是否可以创建文件。
有人能告诉我我在这里做错了什么吗?
我能看到的下一步就是在我的应用程序内部使用coverage模块,并为KeyboardInterrupt添加一个异常处理程序,以便在应用程序终止时保存覆盖率数据。但这太糟糕了。
发布于 2018-11-17 07:39:51
对于像我一样从谷歌跌跌撞撞来到这里的人...
扩展Wolfium的答案,这是由于docker在coverage将数据写入其数据文件之前杀死了python进程(或者更准确地说,python无法优雅地解释docker的默认SIGTERM信号,并且在几秒钟后获得SIGKILL时陷入一堆)。解决这个问题的最快方法是:
exec,例如,将你的图像的停止信号添加到SIGINT,这是大多数python处理程序实现的:将STOPSIGNAL SIGINT这一行添加到你的Dockerfile中。稍微好一点的解决方案是确保您的python处理程序可以适当地响应SIGTERM。
发布于 2018-02-10 00:34:52
这并不完全是我最初想要的,但这是我最终不得不做的,以使这一切都正常工作。发布这篇文章是为了回答我自己的问题,以防其他人发现自己陷入了同样的困境,并想知道我是如何解决这个问题的。
我最终不得不在我的app.py脚本中使用coverage api。我一直想不通为什么在运行容器时使用"coverage run“作为入口点或cmd时不起作用。我还是不知道为什么。如果有人知道为什么会分享,我会很高兴的。
在我的app.py中,我做了以下事情
当它最初启动时,我检查是否设置了一个运行时标志,我称之为"withcoverage“。
if withcoverage:
print "starting code coverage engine"
import coverage
os.system("mkdir %s/tmpcoveragedata" % os.environ['HEYTHERE_HOME'])
coveragedatafile = ".coverage-"+str(int(time.time()))
cov = coverage.Coverage(data_file=os.environ['APP_HOME']+"/tmpcoveragedata/"+coveragedatafile,config_file=os.environ['APP_HOME']+"/.coveragerc")
cov.start()然后,我继续照常启动服务器。但我必须添加一个try:块来捕获KeyboardInterrupt (SIGINT),并在发生这种情况时在退出之前保存覆盖范围。如下所示:
try:
# start the server etc.. etc..
.
.
.
except KeyboardInterrupt as e:
if withcoverage:
print "saving coverage stats"
save_coverage(cov)
os.system("bash -c 'cd %s/tmpcoveragedata && rm -f .coverage ; cd %s/tmpcoveragedata && ln -s %s .coverage" % (os.environ['APP_HOME'],os.environ['APP_HOME'],coveragedatafile))
os.system("bash -c 'cd %s/tmpcoveragedata/ && coverage report > %s/tmpcoveragedata/coverage-report-%s.txt'" % (os.environ['APP_HOME'],os.environ['APP_HOME'],coveragedatafile))
print "exiting program"
exit()现在,当我构建容器时,我将主源存储库中的卷挂载到容器内的tmpcoveragedata目录中。然后,当容器接收到SIGINT并退出时生成的覆盖率文件和报告被保存到该临时文件夹中,该临时文件夹被映射到运行容器的机器上存储的适当的覆盖率数据。
下面是我的docker run命令
docker run --name stage1-latest-container-instance -d -v /app_home/coveragedata:/app_home/tmpcoveragedata -p 1234:1234 stage1-latest这是现在工作的..它将运行具有覆盖率的应用程序..使用SIGINT在退出时保存覆盖范围。
不过,有一点需要注意。当简单地停止容器时,
docker stop <container-name> 还不够。我需要执行以下操作,以便当我的自动化停止容器时,应用程序可以看到KeyboardInterrupt。
docker kill --signal=SIGINT stage1-latest-container-instance希望这篇文章能帮助其他处理这个问题的人。
发布于 2018-04-27 03:34:10
很好的信息和细节,解释和展示解决方法。它在正确的方向上帮助了我很多。
根本原因是当在docker中运行时,您的补丁覆盖的服务正在运行,并且在docker中作为入口点运行的覆盖率直到运行完毕才会保存。
因此,如果你能够像你做的那样打破它,对我也是如此。只是我正在使用Flask,我发现它使用的dev服务器捕获键中断异常,并且我没有办法关闭覆盖范围内的服务服务器。最后,我找到了一种方法,可以从内部向flask dev服务器发出信号,正如您首先向我展示的那样,它的工作方式非常出色。
非常感谢你的分享。
https://stackoverflow.com/questions/48692363
复制相似问题