AWSの無料枠EC2でelasticsearchを動かす時にメモリが足りない!!

でもあります


みなさんこんにちは 久しぶりに公開できる技術記事が書けてうれしい大学院生のたかです

主語を大きくしてしまった(恣意的)のですが
今回は
– AWS 無料枠(t2.micro,t3.micro)で
– docker-composeを用いて
– elasticsearchを実行させた時
圧倒的メモリリソース不足でEC2(VM)がフリーズするという経験をしたので,どのように解決したかを残していこうかと思います

TL;DR

スワップをEBS or ストレージを作成して
elastic searchのbootstrap.memory_lockをfalseにしよう

背景

私の研究室ではEsaというwikiツールを使って情報のストックをしています.
今回はお仕事の方で社内Wikiを作成することになったので,同じようなWikiかつOSSのものを探していたところGrowi(https://growi.org/ja/#features)と出会いました.

https://growi.org/ja/#features
Growi: https://growi.org/ja/#features

さてこのGrowi, ありがたいことにdocker-composeでのデプロイが可能なのです!

ほぼdocker-compose upだけで同じサービスが立つのはありがたいですね..

というわけで今回はこのGrowiをAWSの無料枠でつかえるEC2(VM) t2.microやt3.microで作ってみました.

環境

AWS EC2 t3.micro (CPU x2, RAM 1GB)
Ubuntu 20.04
docker-compose v2

いざdocker-compose upするもメモリ爆発からのフリーズ(ssh不可)

Growiは大きく3つのサービスが連携して動いています
– nodeで動いているgrowi_app
– 記事検索用のelasticsearch
– データベースのmongodb

elasticsearchを使ったことのある方ならご存知かなと思うのですが,彼はとってもメモリ(物理RAM)が大好物です

とりあえずGrowiのdocker-compose upした直後を覗いてみましょう

top - 08:57:37 up 1 day, 14:39,  1 user,  load average: 0.03, 0.04, 0.01
Tasks: 126 total,   1 running, 125 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.3 us,  0.3 sy,  0.0 ni, 99.2 id,  0.0 wa,  0.0 hi,  0.0 si,  0.2 st
MiB Mem :    941.2 total,     79.4 free,    554.9 used,    307.0 buff/cache
MiB Swap:   4096.0 total,   3632.0 free,    464.0 used.     79.0 avail Mem

    PID USER      PR  NI    VIRT  %MEM    RES    SHR   SWAP   USED S  %CPU     TIME+ COMMAND
   5773 ubuntu    20   0 2828352  50.4 486224 156488 130428 616652 S   0.3  38:34.30 java
   6149 ubuntu    20   0  984528   5.9  56424  13172  65292 121716 S   0.0   1:10.63 node
   5686 systemd+  20   0 1595984   2.4  23584   7148  58812  82396 S   0.3  10:29.00 mongod
    739 root      20   0 1889352   2.3  22352   9660  10664  33016 S   0.0   0:36.92 dockerd
    289 root      rt   0  280156   1.9  17964   8220      0  

WordPressのコードの問題でずれてしまっているのですが,
要約すると
物理メモリは残り79.4MBしかない
スワップがあるのにもかかわらずスワップを利用していない
メモリの50.4%をjava(elasticsearch)が占めている
ということで,この状態でGrowiに大体4人以上アクセスしたり,記事を追加したりすると
OOM(Out of Memory)状態となりEC2インスタンスが激重になります

わかりやすいところで言うと
– psコマンドで50秒かかる
– そもそもsshdが応答しなくなる→切断
– 原因のkill java/docker-compose downを実行するも5分待機しても進まない
– AWSコンソールからのrebootすら効かない

といった地獄が待っています

ちなみにjavaプロセスが実行しているのがelasticsearchだということはpsコマンドで確認できます

ubuntu@ip-xxxxxxxxx:~$ ps aux |grep java
ubuntu      5773  1.6 49.8 2828408 480364 ?      SLl  Dec21  38:47 /usr/share/elasticsearch/jdk/bin/java -Xshare:auto -Des.networkaddress.cache.ttl=60 -Des.networkaddress.cache.negative.ttl=10 -XX:+AlwaysPreTouch -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true -XX:-OmitStackTraceInFastThrow -XX:+ShowCodeDetailsInExceptionMessages -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true -Dio.netty.recycler.maxCapacityPerThread=0 -Dio.netty.allocator.numDirectArenas=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Dlog4j2.formatMsgNoLookups=true -Djava.locale.providers=SPI,COMPAT --add-opens=java.base/java.io=ALL-UNNAMED -XX:+UseG1GC -Djava.io.tmpdir=/tmp/elasticsearch-3065984185847890629 -XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError -XX:HeapDumpPath=data -XX:ErrorFile=logs/hs_err_pid%p.log -Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m -Des.cgroups.hierarchy.override=/ -Xms5m -Xmx128m -XX:MaxDirectMemorySize=67108864 -XX:G1HeapRegionSize=4m -XX:InitiatingHeapOccupancyPercent=30 -XX:G1ReservePercent=15 -Des.path.home=/usr/share/elasticsearch -Des.path.conf=/usr/share/elasticsearch/config -Des.distribution.flavor=default -Des.distribution.type=docker -Des.bundled_jdk=true -cp /usr/share/elasticsearch/lib/* org.elasticsearch.bootstrap.Elasticsearch -Ebootstrap.memory_lock=true
ubuntu     16299  0.0  0.2   8164  2120 pts/0    S+   09:09   0:00 grep --color=auto java

最後の-Ebootstrap.memory_lock=true というのが実はelasticsearchのメモリに関する重要な設定だったりします(このほかにもXms5m -Xmx128mがありますが,一旦置いておきます)

-Ebootstrap.memory_lock=trueとはなにか

elasticsearchは基本的にスワップメモリの利用を推奨していません

It should be obvious, but it bears spelling out clearly: swapping main memory to disk will crush server performance. Think about it: an in-memory operation is one that needs to execute quickly.

https://www.elastic.co/guide/en/elasticsearch/guide/current/heap-sizing.html#_swapping_is_the_death_of_performance

なのでデフォルトでスワップを使わない設定-Ebootstrap.memory_lockがtrueになっています

https://www.elastic.co/guide/en/elasticsearch/reference/master/_memory_lock_check.html

しかし今回のような最小限のリソースといいますか,制約が強い環境ではスワップを使う設定にしないとelasticsearchよりも先にosがdeathします

しかしながら -Ebootstrap.memory_lockをfalseにしたら検索がめちゃめちゃ遅くなっても困りますよね…
というわけで今回はそこら辺を調査してみました

bootstrap.memory_lockをfalseへ

docker-compose.ymlに環境変数を追加するか

elasticsearch:
    build:
      context: ./elasticsearch
      dockerfile: ./Dockerfile
    environment:
      - bootstrap.memory_lock=false
      - "ES_JAVA_OPTS=-Xms5m -Xmx128m"  # increase amount if you have enough memory
      - LOG4J_FORMAT_MSG_NO_LOOKUPS=true # CVE-2021-44228 mitigation for Elasticsearch <= 6.8.20/7.16.0
    ulimits:
      memlock:
        soft: -1
        hard: -1
    restart: unless-stopped
    volumes:
      - es_data:/usr/share/elasticsearch/data
      - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
/usr/share/elasticsearch/config/elasticsearch.ymlにパラメータを追加します
bootstrap.memory_lock: false
bootstrap.memory_lock=%MEMRES(KiB)SHR(KiB)SWAP(KiB)USED(KiB)
true51.749841215677295924594336
false35.33402841542434616374900
-16.4-158128-141348-61308-219436
topでメモリの使用量の差を確認する

メモリ使用量は明確に差が出ている
しかし、興味深いことにアプリケーションにおけるスワップ利用率(計算式 SWAP/USED)自体は
trueの方が16.1%, falseは9.2%とtrueの時の方が高くなっている なんでだろうね..(思考放棄)

しかし、elastic searchではfalseにしてスワップを利用することを非推奨としている
そのため、Growi上での検索に要する時間を計測し、差があるか確認する
検索機能でGrowiのデフォルト機能で作成してある記事
sandboxcan use the mark tagという文字列を検索してみる
キャッシュを回避するため、今までに検索したことのないキーワードをチョイスする
試行回数は3回とした。

bootstrap.memory_lock= true

bootstrap.memory_lock= false

bootstrap.memory_lock=responsetime
true335[ms],351[ms],363[ms]
false315[ms],374[ms],302[ms]
それぞれの設定のときの検索結果を取得するまでのレスポンスタイム

揺れがあったが(trueの最速レスポンスタイムに対して-6%~16%)、ほとんど差がないと言ってよいと思われる
また、falseの3回目は体感できるくらいに検索が高速化されていた
これはおそらくelasticsearchのインデックスが内部で書き換わってそう..

というわけでAWSのt3.microインスタンスでも動かすためにメモリ消費量をできるだけ抑えたい
今回はfalseで続行!

これが省リソース上でのelasticsearchのデプロイ周りで何かの助けになれば幸いです!

また,私が今回わからなかったこれをfalseにすることへのデメリットなどご存知のかたいらっしゃったらぜひコメントで教えてください!

コメントを残す