K3s MySQL 시작 OOM 검색 을 한 번 기록 합 니 다.

예전 에는 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
dmesgmysqld 분배 가 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
", pid, probe, args->addr, args->len, args->flags); /* printf("%s
", ustack(perf, 10)); */ } tracepoint:syscalls:sys_enter_brk / comm == "mysqld" / { printf("%d %s brk %d
", 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)을 가 져 오 려 고 시 도 했 지만 유용 한 정 보 를 가 져 오지 못 했 습 니 다.my sql 이 컴 파일 되 지 않 았 을 때 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
", 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을 볼 수 있 습 니 다.my sql 은 시스템 자원 제한 에 따라 메모 리 를 분배 할 것 으로 추 정 됩 니 다.
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

좋은 웹페이지 즐겨찾기