Rootop 服务器运维与web架构

2020-03-08
发表者 Venus
SQLSTATE[HY000] [2002] No such file or directory已关闭评论

SQLSTATE[HY000] [2002] No such file or directory

在访问项目的时候报错:

SQLSTATE[HY000] [2002] No such file or directory

首先这是用pdo连接的mysql数据库,框架是thinkphp5,其实知道问题在哪里,但是要知道原理是什么。

讲一下知识点:
连接mysql时候php连接参数一般两种,localhost和127.0.0.1或者其他ip地址或域名。
区别为:
localhost 通过socket通讯也就是mysql.sock,在php.ini中默认配置如下。

127.0.0.1 是通过tcp/ip通信。也就是通过ip地址连接。

如果用localhost,需要修改php.ini指向正确的mysq.sock路径.
比如:

pdo_mysql.default_socket=/tmp/mysql.sock # 这个路径是由数据库的my.cnf中配置的

如果你用的是虚拟主机,接触不到php的配置文件,那么只需要把localhost改为127.0.0.1,或者改为平台商提供给你的数据库连接地址,
一般为域名,比如 rootop-db.hk.mysql.rootop.org 这种域名式的地址

来看一下为什么会出这个错误。
通过phpinfo可以看到当前php中的mysql.sock路径,如图。

我现在把mysql的sock文件修改为如下:

[client]
port            = 3306
socket          = /var/tmp/mysql.sock

[mysqld]
port            = 3306
socket          = /var/tmp/mysql.sock

重启mysql

[root@MiWiFi-R3P-srv ~]# ll /var/tmp/mysql.sock
srwxrwxrwx 1 mysql mysql 0 3月   8 20:11 /var/tmp/mysql.sock

新的路径已经生效,但是php里默认还是在/tmp下,这时,我们用localhost去连接数据库。
tp的连接数据库代码如下图。

页面报错如下:

因为找不到sock文件,所以页面报错。修改为127.0.0.1,就可以正常获取数据库中的数据了,因为他不走socket连接了。

那么就想用localhost怎么办呢?
1、修改php.ini,改为正确的mysql.sock路径(方法上面已经提过了)。
2、修改连接参数地址改为 localhost:3306
如图

发现也可以获取数据,为什么改为localhost:3306也可以?因为在后面加上:3306后,就不代表用localhost(socket)的方式去连接了。
实际在连接数据库的时候,会把localhost解析为127.0.0.1,也就是通过ip方式访问了。
等于
localhost = socket方式连接
localhost:3306 = ip方式连接
127.0.0.1 = ip方式连接

看看原生php连接数据库

报错如下:

改为localhost:3306后可以获取。
这里有个有意思的地方就是,不管原生写法还是tp框架,当host为localhost时,port参数就没用了,因为走socket,当为localhost:3306时,port参数也没有用,只有当地址为127.0.0.1或其它ip时(不带端口号),后面的port参数才有效。

参考:
php pdo文档:https://www.php.net/manual/en/ref.pdo-mysql.connection.php

2020-02-23
发表者 Venus
nginx -t 语法检查通过单测试配置文件失败已关闭评论

nginx -t 语法检查通过单测试配置文件失败

群里求助nginx无法启动,提示下面错误:

nginx: the configuration file /www/server/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /www/server/nginx/conf/nginx.conf test failed

按照以前来经验来看,会紧接着提示错误具体位置,但是这里没提示。
检查 nginx.conf 中配置

error_log  /www/wwwlogs/nginx_error.log crit;

把错误级别从 crit 改为 debug

error_log  /www/wwwlogs/nginx_error.log debug;

再执行 nginx -t后错误位置出现了,只要有错误日志了,就好排查问题了。

分析可能是nginx include的虚拟主机配置文件,有个lua脚本的配置,但是代码里缺少了一个resty.redis模块。
导致nginx -t检测失败,当前的错误级别也没打印出lua的错误信息。

2020-01-07
发表者 Venus
排查redis占用内存达90%以上已关闭评论

排查redis占用内存达90%以上

帮别人排查一个问题,项目还没上线但redis占用内存很高。思路如下:

1、登陆redis控制台,首先用 keys * 获取所有的key

> keys *
x:x:a
x:x:b
x:x:c

发现key也就十来个,用 TYPE x:x:a 发现a是一个list数据类型

用lrange命令查看list中指定索引的值
用法: lrange key start end #获取列表中从 start 到 end 的值。
start 从0开始计,start、end也可为负数,倒数第一元素的位置为-1,倒数第二为-2,以此类推
lrange key 0 -1 # 第一个元素到倒数第一个元素(全部元素)

> lrange 0 1

发现值为json格式的数据

# 统计list长度,用 LLEN 命令可以返回列表的长度

> LLEN x:x:a

统计出 x:x:a 这个key有十几万多个索引

写了个脚本统计了下所有的key长度约370万,这样推测是数据量太大导致占用内存非常高。
经询问项目里有个模块用redis,个人推测要么用来做消息队列但没被消费,要么是做缓存,但是旧数据没有清理导致堆积越来越大。

PS:主要介绍了几个redis操作命令。

2019-12-28
发表者 Venus
关于iptables放行端口无效已关闭评论

关于iptables放行端口无效

之前在iptables配置时都是直接去修改配置文件,然后重启,这次在命令行添加放行端口发现端口还不通。

# 查看当前iptables netfilter表的INPUT链规则

[root@HKSRV2426 ~]# iptables -L -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:22 state NEW 
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:443 
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:5555 state NEW 
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED 
ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:22 
REJECT     all  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-host-prohibited 

# 比如放行21端口

[root@HKSRV2426 ~]# iptables -A INPUT  -p tcp --dport 21 -j ACCEPT

这么添加还是无法访问21端口。

# 查看规则其实是在第8条规则下面新增的。

[root@HKSRV2426 ~]# iptables -L -n --line-number
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:22 state NEW 
2    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:443 
3    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:5555 state NEW 
4    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED 
5    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           
6    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
7    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:22 
8    REJECT     all  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-host-prohibited 
9    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:21 

第8条规则已经REJECT拒绝包了,下面的规则就不需要再继续匹配了。
所以防火墙规则一般是允许的放在拒绝上面,最后为默认规则才是拒绝或者丢弃。

# 可以通过插入规则解决,比如在规则3上面插入一条

[root@HKSRV2426 ~]# iptables -I INPUT 3 -p tcp --dport 3306 -j ACCEPT

分析原因:
# 查看防火墙配置文件

[root@HKSRV2426 ~]# vi /etc/sysconfig/iptables
略·
-A INPUT -j REJECT --reject-with icmp-host-prohibited

上面这条规则就是上面查询到的第8条规则。

REJECT和DROP基本一样,区别在于它除了阻塞包之外, 还向发送者返回错误信息。

--reject-with # 设置返回错误信息种类 
icmp-host-prohibited # 种类

全部的种类可以通过下面命令查到。

[root@HKSRV2426 ~]# iptables -j REJECT --help
Valid reject types:
icmp-net-unreachable     	ICMP network unreachable
net-unreach              	alias
icmp-host-unreachable    	ICMP host unreachable
host-unreach             	alias
icmp-proto-unreachable   	ICMP protocol unreachable
proto-unreach            	alias
icmp-port-unreachable    	ICMP port unreachable (default)
port-unreach             	alias
icmp-net-prohibited      	ICMP network prohibited
net-prohib               	alias
icmp-host-prohibited     	ICMP host prohibited
host-prohib              	alias
tcp-reset                	TCP RST packet
tcp-rst                  	alias
icmp-admin-prohibited    	ICMP administratively prohibited (*)
admin-prohib             	alias

2019-12-27
发表者 Venus
phpstudy服务器面板已关闭评论

phpstudy服务器面板

phpstudy最早做本地php开发环境,现在出了服务器版。
phpstudy面板官方说是基于docker容器实现的,会创建一个容器用于运行面板服务。
此面板适用于不熟悉服务器操作的初学者用。

根据官方文档安装好后开始查看运行环境。

[root@VM_0_4_centos ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                   PORTS                  NAMES
8d3fcc05a239        centos_ssl:v1       "/bin/bash"         4 hours ago          Exited (0) 3 hours ago                          centos_ssl
23403cc60eb8        centos:v4           "/bin/bash"         4 hours ago          Up 4 hours                                      centos_env

发现有一个容器名为 centos_env 的在运行。

# 然后有个疑问

[root@VM_0_4_centos ~]# netstat -tnlp # 宿主机上执行

看到不少监听的进程而且有进程名,而不像以前用的docker都是docker-proxy监听的端口。
进入容器name为centos_env的容器,netstat也发现有同样的进程。这就表明了容器内的服务直接寄宿在宿主机上
也就意味着容器是跑在host网络模式下。这样容器内监听了什么地址,宿主机上直接就能看到,不需要桥接网络方式的映射端口。

docker网络有4中模式,分别为:

none模式,使用--net=none指定,该模式关闭了容器的网络功能。
host模式,使用--net=host指定,容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
bridge模式,使用--net=bridge指定,默认设置 ,此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及Iptables nat表配置与宿主机通信。
container模式,使用--net=container:NAME_or_ID指定,创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围。

# 查phpstudy的安装脚本发现,运行了2个容器,都指定了host网络模式

docker create -it --name centos_env --network=host -v /usr/local/phpstudy:/usr/local/phpstudy -v /www:/www  centos:v4
docker create -it --name centos_ssl --network=host -v /usr/local/phpstudy:/usr/local/phpstudy -v /www:/www  centos_ssl:v1

# 运行一个自己的容器

[root@VM_0_4_centos ~]# docker run -dit --name test --net=bridge -p 8888:80 centos:v4
204d5854d4bd91167f33c54ef732adb58d11a1eea743a4a0f39374547f4026e3
docker: Error response from daemon: driver failed programming external connectivity on endpoint test (800abef340ef085a242ca05f98e2e6d8cbf400ea634dfd9ff05a96fd2f0a09a6):  (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 8888 -j DNAT --to-destination 172.18.0.2:80 ! -i docker0: iptables: No chain/target/match by that name.

提示找不到规则链

这个问题好一顿排查,之前都是直接yum安装docker,没遇到过此问题。

# 手动查nat表是没有DOCKER这条链的,目前还不知道链是由什么服务去生成的。

[root@VM_0_4_centos ~]# iptables -t nat -L -n | grep DOCKER
[root@VM_0_4_centos ~]# #没有返回信息

# 通过ps查看有两个关于docker服务的进程

[root@VM_0_4_centos ~]# ps aux | grep docker
root      6996  0.2  1.9 348624 35976 ?        Sl   14:14   0:00 dockerd
root      7000  0.1  0.9 375516 17532 ?        Ssl  14:14   0:00 containerd --config /var/run/docker/containerd/containerd.toml --log-level info

dockerd # docker守护进程 docker daemon
containerd # 容器虚拟化技术,从docker中剥离出来,形成开放容器接口(OCI)标准的一部分。

可以参考:https://www.cnblogs.com/embedded-linux/p/10850491.html

[root@VM_0_4_centos ~]# containerd --help | grep iptables
[root@VM_0_4_centos ~]# 
[root@VM_0_4_centos ~]# dockerd --help | grep iptables
      --iptables                                Enable addition of iptables rules (default true)

对这2个命令查帮助文档,只在dockerd中发现有关于iptables的信息。

现在试试对dockerd加上参数启动

[root@VM_0_4_centos ~]# ps aux | grep docker
root     14107  0.6  1.8 419964 34060 pts/0    Sl   14:21   0:07 dockerd
root     14112  0.0  0.9 376576 18764 ?        Ssl  14:21   0:00 containerd --config /var/run/docker/containerd/containerd.toml --log-level info
root     14577  0.0  0.2  11780  4484 ?        Sl   14:22   0:00 containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/287264f03a04ce0f30af6f0b8f73923ca563eba5862a09f6df179f978ac5dd25 -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/local/phpstudy/docker-18.09.6/containerd -runtime-root /var/run/docker/runtime-runc
root     18451  0.0  0.0 112708   976 pts/0    R+   14:40   0:00 grep --color=auto docker
[root@VM_0_4_centos ~]# kill -9 14107
[root@VM_0_4_centos ~]# docker ps
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
[root@VM_0_4_centos ~]# dockerd --iptables & 

# 再去查看是否有DOCKER这条链

[root@VM_0_4_centos ~]# iptables -t nat -L -n | grep DOCKER
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL
Chain DOCKER (2 references)

发现有了DOCKER链

# 再次创建容器并映射端口测试

[root@VM_0_4_centos ~]# docker run -dit --name test --net=bridge -p 8888:80 centos:v4
ef23d213b1685d4e4e1fb1b4a6d6b39d3ca3d2455b33d0de175e734ccb7e3d72
INFO[2019-12-20T14:54:54.889562578+08:00] shim containerd-shim started                  address="/containerd-shim/moby/ef23d213b1685d4e4e1fb1b4a6d6b39d3ca3d2455b33d0de175e734ccb7e3d72/shim.sock" debug=false pid=20626
[root@VM_0_4_centos ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
ef23d213b168        centos:v4           "/bin/bash"         4 seconds ago       Up 3 seconds        0.0.0.0:8888->80/tcp   test
287264f03a04        centos:v4           "/bin/bash"         32 minutes ago      Up 32 minutes                              centos_env

# PS

dockerd --iptables & # 只有这么启动守护进程,重启机器才能创建对应链,不带的话,重启机器还是没有DOCKER链。

所以要实现使用docker,需要修改phpstudy服务启动脚本

/usr/local/phpstudy/system/phpstudyctl
找到dockerd改为dockerd --iptables

# 关于容器里的 WorkerMan 怎么启动的(web面板服务)
通过docker inspect centos_env 也没找到CMD或者ENTRYPOINT设置启动服务。就很无奈不知道怎么启动的
在物理机的rc.local中发现启动脚本

[root@VM_0_6_centos ~]# cat /etc/rc.local 
部分略·
/usr/local/phpstudy/system/phpstudyctl -start

然后去查 phpstudyctl 这个脚本发现 start 参数对应执行的函数为 Start_Phpstudy
在此函数里才找到启动WorkerMan的方法

#start webpanel
nCount=20
pid=`/usr/local/phpstudy/system/module/getPidByExe /usr/local/phpstudy/web/php-7.3.8/bin/php`
while [[ $pid == "0" ]] && [[ $nCount>0 ]]
do
	docker exec centos_env $SHELL /usr/local/phpstudy/web/start > /dev/null 2>&1 &
	((nCount-=1))
	sleep 1
	pid=`/usr/local/phpstudy/system/module/getPidByExe /usr/local/phpstudy/web/php-7.3.8/bin/php`
done

才发现是通过执行docker exec 执行容器内命令实现的启动。

# 关于服务器防火墙配置
面板是运行在docker中,那么是怎么实现配置物理机的防火墙规则?
在 /usr/local/phpstudy/web/service/app/ 下找到 security.php 文件,部分代码如下:

<?php // 防火墙管理 require_once __DIR__.'/../app/base.php'; $user = Auth::doauth(); $type = get('type'); $req_data = json_encode(array('command'=>$type,'data'=>'','uid'=>$user['uid']));

// 防火墙状态
if($type == 'security_firwall_open' || $type== 'security_firwall_close'){
        $res = Socket::request($req_data);
        $res = json_decode($res,true);
        if($res['result']==0){
                xpexit(json_encode(array('code'=>1,'msg'=>$res['msg'])));
        }
        xpexit(json_encode(array('code'=>0,'msg'=>'操作成功')));
}

发现是调用了 Socket 类的 request 方法
最后找到是在 /usr/local/phpstudy/web/service/app/lib/socket.php 中通过socket实现。

<?php
/**
 * 网络通信模块
 */
class Socket{

        public static function request($data){
                error_reporting(E_ALL);
                set_time_limit(0);
                $host = "127.0.0.1";
                $port = 8090;

发现是连接的127.0.0.1的8090端口。也就是宿主机本身。

查看 /usr/local/phpstudy/system/phpstudyctl 脚本中的 Start_Phpstudy 函数的部分代码

#start phpstudy
pid=`/usr/local/phpstudy/system/module/getPidByExe /usr/local/phpstudy/system/phpstudy`
if [[ $pid == "0" ]];then
		/usr/local/phpstudy/system/phpstudy -d > /dev/null 2>&1 &
fi

phpstudy 这个服务就监听在8090端口。可以推测是这个服务去配置了防火墙规则。

因为容器启动时用的是host网络模式,所以可以用127.0.0.1直接访问。
这也就解释了面板虽然在容器中,但是可以操纵宿主机防火墙规则的原因。

# 关于容器的几个服务及命令
containerd
dockerd
containerd-shim
docker-proxy # 只要映射了端口,就会启动proxy
ctr
runc

root@rootop:~# /usr/bin/dockerd -h | grep config
      --config-file string                      Daemon configuration file (default "/etc/docker/daemon.json")