研究開発部 Architectグループ ML Platformチームのジャン(a.k.a jc)です。今回はKubernetes(EKS)におけるコスト按分のお話しをします。
背景
2022年に弊社研究開発部でAWS EKS上にCircuitというアプリケーション基盤を導入しました。
より多くのアプリケーションがCircuitを使うようになり、コストが増加してきました。 そのためNamespaceごとにコストを按分することで、アプリケーションの費用対効果を把握しておきたいと考えました。
ネットワークのコスト按分は難しいため、コスト割合の大きいCloudWatchとEC2を按分することにしました。今回はそれぞれの方法を紹介します。
CloudWatchのコスト按分
まずはCloudWatchのコスト按分から始めます。Namespaceごとにlog groupを作成し、AWSタグをつけることでログコストの按分を実現できます。
Circuitでは、Fluent Bitを使ってログをCloudWatchに連携しているため、Fluent Bitのプラグインcloudwatch
の引数log_group_name
を/eks/$(kubernetes['Namespace_name'])
に指定することで各Namespaceをログ専用のlog groupに送信できます。
[OUTPUT] Name cloudwatch Match application.* region ${AWS_REGION} log_group_name /eks/$(kubernetes['Namespace_name']) log_stream_name $(kubernetes['pod_name'])/$(kubernetes['container_name']) auto_create_group true extra_user_agent container-insights log_retention_days 90 new_log_group_tags Billing=infra
ただ現時点(2024/8/14)では変数を使ってタグをつけられない(=引数new_log_group_tags
は変数に対応していない)ため、
- アプリケーション関連Namespaceはlog groupsを事前にterraformを作っておき、タグをつける
- インフラ関連(=addonやkube-systemなど)のNamespaceはプラグイン
cloudwatch
でlog groupを自動作成し、Billing=infra
というタグをつける
注意点としては、プラグインcloudwatch_log
はcloudwatch
のリプレイスとはいえ、new_log_group_tags
の引数がないため、引数new_log_group_tags
を利用したい場合はcloudwatch
を使う必要があります。
Cloudwatch logs created with tags · Issue #365 · aws/amazon-cloudwatch-logs-for-fluent-bit · GitHub
最終的には、以下のConfigMapを作成して、AWS公式のFluent Bitのyamlに上書きした後、 Namespaceごとのlog groupにタグをつけられました。
apiVersion: v1 kind: ConfigMap metadata: name: fluent-bit-config data: application-log.conf: | [INPUT] Name tail Tag application.* Exclude_Path /var/log/containers/cloudwatch-agent*, /var/log/containers/fluent-bit*, /var/log/containers/aws-node*, /var/log/containers/kube-proxy* Path /var/log/containers/*.log multiline.parser docker, cri DB /var/fluent-bit/state/flb_container.db Mem_Buf_Limit 50MB Skip_Long_Lines On Refresh_Interval 10 Rotate_Wait 30 storage.type filesystem Read_from_Head ${READ_FROM_HEAD} [FILTER] Name kubernetes Match application.* Kube_URL https://kubernetes.default.svc:443 Kube_Tag_Prefix application.var.log.containers. Merge_Log On Merge_Log_Key log_processed K8S-Logging.Parser On K8S-Logging.Exclude Off Labels On Annotations On Use_Kubelet On Kubelet_Port 10250 Buffer_Size 0 [OUTPUT] Name cloudwatch Match application.* region ${AWS_REGION} log_group_name /eks/$(kubernetes['Namespace_name']) log_stream_name $(kubernetes['pod_name'])/$(kubernetes['container_name']) auto_create_group true extra_user_agent container-insights log_retention_days 90 new_log_group_tags Billing=infra
AWS Billing and Cost ManagementでCloudWatchのコストを確認すると、Namespaceごとにコストが分かれていることが確認できます。
EC2のコスト按分
EKS Split Cost Allocation
AWSが先日EKSのコストを按分するEKS Split Cost Allocationという機能をリリースしました。 調査した結果、EKS Split Cost Allocationのメリットとデメリットを以下にまとめました。
Pros
- EKS側の追加設定は不要
- 最小粒度podまでEC2のコスト按分ができる
Cons
- 按分結果を表示するためにBilling and Cost Managementの他に別途専用のダッシュボードを作成する必要がある
- また、このダッシュボード作成するのに、コストデータをS3に出力してAthenaでクエリを実行するという手間がかかる。それに伴うデータのエキスポートとクエリの実行というデータパイプラインの実装・運用が必要になる
KarpenterのNodeClassによる按分
EC2 nodeを手動管理するのは大変なので、CircuitではKarpenterというオープンソースのnodeスケジューラを利用しています。Karpenterの詳細はこちらを参照してください。
KarpenterのNodeClassesにタグをつけると、EC2関連のリソース(EC2 Instances, EBS volumesとLaunch Templates)にAWSタグがつくようになります。 また、NodePoolsで設定したlabelはEC2 nodeに反映されますが、AWSタグをつけるオプションがないため、NamespaceごとにNodeClassを作成します。この方法のメリットとデメリットを以下にまとめました。
Pros
- NamespaceごとにKapenterのNodeClassを作れば、EC2のコスト按分ができる
- 新しくデータパイプラインやダッシュボードを作成・運用する必要がない
Cons
- DeploymentにnodeSelectorやtolerationsなどでNodeを指定しておく必要がある
- 異なるNamespaceのPodが同じNodeにスケジューリングされないので、Namespace間のnodeの相乗り効果を享受できなくなる。コストを抑えるためにはより綿密にリソースを調整する必要がある
データパイプラインとダッシュボードの運用・管理コストを考慮すると、KarpenterのNodeClassによる按分が現状では最適な方法だと考えています。
CloudWatchのコスト按分と同様に、AWS Billing and Cost Managementのコストを確認すると、Namespaceごとにコストが分かれていることが確認できます。
まとめ
AWSタグを使ったKubernetes(EKS)のEC2とCloudWatchコスト按分方法を紹介しました。ご参考になれば幸いです。
より明確的にアプリケーションごとの費用対効果を把握するためには、アプリケーションごとにコスト按分が必要です。Log groupの粒度をさらに細くすれば良いですが、EC2のコスト按分は難しいです。そのため、今後はserviceごとに按分できるOpencostの導入を検討しています。
研究開発部ではMLOps/DevOpsエンジニア・プラットフォームエンジニアを募集しています。