如图,你觉得当前系统可用内存还有多少?实际上剩余内存不是
1.5gb
,而是2.5Gb
。
你需要知道这个命令展示的 buffer/cache
实际上指的是缓冲区数据大小,是被操作系统分配了但还未使用的内存空间。
-
buffer
全称buffer cache
,是块设备的缓冲区,即磁盘缓冲区。操作系统会先把磁盘块读取到buffer cache
中,cache
空间不够时会通过一定的策略清除部分cache
数据。 -
cache
全称page cache
,是文件系统的缓冲区,即内存缓冲区。几个块组成一个页,只缓存文件的内容,比buffer cache
更优先提供给应用程序使用。 -
swap
线上一般关闭,使用了交换空间意味着应用性能会较大幅度下降。
如果 cache
的值比较大,说明被缓存的文件数量比较多,这样的话磁盘的读取IO压力就会相对小。
redis
一旦开启持久化,尤其是 aof
持久化方式,这个 buffer/cache
就会增长的比较快。但实际上这个参数变大并不会影响系统使用,应用需要的内存还是可以分配。
但容器环境可就不一样了哦。容器部署的情况下,无论是 docker stat
还是 cadvisor
统计方式,都会将 buffer/cache
计算到已使用的内存中,所以配套的指标阈值告警也会存在问题。这个问题在 docker17.06
版本得到解决,但还是建议实际测试下自己的 docker
和监控方案。
redis
创建子进程做 aofrewrite
时,如果内存资源不足,会导致 fork
失败,fd
不会被释放,报错提示不能申请内存。可以通过 /proc/${pid}/fd
下看某 redis
线程具体的 fd
数量。
通过 redis-cli
查看 info memory
内存使用大概了23Gb
,机器本身有 32Gb
可用,buffer/cache
目前使用约 2Gb
,意味着剩余可分配内存约 8Gb
。
关于 redis
启动时日志提醒了三个配置项优化点:
30914:M 03 Jan 16:30:34.425 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
30914:M 03 Jan 16:30:34.425 # Server started, Redis version 3.2.11
30914:M 03 Jan 16:30:34.425 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
30914:M 03 Jan 16:30:34.425 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
-
tcp
连接问题
意思就是这个参数系统设置为
128
,但你的 redis配置设置的511
超过了系统值。解决方式是通过修改内核配置文件sysctl.conf
,添加net.core.somaxconn= 1024
,即将这个系统参数设置大一点,这叫操作系统性能优化。
- 内存分配策略设置不当
应用程序向操作系统申请内存时,内存不足内核会怎么处理?
-
overcommit_memory=0
,系统默认值,即系统剩余内存不能满足你这次的申请需要,就返回报错信息; -
overcommit_memory=1
,redis.conf
建议值,即允许分配所有的物理内存,就是先给你再说,最终还是不够就直接oom
。 -
overcommit_memory=2
,允许分配一定量的物理内存(overcommit_ratio
)和交换空间的内存值,一般不会使用,除非你的redis
内存硬件资源不足,又不能关机加内存,临时可以用这种方式。
所以啊,老老实实设置
vm.overcommit=1
吧。
- 系统默认设置的
透明大页
可能会存在性能问题
还是页式存储的问题,每个内存页默认为
4kb
或8kb
大小。如果要在内存中运行比较大的应用程序,那么操作系统以4kb
为单位进行物理地址映射时就会产生比较多的缺页中断和 TLBMiss,从而大大影响性能。
为了能够动态的修改虚拟内存单位的大小,linux
引入了hugetlbfs
文件系统,最大支持设置每页2Mb
内存空间。
对于目前大多数做了4k
优化的应用程序而言,一旦动态修改了这个参数,分配系统内存时就会涉及各种内存锁,应用程序性能随机就会下降,所以我们要禁用这个参数。
在 /etc/rc.local
文件添加两行即可:
echo never > /sys/kernel/mm/redhat_transparent_hugepage/defrag
echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled
线上环境 k8s
集群安装成功后,服务拉镜像部署的过程中机房断电了。你们猜猜会影响了什么?
-
xfs
文件系统损坏 -
etcd
心跳检测断连
etcd
断连的话,从集群摘除再重新加入集群就好了? 但是 systemctl restart etcd
依然不生效,必须先 systemctl stop etcd
再 systemctl start etcd
才行。之前 docker
和 nginx
也出现过这种问题,执行 systemctl reload nginx
和 systemctl restart docker
也都有过无效的情况,必须先停止再启动,更底层的原因就没有去深究了。
重点的 xfs
文件系统损坏的问题。容器启动失败,查看日志可以很明显看到文件系统相关的报错。先使用 xfs_repair -n
检查确定某台节点的文件系统损坏。再检查了一下故障实例所在的节点,确实都在这台机器上,且这台机器上的部分实例是能正常运行的。
xfs_repair
命令就可以修复损坏的 xfs
文件系统(垃圾浪潮磁盘真的经常出问题),但是执行这个命令之前你要umount
相关的磁盘。如果这个磁盘挂载的目录正在使用中怎么办呢?你 umount -f
都没用,永远都是 device is busy
,这可怎么办?
通过 fuser
这个工具可以找出某个目录被哪些进程占用着,也可以杀这些进程(线上环境小心操作,建议先把服务调度到其他节点),知道了进程就可以通过 lsof、netstat
查看端口,猜测哪些服务使用,能否先 kill
掉。但 yum install fuser
发现没这个包?百度一下 yum install -y psmisc
就行了,这种问题在 centos
里是很常见的坑了。rhel8
改成 dnf
命令后就会提示你这个命令在哪个包里能使用,但估计还要一两年才能普及 centos8
吧。
结果 fuser -cu
看占用,fuser -ck
杀相关进程。没错能杀掉,但得多次执行后这目录又会被占用,因为有文件源源不断地在写入啊,即使你 fuser -ck /xxoo && umount
还是不行哦。
我和运维当时都懵逼了,我们频繁执行几次,特么居然成功了,别问我为什么,我也不知道为什么。和 systemctl
有时候 restart
失败一样:未知力量。然后修复 xfs
,重点是修复后,之前被 k8s
调度到这台节点的容器有好几个都无法启动,pod
状态为:ImageInspectError
。
这个状态是镜像校验过程发生错误,第一,我们部署包关于镜像校验脚本都没改动啊,第二,k8s集群本身是不是有对镜像的检验,第三,上传到docker-registy的镜像是否完整(第二次意识到harbor的重要性,第一次是跨集群镜像同步需求),第四,本地拉到的镜像是不是出问题了。
然后简单问了一下运维这次的部署包有变更吗,最近没有改动。k8s本身我记得是没有对docker image做什么判断改动的,就算有也应该最后去看(主要是其他服务正常,散列类型的出错不太可能是集群本身环境问题)然后docker-registry都仓库了我也没去验证啊,那我只能认为是下拉镜像过程sha1变了,或者拉下来后断电导致磁盘坏道,导致某些服务的overlayfs坏了,通过systemctl status docker和journalctl -f日志确实看到overlayfs报错(etcd断连同样这样看到的,这是基操),返回状态码-2,谷歌一下都说文件系统损坏,可我特么刚修复xfs文件系统啊。然后我认为是原因是后者,文件系统是当时出问题了,拉下来的镜像在repair后不完整了,这时候你想到了如何解决吗?
我先手动改yaml nodeName强制服务调度到非断电机器,结果服务正常了。(你要是K8s上万台就GG了)我查看某个ImageInspectErr服务,-owide查看镜像名和调度节点发现出问题的都在断电机器上。然后ssh去那个机器(你要是没权限就GG了),尝试性的在这个k8s节点docker命令删除本地镜像,结果特么打死都删除不了(因为有容器跑着啊),我特么迟疑了3s才想起来k8s deployment确保一直有一个容器服务启动,然后还要edit deploy replicas=0。然后删啊删,然后又改回来replicas=1,ok一切都成功啦!!!
结果特么还有个末尾,有个服务正常Running,0/1,logs查看一直都是sleep。这尼玛蛋疼一比,进去服务里看日志,尼玛Nodejs服务,提示丢失ippxxoo mudule,what??我特么写Nodejs的也没发现我们用了这个模块啊,问了运维他说上午这个服务运行是正常的。事后解决才想起,可能底层overlayfs丢失的磁盘数据里包含了这个modules吧。同样的道理操作解决(这个问题在上一条之前发现,困扰了十分钟)。本来只是因为开发网玩坏了codis导致找运维上物理机,结果全程2小时操蛋。另外目前docker更推荐的存储是overlay2,但建议内核 >4.0(我们是3.10跑着容器服务,才过很多坑了,如果你的大规模使用k8s,其实有坑也不用踩,最终一致性的集群,OOM之前也会给你重启,很多时候却是临时解决了可用性问题)
其实这个开发网codis问题后面还引发了半小时zk注册节点和codis-dashboard问题,就不说了,心累