php-fpm reload 时 nginx 出现 error log 分析

起因

每次 Jenkins 发版的时候 nginx 总会有几个 error log 出现,量也不是很大,通过时间点查看 access log,发现 http status 是 502。

2019/08/08 10:07:35 [error] 11346#0: *395555896 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 100.79.15.33, server: xxxxxx, request: "GET /api/v2/current-ad?language=zh_CN HTTP/1.1", upstream: "fastcgi://unix:/tmp/php-cgi-56.sock:", host: "xxxxxx"
2019/08/08 10:07:35 [error] 11347#0: *395557146 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 100.79.15.23, server: xxxxxx, request: "POST /hw/v2/scene/bundle/lists HTTP/1.1", upstream: "fastcgi://unix:/tmp/php-cgi-56.sock:", host: "xxxxxx"

解决方案

通过查找资料找到了需要配置的参数:

process_control_timeout = 10

参数含义是 设置子进程接受主进程复用信号的超时时间,控制子进程处理来自master的信号的时间,默认为 0 如果正在处理请求, 很可能会收到错误报警。

现在我们需要在 php-fpm.conf [global] 配置块里面配置,我这里配置为 10s,没有配置了 [global] 里面会导致 php-fpm 启动失败,切记!
配置完成之后需要 reload 加载最新配置,第一次 reload 还是会有 error 的,因为我们的配置还没有生效,之后的 reload 就可以解决此问题,这就可以放心的发版了!

记一次 Laravel MethodNotAllowedHttpException 问题排查

问题描述

线上一个 laravel 4.2 的项目有 Exception 产生,通过查看 log 发现每天会有 2 条左右出现,量也不大,通过日志能看出是 MethodNotAllowedHttpException 异常,下面记录一下解决过程。

[2019-05-17 06:04:38] Oregon.ERROR: Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException in /******/endpoint/bootstrap/compiled.php:5753
Stack trace:
#0 /******/endpoint/bootstrap/compiled.php(5749): Illuminate\Routing\RouteCollection->methodNotAllowed(Array)
#1 /******/endpoint/bootstrap/compiled.php(5727): Illuminate\Routing\RouteCollection->getOtherMethodsRoute(Object(Illuminate\Http\Request), Array)
#2 /******/endpoint/bootstrap/compiled.php(5051): Illuminate\Routing\RouteCollection->match(Object(Illuminate\Http\Request))
#3 /******/endpoint/bootstrap/compiled.php(5039): Illuminate\Routing\Router->findRoute(Object(Illuminate\Http\Request))
#4 /******/endpoint/bootstrap/compiled.php(5031): Illuminate\Routing\Router->dispatchToRoute(Object(Illuminate\Http\Request))
#5 /******/endpoint/bootstrap/compiled.php(720): Illuminate\Routing\Router->dispatch(Object(Illuminate\Http\Request))
#6 /******/endpoint/bootstrap/compiled.php(696): Illuminate\Foundation\Application->dispatch(Object(Illuminate\Http\Request))
#7 /******/endpoint/bootstrap/compiled.php(7803): Illuminate\Foundation\Application->handle(Object(Illuminate\Http\Request), 1, true)
#8 /******/endpoint/bootstrap/compiled.php(8410): Illuminate\Session\Middleware->handle(Object(Illuminate\Http\Request), 1, true)
#9 /******/endpoint/bootstrap/compiled.php(8357): Illuminate\Cookie\Queue->handle(Object(Illuminate\Http\Request), 1, true)
#10 /******/endpoint/bootstrap/compiled.php(11154): Illuminate\Cookie\Guard->handle(Object(Illuminate\Http\Request), 1, true)
#11 /******/endpoint/bootstrap/compiled.php(657): Stack\StackedHttpKernel->handle(Object(Illuminate\Http\Request))
#12 /******/endpoint/public/index.php(48): Illuminate\Foundation\Application->run()
#13 {main} [] []

排查过程

  通过查找 nginx access log 找到对应的信息,是 post 请求 index.php,我用 postMan 来进行模拟并没有复现这个 exception,我拿到的只是一个 200 的 Response,并不是 500,这应该是扫描器批量扫描的,或者是 spider 爬的,并没有走域名,而是从 ip 直接访问。

**.93.2.** **.71.95.** 182.61.178.228 - "17/May/2019:06:04:39 +0800" "POST /index.php HTTP/1.1" 453 4534 0.680 500 unix:/tmp/php-cgi-72.sock - 0.004 "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:28.0) Gecko/20100101 Firefox/28.0" 

查找 nginx error log 没有对应的 log,问题到这里就僵住了,返回去看 exception log 找到抛出异常之前最后一个执行的方法

Illuminate\Routing\RouteCollection->getOtherMethodsRoute

/* 代码路径 */
/vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php  :186
    /**
     * Get a route (if necessary) that responds when other available methods are present.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  array  $others
     * @return \Illuminate\Routing\Route
     *
     * @throws \Symfony\Component\Routing\Exception\MethodNotAllowedHttpException
     */
    protected function getOtherMethodsRoute($request, array $others)
    {
        if ($request->method() == 'OPTIONS')
        {
            return (new Route('OPTIONS', $request->path(), function() use ($others)
            {
                return new Response('', 200, array('Allow' => implode(',', $others)));

            }))->bind($request);
        }

        $this->methodNotAllowed($others);
    }

  看到这就大体明白了,如果 method 是 OPTIONS 会有 200 的正常 Response,否则就抛出错误,猜测 Http method 肯定是一个比较冷门的,导致框架报错,从 postMan 找一个比较冷门的 method UNLOCK 试了一下,果然状态 500,成功复现。

  这里有一段小插曲:为什么状态是 500 呢,应该是 405 才对,查看代码发现处理 error 的逻辑里面调用了一个已经优化掉的日志类,去掉那一行调用,一切正常。

- 阅读全文 -

filebeat 安装 + 配置

说明

公司业务日志系统是 ELK 的技术栈,每个机器都需要有 agent 来负责收集,filebeat 是比较不错的选择。

安装步骤

Ubuntu

curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.5.4-amd64.deb
sudo dpkg -i filebeat-6.5.4-amd64.deb

CentOS

curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.5.4-x86_64.rpm
sudo rpm -vi filebeat-6.5.4-x86_64.rpm

官方文档:
https://www.elastic.co/guide/en/beats/filebeat/index.html
https://www.elastic.co/guide/en/beats/filebeat/6.5/filebeat-installation.html

配置文件路径

/etc/filebeat/filebeat.yml

配置文件内容

filebeat.inputs:
# Nginx access log #
- type: log
  paths:
    - /www/wwwlogs/cloud-bj.yeelight.com.log
  fields:
    region: Beijing
    type: nginx-log

# Nginx error log #
- type: log
  paths:
    - /www/wwwlogs/cloud-bj.yeelight.com.error.log
  fields:
    region: Beijing
    type: cloud-error-log

# PHP error log #
- type: log
  paths:
    - /www/wwwlogs/php_errors.log
  fields:
    region: Beijing
    type: php-errors-log

# Laravel Framework log #
- type: log
  paths:
    - /www/wwwroot/cloud-manage/app/storage/logs/laravel-*.log
  fields:
    region: Beijing
    type: cloud-manage-laravel

  multiline.pattern: '^\['
  multiline.negate: true
  multiline.match: after

# Single log DAU #
- type: log
  paths:
    - /www/wwwroot/cloud-manage/app/storage/logs/single-log-*.log
  fields:
    region: Beijing
    type: single-log

output.logstash:
  hosts: ["192.168.0.63:5044"]

log 文件路径

/var/log/filebeat

filebeat 开机启动

vim /etc/rc.local
#!/bin/bash
/etc/init.d/filebeat start
chmod +x /etc/rc.local

Kafka 配置外网访问

说明


Kafka 有的时候会跨机房接收消息,本地开发机也需要向远程服务器发一些消息,默认是不支持外连的,所以需要配置一下远程访问。

Kafka 服务器 host 修改

查看 hostname,查看内网 IP 并记录

cat /etc/hostname
# vm172-31-110-6.ksc.com #

ifconfig

编辑 host 文件

vim /etc/hosts
# 添加在文件底部 #
172.31.110.6 vm172-31-110-6.ksc.com

Kafka 配置文件修改

修改 server.properties 配置文件,具体目录视情况而定。

vim /opt/kafka_2.12-2.2.0/config/server.properties

# 修改 listeners #
listeners=PLAINTEXT://vm172-31-110-6.ksc.com:9092

需要留意一下 advertised.listeners 要注释掉,listeners 才会有效。

修改 zookeeper.connect 配置,如果 Zookeeper 和 Kafka 不在同一台机器,请配置 Zookeeper 地址。

zookeeper.connect=vm172-31-110-6.ksc.com:2181

客户端 host 配置

在客户端机器修改 host 文件映射到远程外网 IP,内网的 consumer 机器请配置内网 IP 映射。

xxx.xx.xx.xx  vm172-31-110-6.ksc.com

Apache Kafka 安装步骤

概览

安装过程总共分为 3 大块,第一 Java 环境不必多说,第二 Zookeeper 安装,第三 Kafka 安装。

概念了解

Kafka 有几个重要的概念需要先了解一下

名词 解释
broker 可以理解为 Kafka 所在的服务器
ZooKeeper 分布式服务框架在 Kafka 中的作用主要负责保存 topic,partition 元数据,和对 broker 的监控及治理,以及 partition 的 leader 选举(partition 可以有多个副本,但是只有一个处于工作状态,副本只是负责同步数据,当 leader partition 死掉了,会把一个副本的 partition 升级为 leader )
topic 主题,可以理解为消息的分类
partition 分区,从大的概念来说 topic 中的消息都是存放在 patition 中,一个 topic 可以有多个 partition, 一个 partition 可以有多个副本
offset 偏移量,在 Kafka 中 offset 是 partition 中消息序列号,可以认为是这个消息的唯一标识
segment 多个大小相等的 segment file (段) 组成了一个 partition

Java 环境安装

https://yusure.cn/backend/320.html

ZooKeeper框架安装

下载ZooKeeper

官网:http://zookeeper.apache.org/releases.html
请选择新版本,替换下载链接,目前最新版本 zookeeper-3.4.14

cd /opt
wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz
tar zvxf zookeeper-3.4.14.tar.gz

创建配置文件

cd zookeeper-3.4.14
# 创建数据目录 #
mkdir data

cd conf
cp zoo_sample.cfg zoo.cfg

修改 dataDir

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial 
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between 
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=/opt/zookeeper-3.4.14/data
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the 
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1

切到 ZK 安装目录,接下来就可以启动 zookeeper 服务器了。

启动 ZooKeeper 服务器

cd /opt/zookeeper-3.4.14 && bin/zkServer.sh start

停止 Zookeeper 服务器

cd /opt/zookeeper-3.4.14 && bin/zkServer.sh stop

重启 Zookeeper 服务器

cd /opt/zookeeper-3.4.14 && bin/zkServer.sh restart

启动响应:

[root@vm172-31-110-6 zookeeper-3.4.14]# bin/zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper-3.4.14/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED

启动 CLI 连接 ZK,测试连通性,会看到 Welcome to ZooKeeper! 的回显。

cd /opt/zookeeper-3.4.14 && bin/zkCli.sh

Apache Kafka安装

下载 Kafka

官方下载地址:http://archive.apache.org/dist/kafka/

cd /opt
wget http://archive.apache.org/dist/kafka/2.2.0/kafka_2.12-2.2.0.tgz
tar zvxf kafka_2.12-2.2.0.tgz
cd /opt/kafka_2.12-2.2.0

配置文件

config/server.properties

启动 Kafka

bin/kafka-server-start.sh config/server.properties
# 生产环境,后台运行 #
cd /opt/kafka_2.12-2.2.0 && nohup bin/kafka-server-start.sh config/server.properties &

停止 Kafka

cd /opt/kafka_2.12-2.2.0 && bin/kafka-server-stop.sh config/server.properties

基本操作

创建一个名为 Hello-Kafka 的主题,其中包含一个分区和一个副本因子。
创建 topic 后,您可以在终端窗口中获取通知,可以在 /tmp/kafka-logs 中看到创建 topic 的日志。

bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic Hello-Kafka

要获取 Kafka 服务器中的 topic 列表

bin/kafka-topics.sh --list --zookeeper localhost:2181

启动生产者以发送消息

bin/kafka-console-producer.sh --broker-list localhost:9092 --topic Hello-Kafka

启动消费者以接收消息

bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic Hello-Kafka --from-beginning

删除主题

bin/kafka-topics.sh --zookeeper localhost:2181 --delete --topic Hello-kafka

界面管理工具

Kafka Manager https://yusure.cn/backend/310.html

总结

Apache Kafka 教程 https://www.w3cschool.cn/apache_kafka/
以上是 Kafka 安装和一些使用的简单操作,生产环境 Kafka Cluster 的配置和运维比这复杂一些,需要修改不同的配置文件。