でもあります
みなさんこんにちは 久しぶりに公開できる技術記事が書けてうれしい大学院生のたかです
主語を大きくしてしまった(恣意的)のですが
今回は
– 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)と出会いました.
さてこの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= | %MEM | RES(KiB) | SHR(KiB) | SWAP(KiB) | USED(KiB) |
true | 51.7 | 498412 | 156772 | 95924 | 594336 |
false | 35.3 | 340284 | 15424 | 34616 | 374900 |
差 | -16.4 | -158128 | -141348 | -61308 | -219436 |
メモリ使用量は明確に差が出ている
しかし、興味深いことにアプリケーションにおけるスワップ利用率(計算式 SWAP/USED)自体は
trueの方が16.1%, falseは9.2%とtrueの時の方が高くなっている なんでだろうね..(思考放棄)
しかし、elastic searchではfalseにしてスワップを利用することを非推奨としている
そのため、Growi上での検索に要する時間を計測し、差があるか確認する
検索機能でGrowiのデフォルト機能で作成してある記事sandbox
のcan use the mark tag
という文字列を検索してみる
キャッシュを回避するため、今までに検索したことのないキーワードをチョイスする
試行回数は3回とした。
bootstrap.memory_lock= true
bootstrap.memory_lock= false
bootstrap.memory_lock= | responsetime |
true | 335[ms],351[ms],363[ms] |
false | 315[ms],374[ms],302[ms] |
揺れがあったが(trueの最速レスポンスタイムに対して-6%~16%)、ほとんど差がないと言ってよいと思われる
また、falseの3回目は体感できるくらいに検索が高速化されていた
これはおそらくelasticsearchのインデックスが内部で書き換わってそう..
というわけでAWSのt3.microインスタンスでも動かすためにメモリ消費量をできるだけ抑えたい
今回はfalseで続行!
これが省リソース上でのelasticsearchのデプロイ周りで何かの助けになれば幸いです!
また,私が今回わからなかったこれをfalseにすることへのデメリットなどご存知のかたいらっしゃったらぜひコメントで教えてください!