最近在公司搭建一个基于 Docker 的 PHP 环境。
因为搭建 Docker PHP 环境的需要,已经把自己日常的业务开发迁移到了 Docker 上。如同往常一样 git pull
拉下最新代码,然后用浏览器打开正在开发中的项目,发现页面上一片空白了,打开 Chrome 的控制台,出现 Javascript 相关的报错,并且在文件底部发现一连串的乱码:
�����������������
用 curl 拉下来然后用 vim 打开如下:
想当然的认为这可能是前端的锅。于是和前端的同学,一起打开文件进行对比,但并没有发现非常可疑的点。然后就觉得是 Tengine 产生的问题,因为切换到自己的真实的宿主机用 Nginx 1.8.0 访问,没有这个问题。切换到 Docker 的环境就有这个问题,而两者的配置又几乎是一样的。
为了验证是否是 Tengine 产生的问题,在 Docker 容器中依次安装了 Tengine 2.1.2 (最新版), Nginx 1.9.10 (最新版), Nginx 1.8.0 (和宿主机相同),均采用同一配置文件进行启动,然而无一幸免的都还是出现了这个问题。
用排除法的话就可以认为问题可能在网络通信或者是容器本体上了,尝试先排除网络的影响。绕过网络的映射,直接在 Docker 的容器内部使用 curl 访问来检查,发现仍然存在这个问题,所以问题基本可以限定在容器上面。
为了验证到底是不是容器产生的问题,在运维同学的建议下,第二天在虚拟机中特意安装了一个 CentOS 6.7,以及安装上公司运维组预编译的 PHP 和 Tengine 的 RPM 包,然后采用完全一样的配置,再次用 curl 访问同样的文件,没有观察到这个现象。所以确实可以确定已经是容器产生的问题。
尽管这里几乎已经确定问题是哪里产生的,仍然陷入了死胡同,想要 Google 也不知道到底用什么关键词。这个时候再试着在 Tengine 的日志中输出的 HTTP Response 的消息体给记录下来,在 Tengine 的配置中加上了这些代码,用 Lua 来获取 HTTP 的 Response,并记录进日志:
log_format bodylog '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" $request_time '
'<"$request_body" >"$resp_body"';
lua_need_request_body on;
# 上面的配置加入 http {} 区域
access_log /tmp/access.log bodylog;
set $resp_body "";
body_filter_by_lua '
local resp_body = string.sub(ngx.arg[1], 1, 10000) #10000这里是sub函数的截取长度,可以按需要改大点
ngx.ctx.buffered = (ngx.ctx.buffered or "") .. resp_body
if ngx.arg[2] then
ngx.var.resp_body = ngx.ctx.buffered
end
';
# 上面的配置加入 server {} 区域
# 然后开启对应的 access_log 即可
(来自 https://gist.github.com/morhekil/1ff0e902ed4de2adcb7a)
不过遗憾的是,访问日志里面并没有出现乱码,而在 curl 的结果中,乱码又确实还是存在的。
事已至此,想到可能需要对 Tengine 或者 Nginx 调试一下可能才能发现到底问题是出在什么地方了。所以想到了曾经跟踪一个 PHP 的 Segmentation Fault 时用过的 strace,它可以打印出一个进程的所有的系统调用(System Call),从而观察程序大致上做了一些什么事情。于是马上安装上 strace,用带 -f
的参数来运行 Tengine。
strace -f tengine
(# -f
参数是 follow forks,因为 Tengine / NGINX 的实际接受用户请求的 Worker 是 fork 新建的进程)
然后照常用 curl 进行请求,获得输出:
...
[pid 22866] epoll_wait(11, {}, 512, 100) = 0
[pid 22866] epoll_wait(11, {}, 512, 100) = 0
[pid 22866] epoll_wait(11, {}, 512, 100) = 0
[pid 22866] epoll_wait(11, {}, 512, 100) = 0
[pid 22866] epoll_wait(11, <unfinished ...>
[pid 22865] <... epoll_wait resumed> {?} 0x7f0d78c9b000, 512, -1) = 1
[pid 22865] accept4(7, 0x7ffc7958c0b0, 0x7ffc7958c12c, SOCK_NONBLOCK) = 11
[pid 22865] epoll_ctl(9, EPOLL_CTL_ADD, 11, {...}) = 0
[pid 22865] epoll_wait(9, {?} 0x7f0d78c9b000, 512, 60000) = 1
[pid 22865] recvfrom(11, 0x7f0d78c3d800, 1024, 0, NULL, NULL) = 126
[pid 22865] stat(0x7f0d78c96f37, {...}) = 0
[pid 22865] open(0x7f0d78d6d3e0, O_RDONLY|O_NONBLOCK) = 12
[pid 22865] fstat(12, {...}) = 0
[pid 22865] pread(12, 0x7f0d78ef0000, 8857, 0) = 8857
[pid 22865] writev(11, [?] 0x7ffc7958b660, 1) = 286
[pid 22865] sendfile(11, 12, 0x7ffc7958b658, 8857) = 8857
[pid 22865] write(4, 0x7f0d78efc000, 9811) = 9811
[pid 22865] close(12) = 0
[pid 22865] setsockopt(11, SOL_TCP, TCP_NODELAY, 0x7ffc7958bfac, 4) = 0
[pid 22865] recvfrom(11, "", 1024, 0, NULL, NULL) = 0
[pid 22865] write(5, 0x7ffc7958b030, 87) = 87
[pid 22865] close(11) = 0
[pid 22865] epoll_wait(9, <unfinished ...>
[pid 22866] <... epoll_wait resumed> {}, 512, 100) = 0
[pid 22866] epoll_wait(11, {}, 512, 100) = 0
[pid 22866] epoll_wait(11, {}, 512, 100) = 0
[pid 22866] epoll_wait(11, {}, 512, 100) = 0
[pid 22866] epoll_wait(11, {}, 512, 100) = 0
...
(题外话: 这里还可以直观的看到 epoll 实质上是一个死循环)
简单解读一下,前面的 epoll,read,write 相关的代码应该都是从客户端获取请求,并且写入日志,然后注意到了 sendfile。先前了解过 sendfile,它提供了从一个文件描述符到另一个文件描述符的高效的复制数据的方式。传统的基于 read, write 的方式需要把数据在用户空间进行操作,而 sendfile 是直接在内核空间进行的操作,所以性能要好。这时候就想到的就是 Tengine / Nginx 其实是有一个配置也就是 Sendfile On;
来激活 sendfile 来提供静态文件的访问速度的,所以就想到可能问题就是在这里,遂尝试关闭。果然 curl 拿到的文件不再有末尾的乱码。
至此问题是解决了,但是我们还是要来探究一下到底是为什么 sendfile 在这种场合下就不工作了。有了关键词之后,问题就变得相对容易 Google 了。
首先找到了 Vargrant 也存在这个问题(因为默认也是基于 VirtualBox 的),并且在 Apache 和 NGINX 中都存在这个情况:
https://jeremyfelt.com/2013/01/08/clear-nginx-cache-in-vagrant/ https://github.com/mitchellh/vagrant/issues/351#issuecomment-1339640
然后就顺藤摸瓜的找到了 VirtualBox 的官方的 ticket:
https://www.virtualbox.org/ticket/12597
原来是 VirtualBox 的共享目录在使用 sendfile 来进行复制文件的时候,会错误的访问到缓存的内容导致的。看了下报告 bug 的时间已经是2年前,果然 VirtualBox 毕竟不是 Oracle 亲生的,这个问题并没有得到重视,暂时还是先只能通过关掉 sendfile 来解决这个问题。
欢迎关注我们的公众号
Original url: Access
Created at: 2019-09-26 16:55:51
Category: default
Tags: none
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
java windows火焰图_mob64ca12ec8020的技术博客_51CTO博客 - 在windows下不可行,不知道作者是怎样搞的 监听SpringBoot 服务启动成功事件并打印信息_监听springboot启动完毕-CSDN博客 SpringBoot中就绪探针和存活探针_management.endpoint.health.probes.enabled-CSDN博客 u2u转换板 - 嘉立创EDA开源硬件平台 Spring Boot 项目的轻量级 HTTP 客户端 retrofit 框架,快来试试它!_Java精选-CSDN博客 手把手教你打造一套最牛的知识笔记管理系统! - 知乎 - 想法有重合-理论可参考 安宇雨 闲鱼 机械键盘 客制化 开贴记录 文本 linux 使用find命令查找包含某字符串的文件_beijihukk的博客-CSDN博客_find 查找字符串 ---- mac 也适用 安宇雨 打字音 记录集合 B站 bilibili 自行搭建 开坑 真正的客制化 安宇雨 黑苹果开坑 查找工具包maven pom 引用地 工具网站 Dantelis 介绍的玩轴入坑攻略 --- 关于轴的一些说法 --- 非官方 ---- 心得而已 --- 长期开坑更新 [本人问题][新开坑位]关于自动化测试的工具与平台应用 机械键盘 开团 网站记录 -- 能做一个收集的程序就好了 不过现在没时间 -- 信息大多是在群里发的 - 你要让垃圾佬 都去一个地方看难度也是很大的 精神支柱 [超级前台]sprinbboot maven superdesk-app 记录 [信息有用] [环境准备] [基本完成] [sebp/elk] 给已创建的Docker容器增加新的端口映射 - qq_30599553的博客 - CSDN博客 [正在研究] Elasticsearch, Logstash, Kibana (ELK) Docker image documentation elasticsearch centos 安装记录 及 启动手记 正式服务器 39 elasticsearch 问题合集 不断更新 6.1.1 | 6.5.1 两个版本 博客程序 - 测试 - bug记录 等等问题 laravel的启动过程解析 - lpfuture - 博客园 OAuth2 Server PHP 用 Laravel 搭建带 OAuth2 验证的 RESTful 服务 | Laravel China 社区 - 高品质的 Laravel 和 PHP 开发者社区 利用Laravel 搭建oauth2 API接口 附 Unauthenticated 解决办法 - 煮茶的博客 - SegmentFault 思否 使用 OAuth2-Server-php 搭建 OAuth2 Server - 午时的海 - 博客园 基于PHP构建OAuth 2.0 服务端 认证平台 - Endv - 博客园 Laravel 的 Artisan 命令行工具 Laravel 的文件系统和云存储功能集成 浅谈Chromium中的设计模式--终--Observer模式 浅谈Chromium中的设计模式--二--pre/post和Delegate模式 浅谈Chromium中的设计模式--一--Chromium中模块分层和进程模型 DeepMind 4 Hacking Yourself README.md update 20211011
Laravel China 简书 知乎 博客园 CSDN博客 开源中国 Go Further Ryan是菜鸟 | LNMP技术栈笔记 云栖社区-阿里云 Netflix技术博客 Techie Delight Linkedin技术博客 Dropbox技术博客 Facebook技术博客 淘宝中间件团队 美团技术博客 360技术博客 古巷博客 - 一个专注于分享的不正常博客 软件测试知识传播 - 测试窝 有赞技术团队 阮一峰 语雀 静觅丨崔庆才的个人博客 软件测试从业者综合能力提升 - isTester IBM Java 开发 使用开放 Java 生态系统开发现代应用程序 pengdai 一个强大的博主 HTML5资源教程 | 分享HTML5开发资源和开发教程 蘑菇博客 - 专注于技术分享的博客平台 个人博客-leapMie 流星007 CSDN博客 - 舍其小伙伴 稀土掘金 Go 技术论坛 | Golang / Go 语言中国知识社区
最新评论