以前用Docker Compose做开发环境,MySQL也在Docker里运行,一切使用正常。后来开发环境迁移到K3s(轻量级的K8s),但是MySQL一启动就被OOM Killer干掉,所以一直没迁移MySQL。

重现

使用Kubectl直接运行一个MySQL即可重现:

apiVersion: apps/v1

kind: Deployment

metadata:

name: mysql

spec:

replicas: 1

selector:

matchLabels:

app: mysql

template:

metadata:

labels:

app: mysql

spec:

containers:

- name: mysql

image: mysql:5.7

imagePullPolicy: IfNotPresent

env:

- name: MYSQL_ROOT_PASSWORD

value: root

resources:

limits:

memory: 4G

cpu: 500m

dmesg 可以看mysqld分配超过了3.7G内存然后被杀掉:

[ 839.399262] Tasks state (memory values in pages):

[ 839.399263] [ pid ] uid tgid total_vm rss pgtables_bytes swapents oom_score_adj name

...

[ 839.399278] [ 34888] 0 34888 4208240 974177 7962624 0 -998 mysqld

[ 839.399280] oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=...,mems_allowed=0,oom_memcg=/kubepods/pod..,task_memcg=/kubepods/pod../56..,task=mysqld,pid=34888,uid=0

[ 839.399294] Memory cgroup out of memory: Killed process 34888 (mysqld) total-vm:16832960kB, anon-rss:3895388kB, file-rss:1320kB, shmem-rss:0kB

[ 839.496988] oom_reaper: reaped process 34888 (mysqld), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

排查

优化MySQL配置

第一反应是MySQL配置有问题,于是修改配置减少各种buffer的大小:

[mysqld]

innodb_buffer_pool_size = 32M

innodb_buffer_pool_instances=1

innodb_log_file_size = 64M

innodb_log_buffer_size = 8M

key_buffer_size = 16k

myisam_sort_buffer_size = 16k

max_connections = 50

open_files_limit = 4096

max_allowed_packet = 1M

table_open_cache = 16

sort_buffer_size = 512k

net_buffer_length = 8K

read_buffer_size = 256K

read_rnd_buffer_size = 256K

thread_cache_size = 64

query_cache_size = 0

tmp_table_size = 12M

thread_stack=256K

然而问题依旧,改了不少配置都没有效果,看来需要跟踪或调试。

用bpftrace定位内存分配

这里没有使用strace主要是因为启动就被杀掉,需要修改镜像启动命令然后在容器内安装和执行strace ,而bpftrace可以系统全局跟踪,在容器外就可以完成跟踪,更加方便。

内存分配主要通过skb和mmap系统调用,用bpftrace跟踪这两个系统调用:

#!/usr/bin/bpftrace

tracepoint:syscalls:sys_enter_mmap / comm == "mysqld" /

{

printf("%d %s addr=%ld len=%ld flags=%ld\n", pid, probe, args->addr, args->len, args->flags);

/* printf("%s\n", ustack(perf, 10)); */

}

tracepoint:syscalls:sys_enter_brk / comm == "mysqld" /

{

printf("%d %s brk %d\n", pid, probe, args->brk);

}

sudo ./mysql-oom.bt

Attaching 2 probes...

57950 tracepoint:syscalls:sys_enter_brk brk 0

57950 tracepoint:syscalls:sys_enter_mmap addr=0 len=8740 flags=2

...

...

57950 tracepoint:syscalls:sys_enter_brk brk 1699086336

57950 tracepoint:syscalls:sys_enter_mmap addr=0 len=17179869184 flags=34

可以看到最后用 mmap 一次分配了 16G 内存,然后就被杀了。

尝试获取调用堆栈(ustack),但是都拿不到有用的信息,可能mysql没有编译时没有开启frame pointer:

97694 tracepoint:syscalls:sys_enter_mmap addr=0 len=12288 flags=34

7f84c662730a 0x7f84c662730a ([unknown])

97694 tracepoint:syscalls:sys_enter_mmap addr=0 len=17179869184 flags=34

7f84c4c4064a 0x7f84c4c4064a ([unknown])

拿不到堆栈,再尝试跟踪所有系统系统调用:

#!/usr/bin/bpftrace

tracepoint:syscalls:sys_enter_* / comm == "mysqld" /

{

printf("%d %s\n", pid, probe);

}

...

输出:

Attaching 331 probes...

...

115490 tracepoint:syscalls:sys_enter_close

115490 tracepoint:syscalls:sys_enter_brk

115490 tracepoint:syscalls:sys_enter_newstat

115490 tracepoint:syscalls:sys_enter_getrlimit

115490 tracepoint:syscalls:sys_enter_mmap addr=0 len=17179869184 flags=34

可以看到在最后一次 mmap 前调用了 getrlimit , 猜测是mysql会根据系统资源限制来分配内存。

MySQL源码分析

MySQL源码中直接调用 getrlimit的地方不多,排除了ndb、innodb_memcached、libevent之后,只有一处直接调用:

static uint set_max_open_files(uint max_file_limit)

{

struct rlimit rlimit;

uint old_cur;

DBUG_ENTER("set_max_open_files");

DBUG_PRINT("enter",("files: %u", max_file_limit));

if (!getrlimit(RLIMIT_NOFILE,&rlimit))

{

old_cur= (uint) rlimit.rlim_cur;

DBUG_PRINT("info", ("rlim_cur: %u rlim_max: %u",

(uint) rlimit.rlim_cur,

(uint) rlimit.rlim_max));

if (rlimit.rlim_cur == (rlim_t) RLIM_INFINITY)

rlimit.rlim_cur = max_file_limit;

if (rlimit.rlim_cur >= max_file_limit)

DBUG_RETURN(rlimit.rlim_cur); /* purecov: inspected */

rlimit.rlim_cur= rlimit.rlim_max= max_file_limit;

if (setrlimit(RLIMIT_NOFILE, &rlimit))

max_file_limit= old_cur; /* Use original value */

else

{

rlimit.rlim_cur= 0; /* Safety if next call fails */

(void) getrlimit(RLIMIT_NOFILE,&rlimit);

DBUG_PRINT("info", ("rlim_cur: %u", (uint) rlimit.rlim_cur));

if (rlimit.rlim_cur) /* If call didn't fail */

max_file_limit= (uint) rlimit.rlim_cur;

}

}

DBUG_PRINT("exit",("max_file_limit: %u", max_file_limit));

DBUG_RETURN(max_file_limit);

其中逻辑是:如果系统的文件打开限制是 RLIM_INFINITY 或者比要设置的 max_file_limit 大,都返回系统的限制。

这个函数也只被直接调用一次:

uint my_set_max_open_files(uint files)

{

struct st_my_file_info *tmp;

DBUG_ENTER("my_set_max_open_files");

DBUG_PRINT("enter",("files: %u my_file_limit: %u", files, my_file_limit));

files+= MY_FILE_MIN;

files= set_max_open_files(MY_MIN(files, OS_FILE_LIMIT)); // 获取最大打开文件数

if (files <= MY_NFILE)

DBUG_RETURN(files);

// 分配内存

if (!(tmp= (struct st_my_file_info*) my_malloc(key_memory_my_file_info,

sizeof(*tmp) * files,

MYF(MY_WME))))

DBUG_RETURN(MY_NFILE);

// 初始化

/* Copy any initialized files */

memcpy((char*) tmp, (char*) my_file_info,

sizeof(*tmp) * MY_MIN(my_file_limit, files));

memset((tmp + my_file_limit), 0,

MY_MAX((int) (files - my_file_limit), 0) * sizeof(*tmp));

my_free_open_file_info(); /* Free if already allocated */

my_file_info= tmp;

my_file_limit= files;

DBUG_PRINT("exit",("files: %u", files));

DBUG_RETURN(files);

}

原来MySQL会根据最大可打开文件数,提前为每个文件分配和初始化内存,在这个时候就可能分配过多内存,导致OOM。

解决

启动前设置ulimit

因为K8s目前不支持设置ulimit,不少Helm Chart都在启动前设置ulimit。

command: ["sh","-c", "ulimit -n 4096 && exec /usr/local/bin/docker-entrypoint.sh mysqld"]

修改K3s设置

K3s通过systemd启动,可以修改k3s.service,限制K3s打开文件数,这个限制会传给containerd:

LimitNOFILE=1048576

mysql docker oom_记一次 K3s MySQL 启动 OOM 排查相关推荐

  1. mysql docker oom_如何让docker容器不会被oomkill掉设置oomkilldisable

    Docker资源限制 在默认的情况下,docker没有对容器进行硬件资源的限制,当容器负载过高时会尽肯能第占用宿主机资源.在这基础上,docker提供了针对容器的内存,CPU,IO资源的控制方式.(真 ...

  2. mysql docker 制作_docker 制作自己的mysql镜像

    1.下载mysql,本次以mysql-5.7.29-1.el7.x86_64.rpm-bundle.tar为例 2.编辑启动脚本start.sh,容器启动时会运行这个脚本,主要是安装并初始化mysql ...

  3. Mysql Docker 主从配置

    Mysql Docker 主从配置 操作系统:Windows10 使用的MySQL8.0的docker镜像 总览 前置环境说明     首先在系统中起了一个MySQL8.0的docker镜像,并在其中 ...

  4. 借力 Docker ,三分钟搞定 MySQL 主从复制!

    hello 各位小伙伴大家好,今年 5 月份的时候,松哥和大家聊过如何搭建 MySQL 主从复制: 提高性能,MySQL 读写分离环境搭建(一) 提高性能,MySQL 读写分离环境搭建(二) 不过很多 ...

  5. JDBC操作MySQL Lob字段记实

    JDBC操作MySQL Lob字段记实 虽然Java的持久化框架多如牛毛,但都离不开JDBC技术,JDBC在某些时候是其他框架难以取代的.也是java操作数据库最根本的技术. 上文写了JDBC操作DB ...

  6. Docker入门(三) - 搭建mysql

    搭建mysql 1.下载mysql镜像 ➜ docker pull mysql:5.7 5.7: Pulling from library/mysql 75a822cd7888: Pull compl ...

  7. 解决Docker容器内访问宿主机MySQL数据库服务器的问题

    解决Docker容器内访问宿主机MySQL数据库服务器的问题 参考文章: (1)解决Docker容器内访问宿主机MySQL数据库服务器的问题 (2)https://www.cnblogs.com/ga ...

  8. docker 安装mysql_docker学习4docker安装mysql环境

    前言 docker安装mysql环境非常方便,简单的几步操作就可以了 拉取mysql镜像 先拉取mysql的镜像,可以在docker的镜像仓库找到不同TAG标签的版本https://hub.docke ...

  9. 从零开始学习docker(十)搭建WordPress+Mysql,为什么要用Docker Compose

    我们从这一节开始介绍一个非常非常重要的工具--Docker Compose,这个工具会经常使用. 先从一个案例介绍一下我们为什么要用Docker Compose. 案例:如何通过容器部署一个WordP ...

最新文章

  1. 被誉为「教科书」,牛津大学231页博士论文全面阐述神经微分方程,Jeff Dean点赞...
  2. golang 去除 空白字符 含制表符
  3. Tesseract——OCR图像识别 入门篇
  4. jsforeach异步的问题_js中forEach回调同异步题目
  5. Python学习笔记:Day13 提升开发效率
  6. 【五校联考7day1】游戏
  7. C# 获取FormData数据
  8. 侠客博客v1.0 正式版版本发布
  9. OFCMS 项目开源 java cms 系统 内容管理系统
  10. ubuntu下搭建tftp服务器并且验证功能
  11. 华为交换机开启ftp服务,上传和下载文件,get和put操作实例
  12. 国际云安全证书CCSK让他们在职场中脱颖而出
  13. 铃木dl250参数_豪爵铃木DL250 ABS测评-通勤篇
  14. Maven server structure problem
  15. 金融贷超、贷款行业防止撸贷方案分析
  16. 计算机往届生考研失败找工作,考研二战失败如何找工作 考研往届生找工作的方法...
  17. reactjs中使用高德地图计算两个经纬度之间的距离
  18. 3的16次方用计算机怎么算,excel2016次方公式怎么用
  19. 权威认可 | 悬镜云鲨RASP荣获信通院“2021金融行业年度卓越创新案例”
  20. Centos8.0编译安装稳定最新版的nginx

热门文章

  1. 平板电脑win10 android比较好,华为MateBook名不副实!Win10平板电脑最合适
  2. 浅谈强化学习二之马尔卡夫决策过程与动态规划
  3. 微信小程序九宫格布局
  4. 计算机毕业论文怎样写系统的意义,毕业论文写作的目的意义及步骤-计算机论文...
  5. Node 的单元测试--jasmine
  6. 华为云磁盘挂载与分区扩容
  7. amd为什么还用针脚_闲聊CPU针脚 一年一换都怪AMD不给力?
  8. 李德毅院士:大数据认知
  9. 4.3.3 风管翻模
  10. uni-app h5 使用微信JSSDK的方式