EKS上でElasticsearchを稼働させていた時のこと。
Elasticsearch内でインデックスが多くなりすぎ、java.io.IOException: Too many open files
が発生しました。
File descriptor数が上限に達してしまっていることが原因だろう、と原因については割と早く当たりがつきました。
そこでnofile
を変更しようとなったのですが、ではどうやってnofile
を変更するか、というところで時間がかかってしまったので、うまくいった方法とうまくいかなかったことをまとめます。
環境(EKS)
Kubernetes バージョン: 1.12
プラットフォームのバージョン: 7
うまくいった方法
docker.service
が読み込む環境変数ファイルを編集する
参考: ulimit of nofile in Amazon ECS-optimized AMI
方針として、dockerコンテナのnofile
を変更するために、Workerノードで実行されるdocker daemonのdefault-ulimit
設定を変更することにします。
Workerノードの作成時に、以下の設定を追記してdaemonを再起動します。xxxxx
には、nofile
のsoft limit/hard limitとして適切な値を記載します。
OPTIONS="--default-ulimit nofile=xxxxx:xxxxx"
/etc/sysconfig/docker
は、docker.service
が起動する時にサービスファイル内で読み込まれる環境変数ファイルです。
systemd
によってdaemonが起動する時に、上記のオプション付きで起動され、コンテナを起動する際のデフォルトのnofile
を変更することができます。
実際に設定を投入する際には、CloudFormationテンプレートにて、UserData
にノード作成時の処理を記載すれば良いです。
設定投入後、サービスを再起動します。
Resources:
NodeLaunchConfig:
Properties:
UserData:
Fn::Base64: !Sub |
#!/bin/bash
echo 'OPTIONS="--default-ulimit nofile=xxxxx:xxxxx"' >> /etc/sysconfig/docker
systemctl restart docker.service
うまくいかなかった方法
以下、うまくいかなかった方法を列挙します。
なお、なぜうまくいかなかったのか、と言うところまでは調査しきれていないです(そのため、単なる記載誤りの可能性もあります)。
/etc/sysconfig/docker
ではなく、ExecStart
に直接オプションを追記する
参考: Can I set ulimit for containers in k8s?
同じくdefault-ulimit
を変更するために、docker.service
のサービスファイルを、以下のように編集して再起動してみました。
ExecStart=/usr/bin/dockerd --default-ulimit nofile=xxxxx:xxxxx
こちらの方法では、nofile
の値が反映されませんでした。
/etc/docker/daemon.json
に、default-ulimit
設定を追記する
参考: How do I set ulimit for containers in Kubernetes?
こちらもdefault-ulimit
を変更する方針。
docker daemonの設定ファイルに、default-ulimit
設定を記載してみましたが、こちらも正しく反映されませんでした。
{
...
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Soft": xxxxx,
"Hard": xxxxx
}
},
...
}
Kubernetesのマニフェスト中で、InitContainer
を使って設定する
参考: GKE でカーネルパラメータの設定
参考: How to set ulimit
using kubernetes?
時系列的には、一番最初に試した方法です。
InitContainer
でカーネルパラメータが設定できるなら、nofile
も設定できないかな、と思って試したものです。
nofile
の変更はプロセス単位で反映されます。そのため、この方法でnofile
を変更した場合、変更はInitContainer
として実行したコンテナにのみ反映され、Pod内で実際にワークロードを実行したいコンテナには引き継がれません(カーネルはホストと共有しているため、カーネルパラメータの変更は有効です)。
...
spec:
initContainers:
- image: alpine:3.6
command: ["sh", "-c", "ulimit", "-n", "xxxxx"]
name: init-container
securityContext:
privileged: true
...
まとめ
上記の通り、docker daemonに対してnofile
を設定するのが良さそうです。
が、何で/etc/sysconfig/docker
に書く方法だけうまくいくんだろう・・・
現状、コンテナごとにnofile
を変更する手立てはなく、Workerノードで稼働しているdocker daemonのデフォルト設定を変更することが必要です。
そのため、複数のアプリケーションでnofile
に上限を設けることが必要な場合は、最も大きな上限に合わせなければなりません。
(Kubernetesでも、コンテナごとに変更する機能はサポートしていないようです。こちらに言及があります。)
また、nofile
以外のリソース制限についても、同様の方法で変更することができそうです。