OOM Killer に kill されないように Docker Container のメモリ使用量を増加させる方法

Written by @ryysud

Oct 8, 2018 21:13 · 1190 words · 3 minutes read #linux #docker

まえおき

“OOM Killer に関連するカーネルパラメータまとめ” なる記事を書いて、OOM Killer 周りのカーネルパラメータを理解したので適当にスクリプトを書くことにしました。

特定のプロセスを OOM Killer の kill 対象から除外する方法

OOM Killer の kill 対象から除外したいプロセスに対して、以下のどちらかを適用することで実現可能です。

  • /proc/${pid}/oom_score_adj の値を -1000 に変更する(Linux 2.6.36 以降ではこちらのパラメータが推奨)
  • /proc/${pid}/oom_adj の値を -17 に変更する

後述のスクリプトは Linux 2.6.36 以降であることを前提にしておりますので、Linux 2.6.36 以前で実行したい場合には、以下の処理をよしなに書き換えてください。

# Linux 2.6.36 以降
echo -n -1000 > /proc/${pid}/oom_score_adj

# Linux 2.6.36 以前
echo -n -17 > /proc/${pid}/oom_adj

事前に必要な作業

stress コマンドを用いるので、ベースイメージとしているOSに応じて適切な形でインストールしてください。( Alpine は stress コマンドを自前でビルドです…。悪しからず…。)

# RedHat系
$ yum install -y epel-release
$ yum install -y stress

# Debian系
$ apt-get update
$ apt-get install -y stress

# Alpine
$ apk add --update bash g++ make
$ wget -P /tmp https://people.seas.harvard.edu/~apw/stress/stress-1.0.4.tar.gz
$ cd /tmp
$ tar -xf stress-1.0.4.tar.gz
$ cd stress-1.0.4
$ ./configure
$ make
$ make install

スクリプトを Docker Container 内で実行する際には root ユーザーで –privileged オプションを忘れずに!

$ docker exec -it -u root --privileged <container name$ [bash|ash]

スクリプト

4つの worker process を起動して、それぞれに 1GB のメモリを確保させる形で合計 4GB のメモリを使用するロジックとなっております。お試しになりたい環境によって数値は調整してください。

#!/usr/bin/env bash

set -e

readonly WORKERS='4'
readonly MALLOC_MEMORY='1G'

stress --vm ${WORKERS} --vm-bytes ${MALLOC_MEMORY} --vm-hang 0 &
for pid in $(pgrep -f stress) ; do
    echo -n -1000 > /proc/${pid}/oom_score_adj
done

実際に試してみる

RedHat系(CentOS)

使用可能メモリに 1GB の制限をかけてコンテナを起動

$ docker run -d \
    --memory 1g \
    --name centos \
    centos:7 \
    sleep 10000000000

$ docker stats --no-stream centos
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT   MEM %               NET I/O             BLOCK I/O           PIDS
51ffd35556c2        centos              0.00%               888KiB / 1GiB       0.08%               898B / 0B           3.28MB / 69.6kB     1

rootユーザーで Docker Container に侵入(–privileged オプションを忘れずに!)

$ docker exec -it -u root --privileged centos bash

stress コマンドでメモリが食いつぶされてコンテナが停止する

# Docker Container の中
$ yum install -y epel-release
$ yum install -y stress
$ vi /tmp/stress.sh
$ bash /tmp/stress.sh

Debian系(Ubuntu)

使用可能メモリに 1GB の制限をかけてコンテナを起動

$ docker run -d \
    --memory 1g \
    --name ubuntu \
    ubuntu:18.04 \
    sleep 10000000000

rootユーザーで Docker Container に侵入(–privileged オプションを忘れずに!)

$ docker exec -it -u root --privileged ubuntu bash

stress コマンドでメモリが食いつぶされてコンテナが停止する

# Docker Container の中
$ apt-get update
$ apt-get install -y stress
$ apt-get install -y vim
$ vi /tmp/stress.sh
$ bash /tmp/stress.sh

Alpine

使用可能メモリに 1GB の制限をかけてコンテナを起動

$ docker run -d \
    --memory 1g \
    --name alpine \
    alpine:3.8 \
    sleep 10000000000

rootユーザーで Docker Container に侵入(–privileged オプションを忘れずに!)

$ docker exec -it -u root --privileged alpine ash

stress コマンドでメモリが食いつぶされてコンテナが停止する

# Docker Container の中
$ apk add --update bash g++ make
$ wget -P /tmp https://people.seas.harvard.edu/~apw/stress/stress-1.0.4.tar.gz
$ cd /tmp
$ tar -xf stress-1.0.4.tar.gz
$ cd stress-1.0.4
$ ./configure
$ make
$ make install
$ vi /tmp/stress.sh
$ bash /tmp/stress.sh

まとめ

stress コマンドに依存しているスクリプトで完全にイケてないので、誰かが更にいい感じのスクリプトをインターネットに放り投げてくれることを祈ります。アーメン。