【生产问题】时隔大半年,分享一次Nginx反向代理的需求

博客前面分享了一篇《分享一个 Nginx 正向代理的另类应用案例》,时隔不久,身为救火队员、万金油的博主又再一次接到了一个奇葩需求:

场景和上次有些类似,也是部门引进的第三方应用,部署在各个网络区域,从 OA 办公区域无法直接访问。目前,运营人员都需要登陆 Windows 跳板机,才能打开这些应用的 WEB 控制台。既不方便,而且还有一定 Windows 服务器的维护工作量,于是找到我们团队,希望通过运维手段来解决。

拿到这个需求后,我先问了下各个应用的基本情况,得知每个应用的框架基本是一样的,都是通过 IP+端口直接访问,页面 path 也基本一样,没有唯一性。然后拿到了一个应用 WEB 控制台地址看了下,发现 html 引用的地址都是相对路径。

乍一想,这用 Nginx 代理不好弄吧?页面 path 一样,没法根据 location 来反代到不同的后端,只能通过不同 Nginx 端口来区分,那就太麻烦了!每次他们新上一个应用,我们就得多加一个新端口来映射,这种的尾大不掉、绵绵不绝事情坚决不干,Say pass。

再一想,我就想到了上次那个正向代理另类应用方案,感觉可以拿过来改改做动态代理。原理也简单:先和用户约定一个访问形式,比如:

Nginx 代理地址为 myproxy.oa.com,需要代理到 IP 为 192.168.2.100:8080 的控制器,用户需要访问 http://myproxy.oa.com/192.168.2.100:8080/path

Read more...

【问题小排查】排查 CLOSE_WAIT 堆积

TCP 连接的 CLOSE_WAIT 状态,正常情况下是短暂的,如果出现堆积,一般说明应用有问题。

CLOSE_WAIT 堆积的危害

每个CLOSE_WAIT连接会占据一个文件描述,堆积大量的CLOSE_WAIT可能造成文件描述符不够用,导致建连或打开文件失败,报错too many open files:

1
dial udp 9.215.0.48:9073: socket: too many open files

如何判断?

检查系统CLOSE_WAIT连接数:

1
lsof | grep CLOSE_WAIT | wc -l

检查指定进程CLOSE_WAIT连接数:

1
lsof -p $PID | grep CLOSE_WAIT | wc -l

为什么会产生大量 CLOSE_WAIT?

我们看下 TCP 四次挥手过程:

tcp_established

Read more...

【问题小排查】关于怎么查IO高负载

系统如果出现 IO WAIT 高,说明 IO 设备的速度跟不上 CPU 的处理速度,CPU 需要在那里干等, 这里的等待实际也占用了 CPU 时间,导致系统负载升高,可能就会影响业务进程的处理速度,导致业务超时。

如何判断 ?

使用top命令看下当前负载:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
top - 19:42:06 up 23:59,  2 users,  load average: 34.64, 35.80, 35.76
Tasks: 679 total,   1 running, 678 sleeping,   0 stopped,   0 zombie
Cpu(s): 15.6%us,  1.7%sy,  0.0%ni, 74.7%id,  7.9%wa,  0.0%hi,  0.1%si,  0.0%st
Mem:  32865032k total, 30989168k used,  1875864k free,   370748k buffers
Swap:  8388604k total,     5440k used,  8383164k free,  7982424k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 9783 mysql     20   0 17.3g  16g 8104 S 186.9 52.3   3752:33 mysqld
 5700 nginx     20   0 1330m  66m 9496 S  8.9  0.2   0:20.82 php-fpm
 6424 nginx     20   0 1330m  65m 8372 S  8.3  0.2   0:04.97 php-fpm

%wa(wait) 表示 IO WAIT 的 cpu 占用,默认看到的是所有核的平均值,要看每个核的%wa值需要按下 “1”:

Read more...

【问题小排查】Linux任务计划crontab不执行的问题排查

朋友弄了一个小项目,要我帮忙做下 Linux 系统运维,上线一段时间后,发现项目偶尔会挂掉导致服务不可用。 开发朋友一时之间也没空去研究项目奔溃的根因,只好由我这个运维先写一个项目进程自拉起脚本, 通过 Linux 任务计划每分钟检查一下进程是否存在来避免项目挂了没人管的情况。

自拉起脚本很简单,随便写几行就搞定了:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/bin/bash
processcount=$(pgrep my_app|wc -l)
cd $(cd $(dirname $0) && pwd)
if [[ 0 -eq $processcount ]]
then
        echo "[ $(date) ] : my_app is down, start it!" | tee -ai ./checkprocess.log
        bash ./start.sh #这里是项目的重启脚本
else
        echo my_app is OK!
fi

然后丢到 crontab,1 分钟执行一次:

1
* * * * * bash /data/app_server/checkprocess.sh >/dev/null 2>&1

-_-不过进程还是挂了

Read more...

【问题小解决】PID耗尽的危害

如何判断 PID 耗尽

首先要确认当前的 PID 限制,检查全局 PID 最大限制:

1
cat /proc/sys/kernel/pid_max

也检查下线程数限制:

1
cat /proc/sys/kernel/threads-max

再检查下当前用户是否还有ulimit限制最大进程数。

确认当前实际 PID 数量,检查当前用户的 PID 数量:

1
ps -eLf | wc -l

如果发现实际 PID 数量接近最大限制说明 PID 就可能会爆满导致经常有进程无法启动, 低版本内核可能报错:Cannot allocate memory,这个报错信息不准确, 在内核 4.1 以后改进了: https://github.com/torvalds/linux/commit/35f71bc0a09a45924bed268d8ccd0d3407bc476f

Read more...

【问题小解决】MySQL高可用集群之双主多从

通常MySQL主从复制主要用来解决读写分离,分担服务器压力。MySQL互为主备实现服务的高可用;这里同时基于高可用和负载均衡。

mysql-ha

环境准备

主机名/角色VIPIP地址操作系统MySQL版本
Node0/master1172.16.10.100172.16.10.10CentOS8.1.19118.0.17
Node1/master2172.16.10.11CentOS8.1.19118.0.17
Node2/slave1172.16.10.12CentOS8.1.19118.0.17

安装MySQL

在所有节点上执行dnf -y install mysql mysql-server,并在master节点上配置server-id并开启bin-log

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
vi /etc/my.cnf.d/mysql-server.cnf
[mysqld]
# 主数据库端ID号,全局唯一,通常用IP地址最后一位
server_id = 10
# 开启二进制日志
log-bin = mysql-bin
# 需要复制的数据库名,如果复制多个数据库,重复设置这个选项即可
binlog-do-db = test
# 将从服务器从主服务器收到的更新记入到从服务器自己的二进制日志文件中
log-slave-updates
# 控制binlog的写入频率。每执行多少次事务写入一次(这个参数性能消耗很大,但可减小MySQL崩溃造成的损失)
sync_binlog = 1
# 下面这两个参数非常重要
# 这个参数一般用在主主同步中,用来错开自增值, 防止键值冲突,master2上面改为2
auto_increment_offset = 1
# 这个参数一般用在主主同步中,用来错开自增值, 防止键值冲突
auto_increment_increment = 2
# 二进制日志自动删除的天数,默认值为0,表示“没有自动删除”,启动时和二进制日志循环时可能删除
expire_logs_days = 7
# 将函数复制到slave
log_bin_trust_function_creators = 1

执行systemctl enable --now mysqld运行Mysql

Read more...

【生产问题】分享一次Nginx正向代理的需求

最近接到了一个需求:通过 Nginx 代理把现网一个自研代理程序给替换掉,感觉有点意思,也有所收益,简单分享下。

需求背景

部门的生产环境异常复杂,有部分第三方引入的系统位于特殊网络隔离区域,请求这些系统需要通过 2 层网络代理,如图所示:

12

中心源系统请求目标系统 API 的形式各异,我简单收集了下,至少有如下 3 种:

1
2
3
4
5
curl --digest -u admin:xxxxxx 'http://10.xxx.xxx.xxx:8080/foo/boo?Id=123456789&vId=1234' 
 
curl -d '{"eventId": 20171116, "timestamp": 123456, "caller": "XXP", "version": "1.0", "interface": {"interfaceName": "XXPVC", "para": {"detail": {"owner": "xxxxxxx"}}}, "password": "xxxxxx", "callee": "XXPVC"}' http://10.x.x.x:8080/t/api
 
curl -X PUT -H "Content-Type: application/json" -d'{"vp":{"id":"ab27adc8-xxx-xxxx-a732-fbde162ebdd3"}}' "http://10.x.x.x/v1.0/peers/show_connectioninfos"
Read more...

【基础知识】Linux的xargs命令

昨天在给服务器做年终“大扫除”整理时,发现有个目录下因为文件过多而删除失败,最终使用xargs才搞定,于是顺便来记录下。

在执行某些命令时,当 Linux 某个目录下文件过多就会因为“参数列表过长”而报错无法执行。比如,我要清空/var/spool/clientmqueue/下的庞大数量的临时文件, 如果直接执行rm -f *,有时就会会出现“参数列表过长”的错误提示,因为 linux 下一般的命令的参数的总长度不能超过 4096 个字节。

这时,xargs就应该上场了了,由于服务器数量很多,我直接在每台服务器上执行如下命令,即可清理此文件夹内的所有文件:

1
2
#代码中的$8,不通系统发行版本可能有所区别,具体使用 ls -l 查看文件名在那一列即可
cd /var/spool/clientmqueue/  && ls -l /var/spool/clientmqueue/ | awk {'print $8'} | xargs rm -f
Read more...

Linux常用命令详解

  • ls
1
2
3
4
5
ls                # 显示当前目录的内容
ls -la            # 显示当前目录的所有文件(包括隐藏文件),并显示详细信息
ls /home/user     # 显示指定目录的内容
ls -R             # 递归地列出目录内容,包括子目录及其内容
ls -h             # 与 -l 结合使用,显示文件大小以可读形式(如 KB、MB)
  • touch
1
2
3
touch newfile.txt          # 创建一个名为 newfile.txt 的空文件
touch file1 file2          # 创建多个文件(file1 和 file2)
touch -c existingfile.txt  # 如果文件存在,则更新时间戳;如果不存在,不会创建新文件
  • cat
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 使用 -n 选项:为每行添加行号,包括空行
cat -n file.txt
# 输出示例:
#  1  This is a line.
#  2  
#  3      This is indented.
#  4  Another line.
# 注:此选项为文件中的每一行编号,从 1 开始计数。

# 使用 -b 选项:为非空行添加行号,空行不编号
cat -b file.txt
# 输出示例:
#  1  This is a line.
# 
#  2      This is indented.
#  3  Another line.
# 注:与 -n 类似,但只为非空行编号。

# 使用 -s 选项:压缩连续的空行,只显示一个空行
cat -s file.txt
# 输出示例:
# This is a line.
# 
#     This is indented.
# Another line.
# 注:多个连续空行会被压缩为一个空行。

# 使用 -E 选项:显示每行末尾的 $ 符号,标示行的结束
cat -E file.txt
# 输出示例:
# This is a line.$
# $
#     This is indented.$
# Another line.$
# 注:在每一行的末尾加上 "$" 以标示行结束,有助于确认行尾换行符的位置。

# 使用 -T 选项:将 Tab 字符显示为 ^I
cat -T file.txt
# 输出示例:
# This is a line.
# 
# ^IThis is indented.
# Another line.
# 注:将 Tab 字符用 ^I 进行可视化显示,这样方便辨认文件中的制表符。

# 使用 -A 选项:显示所有不可见字符,等同于 -vET
cat -A file.txt
# 输出示例:
# This is a line.$
# $
# ^IThis is indented.$
# Another line.$
# 注:显示不可见字符、换行符 ($) 和制表符 (^I),有助于检查文件中所有控制字符和隐藏字符。
  • kill
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
kill -l
# 输出示例:
#  1) SIGHUP   2) SIGINT   3) SIGQUIT   4) SIGILL   5) SIGTRAP   ...
# 注:列出系统支持的所有信号,以及信号的编号。每个信号都有不同的作用,例如 `SIGHUP` 代表挂起、`SIGKILL` 表示强制终止等。

kill -9 1234
# 注:向进程 ID 为 1234 的进程发送 `SIGKILL` 信号(9),强制终止该进程。
# 该信号会立即结束进程,无法被捕获或忽略,通常用于无法响应 `SIGTERM` 的进程。
# 没有 -9,默认终止信号 (SIGTERM)

kill -HUP 1234
# 注:向进程 ID 为 1234 的进程发送 `SIGHUP` 信号(1)。
# 通常用于让守护进程重新加载配置,而不是完全终止。

# SIGHUP (1):挂起信号,通常用于让进程重新读取配置文件,比如 nginx、cron
# SIGINT (2):中断信号,通常由 Ctrl+C 发送
# SIGQUIT (3):退出信号,产生核心转储
# SIGKILL (9):强制终止信号,不能被忽略或捕获
# SIGTERM (15):请求终止信号,进程可以捕获并自行处理
# SIGSTOP:停止进程,类似于 Ctrl+Z,不能被捕获
Read more...

Oh My Zsh自动补全

shell 有多种,大多数人接触比较多的是 bash, 不管是 mac 还是各个 linux 发行版,默认的 shell 基本都是 bash, 虽然 bash 功能已经丰富了,但对于极客们来说,界面不够炫,提示功能也不够强大。而 zsh 功能及其强大,只是配置过于复杂, 后来就有了 oh-my-zsh 开源项目,配置难度大大降低。 Github地址: https://github.com/robbyrussell/oh-my-zsh 安装 1 sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" 更改系统默认 shell 1 chsh -s /bin/zsh 更改zsh配置文件 1 vim ~/.zshrc 修改主题 1 ZSH_THEME="robbyrussell" 配置插件 oh-my-zsh 还支持插件,插件存放目录为: 1 ~/.oh-my-zsh/plugins 这个目录中每个子目录都是一个插件,目录名即为插件名,默认不开启,需要在~/.zshrc中该配置开启,比如: 1 2 3 4 5 6 7 8 9 10 11 plugins=( git git-flow docker kubectl brew npm helm zsh-autosuggestions zsh-syntax-highlighting ) 这些插件可以给你常用的命令做用法提示,使用 tab 键触发。我这里再推荐另外三个不是内置的插件, 需要将它们单独下载到~/.
Read more...