Dstat-监控Linux下系统资源性能统计工具


2019年3月25日 13:20:02   2,210 次浏览
什么是Dstat ?

stat是一个功能强大,灵活多变的工具,类似于 vmstat、iostat、mpstat,所不同的是,可以监控多个系统指标,如 CPU、网络、内存、中断等,可以将结果显示到终端,也可保存到文件。另外,该程序是通过 Python 实现的。

Dstat用法:

dstat [-afv] [options..] [delay [count]]

常用选项:
-c, –cpu
统计CPU状态,包括 user, system, idle (空闲等待时间百分比), wait (等待磁盘IO),
hardware interrupt (硬件中断), software interrupt (软件中断) 等;
-d, –disk
统计磁盘读写状态,主要包括了读写信息;
-l, –load
统计系统负载情况,包括1分钟、5分钟、15分钟平均值;
-m, –mem
统计系统物理内存使用情况,包括used, buffers, cache, free;
-s, –swap
统计swap已使用和剩余量;
-n, –net
统计网络使用情况,包括接收和发送数据;
-p, –proc
统计进程信息,包括runnable、uninterruptible、new;
-N eth1,total
统计eth1接口汇总流量;
-r, –io
统计I/O请求,包括读写请求;
-y, –sys
统计系统信息,包括中断、上下文切换;
-t
显示统计时时间,对分析历史数据非常有用;
–fs
统计文件打开数和inodes数;

如何安装

在CentOS和Redhat中

yum install dstat -y

dstat不支持python3,如果你的系统中使用了python3需要修改dstat文件头部改为python2

< 11 NGINX-1 - [root]: ~ > # which dstat
/usr/bin/dstat

< 11 NGINX-1 - [root]: ~ > # vim /usr/bin/dstat
#!/usr/bin/python2.7
### This program is free software; you can redistribute it and/or modify
### it under the terms of the GNU Library General Public License as published by
### the Free Software Foundation; version 2 only
###
### This program is distributed in the hope that it will be useful,
### but WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
### GNU Library General Public License for more details.
###
### You should have received a copy of the GNU Library General Public License
### along with this program; if not, write to the Free Software
### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
### Copyright 2004-2007 Dag Wieers <dag@wieers.com>


from __future__ import generators


try:
    import sys, os, time, sched, re, getopt
    import types, resource, getpass, glob, linecache
    except KeyboardInterrupt:
pass


VERSION = '0.7.2'
theme = { 'default': '' }
if sys.version_info < (2, 2):
    sys.exit('error: Python 2.2 or later required')

### Workaround for python <= 2.2.1

dsata如果不加选项默认选项是-a或cdngy选项

上面的输出结果:

  1. CPU统计数据:用户(usr)进程的CPU使用情况,系统(sys)进程,以及空闲(idl)和等待(wai)进程数,硬中断(hiq)和软中断(siq)。
  2. 磁盘统计信息:磁盘上的读取(读取)和写入(写入)操作的总数。
  3. 网络统计:网络接口上接收(recv)和发送(发送)的总字节数。
  4. 分页统计信息:信息被复制到内存中(内)和外出(外出)的次数。
  5. 系统统计信息:中断数(int)和上下文切换(csw)。

要显示提供的信息vmstat,请使用-v--vmstat选项:

  1. 进程统计:运行(run),阻塞(blk)和新(new)生成进程的数量。
  2. 内存统计:使用(used),缓冲(buff),缓存(cach)和剩余(free)内存的数量。
Dstat进阶操作
  1. -c – CPU使用率
  2. --top-cpu – 使用大多数CPU的过程
  3. -dn – 磁盘和网络统计信息
  4. --top-mem – 消耗最多内存的进程
dstat -c --top-cpu -dn --top-mem

我们可以通过启用--output选项,将dstat的输出存储在文件中,以便在以后进行分析。

示例:显示时间,CPU,内存,系统负载统计数据,每隔1秒刷新一次,捕捉5次结果后退出dstat。

dstat --time --cpu --mem --load --output report.csv 1 5

显示每个CPU的详细信息(包括cpu0,cpu1等)和总使用情况。它显示每个CPU(用户时间,系统时间,空闲时间,和等待时间)活动进程

dstat -C 0,1,2,total

显示有关特定磁盘的磁盘利用率(读取和写入)和磁盘I / O(读取和写入)利用率的详细信息。如果要检查总磁盘利用率和I / O,请使用dstat --disk --io

显示特定网络利用率(数据接收和数据发送)的详细信息。如果要显示所有以太网利用率,请使用dstat --net

dstat --net -N eno16777728

显示有关top cpu,top cputime(使用最多CPU时间(以ms为单位)的进程),top磁盘I / O活动,top磁盘块I / O活动,top memory和top latency usage的详细信息。

dstat --top-cpu --top-cputime --top-io --top-bio --top-mem --top-latency

显示有关(CPU,磁盘,内存,进程,负载和网络)使用情况的详细信息,这对于服务器负载过高时的基本故障排除非常常见。

dstat --cpu --mem --proc --load --disk --net

显示网络有关tcp(listen,established,syn,time_wait,close),udp(listen,active)和socket(total,tcp,udp,raw,ip-fragments)用法的详细信息。

dstat --tcp --udp --socket

nginx反向代理后响应200但是没有返回数据


2019年3月13日 15:09:00   4,783 次浏览

最近公司在上线新的项目,原来的环境都是每个项目配置一个独立的域名。因微信回调域名添加有限制貌似最多只能添加5个,新项目又急着上线,注册新的微信公众号资料繁琐且时间紧张,只能改造公司内部的项目了,一台服务器要配置一个独立域名通过多个目录形式访问不同的多个前端项目,这时候nginx开始上场了 。单个项目还好说,如下修改nginx的nginx.conf配置文件

前端打包react的文件后的dist目录,然后使用nginx反向代理

网上大多数的文章都是这样配置的,单个项目

try_files $uri $uri/ /index.html;

多个项目配置

try_files $uri $uri/ /Aproject/index.html;
try_files $uri $uri/ /Bproject/index.html;

然而这样配置后页面都是200状态接口依旧还是没有返回数据

// 访问这里的接口是正常的,后台接口是用node.js写的,返回的是json格式

location /style {
 proxy_pass   http://localhost:80808;      
 proxy_set_header  Host       $host;
 proxy_set_header  X-Real-IP  $remote_addr;
 proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;

}
  1. // 访问这里的接口会返回200,但是没有响应体
  2. // 去掉下面的rewrite会显示404
location /api {
 rewrite  ^/api/?(.*)$ /$1 break;
 add_header backendIP $upstream_addr;
 add_header backendCode $upstream_status;
 proxy_pass http://xxxx;

}

修改node项目的路由代码将browserHistory改成history模式

删除process.env.API_ENV == ‘dev’ ? “/” :

package.json文件增加homepage

修改静态资源调用代码

最后nginx配置如下

server
{
   listen 1080;
   server_name 192.168.1.36;
       root   /data/apps-data/h5/;
       index index.php index.html index.htm default.html default.htm default.php;
       error_page      404  /errors/404.htm;
       error_page      403  /errors/403.html;
       location /flight {
               alias   /data/apps-data/h5/flight/;
               try_files $uri $uri/ /flight/index.html;
               proxy_set_header   Host             $host;
               proxy_set_header   X-Real-IP        $remote_addr;
               proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
               proxy_set_header   X-Forwarded-Proto  $scheme;
                 }
               location /flight/flightList/static/ {
               alias   /data/apps-data/h5/flight/static/;
               }
       location /hotel {
               alias   /data/apps-data/h5/hotel/;
               try_files $uri $uri/ /hotel/index.html;
               proxy_set_header   Host             $host;
               proxy_set_header   X-Real-IP        $remote_addr;
               proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
               proxy_set_header   X-Forwarded-Proto  $scheme;
               proxy_set_header Upgrade $http_upgrade;
               proxy_set_header Connection $connection_upgrade;
   }
       location /auth {
               alias   /data/apps-data/h5/auth/;
               try_files $uri $uri/ /auth/index.html;
               proxy_set_header   Host             $host;
               proxy_set_header   X-Real-IP        $remote_addr;
               proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
               proxy_set_header   X-Forwarded-Proto  $scheme;
       }
       }

最后配置二层代理线上代理线下的nginx服务

server
{
   listen 80;
   server_name hello.com;
   location / {
       proxy_redirect ~^http://work.baidu.com:1018/(.*) http://hello.com/$1;
       proxy_connect_timeout 600;
       proxy_read_timeout 600;
       proxy_send_timeout 600;
       proxy_buffer_size       64k;
       proxy_buffers           4 64k;
       proxy_busy_buffers_size 128k;
       proxy_temp_file_write_size 128k;
       proxy_set_header Host $host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_pass http://work.baidu.com:1018/;
       expires -1;
       }
   access_log logs/hotel.log;
   error_log  logs/hotel-error.log;
}

 

Linux性能监测:IO篇


2019年2月28日 17:26:00   1,292 次浏览

说到IO性能,就不得不说我曾经干过的一件傻事,这件事情让我记忆犹新。那还是我在xxx干活的时候,有一次需要进行全量上线,说到上线,那肯定就要对当前应用进行备份了。这个备份的活我都干了好久了,每次都是那么几个命令,先tar,然后再gzip,每次都是一个接一个的备份,而备份一个又要好久;然后我就想偷懒,耍个小聪明,写了一个脚本,一次同时并行备份所有的文件,然后启动脚本就OK了,也不需要人值守。我就这么干了,没过多久,前台使用人员就反馈系统反应慢,我一听,慌了,立马看了下,备份脚本占用了大量的IO,导致IO急剧飙升,导致系统整体性能下降。我都佩服我这个小机灵鬼,立马发现了问题,解决了问题。如何发现问题,请各位继续往下看。

说到IO,就得先说说存储系统。我们知道,在存储系统里面,越靠近CPU的(比如CPU的缓存),价格越贵、容量越小、速度越快;越远离CPU的(比如磁盘),价格越便宜、容量越大、速度越慢。而磁盘通常是计算机最慢的子系统,也是最容易出现性能瓶颈的地方,因为磁盘离CPU距离最远而且CPU访问磁盘要涉及到机械操作,比如转轴、寻轨等。访问硬盘和访问内存之间的速度差别是以数量级来计算的,就像1天和1分钟的差别一样。要监测IO性能,就有必要了解一下基本原理和Linux是如何处理硬盘和内存之间的IO的。

先说理论

而要说IO性能,就不得不先说说缺页中断了。Linux利用虚拟内存极大的扩展了程序地址空间,使得原来物理内存不能容下的程序也可以通过内存和硬盘之间的不断交换(把暂时不用的内存页交换到硬盘,把需要的内存页从硬盘读到内存)来赢得更多的内存,看起来就像物理内存被扩大了一样。事实上这个过程对程序是完全透明的,程序完全不用理会自己哪一部分、什么时候被交换进内存,一切都有内核的虚拟内存管理来完成。当程序启动的时候,Linux内核首先检查CPU的缓存和物理内存,如果数据已经在内存里就忽略,如果数据不在内存里就引起一个缺页中断(Page Fault),然后从硬盘读取缺页,并把缺页缓存到物理内存里。缺页中断可分为主缺页中断(Major Page Fault)和次缺页中断(Minor Page Fault),要从磁盘读取数据而产生的中断是主缺页中断;数据已经被读入内存并被缓存起来,从内存缓存区中而不是直接从硬盘中读取数据而产生的中断是次缺页中断。

上面的内存缓存区起到了预读硬盘的作用,内核先在物理内存里寻找缺页,没有的话产生次缺页中断从内存缓存里找,如果还没有发现的话就从硬盘读取。很显然,把多余的内存拿出来做成内存缓存区提高了访问速度,这里还有一个命中率的问题,运气好的话如果每次缺页都能从内存缓存区读取的话将会极大提高性能。要提高命中率的一个简单方法就是增大内存缓存区面积,缓存区越大预存的页面就越多,命中率也会越高。

内存缓存区(也叫文件缓存区File Buffer Cache)读取页比从硬盘读取页要快得多,所以Linux内核希望能尽可能产生次缺页中断(从文件缓存区读),并且能尽可能避免主缺页中断(从硬盘读),这样随着次缺页中断的增多,文件缓存区也逐步增大,直到系统只有少量可用物理内存的时候Linux才开始释放一些不用的页。我们运行Linux一段时间后会发现虽然系统上运行的程序不多,但是可用内存总是很少,这样给大家造成了Linux对内存管理很低效的假象,事实上Linux把那些暂时不用的物理内存高效的利用起来做预存(内存缓存区)了。

比如,以下这样的一个输出:

[root@Test_Server ~]# cat /proc/meminfo

MemTotal:        1883496 kB
MemFree:          137236 kB
MemAvailable:     286512 kB
Buffers:          107028 kB
Cached:           150352 kB

可以看到内存一共大概2G(MemTotal),其中大概130M可用(MemFree),100多M用来做磁盘缓存(Buffers),150多M用来做文件缓存(Cached)。

好了,有了这些基本知识,我们再来看IO性能的分析。

再说实干

说到实际的,那就要说说实际运维工作中,我们如何发现IO性能瓶颈。

我们进行IO分析,第一步还是需要祭出我们的神器vmstat,关于vmstat如何使用可以参见这篇《Linux vmstat命令详解》,通过vmstat的输出,重点关注bbibowa字段。这几个值变大,都意味着IO的消耗增加。对于读请求大的服务器,一般bbiwa都会比较大,而对于写入量大的服务器,一般bbowa都会比较大。

接下来就是使用第二个神器iostat。通过这个神器,我们就可以监视具体的磁盘性能了。对于iostat这个命令的具体教程,可以参见这篇《Linux iostat命令详解》。

但是,在实际运维工作中,我们都希望找到是哪个进程消耗了IO,所以我们的终极目的是找到这个进程ID。可是通过vmstatiostat都没法达到我们的目的。所以,神器pidstat命令就登场了,通过这个命令,我们就可以知道是谁在后台偷用IO了。我想我整理的这篇《Linux pidstat命令详解》对你应该有一些帮助。

现在定位到进程级别了,很多时候,我们需要知道这个进程到底打开了哪些文件,这个进程到底和哪些进程关联,这个时候就不得不提到lsof命令了。

Linux ip命令详解


2019年2月28日 16:41:00   1,375 次浏览

我之前一直都使用ifconfig命令,觉的这个命令挺好用的,后来有次安装Linux发型版本,发现没有了ifconfig这个工具了,无奈之下,到处百度,发现了ip这个命令,一顿学习后,发现ip命令完全可以替代ifconfig命令,而且功能还比ifconfig命令强大,所以,还等什么,借用现在的一个网络流行词:盘它。

命令简介

作为每个Linux网络管理员和所有的Linux使用者们,ip命令是必备工具,这个命令完美的取代了ifconfig命令,所以这篇文章就将全面的总结ip命令,希望通过我的这篇总结,让大家可以更熟悉ip命令。

命令详解

ip常用命令格式如下:

  1. ip [ OPTIONS ] OBJECT { COMMAND | help }

对象OBJECT={ link | addr | addrlabel | route | rule | neigh | ntable | tunnel | maddr | mroute | mrule | monitor | xfrm | token }

选项OPTIONS={ -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] | -h[uman-readable] | -iec | -f[amily] { inet | inet6 | ipx | dnet | link } | -o[neline] | -t[imestamp] | -b[atch] [filename] | -rc[vbuf] [size] }

常用对象的取值含义如下:

  • link:网络设备
  • address:设备上的协议(IP或IPv6)地址
  • addrlabel:协议地址选择的标签配置
  • route:路由表条目
  • rule:路由策略数据库中的规则

常用选项的取值含义如下:

  • -V,-Version:显示指令版本信息
  • -s,-stats,statistics:输出详细信息
  • -h,-human,-human-readable:输出人类可读的统计信息和后缀
  • -o,-oneline:将每条记录输出到一行,用‘\’字符替换换行符
使用实例-检查网卡信息

说实话这个ip命令真的好复杂,选项参数有那么多,所以吧,我们不可能每个功能面面俱到。所以呢,我这里就对我们日常工作中经常用到的一些用法进行总结。

  • 命令:ip addr show
    说明:显示网卡及配置的地址信息
    输出:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
   link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
   inet 127.0.0.1/8 scope host lo
      valid_lft forever preferred_lft forever

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
   link/ether 00:1e:4f:c8:43:fc brd ff:ff:ff:ff:ff:ff
   inet 192.168.0.24/24 brd 192.168.0.255 scope global eth0
      valid_lft forever preferred_lft forever

输出内容详解:
首先这个系统有两个接口:loeth0lo是环回接口,而我们重点关注的则是eth0这个普通网络接口;下面在看看每个子项的含义:

  • <BROADCAST,MULTICAST,UP,LOWER_UP>BROADCAST表示该接口支持广播;MULTICAST表示该接口支持多播;UP表示该网络接口已启用;LOWER_UP表示网络电缆已插入,设备已连接至网络
  • mtu 1500:最大传输单位(数据包大小)为1,500字节
  • qdisc pfifo_fast:用于数据包排队
  • state UP:网络接口已启用
  • qlen 1000:传输队列长度
  • link/ether 00:1e:4f:c8:43:fc:接口的MAC(硬件)地址
  • brd ff:ff:ff:ff:ff:ff:广播地址
  • inet 192.168.0.24/24:IPv4地址
  • brd 192.168.0.255:广播地址
  • scope global:全局有效
  • dynamic enp0s25:地址是动态分配的
  • valid_lft forever:IPv4地址的有效使用期限
  • preferred_lft forever:IPv4地址的首选生存期
  • inet6 fe80::2c8e:1de0:a862:14fd/64:IPv6地址
  • scope link:仅在此设备上有效
  • valid_lft forever:IPv6地址的有效使用期限
  • preferred_lft forever:IPv6地址的首选生存期
使用实例-IP管理
  • 命令:ip addr add 192.168.0.123/24 dev eth0
    说明:设置IP
  • 命令:ip add del 192.168.0.123/24 dev eth0
    说明:删除配置的IP
使用实例-启用/禁用网卡
  • 命令:ip link set eth0 up
    说明:启用被禁用的网卡
  • 命令:ip link set eth0 down
    说明:禁用网卡
使用实例-路由配置

命令:ip route show
说明:查看路由信息
输出:

default via 172.17.175.253 dev eth0
169.254.0.0/16 dev eth0 scope link metric 1002
172.17.160.0/20 dev eth0 proto kernel scope link src 172.17.169.20
  • 输出内容详解:
    • 输出内容第一条是默认的路由,我们可以根据我们的需要改动它
    • metric 1002:跳跃计数,确定网关的优先级,默认20,数值越小优先级越高
    • proto kernel:该路由的协议,主要有redirectkernelbootstaticra等,其中kernel指的是直接由核心判断自动设定
  • 命令:ip route get 119.75.216.20
    说明:通过IP地址查询路由包从哪条路由来
  • 命令:ip route add default via 192.168.0.150/24
    说明:所有的网络数据包都通过192.168.0.150来转发,而不是以前的默认路由
  • 命令:ip route add 172.16.32.32 via 192.168.0.150/24 dev enp0s3
    说明:修改特定网卡的默认路由
  • 命令:ip route del 172.17.160.0/20
    说明:删除路由
  • 命令:ip route flush cache
    说明:刷新路由表
使用实例-显示网络统计数据

这个显示网络统计数据则是ip命令非常重要的一个功能,很多时候,我们都依靠该功能来进行排除网络故障。

  • 命令:ip -s link
    说明:显示所有网络接口的统计数据
    输出:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT qlen 1
   link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
   RX: bytes  packets  errors  dropped overrun mcast  
   361849729592 174114258 0       0       0       0      
   TX: bytes  packets  errors  dropped carrier collsns
   361849729592 174114258 0       0       0       0      

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000
   link/ether 00:16:3e:08:08:55 brd ff:ff:ff:ff:ff:ff
   RX: bytes  packets  errors  dropped overrun mcast  
   32345193376 115901261 0       0       0       0      
   TX: bytes  packets  errors  dropped carrier collsns
   139742200499 114451909 0       0       0       0
  • 输出重点内容详解:
    • RX:表示接收
    • TX:表示发送
    • bytes:接收/发送的字节数
    • packets:接收/发送的包数
    • errors:接收/发送的带有错误的包总数
    • dropped:由于处理资源不足导致接收/发送的丢弃的包数
    • overrun:因接收溢出(环形缓冲区)导致丢失的包;通常如果接口溢出,则表示内核中存在严重问题,或者说服务器上该网络设备的处理设备太慢
    • mcast:接收到的多播包数
    • carrier:因数据链路错误导致发送失败的包数
    • collsns:因在网络上发送冲突而导致的失败数
  • 命令:ip -s -s link ls eth0
    说明:获取一个特定网络接口的信息;在网络接口名字后面添加选项ls即可。使用多个选项-s会输出指定接口详细的信息;特别是在排除网络连接故障时,这会非常有用。
    输出:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000
   link/ether 00:16:3e:08:08:55 brd ff:ff:ff:ff:ff:ff
   RX: bytes  packets  errors  dropped overrun mcast  
   32469801665 116402997 0       0       0       0      
   RX errors: length   crc     frame   fifo    missed
              0        0       0       0       0      
   TX: bytes  packets  errors  dropped carrier collsns
   140235841575 115066014 0       0       0       0      
   TX errors: aborted  fifo   window heartbeat transns
              0        0       0       0       2

 

 

线上Redis 内存分析方法


2019年1月09日 14:31:00   1,318 次浏览

吃过午饭正准备午睡会呢,结果收到了redis内存不足的告警通知,第一时间登录监控平台查看详情,内存使用率居然上升了80%究竟是什么原因导致的呢?接下来便开始分析问题原因。

线上经常遇到用户想知道自己 Redis 实例中数据的内存分布情况。为了不影响线上实例的使用,我们一般会采用 bgsave 生成 dump.rdb 文件,再结合 redis-rdb-tools 和 sqlite 来进行静态分析。总的来说,整个分析的过程简单而实用,是每一个使用 Redis 的用户都非常值得掌握的一个方法。

生成内存快照

redis-rdb-tools 是一个 python 的解析 rdb 文件的工具,在分析内存的时候,我们主要用它生成内存快照。主要有以下三个功能:

  • 生成内存快照
  • 转储成 json 格式
  • 使用标准的 diff 工具比较两个 dump 文件

redis-rdb-tools 安装

redis-rdb-tools 有两种安装方式,任选其一即可。

使用 PYPI 安装

pip install rdbtools

从源码安装

git clone https://github.com/sripathikrishnan/redis-rdb-tools

cd redis-rdb-tools

sudo python setup.py install

使用 redis-rdb-tools 生成内存快照

生成内存快照的命令为:

rdb -c memory dump.rdb > memory.csv

生成 CSV 格式的内存报告。包含的列有:数据库 ID,数据类型,key,内存使用量(byte),编码。内存使用量包含 key、value 和其他值。

注意:内存使用量是理论上的近似值,在一般情况下,略低于实际值。memory.csv 例子:

$head memory.csv

database,type,key,size_in_bytes,encoding,num_elements,len_largest_element
0,string,"orderAt:377671748",96,string,8,8
0,string,"orderAt:413052773",96,string,8,8
0,sortedset,"Artical:Comments:7386",81740,skiplist,479,41
0,sortedset,"pay:id:18029",2443,ziplist,84,16
0,string,"orderAt:452389458",96,string,8,8

打开生成的memory.csv文件,找出占用内存最大的key,可以发现很多的key都没有设置过期时间,然后找开发确认把相应的key设置过期时间以减少内存使用率。

开发确认后表明有些key并未设置过期时间,需要手动清除缓存。清除之后内存使用率从80%下降到40%, 后续让开发优化业务代码,合理设置过期时间。

 

使用docker-compose部署LNMP环境


2018年12月29日 15:04:00   1,208 次浏览

1、创建相关compose存放目录

< 5 docker-test - [root]: > mkdir -p /apps/compose_lnmp/nginx
< 5 docker-test - [root]: >  cd !$
< 5  docker-test - [root]: /apps/compose_lnmp/nginx >

2、下载nginx软件包,创建dockerfile

< 6  docker-test - [root]: /apps/compose_lnmp/nginx > # wget http://nginx.org/download/nginx-1.14.2.tar.gz

< 7  docker-test - [root]: /apps/compose_lnmp/nginx > # vim Dockerfile

FROM centos:7
MAINTAINER gujiwork
RUN yum install -y gcc gcc-c++ make openssl-devel pcre-devel
ADD nginx-1.14.2.tar.gz /tmp
RUN cd /tmp/nginx-1.14.2 && ./configure --prefix=/usr/local/nginx && make -j 4 && make install
COPY nginx.conf  /usr/local/nginx/conf
EXPOSE 80
WORKDIR /usr/local/nginx
CMD ["./sbin/nginx", "-g", "daemon off;"]

3、这里面用到了nginx.conf,放到compose_lnmp/nginx目录下,将 fastcgi_pass 127.0.0.1:9000改成php容器名, fastcgi_pass php-cgi:9000;

#user  nginx;
worker_processes  2;
worker_cpu_affinity 0101 1010;
#error_log  logs/error.log;
error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
#pid        logs/nginx.pid;
events {
   worker_connections  10240;
}
http {
   include       mime.types;
   default_type  application/octet-stream;
   server_tokens off;  
   #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
   #                  '$status $body_bytes_sent "$http_referer" '
   #                  '"$http_user_agent" "$http_x_forwarded_for"';
   #access_log  logs/access.log  main;
   sendfile        on;
   tcp_nopush on;
   tcp_nodelay on;
   server_names_hash_bucket_size 128;
   server_names_hash_max_size 512;
   client_header_timeout 15s;
   client_body_timeout 15s;
   send_timeout 60s;
   #tcp_nopush     on;
   #keepalive_timeout  0;
   keepalive_timeout  65;
   #gzip  on;
   server {
       listen       80;
       server_name  localhost;
       #charset koi8-r;
       #access_log  logs/host.access.log  main;
       location / {
           root   html;
           index  index.html index.htm;
       }
       #error_page  404              /404.html;
       # redirect server error pages to the static page /50x.html
       #
       error_page   500 502 503 504  /50x.html;
       location = /50x.html {
           root   html;
       }
       # proxy the PHP scripts to Apache listening on 127.0.0.1:80
       #
       #location ~ \.php$ {
       #    proxy_pass   http://127.0.0.1;
       #}
       # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
       #
       location ~ \.php$ {
           root           html;
           fastcgi_pass   php-cgi:9000;
           fastcgi_index  index.php;
           fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
           include        fastcgi_params;
       }
       # deny access to .htaccess files, if Apache's document root
       # concurs with nginx's one
       #
       #location ~ /\.ht {
       #    deny  all;
       #}
   }
   # another virtual host using mix of IP-, name-, and port-based configuration
   #
   #server {
   #    listen       8000;
   #    listen       somename:8080;
   #    server_name  somename  alias  another.alias;
   #    location / {
   #        root   html;
   #        index  index.html index.htm;
   #    }
   #}
   # HTTPS server
   #
   #server {
   #    listen       443 ssl;
   #    server_name  localhost;
   #    ssl_certificate      cert.pem;
   #    ssl_certificate_key  cert.key;
   #    ssl_session_cache    shared:SSL:1m;
   #    ssl_session_timeout  5m;
   #    ssl_ciphers  HIGH:!aNULL:!MD5;
   #    ssl_prefer_server_ciphers  on;
   #    location / {
   #        root   html;
   #        index  index.html index.htm;
   #    }
   #}
include vhost/*.conf;
}

4、创建mysql目录,数据和配置文件做持久化,这里我们使用mysql官方镜像,因此不需要写dockerfile

< 39 docker-test - [root]: /apps/compose_lnmp > # mkdir mysql/{conf,data}

< 39  docker-test - [root]: /apps/compose_lnmp > # tree  mysql/

mysql/

|-- conf

|   `-- my.cnf

`-- data

< 40  docker-test - [root]: /apps/compose_lnmp > # cat mysql/conf/my.cnf

[mysqld]
user=mysql
port=3306
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.socket
pid-file=/var/run/mysql/mysql.pid
log_error=/var/log/mysql/error.log
character_set_server = utf8
max_connections=3600

5、创建php目录及dockerfile,php.ini主要修改了时区为shanghai

< 49  docker-test - [root]: /apps/compose_lnmp > # tree  php/

php/

|-- Dockfile
|-- php-5.6.31.tar.gz
`-- php.ini

< 50  docker-test - [root]: /apps/compose_lnmp > # cat php/Dockfile

FROM centos:7
MAINTAINER gujiwork
RUN yum install -y gcc gcc-c++ gd-devel libxml2-devel libcurl-devel libjpeg-devel libpng-devel openssl-devel make
ADD php-5.6.31.tar.gz /tmp/
RUN cd /tmp/php-5.6.31 && \
   ./configure --prefix=/usr/local/php \
   --with-config-file-path=/usr/local/php/etc \
   --with-mysql --with-mysqli \
   --with-openssl --with-zlib --with-curl --with-gd \
   --with-jpeg-dir --with-png-dir --with-iconv \
   --enable-fpm --enable-zip --enable-mbstring && \
   make -j 4 && make install && \
   cp /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf && \
   sed -i "s/127.0.0.1/0.0.0.0/" /usr/local/php/etc/php-fpm.conf && \
   cp ./sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm && \
   chmod +x /etc/init.d/php-fpm

COPY php.ini /usr/local/php/etc
EXPOSE 9000
CMD /etc/init.d/php-fpm start && tail -F /var/log/messages

6、创建docker-compose维护文件

< 53  docker-test - [root]: /apps/compose_lnmp > # cat docker-compose.yml

version: '3'
services:
 nginx:
   hostname: nginx
   build:
     context: ./nginx
     dockerfile: Dockerfile

   ports:
     - 80:80
   links:
     - php:php-cgi
   volumes:
     - ./wwwroot:/usr/local/nginx/html


 php:
   hostname: php
   build: ./php
   links:
     - mysql:mysql-db
   volumes:
     - ./wwwroot:/usr/local/nginx/html

 mysql:
   hostname: mysql
   image: mysql:5.6
   ports:
     - 3306:3306
   volumes:
     - ./mysql/conf:/etc/mysql/conf.d
     - ./mysql/data:/var/lib/mysql

   #command: --character-set-server=utf8

   environment:
     MYSQL_ROOT_PASSWORD: 123456
     MYSQL_DATABASE: wordpress
     MYSQL_USER: user
     MYSQL_PASSWORD: user123

7、创建web挂载目录,整体目录结构如下

< 56  docker-test - [root]: /apps/compose_lnmp > # tree .
.
|-- docker-compose.yml
|-- mysql
|   |-- conf
|   |   `-- my.cnf
|   `-- data
|-- nginx
|   |-- Dockerfile
|   |-- nginx-1.14.2.tar.gz
|   `-- nginx.conf
|-- php
|   |-- Dockfile
|   |-- php-5.6.31.tar.gz
|   `-- php.ini
`-- wwwroot


6 directories, 8 files

8、执行build构建镜像

< 81  docker-test - [root]: /apps/compose_lnmp > # docker-compose  build

9、出现success表示构建成功

make[1]: Leaving directory `/tmp/nginx-1.14.2'
Removing intermediate container b7fb79c4671e
---> b756345aac5b
Step 6/9 : COPY nginx.conf /usr/local/nginx/conf
---> cb180351db65
Step 7/9 : EXPOSE 80
---> Running in 20805a3b58a5
Removing intermediate container 20805a3b58a5
---> 9f75c3834969
Step 8/9 : WORKDIR /usr/local/nginx
---> Running in 6abf5341ee7b
Removing intermediate container 6abf5341ee7b
---> 0cb88354c8b8
Step 9/9 : CMD ["./sbin/nginx", "-g", "daemon off;"]
---> Running in a2db489f2b5b
Removing intermediate container a2db489f2b5b
---> 76ae0759f3b1
Successfully built 76ae0759f3b1
Successfully tagged compose_lnmp_nginx:latest

10、启动服务测试

< 82  docker-test - [root]: /apps/compose_lnmp > # docker-compose  up -d

Creating network "compose_lnmp_default" with the default driver
Pulling mysql (mysql:5.6)...
5.6: Pulling from library/mysql
177e7ef0df69: Pull complete
cac25352c4c8: Pull complete
8585afabb40a: Pull complete
1e4af4996053: Pull complete
c326522894da: Pull complete
50ec9776c6b3: Pull complete
b81a89945365: Pull complete
80f5ab6567ca: Pull complete
5caf5e4c5eb0: Pull complete
9295ceea71e2: Pull complete
fb029976ca26: Pull complete
Creating compose_lnmp_mysql_1_32333e982f89 ... done
Creating compose_lnmp_php_1_856b9f701287   ... done
Creating compose_lnmp_nginx_1_c5360c9dc627 ... done



< 144  docker-test - [root]: /apps/compose_lnmp/wwwroot > # echo "111" >index.html


#测试

< 144  docker-test - [root]: /apps/compose_lnmp/wwwroot > # curl localhost/

111

 

Docker主机数据挂载到容器


2018年12月19日 22:11:00   1,300 次浏览

Docker数据管理

Docker提供三种不同的方式将数据从宿主机挂载到容器中:volumes,bind mounts 和tmpfs。

olumes

docker管理宿主机文件系统的一部分(/var/lib/docker/volumes)。

管理卷

[root@docker-master ~]# docker volume create nginx-vo1
nginx-vo1

[root@docker-master ~]# docker volume ls

DRIVER              VOLUME NAME
local               144c5839b36278cea8b3bc7bf3d10d051c11e7acc5610d420f25314f240ef574
local               a0c117c96d84eeaeebd548eda827577f17926a595c85ea69a9f4ea9f756c1557
local               f0e495702096c6845110ae74e0ed24455059e1265831acca1bb303458b4400ab
local               nginx-vo1

[root@docker-master ~]# docker volume inspect nginx-vo1
[
   {
       "CreatedAt": "2018-12-12T01:05:59+08:00",
       "Driver": "local",
       "Labels": {},
       "Mountpoint": "/var/lib/docker/volumes/nginx-vo1/_data",
       "Name": "nginx-vo1",
       "Options": {},
       "Scope": "local"
   }
]

用卷创建一个容器

docker run -itd --name=nginx-test --mount src=nginx-vo1,dst=/usr/share/nginx/html nginx

进入容器内部查看nginx web目录

[root@docker-master ~]# docker exec -it nginx-test bash

root@23821377e2f7:/# ls /usr/share/nginx/html/
50x.html  index.html

在宿主机docker volumes目录可以发现有相同的两个html文件,无论是在容器内新创建文件还是在宿主机创建文件,内容都不会同步映射到对应目录

[root@docker-master ~]# ll /var/lib/docker/volumes/nginx-vo1/_data/

total 8
-rw-r--r-- 1 root root 494 Nov 27 20:31 50x.html
-rw-r--r-- 1 root root 612 Nov 27 20:31 index.html

清理

docker container stop nginx-test
docker container rm nginx-test
docker volume rm nginx-vo1

注意:

1.如果没有指定卷,自动创建

2.建议使用–mount,更通用

bind mounts

可以存储在宿主机系统的任意位置。将宿主机的目录映射到容器中做持久卷,删除容器后数据存储在宿主机上不会丢失

用卷创建一个容器

[root@docker-master ~]# mkdir /app/wwwroot -p

[root@docker-master ~]# docker run -itd --name=nginx-test --mount type=bind,src=/app/wwwroot,dst=/usr/share/nginx/html nginx
453be5dc46e410c3daa5c34f219ed5d195c134a538b2e5f85d0c1d333fd7e7ff

[root@docker-master ~]# docker exec -it nginx-test bash

root@453be5dc46e4:/# mount

/dev/mapper/docker-8:2-528671-5a0afb1cab58d9e41d9ec2ecfc57b7045452918f77a15ede38aa9acb69d7bdac on / type xfs (rw,relatime,nouuid,attr2,inode64,logbsize=64k,sunit=128,swidth=128,noquota)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev type tmpfs (rw,nosuid,size=65536k,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666)
sysfs on /sys type sysfs (ro,nosuid,nodev,noexec,relatime)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,relatime,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (ro,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (ro,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/cpuset type cgroup (ro,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/net_cls type cgroup (ro,nosuid,nodev,noexec,relatime,net_cls)
cgroup on /sys/fs/cgroup/blkio type cgroup (ro,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/devices type cgroup (ro,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/freezer type cgroup (ro,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (ro,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/perf_event type cgroup (ro,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/memory type cgroup (ro,nosuid,nodev,noexec,relatime,memory)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime)
/dev/sda2 on /etc/resolv.conf type ext4 (rw,relatime,data=ordered)
/dev/sda2 on /etc/hostname type ext4 (rw,relatime,data=ordered)
/dev/sda2 on /etc/hosts type ext4 (rw,relatime,data=ordered)
shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=65536k)
/dev/sda2 on /usr/share/nginx/html type ext4 (rw,relatime,data=ordered)/dev/sda2 on /usr/share/nginx/html type ext4 (rw,relatime,data=ordered)

进入到容器内使用mount命令可以发现/dev/sda2 on /usr/share/nginx/html type ext4 (rw,relatime,data=ordered)已经挂载了

docker run -itd --name=nginx-test -v /app/wwwroot:/usr/share/nginx/html nginx

验证绑定

docker inspect nginx-test

清理

docker container stop nginx-test
docker container rm nginx-test

注意:

1.如果源文件/目录没有存在,不会自动创建,会抛出一个错误。

docker: Error response from daemon: invalid mount config for type “bind”: bind source path does not exist.
See ‘docker run –help’.

2.如果挂载目标在容器中非空目录,则该目录现有内容将被隐藏。

tmpfs

挂载存储在宿主机系统的内存中,而不会写入宿主机的文件系统。

Docker容器监控系统 cAdvisor+InfluxDB+Grafana


2018年12月19日 14:54:00   1,561 次浏览

cAdvisor:Google开源的工具,用于监控Docker主机和容器系统资源,通过图形页面实时显示数据,但不存储;它通过宿主机/proc、/sys、/var/lib/docker等目录下文件获取宿主机和容器运行信息

InfluxDB:是一个分布式的时间序列数据库,用来存储cAdvisor收集的系统资源数据。

Grafana:可视化展示平台,可做仪表盘,并图表页面操作很方便,数据源支持zabbix、Graphite、InfluxDB、OpenTSDB、Elasticsearch等

它们之间关系:

cAdvisor容器数据采集—–> InfluxDB容器数据存储—–>Grafana可视化展示

部署InfluxDB

[root@docker-master ~]# docker run -itd  -p8083:8083  --name influxdb  tutum/influxdb
Unable to find image 'tutum/influxdb:latest' locally
latest: Pulling from tutum/influxdb
a3ed95caeb02: Pull complete
23efb549476f: Pull complete
aa2f8df21433: Pull complete
ef072d3c9b41: Pull complete
c9f371853f28: Pull complete
a248b0871c3c: Pull complete
749db6d368d0: Pull complete
db2492acfcc3: Pull complete
b7e7d2e12d53: Pull complete
4272a53eef10: Pull complete
9b2fefdb5321: Pull complete
Digest: sha256:2772d80e80284b801c6ef255f7e185dd5290757f0f031d77762390dd4df2a9a3
Status: Downloaded newer image for tutum/influxdb:latest
docker: Error response from daemon: mkdir /var/lib/docker/overlay/6d0d06de343d70cdd581480780933d4c05904bce9cf381efc1ac630f28acd083-init/merged/dev/shm: invalid argument.

在docker run的时候报了一个错, mkdir /var/lib/docker/overlay/6d0d06de343d70cdd581480780933d4c05904bce9cf381efc1ac630f28acd083-init/merged/dev/shm: invalid argument

解决方法:

[root@docker-master ~]# vim /etc/docker/daemon.json
{
"storage-driver": "devicemapper"
}


[root@docker-master ~]# docker run -idt -p 8083:8083 --name influxdb tutum/influxdb
Unable to find image 'tutum/influxdb:latest' locally
latest: Pulling from tutum/influxdb
a3ed95caeb02: Pull complete
23efb549476f: Pull complete
aa2f8df21433: Pull complete
ef072d3c9b41: Pull complete
c9f371853f28: Pull complete
a248b0871c3c: Pull complete
749db6d368d0: Pull complete
db2492acfcc3: Pull complete
b7e7d2e12d53: Pull complete
4272a53eef10: Pull complete
9b2fefdb5321: Pull complete
Digest: sha256:2772d80e80284b801c6ef255f7e185dd5290757f0f031d77762390dd4df2a9a3

创建数据库用来存放cadvisor采集的数据,再创建数据库用户供grafana连接

create database "cadvisor"

查看数据库可以发现数据库已经创建成功了。

# 创建用户

create user "cadvisor" with password 'cadvisor'

OK,数据库用户也已经创建成功。

show users

 

部署cAdvisor

docker run -d \
--volume=/:/rootfs:ro \
--volume=/var/run:/var/run:rw \
--volume=/sys:/sys:ro \
--volume=/var/lib/docker/:/var/lib/docker:ro \
--link influxdb:influxdb \
-p 8081:8080 \
--name=cadvisor \
google/cadvisor:latest \
-storage_driver=influxdb \
-storage_driver_db=cadvisor \
-storage_driver_host=influxdb:8086



Unable to find image 'google/cadvisor:latest' locally
latest: Pulling from google/cadvisor
ab7e51e37a18: Pull complete
a2dc2f1bce51: Pull complete
3b017de60d4f: Pull complete
Digest: sha256:9e347affc725efd3bfe95aa69362cf833aa810f84e6cb9eed1cb65c35216632a
Status: Downloaded newer image for google/cadvisor:latest
c4b2aa505f02e01f61a704dfeac9574ca9d36897896bda9be5c8d688d9ed441f

部署Granfana

docker run -d \
-p 3000:3000 \
-e INFLUXDB_HOST=localhost \
-e INFLUXDB_PORT=8086 \
-e INFLUXDB_NAME=cadvisor \
-e INFLUXDB_USER=cadvisor \
-e INFLUXDB_PASS=cadvisor \
--link influxdb:influxsrv \
--name grafana \
grafana/grafana



Unable to find image 'grafana/grafana:latest' locally
latest: Pulling from grafana/grafana
a5a6f2f73cd8: Pull complete
08e6195c0f29: Pull complete
b7bd3a2a524c: Pull complete
d3421658103b: Pull complete
cd7c84229877: Pull complete
49917e11f039: Pull complete
Digest: sha256:b9a31857e86e9cf43552605bd7f3c990c123f8792ab6bea8f499db1a1bdb7d53
Status: Downloaded newer image for grafana/grafana:latest
65e7390c90d8595495a776a66a03df77e474a4fa69d9d248f71236c5b62a580b

访问http://IP:3000 默认用户:admin 密码:admin

添加数据源Add Type里面选择InfluxDB ,最后选择Save&Test 保存测试。

URL填写:http://influxdb:8086  数据库用户及密码填写:cadvisor

在grafana上绘制图表即可展现相关数据

MySQL连接异常java.sql.SQLException: Value ‘0000-00-00’ can not be represen


2018年12月19日 11:30:00   1,133 次浏览

最近准备上线新业务,业务代码使用的是spring框架,将开发提供的代码仓库地址git clone到测试环境机器进行mvn打包部署到另外的测试机器中,在启动服务是报异常

java.sql.SQLException: Value '0000-00-00' can not be represented as java.sql.Date

gg查了一下相关资料,原来是因为实际的字段在数据库中为空,其默认值为“0000-00-0000:00:00”,在mysql中作为一个特殊值存在。但是在java项目编译的时候会被视为不合法的值,被JVM认为格式不正确。

解决方法: 在jdbc url加上zeroDateTimeBehavior=convertToNull参数,重新启动服务后正常。

配置 Nginx 反向代理 WebSocket


2018年11月29日 18:46:33   1,230 次浏览

一般设置代理只需按以下来设置是没有问题的,但是今天我们要操作的websockt

 

server {
    location / {
        proxy_pass http://127.0.0.1:10086;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header REMOTE-HOST $remote_addr;
        proxy_http_version 1.1;
        proxy_read_timeout 300s;
    }
}

 

nginx -t一下

会出现如下错误:
nginx: [emerg] unknown "connection_upgrade" variable

就是这里出现了个坑

其中涉及到了一个nginx的设计问题 End-to-end and Hop-by-hop Headers
我在这里还是不过多赘述了,以免误人子弟

map在nginx中是为一个或多个变量设置映射表

 

下面是需要添加的几项配置:

http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    server {
        location / {
            #…
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
        }
    }
}