メモ：EB/ECSのデプロイ失敗がfstrimで解消した件

この記事は
===

* AWSのElastic BeanstalkのマルチコンテナDocker(ECS)環境で、デプロイに失敗する事象が継続的に起きていて、色々調べて解消できたのでその記録です

結局なんだったの？
===

* 一言でいえばディスクフルによる異常動作です
* なぜディスクフルになっていたかというと、コンテナ上で削除されたブロックが解放されない事象が起きていました
* これを、fstrimコマンドで強制解放させたら解決しました

事象＆原因
===

eb deployに失敗
---

* まずeb deployするとリリースに失敗していました
* で、下記のログが出ていました
    * どうもローリングアップデートのBatch１は成功しているようなのですが、2でこけているようです
    * 書いてある通り、ELBからのヘルスチェックに返答ができず、デプロイに失敗しているようです

```
INFO    Command execution completed on 2 of 3 instances in environment.
INFO    Batch 2: Registering instance(s) with the load balancer and waiting for them to be healthy.
ERROR   Instance id(s) 'i-xxxxxxxxxxxxxxxx' did not pass health check after command execution. Aborting the operation.
ERROR   Failed to deploy application.        
```

* 恐ろしいことにこのあと、Batch2側は異常な状態になり、dockerコンテナが全滅。どうもdocker daemonが死んでいるような状態になるという事象まで起きていました
* この事象、ちょくちょくリリースしても発生せず、久しぶりに触ると発生するという厄介なものでした（本気出そうとすると再現できなくなる）

docker daemonが異常終了
---

* 発生条件がよくわからず、AWSのサポート（神様）にお願いしたところ、返答が返ってきました
* なぜヘルスチェックに返答ができなかったかというと、デプロイ時にdocker daemonから異常終了していたようなログが出ていたのでこれが理由っぽいとのこと

```
level=info msg="ignoring event" module=libcontainerd namespace=moby topic=/tasks/delete type="*events.TaskDelete"
panic: send on closed channel
```

Thin poolが枯渇
---

* んで、なぜdocker daemonが異常終了していたかというと、messagesに出ているログから、Thin poolが枯渇しかけていたことが理由なのではないかと推測できるとのこと
* Thin poolっていうのは、昔からRHELで使われているファイル管理機構であるDevice mapperの機能で、いわゆるThin provisioning（仮装的なディスク容量）を実現するときに、共通的な領域に物理的ボリュームをプール的に持っておく機構のこと
    * 正直クラウドの時代にDevice mapperとかいう単語みたくなかった・・・
    * これ共有ディスク構成の多重化DBサーバ構築とかで出てくるやつだよね・・・
* 要はディスクがいっぱいになってるよ！ってことでした
    * たしかに久しぶりにリリースしたときに再現するっていうのとも符号があっていますね

```
lvm[2803]: WARNING: Thin pool docker-docker--pool data is now 80.00% full.
lvm[2803]: WARNING: Thin pool docker-docker--pool data is now 85.34% full.
lvm[2803]: WARNING: Thin pool docker-docker--pool data is now 90.12% full.
lvm[2803]: WARNING: Thin pool docker-docker--pool data is now 95.29% full.
```

なぜ枯渇するのか
---

* で、ディスクフルなんてどういうこと？と思って容量の調査を開始
* Thin poolの容量はdocker infoで調べられるのですが、EBのAMIでは12GBというかなり小さな容量しか確保されていないので、ちょっと何かあるとすぐ枯渇しちゃうのは納得
    * 実際にマネコンからEC2の容量を見ても12GBくらいしかなかった
    * ちなみにdfとかで容量見ても、Thin provisioningのせいで本当の容量が出てこない
        * 100GBのボリュームがたくさん出てくる
        * 物理ボリュームはそんなにない
* なので、おそらくコンテナ内にちょっとした一時ゴミファイルなんかが残ってちりつもになってるのが原因なのでは？という結論に達しました。

ゴミファイル探し
---

* しかしいくら探せど、そんな余計なゴミファイルが見つからず
* /tmpの下とか、ウイルス定義ファイルとかが怪しかったのですが、大した容量でもなく・・・

fstrimで解決
---

* 途方にくれていたのですが、色々情報を見ていたところ、[こちら](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-ami-storage-config.html)に情報が
* どうしても容量が減らない時はこのコマンドを打ってみてね、みたいなことが書いてある

```
sudo sh -c "docker ps -q | xargs docker inspect --format='{{ .State.Pid }}' | xargs -IZ fstrim /proc/Z/root/"
```
* ごちゃごちゃ書いてあるのですが、最後はfstrimというコマンドが動いてます
    * fstrimは本来はSSDなんかで論理削除されただけのファイルを完全消去状態に移行させるためのコマンドなんだそうです
    * どうもこのコマンドを叩かないと、削除済みのブロックがThin poolに返却されないということのようです
* これを実行してみたところ、ごそっと容量が削減されました(使用済みが10GB超 => 5GB程度に！）


cron化
---

* ということで、上記のコマンドをcron実行できるように、ebextensionで適当に対応しました
* 以降、デプロイ失敗は発生していません

終わりに
---

* 色々調べてみると、どうもfstrimしないとThin poolにブロックが返却されないのは、[そういうものだという情報](https://github.com/moby/moby/issues/18867)も出てきたりして、それなら初めからEBのAMIに組み込んでおいてほしいなーと思いました
    * 最初からcronにfstrimいれとくとか
    * マウントするときに```--discard```オプションをつけて自動で解放するとか
* EBとかいう地獄プロダクトじゃないとしても、EC2ベースのECSならどこでも起こる問題な気がするなー
* いやそもそもfargate使えよという神の声がきこえてきたので終わります

参考
===

* [Dockerが使用していないディスク領域をdockerイメージが消費するのはなぜですか](https://www.it-swarm.dev/ja/docker/docker%E3%81%8C%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E9%A0%98%E5%9F%9F%E3%82%92docker%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%8C%E6%B6%88%E8%B2%BB%E3%81%99%E3%82%8B%E3%81%AE%E3%81%AF%E3%81%AA%E3%81%9C%E3%81%A7%E3%81%99%E3%81%8B/1049937707/)